Compare commits
12 Commits
fix/monito
...
2.7.0-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9da1d7ab0a | ||
|
|
78bcd7568b | ||
|
|
429312500a | ||
|
|
59e0bf7565 | ||
|
|
ab51370d5a | ||
|
|
9dfb0510c4 | ||
|
|
f65e8f9244 | ||
|
|
5c8b595382 | ||
|
|
71bf9d5583 | ||
|
|
329ded4beb | ||
|
|
dc70247e2c | ||
|
|
f95beee5dc |
69
.github/workflows/main.yml
vendored
69
.github/workflows/main.yml
vendored
@@ -61,31 +61,6 @@ jobs:
|
||||
key: ${{ needs.initialize.outputs.cache_key }}
|
||||
- name: yarn run ${{ matrix.task }}
|
||||
run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
|
||||
ui-coverage:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- initialize
|
||||
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/cache@v2
|
||||
id: cache-repo
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
contracts/build
|
||||
key: ${{ needs.initialize.outputs.cache_key }}
|
||||
- name: yarn workspace ui run coverage
|
||||
run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn workspace ui run coverage
|
||||
- uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
path-to-lcov: ./ui/coverage/lcov.info
|
||||
build-e2e-images:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -98,7 +73,6 @@ jobs:
|
||||
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
|
||||
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
|
||||
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
|
||||
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
|
||||
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
|
||||
- name: Rebuild and push updated images
|
||||
run: |
|
||||
@@ -109,7 +83,6 @@ jobs:
|
||||
if ! check_if_image_exists e2e ${E2E_TAG}; then updated+=("e2e"); fi
|
||||
if ! check_if_image_exists oracle ${ORACLE_TAG}; then updated+=("oracle"); fi
|
||||
if ! check_if_image_exists monitor ${MONITOR_TAG}; then updated+=("monitor"); fi
|
||||
if ! check_if_image_exists ui ${UI_TAG}; then updated+=("ui"); fi
|
||||
if ! check_if_image_exists alm ${ALM_TAG}; then updated+=("alm"); fi
|
||||
if [ ${#updated[@]} -gt 0 ]; then
|
||||
echo "Updated services: ${updated[@]}"
|
||||
@@ -149,12 +122,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
task: [oracle-e2e, monitor-e2e, alm-e2e, 'ui-e2e:ci']
|
||||
task: [oracle-e2e, monitor-e2e, alm-e2e]
|
||||
include:
|
||||
- task: alm-e2e
|
||||
use-cache: true
|
||||
- task: 'ui-e2e:ci'
|
||||
use-cache: true
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -165,7 +136,6 @@ jobs:
|
||||
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
|
||||
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
|
||||
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
|
||||
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
|
||||
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
|
||||
- if: ${{ matrix.use-cache }}
|
||||
uses: actions/cache@v2
|
||||
@@ -187,7 +157,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
task: [oracle, ui, monitor, multiple, repo]
|
||||
task: [oracle, monitor, multiple, repo]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -206,20 +176,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
task: [amb, erc-to-erc, erc-to-native, native-to-erc, amb-stake-erc-to-erc]
|
||||
include:
|
||||
- task: erc-to-erc
|
||||
ui-e2e-grep: 'ERC TO ERC'
|
||||
ui-config: 'e2e-commons/components-envs/ui-erc20.env'
|
||||
- task: erc-to-native
|
||||
ui-e2e-grep: 'ERC TO NATIVE'
|
||||
ui-config: 'e2e-commons/components-envs/ui-erc20-native.env'
|
||||
- task: native-to-erc
|
||||
ui-e2e-grep: 'NATIVE TO ERC'
|
||||
ui-config: 'e2e-commons/components-envs/ui.env'
|
||||
- task: amb-stake-erc-to-erc
|
||||
ui-e2e-grep: 'AMB-STAKE-ERC-TO-ERC'
|
||||
ui-config: 'e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env'
|
||||
task: [amb, erc-to-erc, erc-to-native, native-to-erc]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -230,7 +187,6 @@ jobs:
|
||||
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
|
||||
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
|
||||
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
|
||||
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
|
||||
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
|
||||
echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v2
|
||||
@@ -248,24 +204,9 @@ jobs:
|
||||
run: |
|
||||
docker-compose -f ./e2e-commons/docker-compose.yml pull oracle
|
||||
docker tag ${DOCKER_IMAGE_BASE}/tokenbridge-e2e-oracle:${ORACLE_TAG} poanetwork/tokenbridge-oracle:latest
|
||||
- if: ${{ matrix.ui-e2e-grep }}
|
||||
name: Pull e2e ui image
|
||||
run: |
|
||||
docker-compose -f ./e2e-commons/docker-compose.yml pull ui
|
||||
docker build \
|
||||
--build-arg DOCKER_IMAGE_BASE=${DOCKER_IMAGE_BASE} \
|
||||
--build-arg UI_TAG=${UI_TAG} \
|
||||
--build-arg DOT_ENV_PATH=${{ matrix.ui-config }} \
|
||||
-f ./e2e-commons/Dockerfile.ui \
|
||||
-t ui_ui:latest \
|
||||
.
|
||||
- name: Deploy oracle and ui
|
||||
- name: Deploy oracle
|
||||
run: deployment-e2e/molecule.sh ultimate-${{ matrix.task }}
|
||||
- name: Reset docker socket permissions
|
||||
run: sudo chown -R $USER:docker /var/run/docker.sock
|
||||
- name: Run ui e2e tests
|
||||
if: ${{ matrix.ui-e2e-grep }}
|
||||
run: cd ui-e2e && xvfb-run yarn mocha -g "${{ matrix.ui-e2e-grep }}" -b ./test.js
|
||||
- name: Run oracle e2e tests
|
||||
if: ${{ !matrix.ui-e2e-grep }}
|
||||
run: docker-compose -f ./e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run ${{ matrix.task }}
|
||||
run: docker-compose -f ./e2e-commons/docker-compose.yml run -e ULTIMATE=true e2e yarn workspace oracle-e2e run ${{ matrix.task }}
|
||||
|
||||
@@ -44,29 +44,13 @@ ORACLE_ALWAYS_RELAY_SIGNATURES | If set to `true`, the oracle will always relay
|
||||
ORACLE_RPC_REQUEST_TIMEOUT | Timeout in milliseconds for a single RPC request. Default value is `ORACLE_*_RPC_POLLING_INTERVAL * 2`. | integer
|
||||
ORACLE_HOME_TX_RESEND_INTERVAL | Interval in milliseconds for automatic resending of stuck transactions for Home sender service. Defaults to 20 minutes. | integer
|
||||
ORACLE_FOREIGN_TX_RESEND_INTERVAL | Interval in milliseconds for automatic resending of stuck transactions for Foreign sender service. Defaults to 20 minutes. | integer
|
||||
|
||||
|
||||
## UI configuration
|
||||
|
||||
name | description | value
|
||||
--- | --- | ---
|
||||
UI_TITLE | The title for the bridge UI page. `%c` will be replaced by the name of the network. | string
|
||||
UI_OG_TITLE | The meta title for the deployed bridge page. | string
|
||||
UI_DESCRIPTION | The meta description for the deployed bridge page. | string
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME | name of the home native coin | string
|
||||
UI_HOME_NETWORK_DISPLAY_NAME | name to be displayed for home network | string
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME | name to be displayed for foreign network | string
|
||||
UI_HOME_WITHOUT_EVENTS | `true` if home network doesn't support events | true/false
|
||||
UI_FOREIGN_WITHOUT_EVENTS | `true` if foreign network doesn't support events | true/false
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE | template link to transaction on home explorer. `%s` will be replaced by transaction hash | URL template
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE | template link to transaction on foreign explorer. `%s` will be replaced by transaction hash | URL template
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE | template link to address on home explorer. `%s` will be replaced by address | URL template
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE | template link to address on foreign explorer. `%s` will be replaced by address | URL template
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Home Bridge contract. | integer
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Foreign Bridge contract. | integer
|
||||
UI_PORT | The port for the UI app. | integer
|
||||
UI_STYLES | The set of styles to render the bridge UI page. | core/classic/stake
|
||||
UI_PUBLIC_URL | The public url for the deployed bridge page | string
|
||||
ORACLE_SHUTDOWN_SERVICE_URL | Optional external URL to some other service/monitor/configuration manager that controls the remote shutdown process. GET request should return `application/json` message with the following schema: `{ shutdown: true/false }`. | URL
|
||||
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | Optional interval in milliseconds used to request the side RPC node or external shutdown service. Default is 120000. | integer
|
||||
ORACLE_SIDE_RPC_URL | Optional HTTPS URL(s) for communication with the external shutdown service or side RPC nodes, used for shutdown manager activities. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. | URL(s)
|
||||
ORACLE_SHUTDOWN_CONTRACT_ADDRESS | Optional contract address in the side chain accessible through `ORACLE_SIDE_RPC_URL`, where the method passed in `ORACLE_SHUTDOWN_CONTRACT_METHOD` is implemented. | `address`
|
||||
ORACLE_SHUTDOWN_CONTRACT_METHOD | Method signature to be used in the side chain to identify the current shutdown status. Method should return boolean. Default value is `isShutdown()`. | `function signature`
|
||||
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Foreign chain. Infinite, if not provided. | `integer`
|
||||
ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Home chain. Infinite, if not provided. | `integer`
|
||||
|
||||
|
||||
## Monitor configuration
|
||||
@@ -86,5 +70,3 @@ MONITOR_HOME_TO_FOREIGN_BLOCK_LIST | File with a list of addresses, separated by
|
||||
MONITOR_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`
|
||||
MONITOR_HOME_VALIDATORS_BALANCE_ENABLE | If set, defines the list of home validator addresses for which balance should be checked. | `string`
|
||||
MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE | If set, defines the list of foreign validator addresses for which balance should be checked. | `string`
|
||||
MONITOR_HOME_EXPLORER_API | The HTTPS URL of the Home network explorer API. If set, may be used as a fallback in case of the RPC node failure. | URL
|
||||
MONITOR_FOREIGN_EXPLORER_API | The HTTPS URL of the Foreign network explorer API. If set, may be used as a fallback in case of the RPC node failure. | URL
|
||||
@@ -20,12 +20,10 @@ Sub-repositories maintained within this monorepo are listed below.
|
||||
| Sub-repository | Description |
|
||||
| --- | --- |
|
||||
| [Oracle](oracle/README.md) | Oracle responsible for listening to bridge related events and authorizing asset transfers. |
|
||||
| [UI](ui/README.md) | DApp interface to transfer tokens and coins between chains. |
|
||||
| [Monitor](monitor/README.md) | Tool for checking balances and unprocessed events in bridged networks. |
|
||||
| [Deployment](deployment/README.md) | Ansible playbooks for deploying cross-chain bridges. |
|
||||
| [Oracle-E2E](oracle-e2e/README.md) | End to end tests for the Oracle |
|
||||
| [Monitor-E2E](monitor-e2e/README.md) | End to end tests for the Monitor |
|
||||
| [UI-E2E](ui-e2e/README.md) | End to end tests for the UI |
|
||||
| [Deployment-E2E](deployment-e2e/README.md) | End to end tests for the Deployment |
|
||||
| [Commons](commons/README.md) | Interfaces, constants and utilities shared between the sub-repositories |
|
||||
| [E2E-Commons](e2e-commons/README.md) | Common utilities and configuration used in end to end tests |
|
||||
@@ -68,7 +66,7 @@ Clone the repository:
|
||||
git clone https://github.com/poanetwork/tokenbridge
|
||||
```
|
||||
|
||||
If there is no need to build docker images for the TokenBridge components (oracle, monitor, UI), initialize submodules, install dependencies, compile the Smart Contracts:
|
||||
If there is no need to build docker images for the TokenBridge components (oracle, monitor), initialize submodules, install dependencies, compile the Smart Contracts:
|
||||
```
|
||||
yarn initialize
|
||||
```
|
||||
@@ -91,7 +89,7 @@ Running tests for all projects:
|
||||
yarn test
|
||||
```
|
||||
|
||||
Additionally there are end-to-end tests for [Oracle](oracle-e2e/README.md) and [UI](ui-e2e/README.md).
|
||||
Additionally there are end-to-end tests for [Oracle](oracle-e2e/README.md) and [Monitor](monitor-e2e/README.md).
|
||||
|
||||
For details on building, running and developing please refer to respective READMEs in sub-repositories.
|
||||
|
||||
|
||||
Binary file not shown.
@@ -56,11 +56,6 @@ const OLD_AMB_USER_REQUEST_FOR_SIGNATURE_ABI = [
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'messageId',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'encodedData',
|
||||
@@ -76,11 +71,6 @@ const OLD_AMB_USER_REQUEST_FOR_AFFIRMATION_ABI = [
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'messageId',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'encodedData',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { toWei, toBN } = require('web3-utils')
|
||||
const { toWei, toBN, BN } = require('web3-utils')
|
||||
const { GasPriceOracle } = require('gas-price-oracle')
|
||||
const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants')
|
||||
const { REWARDABLE_VALIDATORS_ABI } = require('./abis')
|
||||
@@ -150,11 +150,8 @@ const tryCall = async (method, fallbackValue) => {
|
||||
|
||||
const getDeployedAtBlock = async contract => tryCall(contract.methods.deployedAtBlock(), 0)
|
||||
|
||||
const getPastEvents = async (
|
||||
contract,
|
||||
{ event = 'allEvents', fromBlock = toBN(0), toBlock = 'latest', options = {} }
|
||||
) => {
|
||||
let events
|
||||
const getPastEventsOrSplit = async (contract, { event, fromBlock, toBlock, options }) => {
|
||||
let events = []
|
||||
try {
|
||||
events = await contract.getPastEvents(event, {
|
||||
...options,
|
||||
@@ -162,19 +159,19 @@ const getPastEvents = async (
|
||||
toBlock
|
||||
})
|
||||
} catch (e) {
|
||||
if (e.message.includes('query returned more than') && toBlock !== 'latest') {
|
||||
if (e.message.includes('query returned more than') || e.message.toLowerCase().includes('timeout')) {
|
||||
const middle = toBN(fromBlock)
|
||||
.add(toBN(toBlock))
|
||||
.divRound(toBN(2))
|
||||
const middlePlusOne = middle.add(toBN(1))
|
||||
|
||||
const firstHalfEvents = await getPastEvents(contract, {
|
||||
const firstHalfEvents = await getPastEventsOrSplit(contract, {
|
||||
options,
|
||||
event,
|
||||
fromBlock,
|
||||
toBlock: middle
|
||||
})
|
||||
const secondHalfEvents = await getPastEvents(contract, {
|
||||
const secondHalfEvents = await getPastEventsOrSplit(contract, {
|
||||
options,
|
||||
event,
|
||||
fromBlock: middlePlusOne,
|
||||
@@ -188,6 +185,31 @@ const getPastEvents = async (
|
||||
return events
|
||||
}
|
||||
|
||||
const getPastEvents = async (
|
||||
contract,
|
||||
{ event = 'allEvents', fromBlock = toBN(0), toBlock = 'latest', options = {} }
|
||||
) => {
|
||||
if (toBlock === 'latest') {
|
||||
return contract.getPastEvents(event, {
|
||||
...options,
|
||||
fromBlock,
|
||||
toBlock
|
||||
})
|
||||
}
|
||||
|
||||
const batchSize = 1000000
|
||||
const to = toBN(toBlock)
|
||||
const events = []
|
||||
|
||||
for (let from = toBN(fromBlock); from.lte(to); from = from.addn(batchSize + 1)) {
|
||||
const opts = { event, fromBlock: from, toBlock: BN.min(to, from.addn(batchSize)), options }
|
||||
const batch = await getPastEventsOrSplit(contract, opts)
|
||||
events.push(batch)
|
||||
}
|
||||
|
||||
return [].concat(...events)
|
||||
}
|
||||
|
||||
const getValidatorList = async (address, eth, options) => {
|
||||
options.logger && options.logger.debug && options.logger.debug('getting validatorList')
|
||||
|
||||
|
||||
Submodule contracts updated: 835742dfd8...c9377114f7
@@ -16,7 +16,7 @@ Alternatively, if there are no changes except the playbooks, you can use the `ma
|
||||
./molecule.sh <scenario_name>
|
||||
```
|
||||
|
||||
In this case `master` branch will be used as a codebase for Monitor, UI, Oracle and Contracts deployed by your local playbook.
|
||||
In this case `master` branch will be used as a codebase for Monitor, Oracle and Contracts deployed by your local playbook.
|
||||
|
||||
## Run the tests
|
||||
|
||||
@@ -29,7 +29,6 @@ Available scenarios:
|
||||
Scenario | Description
|
||||
--- | ---
|
||||
oracle | Deploys and checks standalone Oracle on Ubuntu host
|
||||
ui | Deploys and checks standalone UI on Ubuntu host
|
||||
|
||||
## Ultimate E2E tests
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ platforms:
|
||||
children:
|
||||
- oracle
|
||||
- monitor
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
|
||||
@@ -8,7 +8,6 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
|
||||
@pytest.mark.parametrize("service", [
|
||||
("poabridge"),
|
||||
("tokenbridge-ui"),
|
||||
("tokenbridge-monitor")
|
||||
])
|
||||
def test_services(host, service):
|
||||
@@ -24,7 +23,7 @@ def test_services(host, service):
|
||||
("oracle_bridge_affirmation_1"),
|
||||
("oracle_bridge_senderhome_1"),
|
||||
("oracle_bridge_senderforeign_1"),
|
||||
("ui_ui_1"),
|
||||
("oracle_bridge_shutdown_1"),
|
||||
("monitor_monitor_1")
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
|
||||
@@ -14,6 +14,7 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
("oracle_bridge_affirmation_1"),
|
||||
("oracle_bridge_senderhome_1"),
|
||||
("oracle_bridge_senderforeign_1"),
|
||||
("oracle_bridge_shutdown_1"),
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
container = host.docker(name)
|
||||
|
||||
@@ -15,7 +15,6 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
("/home/poadocker/bridge/contracts"),
|
||||
("/home/poadocker/bridge/oracle"),
|
||||
("/home/poadocker/bridge/monitor"),
|
||||
("/home/poadocker/bridge/ui"),
|
||||
("/home/poadocker/bridge/parity")
|
||||
])
|
||||
def test_existing_folders(host, path):
|
||||
@@ -28,8 +27,7 @@ def test_existing_folders(host, path):
|
||||
("/home/poadocker/bridge/commons/package.json"),
|
||||
("/home/poadocker/bridge/contracts/package.json"),
|
||||
("/home/poadocker/bridge/oracle/package.json"),
|
||||
("/home/poadocker/bridge/monitor/package.json"),
|
||||
("/home/poadocker/bridge/ui/package.json")
|
||||
("/home/poadocker/bridge/monitor/package.json")
|
||||
])
|
||||
def test_existing_package_json(host, path):
|
||||
assert host.file(path).exists
|
||||
@@ -41,8 +39,7 @@ def test_existing_package_json(host, path):
|
||||
("/home/poadocker/bridge/contracts/Dockerfile"),
|
||||
("/home/poadocker/bridge/parity/Dockerfile"),
|
||||
("/home/poadocker/bridge/oracle/Dockerfile"),
|
||||
("/home/poadocker/bridge/monitor/Dockerfile"),
|
||||
("/home/poadocker/bridge/ui/Dockerfile")
|
||||
("/home/poadocker/bridge/monitor/Dockerfile")
|
||||
])
|
||||
def test_existing_docker_files(host, path):
|
||||
assert host.file(path).exists
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: ui-host
|
||||
groups:
|
||||
- example
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
lint:
|
||||
name: ansible-lint
|
||||
enabled: True
|
||||
options:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../../../deployment/site.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
ui-host:
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: ui
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
- destroy
|
||||
@@ -1,48 +0,0 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('ui')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", [
|
||||
("ui_ui_1")
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
container = host.docker(name)
|
||||
assert container.is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("service", [
|
||||
("tokenbridge-ui"),
|
||||
("rsyslog")
|
||||
])
|
||||
def test_services(host, service):
|
||||
assert host.service(service).is_enabled
|
||||
assert host.service(service).is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("filename", [
|
||||
("/etc/rsyslog.d/32-ui-docker.conf"),
|
||||
("/etc/rsyslog.d/37-ui-remote-logging.conf")
|
||||
])
|
||||
def test_logging(host, filename):
|
||||
assert host.file(filename).exists
|
||||
assert host.file(filename).mode == 0o0644
|
||||
|
||||
|
||||
def test_index_page_title(host):
|
||||
assert host.run_test(
|
||||
'curl -s http://localhost:3001 | '
|
||||
'grep "<title>" | '
|
||||
'grep -q "TokenBridge UI app"'
|
||||
)
|
||||
|
||||
|
||||
def test_index_page_error(host):
|
||||
assert host.run_expect(
|
||||
[1],
|
||||
'curl -s http://localhost:3001 | '
|
||||
'grep -i -q "error"'
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: oracle-amb-host
|
||||
groups:
|
||||
- ultimate
|
||||
- amb
|
||||
children:
|
||||
- oracle
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- name: ui-amb-stake-erc-to-erc-host
|
||||
groups:
|
||||
- ultimate
|
||||
- amb-stake-erc-to-erc
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../ultimate-commons/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-amb-host:
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ui-amb-stake-erc-to-erc-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-amb-stake-erc-to-erc
|
||||
test_sequence:
|
||||
- cleanup
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
@@ -18,5 +18,6 @@
|
||||
- oracle_net_rabbit_bridge_transfer
|
||||
- oracle_net_rabbit_bridge_senderhome
|
||||
- oracle_net_rabbit_bridge_senderforeign
|
||||
- oracle_net_rabbit_bridge_convert_to_chai_worker
|
||||
delegate_to: 127.0.0.1
|
||||
become: false
|
||||
|
||||
@@ -13,17 +13,6 @@ platforms:
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- name: ui-erc-to-erc-host
|
||||
groups:
|
||||
- ultimate
|
||||
- erc-to-erc
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
@@ -34,9 +23,6 @@ provisioner:
|
||||
oracle-erc-to-erc-host:
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ui-erc-to-erc-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
|
||||
@@ -13,17 +13,6 @@ platforms:
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- name: ui-erc-to-native-host
|
||||
groups:
|
||||
- ultimate
|
||||
- erc-to-native
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
@@ -36,9 +25,6 @@ provisioner:
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ORACLE_HOME_START_BLOCK: 1
|
||||
ORACLE_FOREIGN_START_BLOCK: 1
|
||||
ui-erc-to-native-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
|
||||
@@ -13,17 +13,6 @@ platforms:
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- name: ui-native-to-erc-host
|
||||
groups:
|
||||
- ultimate
|
||||
- native-to-erc
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
@@ -34,9 +23,6 @@ provisioner:
|
||||
oracle-native-to-erc-host:
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ui-native-to-erc-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
|
||||
@@ -34,14 +34,6 @@ cp hosts.yml.example hosts.yml
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>" # When this parameter is set all bridge logs will be redirected to <ip>:<port> address.
|
||||
<host_ip_B>:
|
||||
# (...)
|
||||
ui:
|
||||
hosts:
|
||||
<host_ip_B>:
|
||||
ansible_user: <user>
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>"
|
||||
<host_ip_C>:
|
||||
ansible_user: <user>
|
||||
#syslog_server_port: "<protocol>://<ip>:<port>"
|
||||
monitor:
|
||||
hosts:
|
||||
<host_ip_B>:
|
||||
@@ -50,18 +42,7 @@ cp hosts.yml.example hosts.yml
|
||||
#monitor_cron_schedule: "*/4 * * * *" # When this parameter is set, it will overwrite default schedule for performing checks
|
||||
```
|
||||
|
||||
The config above would install the Oracle on `<host_ip_A>`, UI on `<host_ip_C>`, and both Oracle, UI and Monitor on `<host_ip_B>`.
|
||||
|
||||
Example config for installing only UI:
|
||||
```yaml
|
||||
<bridge_name>:
|
||||
children:
|
||||
oracle:
|
||||
hosts:
|
||||
ui:
|
||||
hosts:
|
||||
<host_ip>:
|
||||
ansible_user: <user>
|
||||
The config above would install the Oracle on `<host_ip_A>`, and both Oracle and Monitor on `<host_ip_B>`.
|
||||
```
|
||||
|
||||
| Value | Description |
|
||||
|
||||
@@ -44,7 +44,6 @@ The deployed components have the following services:
|
||||
Component | Service Name
|
||||
--- | ---
|
||||
Oracle | poabridge
|
||||
UI | tokenbridge-ui
|
||||
Monitor | tokenbridge-monitor
|
||||
|
||||
Use the default `SysVinit` commands to `start`, `stop`, `restart`, and `rebuild` the service and to check the `status` of the service.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC"
|
||||
UI_PORT: 3003
|
||||
@@ -1,19 +1,14 @@
|
||||
---
|
||||
## General settings
|
||||
ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE"
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME: "xDai"
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "https://dai.poa.network"
|
||||
UI_HOME_NETWORK_DISPLAY_NAME: "xDai chain"
|
||||
UI_HOME_WITHOUT_EVENTS: false
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io"
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Ethereum Mainnet"
|
||||
UI_FOREIGN_WITHOUT_EVENTS: false
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016"
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
@@ -30,20 +25,6 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_OG_TITLE: "POA Bridge UI"
|
||||
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring MakerDAO stable tokens between the Ethereum network to xDai chain in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/dai/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/mainnet/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/dai/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_STYLES: "core"
|
||||
UI_PUBLIC_URL: "https://dai-bridge.poa.network"
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "xdai"
|
||||
MONITOR_PORT: 3003
|
||||
|
||||
@@ -2,5 +2,4 @@
|
||||
ORACLE_BRIDGE_MODE: "ERC_TO_ERC"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x1feB40aD9420b186F019A717c37f5546165d411E"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127"
|
||||
UI_PORT: 3001
|
||||
MONITOR_PORT: 3011
|
||||
|
||||
@@ -2,5 +2,4 @@
|
||||
ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x488Af810997eD1730cB3a3918cD83b3216E6eAda"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x488Af810997eD1730cB3a3918cD83b3216E6eAda"
|
||||
UI_PORT: 3002
|
||||
MONITOR_PORT: 3012
|
||||
|
||||
@@ -2,19 +2,14 @@
|
||||
## General settings
|
||||
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
|
||||
ORACLE_LOG_LEVEL: debug
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME: "POA"
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "https://sokol.poa.network"
|
||||
UI_HOME_NETWORK_DISPLAY_NAME: "POA Sokol"
|
||||
UI_HOME_WITHOUT_EVENTS: false
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x98aFdE294f1C46aA0a27Cc4049ED337F879d8976"
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "https://sokol.poa.network"
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Kovan"
|
||||
UI_FOREIGN_WITHOUT_EVENTS: false
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x5a584f4C30B36f282848dAc9a2b20E7BEF481981"
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
|
||||
|
||||
@@ -32,20 +27,6 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_OG_TITLE: "POA Bridge UI"
|
||||
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_STYLES: "core"
|
||||
UI_PUBLIC_URL: "http://localhost:3001"
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
MONITOR_PORT: 3003
|
||||
|
||||
@@ -2,5 +2,4 @@
|
||||
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x32198D570fffC7033641F8A9094FFDCaAEF42624"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x2B6871b9B02F73fa24F4864322CdC78604207769"
|
||||
UI_PORT: 3000
|
||||
MONITOR_PORT: 3010
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
---
|
||||
## General settings
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME: "POA"
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC: yes
|
||||
ORACLE_LOG_LEVEL: debug
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "http://parity1:8545"
|
||||
UI_HOME_NETWORK_DISPLAY_NAME: "POA Sokol"
|
||||
UI_HOME_WITHOUT_EVENTS: false
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Kovan"
|
||||
UI_FOREIGN_WITHOUT_EVENTS: false
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
|
||||
|
||||
## Home Gasprice
|
||||
@@ -30,20 +25,6 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
|
||||
#ui
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_OG_TITLE: "POA Bridge UI"
|
||||
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_STYLES: "core"
|
||||
UI_PUBLIC_URL: "http://localhost"
|
||||
|
||||
#monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
MONITOR_CACHE_EVENTS: "true"
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
---
|
||||
## General settings
|
||||
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME: "ETC"
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "https://www.ethercluster.com/etc"
|
||||
UI_HOME_NETWORK_DISPLAY_NAME: "Ethereum Classic"
|
||||
UI_HOME_WITHOUT_EVENTS: false
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 7000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io/"
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Ethereum Mainnet"
|
||||
UI_FOREIGN_WITHOUT_EVENTS: false
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x0cB781EE62F815bdD9CD4c2210aE8600d43e7040"
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 7000
|
||||
|
||||
@@ -31,20 +26,6 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000 # in wei
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_OG_TITLE: "POA Bridge UI"
|
||||
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring native tokens from the Ethereum Classic Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/etc/mainnet/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/mainnet/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/etc/mainnet/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_STYLES: "classic"
|
||||
UI_PUBLIC_URL: "https://wetc.app"
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "wetc"
|
||||
MONITOR_PORT: 3003
|
||||
|
||||
@@ -7,11 +7,6 @@ sokol-kovan:
|
||||
ansible_user: ubuntu
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
#syslog_server_port: "udp://127.0.0.1:514"
|
||||
ui:
|
||||
hosts:
|
||||
127.0.0.1:
|
||||
ansible_user: ubuntu
|
||||
#syslog_server_port: "udp://127.0.0.1:514"
|
||||
monitor:
|
||||
hosts:
|
||||
127.0.0.1:
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
dependencies:
|
||||
- role: common
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
- name: Build the containers
|
||||
shell: docker-compose build
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/ui"
|
||||
when: skip_build is undefined
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "{{ bridge_path }}/ui/docker-compose.yml"
|
||||
register: docker_compose_slurp
|
||||
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Set logger to remote server
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Write new docker-compose file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "{{ bridge_path }}/ui/docker-compose.yml"
|
||||
|
||||
- name: Set the local container logs configuration file
|
||||
template:
|
||||
src: 32-ui-docker.conf.j2
|
||||
dest: /etc/rsyslog.d/32-ui-docker.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: Set the log configuration file to send container logs to remote server
|
||||
template:
|
||||
src: 37-ui-remote-logging.conf.j2
|
||||
dest: /etc/rsyslog.d/37-ui-remote-logging.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
when: syslog_server_port is defined
|
||||
|
||||
- name: restart rsyslog
|
||||
service:
|
||||
name: rsyslog
|
||||
state: restarted
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
- include_tasks: pre_config.yml
|
||||
- include_tasks: logging.yml
|
||||
- include_tasks: jumpbox.yml
|
||||
- include_tasks: servinstall.yml
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
- name: Install .env config
|
||||
template:
|
||||
src: .env.j2
|
||||
dest: "{{ bridge_path }}/ui/.env"
|
||||
owner: "{{ compose_service_user }}"
|
||||
mode: '0640'
|
||||
@@ -1,20 +0,0 @@
|
||||
# This role creates a tokenbridge-ui service which is designed to manage docker-compose ui deployment.
|
||||
# /etc/init.d/tokenbridge-ui start, status, stop, restart - does what the services usually do in such cases.
|
||||
# /etc/init.d/tokenbridge-ui rebuild - builds a new ui deployment from scratch.
|
||||
---
|
||||
- name: "Set the service"
|
||||
template:
|
||||
src: tokenbridge-ui.j2
|
||||
dest: "/etc/init.d/tokenbridge-ui"
|
||||
owner: root
|
||||
mode: 755
|
||||
|
||||
- name: "Enable the service"
|
||||
service:
|
||||
name: "tokenbridge-ui"
|
||||
state: started
|
||||
enabled: yes
|
||||
use: service
|
||||
|
||||
- name: Start the service
|
||||
shell: service tokenbridge-ui start
|
||||
@@ -1,42 +0,0 @@
|
||||
COMMON_HOME_BRIDGE_ADDRESS={{ COMMON_HOME_BRIDGE_ADDRESS }}
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
|
||||
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
|
||||
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
|
||||
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME={{ UI_NATIVE_TOKEN_DISPLAY_NAME }}
|
||||
|
||||
UI_HOME_NETWORK_DISPLAY_NAME={{ UI_HOME_NETWORK_DISPLAY_NAME }}
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME={{ UI_FOREIGN_NETWORK_DISPLAY_NAME }}
|
||||
|
||||
UI_HOME_WITHOUT_EVENTS={{ UI_HOME_WITHOUT_EVENTS }}
|
||||
UI_FOREIGN_WITHOUT_EVENTS={{ UI_FOREIGN_WITHOUT_EVENTS }}
|
||||
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE={{ UI_HOME_EXPLORER_TX_TEMPLATE }}
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE={{ UI_FOREIGN_EXPLORER_TX_TEMPLATE }}
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE={{ UI_HOME_EXPLORER_ADDRESS_TEMPLATE }}
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE={{ UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE }}
|
||||
|
||||
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE={{ COMMON_HOME_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK={{ COMMON_HOME_GAS_PRICE_FALLBACK }}
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL={{ UI_HOME_GAS_PRICE_UPDATE_INTERVAL }}
|
||||
{% if COMMON_HOME_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_FACTOR={{ COMMON_HOME_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL={{ COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL }}
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE={{ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE }}
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK={{ COMMON_FOREIGN_GAS_PRICE_FALLBACK }}
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL={{ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL }}
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
|
||||
|
||||
# Default
|
||||
UI_TITLE={{ UI_TITLE }}
|
||||
UI_OG_TITLE={{ UI_OG_TITLE }}
|
||||
UI_DESCRIPTION={{ UI_DESCRIPTION }}
|
||||
UI_PORT={{ UI_PORT }}
|
||||
UI_PUBLIC_URL={{ UI_PUBLIC_URL }}
|
||||
|
||||
UI_STYLES={{ UI_STYLES }}
|
||||
@@ -1,11 +0,0 @@
|
||||
$FileCreateMode 0644
|
||||
template(name="DockerLogFileName_UI" type="list") {
|
||||
constant(value="/var/log/docker/")
|
||||
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="ui_(.*)\\/[a-zA-Z0-9]+\\[")
|
||||
constant(value="/docker.log")
|
||||
}
|
||||
|
||||
if $programname startswith 'ui_' then \
|
||||
?DockerLogFileName_UI
|
||||
|
||||
$FileCreateMode 0600
|
||||
@@ -1,15 +0,0 @@
|
||||
if $programname startswith 'ui_' then {
|
||||
action(
|
||||
type="omfwd"
|
||||
protocol="{{ syslog_server_port.split(":")[0] }}"
|
||||
target="{{ (syslog_server_port.split(":")[1])[2:] }}"
|
||||
port="{{ syslog_server_port.split(":")[2] }}"
|
||||
template="RemoteForwardFormat"
|
||||
queue.SpoolDirectory="/var/spool/rsyslog"
|
||||
queue.FileName="remote"
|
||||
queue.MaxDiskSpace="1g"
|
||||
queue.SaveOnShutdown="on"
|
||||
queue.Type="LinkedList"
|
||||
ResendLastMSGOnReconnect="on"
|
||||
)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
#! /bin/bash
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: tokenbridge-ui
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Start daemon at boot time
|
||||
# Description: Enable service provided by daemon.
|
||||
### END INIT INFO
|
||||
|
||||
WORKDIR="{{ '/home/' + compose_service_user | default('poadocker') + '/' + bridge_path + '/ui' if bridge_path[:1] != "/" else bridge_path + '/ui' }}"
|
||||
|
||||
start(){
|
||||
echo "Starting TokenBridge UI.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose up --detach
|
||||
}
|
||||
|
||||
stop(){
|
||||
echo "Stopping TokenBridge UI.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sleep 2
|
||||
}
|
||||
|
||||
status(){
|
||||
echo "TokenBridge UI status:"
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose ps
|
||||
}
|
||||
|
||||
rebuild(){
|
||||
echo "Rebuild TokenBridge UI.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose up --detach --force-recreate --no-deps --build
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
|
||||
start)
|
||||
start
|
||||
;;
|
||||
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
|
||||
status)
|
||||
status
|
||||
;;
|
||||
|
||||
restart)
|
||||
echo "Restarting TokenBridge UI.."
|
||||
stop
|
||||
start
|
||||
;;
|
||||
|
||||
rebuild)
|
||||
rebuild
|
||||
;;
|
||||
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|rebuild|status}"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -4,11 +4,6 @@
|
||||
become: true
|
||||
roles:
|
||||
- { role: oracle }
|
||||
- name: Install UI
|
||||
hosts: ui
|
||||
become: true
|
||||
roles:
|
||||
- { role: ui }
|
||||
- name: Install Monitor
|
||||
hosts: monitor
|
||||
become: true
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
ARG DOCKER_IMAGE_BASE
|
||||
ARG UI_TAG
|
||||
FROM ${DOCKER_IMAGE_BASE}/tokenbridge-e2e-ui:${UI_TAG}
|
||||
|
||||
ARG DOT_ENV_PATH
|
||||
|
||||
COPY ${DOT_ENV_PATH} ./.env
|
||||
@@ -24,14 +24,12 @@ Shut down and cleans up containers, networks, services, running scripts:
|
||||
| oracle | Launches Oracle containers |
|
||||
| oracle-validator-2 | Launches Oracle containers for second validator |
|
||||
| oracle-validator-3 | Launches Oracle containers for third validator |
|
||||
| ui | Launches UI containers |
|
||||
| blocks | Auto mines blocks |
|
||||
| monitor | Launches Monitor containers |
|
||||
| native-to-erc | Creates infrastructure for ultimate e2e testing, for native-to-erc type of bridge |
|
||||
| erc-to-native | Creates infrastructure for ultimate e2e testing, for erc-to-native type of bridge |
|
||||
| erc-to-erc | Creates infrastructure for ultimate e2e testing, for erc-to-erc type of bridge |
|
||||
| amb | Creates infrastructure for ultimate e2e testing, for arbitrary message type of bridge |
|
||||
| ultimate-amb-stake-erc-to-erc | Creates infrastructure for ultimate e2e testing, for stake token bridge |
|
||||
|
||||
#### Ultimate e2e testing
|
||||
|
||||
|
||||
@@ -15,13 +15,15 @@ It runs the e2e tests on components deployed using the deployment playbooks.
|
||||
Run the Parity nodes, deploy the bridge contracts, deploy Oracle using the deployment playbook.
|
||||
|
||||
```bash
|
||||
./up.sh deploy native-to-erc blocks
|
||||
./e2e-commons/up.sh deploy blocks
|
||||
./deployment-e2e/molecule.sh ultimate-native-to-erc
|
||||
```
|
||||
|
||||
### 2. Run the E2E tests
|
||||
|
||||
```
|
||||
cd ui-e2e; yarn mocha -g "NATIVE_TO_ERC" -b ./test.js
|
||||
```bash
|
||||
cd e2e-commons
|
||||
docker-compose run -e ULTIMATE=true e2e yarn workspace oracle-e2e run native-to-erc
|
||||
```
|
||||
|
||||
## Diagram
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04
|
||||
0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE
|
||||
0x6f359aC418a5f7538F7755A33C9c7dDf38785524
|
||||
|
||||
@@ -8,8 +8,6 @@ while [ "$1" != "" ]; do
|
||||
docker-compose build oracle
|
||||
elif [ "$1" == "monitor" ]; then
|
||||
docker-compose build monitor
|
||||
elif [ "$1" == "ui" ]; then
|
||||
docker-compose build ui
|
||||
elif [ "$1" == "alm" ]; then
|
||||
docker-compose build alm
|
||||
fi
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC
|
||||
COMMON_FOREIGN_RPC_URL=http://localhost:8542
|
||||
COMMON_HOME_RPC_URL=http://localhost:8541
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
|
||||
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR=1
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
UI_PORT=3003
|
||||
UI_STYLES=stake
|
||||
@@ -1,23 +0,0 @@
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda
|
||||
COMMON_FOREIGN_RPC_URL=http://localhost:8542
|
||||
COMMON_HOME_RPC_URL=http://localhost:8541
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
|
||||
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR=1
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
UI_PORT=3002
|
||||
UI_STYLES=core
|
||||
@@ -1,23 +0,0 @@
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127
|
||||
COMMON_FOREIGN_RPC_URL=http://localhost:8542
|
||||
COMMON_HOME_RPC_URL=http://localhost:8541
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
|
||||
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR=1
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
UI_PORT=3001
|
||||
UI_STYLES=core
|
||||
@@ -1,23 +0,0 @@
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0x32198D570fffC7033641F8A9094FFDCaAEF42624
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0x2B6871b9B02F73fa24F4864322CdC78604207769
|
||||
COMMON_FOREIGN_RPC_URL=http://localhost:8542
|
||||
COMMON_HOME_RPC_URL=http://localhost:8541
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
|
||||
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR=1
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
UI_PORT=3000
|
||||
UI_STYLES=core
|
||||
@@ -39,7 +39,6 @@
|
||||
"home": "0x32198D570fffC7033641F8A9094FFDCaAEF42624",
|
||||
"foreign": "0x2B6871b9B02F73fa24F4864322CdC78604207769",
|
||||
"foreignToken": "0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B",
|
||||
"ui": "http://localhost:3000",
|
||||
"monitor": "http://monitor:3010/bridge"
|
||||
},
|
||||
"ercToErcBridge": {
|
||||
@@ -47,7 +46,6 @@
|
||||
"foreign": "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127",
|
||||
"homeToken": "0x792455a6bCb62Ed4C4362D323E0590654CA4765c",
|
||||
"foreignToken": "0x3C665A31199694Bf723fD08844AD290207B5797f",
|
||||
"ui": "http://localhost:3001",
|
||||
"monitor": "http://monitor-erc20:3011/bridge"
|
||||
},
|
||||
"ercToNativeBridge": {
|
||||
@@ -55,7 +53,6 @@
|
||||
"foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
|
||||
"foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9",
|
||||
"chaiToken": "0x06af07097c9eeb7fd685c692751d5c66db49c215",
|
||||
"ui": "http://localhost:3002",
|
||||
"monitor": "http://monitor-erc20-native:3012/bridge"
|
||||
},
|
||||
"amb": {
|
||||
@@ -63,17 +60,9 @@
|
||||
"foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
|
||||
"homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
|
||||
"foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
|
||||
"blockedHomeBox": "0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE",
|
||||
"blockedHomeBox": "0x6f359aC418a5f7538F7755A33C9c7dDf38785524",
|
||||
"monitor": "http://monitor-amb:3013/bridge"
|
||||
},
|
||||
"ambStakeErcToErc": {
|
||||
"home": "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC",
|
||||
"foreign": "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC",
|
||||
"homeToken": "0x6f359aC418a5f7538F7755A33C9c7dDf38785524",
|
||||
"foreignToken": "0x6f359aC418a5f7538F7755A33C9c7dDf38785524",
|
||||
"blockReward": "0xF9698Eb93702dfdd0e2d802088d4c21822a8A977",
|
||||
"ui": "http://localhost:3003"
|
||||
},
|
||||
"homeRPC": {
|
||||
"URL": "http://parity1:8545",
|
||||
"ID": "77"
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
BRIDGE_MODE=STAKE_AMB_ERC_TO_ERC
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
DEPLOYMENT_GAS_LIMIT_EXTRA=0.2
|
||||
HOME_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50
|
||||
HOME_STAKE_TOKEN_ADDRESS=0x6f359aC418a5f7538F7755A33C9c7dDf38785524
|
||||
FOREIGN_STAKE_TOKEN_ADDRESS=0x6f359aC418a5f7538F7755A33C9c7dDf38785524
|
||||
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
|
||||
HOME_RPC_URL=http://parity1:8545
|
||||
HOME_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_DAILY_LIMIT=30000000000000000000000000
|
||||
HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000
|
||||
HOME_MIN_AMOUNT_PER_TX=10000000000000000
|
||||
HOME_TRANSACTIONS_FEE=0
|
||||
FOREIGN_RPC_URL=http://parity2:8545
|
||||
FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_DAILY_LIMIT=15000000000000000000000000
|
||||
FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000
|
||||
FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000
|
||||
HOME_AMB_BRIDGE=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
|
||||
FOREIGN_AMB_BRIDGE=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
|
||||
HOME_MEDIATOR_REQUEST_GAS_LIMIT=2000000
|
||||
FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT=2000000
|
||||
|
||||
BRIDGEABLE_TOKEN_NAME='not used'
|
||||
BRIDGEABLE_TOKEN_SYMBOL='not used'
|
||||
BRIDGEABLE_TOKEN_DECIMALS='18'
|
||||
@@ -68,49 +68,6 @@ services:
|
||||
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
|
||||
networks:
|
||||
- ultimate
|
||||
ui:
|
||||
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-ui:${UI_TAG:-local}
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: ui/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/ui.env
|
||||
command: "true"
|
||||
networks:
|
||||
- ultimate
|
||||
ui-erc20:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: e2e-commons/Dockerfile.ui
|
||||
args:
|
||||
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
|
||||
UI_TAG: ${UI_TAG:-local}
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20.env
|
||||
command: "true"
|
||||
networks:
|
||||
- ultimate
|
||||
ui-erc20-native:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: e2e-commons/Dockerfile.ui
|
||||
args:
|
||||
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
|
||||
UI_TAG: ${UI_TAG:-local}
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20-native.env
|
||||
command: "true"
|
||||
networks:
|
||||
- ultimate
|
||||
ui-amb-stake-erc20-erc20:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: e2e-commons/Dockerfile.ui
|
||||
args:
|
||||
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
|
||||
UI_TAG: ${UI_TAG:-local}
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env
|
||||
command: "true"
|
||||
networks:
|
||||
- ultimate
|
||||
alm:
|
||||
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-alm:${ALM_TAG:-local}
|
||||
build:
|
||||
|
||||
@@ -3,7 +3,7 @@ cd $(dirname $0)
|
||||
|
||||
if [ $CI ]; then exit $rc; fi
|
||||
|
||||
ps | grep node | grep -v grep | awk '{print "kill " $1}' | /bin/bash
|
||||
ps | grep node | grep -v grep | grep -v yarn | awk '{print "kill " $1}' | /bin/bash
|
||||
docker-compose down
|
||||
docker-compose -p validator2 down
|
||||
docker-compose -p validator3 down
|
||||
|
||||
@@ -8,8 +8,6 @@ while [ "$1" != "" ]; do
|
||||
docker-compose pull oracle
|
||||
elif [ "$1" == "monitor" ]; then
|
||||
docker-compose pull monitor
|
||||
elif [ "$1" == "ui" ]; then
|
||||
docker-compose pull ui
|
||||
elif [ "$1" == "alm" ]; then
|
||||
docker-compose pull alm
|
||||
fi
|
||||
|
||||
@@ -44,16 +44,6 @@ cd "$DEPLOY_PATH"
|
||||
node src/utils/deployTestBox.js
|
||||
cd - > /dev/null
|
||||
|
||||
echo -e "\n\n############ Deploying amb stake erc to erc ############\n"
|
||||
cp "$ENVS_PATH/amb-stake-erc-to-erc.env" "$DEPLOY_PATH/.env"
|
||||
node deployMultiBridgeToken.js
|
||||
node deployBridgeTokenRewardable.js
|
||||
cd "$DEPLOY_PATH"
|
||||
node deploy.js
|
||||
cd - > /dev/null
|
||||
node setupStakeTokens.js
|
||||
cd - > /dev/null
|
||||
|
||||
echo -e "\n\n############ Deploying one more test contract for amb ############\n"
|
||||
cd "$DEPLOY_PATH"
|
||||
node src/utils/deployTestBox.js
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
const path = require('path')
|
||||
const { ambStakeErcToErc, validator, secondValidator, thirdValidator } = require('../constants.json')
|
||||
const contractsPath = '../../contracts'
|
||||
require('dotenv').config({
|
||||
path: path.join(__dirname, contractsPath, '/deploy/.env')
|
||||
})
|
||||
const { sendRawTxHome, sendRawTxForeign, privateKeyToAddress } = require(`${contractsPath}/deploy/src/deploymentUtils`)
|
||||
const { web3Home, web3Foreign, deploymentPrivateKey } = require(`${contractsPath}/deploy/src/web3`)
|
||||
const BlockReward = require(`${contractsPath}/build/contracts/BlockRewardMock.json`)
|
||||
const ERC677BridgeTokenRewardable = require(`${contractsPath}/build/contracts/ERC677BridgeTokenRewardable.json`)
|
||||
const ERC677MultiBridgeToken = require(`${contractsPath}/build/contracts/ERC677MultiBridgeToken.json`)
|
||||
|
||||
const { DEPLOYMENT_ACCOUNT_PRIVATE_KEY } = process.env
|
||||
const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
|
||||
|
||||
async function setupStakeTokens() {
|
||||
try {
|
||||
let homeNonce = await web3Home.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
|
||||
|
||||
const blockReward = new web3Home.eth.Contract(BlockReward.abi, ambStakeErcToErc.blockReward)
|
||||
|
||||
console.log('\n[Home] Set token in block reward')
|
||||
const setTokenData = await blockReward.methods.setToken(ambStakeErcToErc.homeToken).encodeABI()
|
||||
await sendRawTxHome({
|
||||
data: setTokenData,
|
||||
nonce: homeNonce,
|
||||
to: blockReward.options.address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url: process.env.HOME_RPC_URL
|
||||
})
|
||||
homeNonce++
|
||||
|
||||
console.log('\n[Home] Set validators rewards in block reward')
|
||||
const setValidatorsRewardsData = await blockReward.methods
|
||||
.setValidatorsRewards([validator.address, secondValidator.address, thirdValidator.address])
|
||||
.encodeABI()
|
||||
await sendRawTxHome({
|
||||
data: setValidatorsRewardsData,
|
||||
nonce: homeNonce,
|
||||
to: blockReward.options.address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url: process.env.HOME_RPC_URL
|
||||
})
|
||||
homeNonce++
|
||||
|
||||
const homeToken = new web3Home.eth.Contract(ERC677BridgeTokenRewardable.abi, ambStakeErcToErc.homeToken)
|
||||
|
||||
console.log('\n[Home] Set block reward in token')
|
||||
const setBlockRewardData = await homeToken.methods.setBlockRewardContract(ambStakeErcToErc.blockReward).encodeABI()
|
||||
await sendRawTxHome({
|
||||
data: setBlockRewardData,
|
||||
nonce: homeNonce,
|
||||
to: homeToken.options.address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url: process.env.HOME_RPC_URL
|
||||
})
|
||||
homeNonce++
|
||||
|
||||
console.log('\n[Home] Add bridge in token')
|
||||
const addBridgeData = await homeToken.methods.addBridge(ambStakeErcToErc.home).encodeABI()
|
||||
await sendRawTxHome({
|
||||
data: addBridgeData,
|
||||
nonce: homeNonce,
|
||||
to: homeToken.options.address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url: process.env.HOME_RPC_URL
|
||||
})
|
||||
homeNonce++
|
||||
|
||||
console.log('\n[Home] transfer token ownership to mediator')
|
||||
const transferOwnershipData = await homeToken.methods.transferOwnership(ambStakeErcToErc.home).encodeABI()
|
||||
await sendRawTxHome({
|
||||
data: transferOwnershipData,
|
||||
nonce: homeNonce,
|
||||
to: homeToken.options.address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url: process.env.HOME_RPC_URL
|
||||
})
|
||||
homeNonce++
|
||||
|
||||
let foreignNonce = await web3Foreign.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
|
||||
const foreignToken = new web3Foreign.eth.Contract(ERC677MultiBridgeToken.abi, ambStakeErcToErc.foreignToken)
|
||||
|
||||
console.log('\n[Foreign] Add bridge in token')
|
||||
const addBridgeForeignData = await homeToken.methods.addBridge(ambStakeErcToErc.foreign).encodeABI()
|
||||
await sendRawTxForeign({
|
||||
data: addBridgeForeignData,
|
||||
nonce: foreignNonce,
|
||||
to: foreignToken.options.address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url: process.env.FOREIGN_RPC_URL
|
||||
})
|
||||
foreignNonce++
|
||||
|
||||
console.log('\n[Foreign] transfer token ownership to mediator')
|
||||
const transferOwnershipForeignData = await homeToken.methods.transferOwnership(ambStakeErcToErc.foreign).encodeABI()
|
||||
await sendRawTxForeign({
|
||||
data: transferOwnershipForeignData,
|
||||
nonce: foreignNonce,
|
||||
to: foreignToken.options.address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url: process.env.FOREIGN_RPC_URL
|
||||
})
|
||||
foreignNonce++
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
setupStakeTokens()
|
||||
@@ -42,6 +42,7 @@ startValidator () {
|
||||
fi
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:home
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:foreign
|
||||
docker-compose $1 run $2 $3 -d oracle yarn manager:shutdown
|
||||
}
|
||||
|
||||
startAMBValidator () {
|
||||
@@ -52,6 +53,7 @@ startAMBValidator () {
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn sender:home
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn sender:foreign
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn manager:shutdown
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
@@ -73,18 +75,6 @@ while [ "$1" != "" ]; do
|
||||
startValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3"
|
||||
fi
|
||||
|
||||
if [ "$1" == "ui" ]; then
|
||||
# this should only rebuild last 3 steps from ui/Dockerfile
|
||||
docker-compose build ui-erc20 ui-erc20-native ui-amb-stake-erc20-erc20
|
||||
|
||||
docker-compose up -d ui ui-erc20 ui-erc20-native ui-amb-stake-erc20-erc20
|
||||
|
||||
docker-compose run -d -p 3000:3000 ui yarn start
|
||||
docker-compose run -d -p 3001:3001 ui-erc20 yarn start
|
||||
docker-compose run -d -p 3002:3002 ui-erc20-native yarn start
|
||||
docker-compose run -d -p 3003:3003 ui-amb-stake-erc20-erc20 yarn start
|
||||
fi
|
||||
|
||||
if [ "$1" == "alm" ]; then
|
||||
docker-compose up -d alm
|
||||
|
||||
@@ -120,13 +110,7 @@ while [ "$1" != "" ]; do
|
||||
fi
|
||||
|
||||
if [ "$1" == "alm-e2e" ]; then
|
||||
docker-compose up -d redis rabbit
|
||||
|
||||
docker-compose run -d oracle-amb yarn watcher:signature-request
|
||||
docker-compose run -d oracle-amb yarn watcher:collected-signatures
|
||||
docker-compose run -d oracle-amb yarn watcher:affirmation-request
|
||||
docker-compose run -d oracle-amb yarn sender:home
|
||||
docker-compose run -d oracle-amb yarn sender:foreign
|
||||
startAMBValidator "" "" "" "redis" "rabbit"
|
||||
|
||||
oracle2name="-p validator2"
|
||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||
|
||||
@@ -28,6 +28,3 @@ MONITOR_HOME_TO_FOREIGN_BLOCK_LIST=
|
||||
|
||||
# MONITOR_HOME_VALIDATORS_BALANCE_ENABLE=0x... 0x... 0x...
|
||||
# MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE=0x... 0x... 0x...
|
||||
|
||||
# MONITOR_HOME_EXPLORER_API=
|
||||
MONITOR_FOREIGN_EXPLORER_API=https://api.bscscan.com/api?apikey=YourApiKeyToken
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
require('dotenv').config()
|
||||
const logger = require('./logger')('alerts')
|
||||
const logger = require('./logger')('detectFailures.js')
|
||||
const eventsInfo = require('./utils/events')
|
||||
const { normalizeAMBMessageEvent } = require('../commons')
|
||||
const { getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
require('dotenv').config()
|
||||
const logger = require('./logger')('stuckTransfers.js')
|
||||
const logger = require('./logger')('detectMediators.js')
|
||||
const { isHomeContract, isForeignContract } = require('./utils/web3Cache')
|
||||
const eventsInfo = require('./utils/events')
|
||||
const { getHomeTxSender, getForeignTxSender } = require('./utils/web3Cache')
|
||||
@@ -13,9 +13,19 @@ function countInteractions(requests) {
|
||||
stats[msg.sender] = {}
|
||||
}
|
||||
if (!stats[msg.sender][msg.executor]) {
|
||||
stats[msg.sender][msg.executor] = 0
|
||||
stats[msg.sender][msg.executor] = {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
pending: 0
|
||||
}
|
||||
}
|
||||
if (msg.status === true) {
|
||||
stats[msg.sender][msg.executor].success += 1
|
||||
} else if (msg.status === false) {
|
||||
stats[msg.sender][msg.executor].failed += 1
|
||||
} else {
|
||||
stats[msg.sender][msg.executor].pending += 1
|
||||
}
|
||||
stats[msg.sender][msg.executor] += 1
|
||||
})
|
||||
return stats
|
||||
}
|
||||
@@ -80,14 +90,8 @@ async function main(mode) {
|
||||
homeToForeignConfirmations,
|
||||
foreignToHomeConfirmations
|
||||
} = await eventsInfo(mode)
|
||||
const homeToForeign = homeToForeignRequests
|
||||
.map(normalize)
|
||||
.map(addExecutionStatus(homeToForeignConfirmations))
|
||||
.filter(x => typeof x.status === 'boolean')
|
||||
const foreignToHome = foreignToHomeRequests
|
||||
.map(normalize)
|
||||
.map(addExecutionStatus(foreignToHomeConfirmations))
|
||||
.filter(x => typeof x.status === 'boolean')
|
||||
const homeToForeign = homeToForeignRequests.map(normalize).map(addExecutionStatus(homeToForeignConfirmations))
|
||||
const foreignToHome = foreignToHomeRequests.map(normalize).map(addExecutionStatus(foreignToHomeConfirmations))
|
||||
|
||||
for (const event of homeToForeign) {
|
||||
// AMB contract emits a single UserRequestForSignature event for every home->foreign request.
|
||||
@@ -98,7 +102,7 @@ async function main(mode) {
|
||||
|
||||
// Executor is definitely a contract if a message execution failed, since message calls to EOA always succeed.
|
||||
// Alternatively, the executor is checked to be a contract by looking at its bytecode size.
|
||||
event.isExecutorAContract = !event.status || (await isForeignContract(event.executor))
|
||||
event.isExecutorAContract = event.status === false || (await isForeignContract(event.executor))
|
||||
}
|
||||
for (const event of foreignToHome) {
|
||||
// AMB contract emits a single UserRequestForAffirmation event for every foreign->home request.
|
||||
@@ -109,7 +113,7 @@ async function main(mode) {
|
||||
|
||||
// Executor is definitely a contract if a message execution failed, since message calls to EOA always succeed.
|
||||
// Alternatively, the executor is checked to be a contract by looking at its bytecode size.
|
||||
event.isExecutorAContract = !event.status || (await isHomeContract(event.executor))
|
||||
event.isExecutorAContract = event.status === false || (await isHomeContract(event.executor))
|
||||
}
|
||||
const C2C = event => event.isSenderAContract && event.isExecutorAContract
|
||||
const U2C = event => !event.isSenderAContract && event.isExecutorAContract
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
require('dotenv').config()
|
||||
const logger = require('./logger')('getBalances')
|
||||
const logger = require('./logger')('metrics')
|
||||
const { readFile } = require('./utils/file')
|
||||
|
||||
const {
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
const fetch = require('node-fetch')
|
||||
const logger = require('../logger')('web3Cache')
|
||||
const { readCacheFile, writeCacheFile } = require('./file')
|
||||
const { web3Home, web3Foreign } = require('./web3')
|
||||
const { getPastEvents: commonGetPastEvents } = require('../../commons')
|
||||
|
||||
const {
|
||||
MONITOR_BRIDGE_NAME,
|
||||
MONITOR_CACHE_EVENTS,
|
||||
MONITOR_FOREIGN_EXPLORER_API,
|
||||
MONITOR_HOME_EXPLORER_API
|
||||
} = process.env
|
||||
const { MONITOR_BRIDGE_NAME, MONITOR_CACHE_EVENTS } = process.env
|
||||
|
||||
let isDirty = false
|
||||
|
||||
@@ -58,36 +52,6 @@ async function isForeignContract(address) {
|
||||
return cachedForeignIsContract[address]
|
||||
}
|
||||
|
||||
function getPastEventsWithAPIFallback(contract, options) {
|
||||
return commonGetPastEvents(contract, options).catch(async e => {
|
||||
const [api, web3] =
|
||||
options.chain === 'home' ? [MONITOR_HOME_EXPLORER_API, web3Home] : [MONITOR_FOREIGN_EXPLORER_API, web3Foreign]
|
||||
if (api && e.message.includes('exceed maximum block range')) {
|
||||
logger.debug('BLOCK RANGE EXCEED, using fallback to the explorer API')
|
||||
|
||||
const abi = contract.options.jsonInterface.find(abi => abi.type === 'event' && abi.name === options.event)
|
||||
|
||||
const url = new URL(api)
|
||||
url.searchParams.append('module', 'logs')
|
||||
url.searchParams.append('action', 'getLogs')
|
||||
url.searchParams.append('address', contract.options.address)
|
||||
url.searchParams.append('fromBlock', options.fromBlock)
|
||||
url.searchParams.append('toBlock', options.toBlock || 'latest')
|
||||
url.searchParams.append('topic0', web3.eth.abi.encodeEventSignature(abi))
|
||||
|
||||
const logs = await fetch(url).then(res => res.json())
|
||||
|
||||
return logs.result.map(log => ({
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: parseInt(log.blockNumber.slice(2), 16),
|
||||
returnValues: web3.eth.abi.decodeLog(abi.inputs, log.data, log.topics.slice(1))
|
||||
}))
|
||||
} else {
|
||||
throw new Error(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function getPastEvents(contract, options) {
|
||||
if (MONITOR_CACHE_EVENTS !== 'true') {
|
||||
return commonGetPastEvents(contract, options)
|
||||
@@ -130,14 +94,14 @@ async function getPastEvents(contract, options) {
|
||||
// requested: A...B
|
||||
// cached: C...D
|
||||
logger.debug(`Fetching events for blocks ${fromBlock}...${toBlock}`)
|
||||
result = await getPastEventsWithAPIFallback(contract, options)
|
||||
result = await commonGetPastEvents(contract, options)
|
||||
} else if (fromBlock < cachedFromBlock && toBlock <= cachedToBlock) {
|
||||
// requested: A...B
|
||||
// cached: C...D
|
||||
logger.debug(`Cache hit for blocks ${cachedFromBlock}...${toBlock}`)
|
||||
logger.debug(`Fetching events for blocks ${fromBlock}...${cachedFromBlock - 1}`)
|
||||
result = [
|
||||
...(await getPastEventsWithAPIFallback(contract, { ...options, toBlock: cachedFromBlock - 1 })),
|
||||
...(await commonGetPastEvents(contract, { ...options, toBlock: cachedFromBlock - 1 })),
|
||||
...cachedEvents.filter(e => e.blockNumber <= toBlock)
|
||||
]
|
||||
} else if (fromBlock < cachedFromBlock && cachedToBlock < toBlock) {
|
||||
@@ -147,9 +111,9 @@ async function getPastEvents(contract, options) {
|
||||
logger.debug(`Fetching events for blocks ${fromBlock}...${cachedFromBlock - 1}`)
|
||||
logger.debug(`Fetching events for blocks ${cachedToBlock + 1}...${toBlock}`)
|
||||
result = [
|
||||
...(await getPastEventsWithAPIFallback(contract, { ...options, toBlock: cachedFromBlock - 1 })),
|
||||
...(await commonGetPastEvents(contract, { ...options, toBlock: cachedFromBlock - 1 })),
|
||||
...cachedEvents,
|
||||
...(await getPastEventsWithAPIFallback(contract, { ...options, fromBlock: cachedToBlock + 1 }))
|
||||
...(await commonGetPastEvents(contract, { ...options, fromBlock: cachedToBlock + 1 }))
|
||||
]
|
||||
} else if (cachedFromBlock <= fromBlock && toBlock <= cachedToBlock) {
|
||||
// requested: A.B
|
||||
@@ -163,7 +127,7 @@ async function getPastEvents(contract, options) {
|
||||
logger.debug(`Fetching events for blocks ${cachedToBlock + 1}...${toBlock}`)
|
||||
result = [
|
||||
...cachedEvents.filter(e => e.blockNumber >= fromBlock),
|
||||
...(await getPastEventsWithAPIFallback(contract, { ...options, fromBlock: cachedToBlock + 1 }))
|
||||
...(await commonGetPastEvents(contract, { ...options, fromBlock: cachedToBlock + 1 }))
|
||||
]
|
||||
} else {
|
||||
throw new Error(
|
||||
@@ -192,6 +156,7 @@ function saveCache() {
|
||||
if (isDirty) {
|
||||
logger.debug('Saving cache on disk')
|
||||
writeCacheFile(homeTxSendersCacheFile, cachedHomeTxSenders)
|
||||
writeCacheFile(foreignTxSendersCacheFile, cachedForeignTxSenders)
|
||||
writeCacheFile(homeIsContractCacheFile, cachedHomeIsContract)
|
||||
writeCacheFile(foreignIsContractCacheFile, cachedForeignIsContract)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
"scripts": {
|
||||
"start": "mocha",
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
"amb": "ULTIMATE=true mocha test/amb.js",
|
||||
"amb": "mocha test/amb.js",
|
||||
"native-to-erc": "mocha test/nativeToErc.js",
|
||||
"erc-to-erc": "mocha test/ercToErc.js",
|
||||
"erc-to-native": "mocha test/ercToNative.js",
|
||||
"alm": "mocha test/amb.js"
|
||||
},
|
||||
"author": "",
|
||||
|
||||
@@ -28,30 +28,33 @@ describe('arbitrary message bridging', () => {
|
||||
let requiredSignatures = 1
|
||||
before(async () => {
|
||||
// Only 1 validator is used in ultimate tests
|
||||
if (process.env.ULTIMATE !== 'true') {
|
||||
requiredSignatures = 2
|
||||
// Set 2 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
web3: homeWeb3,
|
||||
requiredSignatures: 2,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
|
||||
// Set 2 required signatures for foreign bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: foreignBridge,
|
||||
web3: foreignWeb3,
|
||||
requiredSignatures: 2,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
if (process.env.ULTIMATE === 'true') {
|
||||
return
|
||||
}
|
||||
console.log('Calling setRequiredSignatures(2)')
|
||||
|
||||
requiredSignatures = 2
|
||||
// Set 2 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
web3: homeWeb3,
|
||||
requiredSignatures: 2,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
|
||||
// Set 2 required signatures for foreign bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: foreignBridge,
|
||||
web3: foreignWeb3,
|
||||
requiredSignatures: 2,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
})
|
||||
describe('Home to Foreign', () => {
|
||||
describe('Subsidized Mode', () => {
|
||||
|
||||
@@ -25,6 +25,11 @@ const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BR
|
||||
|
||||
describe('erc to erc', () => {
|
||||
before(async () => {
|
||||
if (process.env.ULTIMATE === 'true') {
|
||||
return
|
||||
}
|
||||
console.log('Calling setRequiredSignatures(2)')
|
||||
|
||||
// Set 2 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
|
||||
@@ -34,6 +34,11 @@ const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME
|
||||
|
||||
describe('erc to native', () => {
|
||||
before(async () => {
|
||||
if (process.env.ULTIMATE === 'true') {
|
||||
return
|
||||
}
|
||||
console.log('Calling setRequiredSignatures(2)')
|
||||
|
||||
// Set 2 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
@@ -144,47 +149,53 @@ describe('erc to native', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should not process transaction from blocked users', async () => {
|
||||
const originalBalance1 = await erc20Token.methods.balanceOf(user.address).call()
|
||||
const originalBalance2 = await erc20Token.methods.balanceOf(blockedUser.address).call()
|
||||
// allowance/block lists files are not mounted to the host during the ultimate test
|
||||
if (process.env.ULTIMATE !== 'true') {
|
||||
it('should not process transaction from blocked users', async () => {
|
||||
const originalBalance1 = await erc20Token.methods.balanceOf(user.address).call()
|
||||
const originalBalance2 = await erc20Token.methods.balanceOf(blockedUser.address).call()
|
||||
|
||||
// check that account has tokens in home chain
|
||||
const balance1 = await homeWeb3.eth.getBalance(user.address)
|
||||
const balance2 = await homeWeb3.eth.getBalance(blockedUser.address)
|
||||
assert(!toBN(balance1).isZero(), 'Account should have tokens')
|
||||
assert(!toBN(balance2).isZero(), 'Account should have tokens')
|
||||
// check that account has tokens in home chain
|
||||
const balance1 = await homeWeb3.eth.getBalance(user.address)
|
||||
const balance2 = await homeWeb3.eth.getBalance(blockedUser.address)
|
||||
assert(!toBN(balance1).isZero(), 'Account should have tokens')
|
||||
assert(!toBN(balance2).isZero(), 'Account should have tokens')
|
||||
|
||||
// send transaction to home bridge
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: user.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
gas: '1000000',
|
||||
value: homeWeb3.utils.toWei('0.01')
|
||||
// send transaction to home bridge
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: user.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
gas: '1000000',
|
||||
value: homeWeb3.utils.toWei('0.01')
|
||||
})
|
||||
|
||||
// send transaction to home bridge
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: blockedUser.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
gas: '1000000',
|
||||
value: homeWeb3.utils.toWei('0.01')
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).lte(toBN(originalBalance1))) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
|
||||
await sleep(3000)
|
||||
|
||||
const balance = await erc20Token.methods.balanceOf(blockedUser.address).call()
|
||||
assert(
|
||||
toBN(balance).eq(toBN(originalBalance2)),
|
||||
'Bridge should not process collected signatures from blocked user'
|
||||
)
|
||||
})
|
||||
|
||||
// send transaction to home bridge
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: blockedUser.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
gas: '1000000',
|
||||
value: homeWeb3.utils.toWei('0.01')
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).lte(toBN(originalBalance1))) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
|
||||
await sleep(3000)
|
||||
|
||||
const balance = await erc20Token.methods.balanceOf(blockedUser.address).call()
|
||||
assert(toBN(balance).eq(toBN(originalBalance2)), 'Bridge should not process collected signatures from blocked user')
|
||||
})
|
||||
}
|
||||
it('should not invest dai when chai token is disabled', async () => {
|
||||
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
@@ -273,121 +284,123 @@ describe('erc to native', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('handling of chai swaps', async () => {
|
||||
before(async () => {
|
||||
// Next tests check validator nonces, this will force every validator to submit signature/affirmation
|
||||
// Set 3 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
web3: homeWeb3,
|
||||
requiredSignatures: 3,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
if (process.env.ULTIMATE !== 'true') {
|
||||
describe('handling of chai swaps', async () => {
|
||||
before(async () => {
|
||||
// Next tests check validator nonces, this will force every validator to submit signature/affirmation
|
||||
// Set 3 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
web3: homeWeb3,
|
||||
requiredSignatures: 3,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
|
||||
// Set 3 required signatures for foreign bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: foreignBridge,
|
||||
web3: foreignWeb3,
|
||||
requiredSignatures: 3,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Set 3 required signatures for foreign bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: foreignBridge,
|
||||
web3: foreignWeb3,
|
||||
requiredSignatures: 3,
|
||||
options: {
|
||||
it('should not handle transfer event in paying interest', async () => {
|
||||
await foreignBridge.methods.setInterestReceiver(user.address).send({
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
gas: '1000000'
|
||||
})
|
||||
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
|
||||
await foreignBridge.methods.payInterest().send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await promiseRetry(async (retry, number) => {
|
||||
if (number < 6) {
|
||||
retry()
|
||||
} else {
|
||||
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
|
||||
assert(
|
||||
nonce === initialNonce,
|
||||
'Validator should not process transfer event originated during converting Chai => Dai'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should not handle chai withdrawal transfer event in executeSignatures as a regular transfer', async () => {
|
||||
await foreignBridge.methods.setMinDaiTokenBalance('0').send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
}) // set investing limit to 0
|
||||
await foreignBridge.methods.convertDaiToChai().send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
}) // convert all existing dai tokens on bridge account to chai, in order to start from zero balance
|
||||
|
||||
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
|
||||
|
||||
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
// send transaction to home bridge
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: user.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
gas: '1000000',
|
||||
value: homeWeb3.utils.toWei('0.01')
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).lte(toBN(originalBalance))) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
|
||||
await promiseRetry(async (retry, number) => {
|
||||
if (number < 6) {
|
||||
retry()
|
||||
} else {
|
||||
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
|
||||
assert(
|
||||
nonce === initialNonce + 1,
|
||||
'Validator should not process transfer event originated during converting Chai => Dai'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
// Set 2 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
web3: homeWeb3,
|
||||
requiredSignatures: 2,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
|
||||
// Set 2 required signatures for foreign bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: foreignBridge,
|
||||
web3: foreignWeb3,
|
||||
requiredSignatures: 2,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should not handle transfer event in paying interest', async () => {
|
||||
await foreignBridge.methods.setInterestReceiver(user.address).send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
|
||||
await foreignBridge.methods.payInterest().send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await promiseRetry(async (retry, number) => {
|
||||
if (number < 6) {
|
||||
retry()
|
||||
} else {
|
||||
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
|
||||
assert(
|
||||
nonce === initialNonce,
|
||||
'Validator should not process transfer event originated during converting Chai => Dai'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should not handle chai withdrawal transfer event in executeSignatures as a regular transfer', async () => {
|
||||
await foreignBridge.methods.setMinDaiTokenBalance('0').send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
}) // set investing limit to 0
|
||||
await foreignBridge.methods.convertDaiToChai().send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
}) // convert all existing dai tokens on bridge account to chai, in order to start from zero balance
|
||||
|
||||
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
|
||||
|
||||
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
// send transaction to home bridge
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: user.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
gas: '1000000',
|
||||
value: homeWeb3.utils.toWei('0.01')
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).lte(toBN(originalBalance))) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
|
||||
await promiseRetry(async (retry, number) => {
|
||||
if (number < 6) {
|
||||
retry()
|
||||
} else {
|
||||
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
|
||||
assert(
|
||||
nonce === initialNonce + 1,
|
||||
'Validator should not process transfer event originated during converting Chai => Dai'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
// Set 2 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
web3: homeWeb3,
|
||||
requiredSignatures: 2,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
|
||||
// Set 2 required signatures for foreign bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: foreignBridge,
|
||||
web3: foreignWeb3,
|
||||
requiredSignatures: 2,
|
||||
options: {
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -46,6 +46,11 @@ const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_NATIVE_TO_ERC_ABI, CO
|
||||
|
||||
describe('native to erc', () => {
|
||||
before(async () => {
|
||||
if (process.env.ULTIMATE === 'true') {
|
||||
return
|
||||
}
|
||||
console.log('Calling setRequiredSignatures(2)')
|
||||
|
||||
// Set 2 required signatures for home bridge
|
||||
await setRequiredSignatures({
|
||||
bridgeContract: homeBridge,
|
||||
|
||||
@@ -15,6 +15,7 @@ COMMON_HOME_GAS_PRICE_FALLBACK=1000000000
|
||||
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR=1
|
||||
ORACLE_HOME_TX_RESEND_INTERVAL=300000
|
||||
ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT=
|
||||
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
@@ -22,6 +23,7 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
ORACLE_FOREIGN_TX_RESEND_INTERVAL=1200000
|
||||
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT=4000
|
||||
|
||||
ORACLE_QUEUE_URL=amqp://rabbit
|
||||
ORACLE_REDIS_URL=redis://redis
|
||||
|
||||
@@ -150,10 +150,6 @@ In case you need to reset your bridge or setup a new one (with different configu
|
||||
- `yarn sender:home`
|
||||
- `yarn sender:foreign`
|
||||
|
||||
### Bridge UI
|
||||
|
||||
See the [UI instructions](../ui/README.md) to configure and use the optional Bridge UI.
|
||||
|
||||
### Build the image without running the oracle
|
||||
|
||||
To build the image change the directory:
|
||||
|
||||
@@ -69,9 +69,12 @@ const bridgeConfig = {
|
||||
foreignBridgeAbi: foreignAbi,
|
||||
eventFilter: {},
|
||||
validatorAddress: ORACLE_VALIDATOR_ADDRESS || privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY),
|
||||
maxProcessingTime
|
||||
maxProcessingTime,
|
||||
shutdownKey: 'oracle-shutdown'
|
||||
}
|
||||
|
||||
const toBNOrNull = x => (x ? toBN(x) : null)
|
||||
|
||||
const homeConfig = {
|
||||
chain: 'home',
|
||||
eventContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS,
|
||||
@@ -80,6 +83,7 @@ const homeConfig = {
|
||||
bridgeAbi: homeAbi,
|
||||
pollingInterval: process.env.ORACLE_HOME_RPC_POLLING_INTERVAL,
|
||||
startBlock: toBN(process.env.ORACLE_HOME_START_BLOCK || 0),
|
||||
blockPollingLimit: toBNOrNull(process.env.ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT),
|
||||
web3: web3Home
|
||||
}
|
||||
|
||||
@@ -91,6 +95,7 @@ const foreignConfig = {
|
||||
bridgeAbi: foreignAbi,
|
||||
pollingInterval: process.env.ORACLE_FOREIGN_RPC_POLLING_INTERVAL,
|
||||
startBlock: toBN(process.env.ORACLE_FOREIGN_START_BLOCK || 0),
|
||||
blockPollingLimit: toBNOrNull(process.env.ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT),
|
||||
web3: web3Foreign
|
||||
}
|
||||
|
||||
|
||||
20
oracle/config/shutdown-manager.config.js
Normal file
20
oracle/config/shutdown-manager.config.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const baseConfig = require('./base.config')
|
||||
|
||||
const {
|
||||
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL,
|
||||
ORACLE_SHUTDOWN_SERVICE_URL,
|
||||
ORACLE_SHUTDOWN_CONTRACT_ADDRESS,
|
||||
ORACLE_SHUTDOWN_CONTRACT_METHOD
|
||||
} = process.env
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
id: 'shutdown-manager',
|
||||
name: 'shutdown-manager',
|
||||
pollingInterval: ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL || 120000,
|
||||
checksBeforeResume: 3,
|
||||
checksBeforeStop: 1,
|
||||
shutdownServiceURL: ORACLE_SHUTDOWN_SERVICE_URL,
|
||||
shutdownContractAddress: ORACLE_SHUTDOWN_CONTRACT_ADDRESS,
|
||||
shutdownMethod: (ORACLE_SHUTDOWN_CONTRACT_METHOD || 'isShutdown()').trim()
|
||||
}
|
||||
@@ -77,6 +77,12 @@ services:
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_rabbit_bridge_request
|
||||
bridge_shutdown:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: bridge_shutdown
|
||||
networks:
|
||||
- net_db_bridge_shutdown
|
||||
|
||||
networks:
|
||||
net_db_bridge_request:
|
||||
@@ -91,6 +97,8 @@ networks:
|
||||
driver: bridge
|
||||
net_db_bridge_senderforeign:
|
||||
driver: bridge
|
||||
net_db_bridge_shutdown:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_request:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_collected:
|
||||
|
||||
@@ -61,6 +61,12 @@ services:
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_rabbit_bridge_request
|
||||
bridge_shutdown:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: bridge_shutdown
|
||||
networks:
|
||||
- net_db_bridge_shutdown
|
||||
|
||||
networks:
|
||||
net_db_bridge_request:
|
||||
@@ -75,6 +81,8 @@ networks:
|
||||
driver: bridge
|
||||
net_db_bridge_senderforeign:
|
||||
driver: bridge
|
||||
net_db_bridge_shutdown:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_request:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_collected:
|
||||
|
||||
@@ -21,12 +21,13 @@ services:
|
||||
command: [redis-server, --appendonly, 'yes']
|
||||
hostname: redis
|
||||
image: redis:4
|
||||
networks:
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_db_bridge_collected
|
||||
- net_db_bridge_affirmation
|
||||
- net_db_bridge_senderhome
|
||||
- net_db_bridge_senderforeign
|
||||
- net_db_bridge_shutdown
|
||||
restart: unless-stopped
|
||||
volumes: ['~/bridge_data/redis:/data']
|
||||
bridge_request:
|
||||
@@ -34,8 +35,8 @@ services:
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn watcher:signature-request
|
||||
@@ -47,8 +48,8 @@ services:
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn watcher:collected-signatures
|
||||
@@ -60,8 +61,8 @@ services:
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn watcher:affirmation-request
|
||||
@@ -73,8 +74,8 @@ services:
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn sender:home
|
||||
@@ -86,14 +87,25 @@ services:
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn sender:foreign
|
||||
networks:
|
||||
- net_db_bridge_senderforeign
|
||||
- net_rabbit_bridge_senderforeign
|
||||
bridge_shutdown:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn manager:shutdown
|
||||
networks:
|
||||
- net_db_bridge_shutdown
|
||||
|
||||
networks:
|
||||
net_db_bridge_request:
|
||||
@@ -106,6 +118,8 @@ networks:
|
||||
driver: bridge
|
||||
net_db_bridge_senderforeign:
|
||||
driver: bridge
|
||||
net_db_bridge_shutdown:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_request:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_collected:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"sender:home": "./scripts/start-worker.sh sender home-sender",
|
||||
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
|
||||
"confirm:transfer": "./scripts/start-worker.sh confirmRelay transfer-watcher",
|
||||
"manager:shutdown": "./scripts/start-worker.sh shutdownManager shutdown-manager",
|
||||
"dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer, sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn sender:home' 'yarn sender:foreign'",
|
||||
"test": "NODE_ENV=test mocha",
|
||||
"test:watch": "NODE_ENV=test mocha --watch --reporter=min",
|
||||
|
||||
@@ -4,6 +4,7 @@ const { connectSenderToQueue } = require('./services/amqpClient')
|
||||
const { redis } = require('./services/redisClient')
|
||||
const GasPrice = require('./services/gasPrice')
|
||||
const logger = require('./services/logger')
|
||||
const { getShutdownFlag } = require('./services/shutdownState')
|
||||
const { sendTx } = require('./tx/sendTx')
|
||||
const { getNonce, getChainId } = require('./tx/web3')
|
||||
const {
|
||||
@@ -12,6 +13,7 @@ const {
|
||||
privateKeyToAddress,
|
||||
syncForEach,
|
||||
waitForFunds,
|
||||
waitForUnsuspend,
|
||||
watchdog,
|
||||
nonceError
|
||||
} = require('./utils/utils')
|
||||
@@ -44,30 +46,40 @@ async function initialize() {
|
||||
GasPrice.start(config.id)
|
||||
|
||||
chainId = await getChainId(web3Instance)
|
||||
connectSenderToQueue({
|
||||
queueName: config.queue,
|
||||
oldQueueName: config.oldQueue,
|
||||
resendInterval: config.resendInterval,
|
||||
cb: options => {
|
||||
if (config.maxProcessingTime) {
|
||||
return watchdog(() => main(options), config.maxProcessingTime, () => {
|
||||
logger.fatal('Max processing time reached')
|
||||
process.exit(EXIT_CODES.MAX_TIME_REACHED)
|
||||
})
|
||||
}
|
||||
|
||||
return main(options)
|
||||
}
|
||||
})
|
||||
connectQueue()
|
||||
} catch (e) {
|
||||
logger.error(e.message)
|
||||
process.exit(EXIT_CODES.GENERAL_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
function connectQueue() {
|
||||
connectSenderToQueue({
|
||||
queueName: config.queue,
|
||||
oldQueueName: config.oldQueue,
|
||||
resendInterval: config.resendInterval,
|
||||
cb: options => {
|
||||
if (config.maxProcessingTime) {
|
||||
return watchdog(() => main(options), config.maxProcessingTime, () => {
|
||||
logger.fatal('Max processing time reached')
|
||||
process.exit(EXIT_CODES.MAX_TIME_REACHED)
|
||||
})
|
||||
}
|
||||
|
||||
return main(options)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function resume(newBalance) {
|
||||
logger.info(`Validator balance changed. New balance is ${newBalance}. Resume messages processing.`)
|
||||
initialize()
|
||||
connectQueue()
|
||||
}
|
||||
|
||||
function unsuspend() {
|
||||
logger.info(`Oracle sender was unsuspended.`)
|
||||
connectQueue()
|
||||
}
|
||||
|
||||
async function readNonce(forceUpdate) {
|
||||
@@ -102,6 +114,13 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
||||
return
|
||||
}
|
||||
|
||||
if (await getShutdownFlag(logger, config.shutdownKey, true)) {
|
||||
logger.info('Oracle sender was suspended via the remote shutdown process')
|
||||
channel.close()
|
||||
waitForUnsuspend(() => getShutdownFlag(logger, config.shutdownKey, true), unsuspend)
|
||||
return
|
||||
}
|
||||
|
||||
const txArray = JSON.parse(msg.content)
|
||||
logger.debug(`Msg received with ${txArray.length} Tx to send`)
|
||||
const gasPrice = GasPrice.getPrice().toString(10)
|
||||
@@ -176,7 +195,11 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
||||
)
|
||||
|
||||
const message = e.message.toLowerCase()
|
||||
if (isResend || message.includes('transaction with the same hash was already imported')) {
|
||||
if (message.includes('replacement transaction underpriced')) {
|
||||
logger.info('Replacement transaction underpriced, forcing gas price update')
|
||||
GasPrice.start(config.id)
|
||||
failedTx.push(job)
|
||||
} else if (isResend || message.includes('transaction with the same hash was already imported')) {
|
||||
resendJobs.push(job)
|
||||
} else {
|
||||
// if initial transaction sending has failed not due to the same hash error
|
||||
|
||||
@@ -73,17 +73,20 @@ async function start(chainId, fetchOnce) {
|
||||
throw new Error(`Unrecognized chainId '${chainId}'`)
|
||||
}
|
||||
|
||||
const fetchFn = gasPriceSupplierUrl === 'gas-price-oracle' ? null : () => fetch(gasPriceSupplierUrl)
|
||||
if (fetchOnce) {
|
||||
await fetchGasPrice(speedType, factor, bridgeContract, fetchFn)
|
||||
return getPrice()
|
||||
let fetchFn = null
|
||||
if (gasPriceSupplierUrl !== 'gas-price-oracle') {
|
||||
fetchFn = () => fetch(gasPriceSupplierUrl, { timeout: 2000 })
|
||||
}
|
||||
|
||||
fetchGasPriceInterval = setIntervalAndRun(
|
||||
() => fetchGasPrice(speedType, factor, bridgeContract, fetchFn),
|
||||
updateInterval
|
||||
)
|
||||
return null
|
||||
if (fetchOnce) {
|
||||
await fetchGasPrice(speedType, factor, bridgeContract, fetchFn)
|
||||
} else {
|
||||
fetchGasPriceInterval = await setIntervalAndRun(
|
||||
() => fetchGasPrice(speedType, factor, bridgeContract, fetchFn),
|
||||
updateInterval
|
||||
)
|
||||
}
|
||||
return getPrice()
|
||||
}
|
||||
|
||||
function getPrice() {
|
||||
|
||||
@@ -3,6 +3,7 @@ const path = require('path')
|
||||
const {
|
||||
web3Home,
|
||||
web3Foreign,
|
||||
web3Side,
|
||||
web3HomeFallback,
|
||||
web3ForeignFallback,
|
||||
web3HomeRedundant,
|
||||
@@ -30,4 +31,8 @@ web3ForeignFallback.currentProvider.setLogger(logger)
|
||||
web3HomeRedundant.currentProvider.setLogger(logger)
|
||||
web3ForeignRedundant.currentProvider.setLogger(logger)
|
||||
|
||||
if (web3Side) {
|
||||
web3Side.currentProvider.setLogger(logger)
|
||||
}
|
||||
|
||||
module.exports = logger
|
||||
|
||||
23
oracle/src/services/shutdownState.js
Normal file
23
oracle/src/services/shutdownState.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const { redis } = require('./redisClient')
|
||||
|
||||
let isShutdown = false
|
||||
async function getShutdownFlag(logger, shutdownKey, force = false) {
|
||||
if (force) {
|
||||
logger.debug('Reading current shutdown state from the DB')
|
||||
isShutdown = (await redis.get(shutdownKey)) === 'true'
|
||||
logger.debug({ isShutdown }, 'Read shutdown state from the DB')
|
||||
}
|
||||
return isShutdown
|
||||
}
|
||||
|
||||
async function setShutdownFlag(logger, shutdownKey, value) {
|
||||
logger.info({ isShutdown: value }, 'Updating current shutdown state in the DB')
|
||||
isShutdown = value
|
||||
await redis.set(shutdownKey, value)
|
||||
logger.debug('Updated state in the DB')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getShutdownFlag,
|
||||
setShutdownFlag
|
||||
}
|
||||
@@ -6,6 +6,7 @@ const { RETRY_CONFIG } = require('../utils/constants')
|
||||
const {
|
||||
COMMON_HOME_RPC_URL,
|
||||
COMMON_FOREIGN_RPC_URL,
|
||||
ORACLE_SIDE_RPC_URL,
|
||||
ORACLE_RPC_REQUEST_TIMEOUT,
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL,
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL
|
||||
@@ -41,6 +42,18 @@ const web3Home = new Web3(homeProvider)
|
||||
const foreignProvider = new HttpListProvider(foreignUrls, foreignOptions)
|
||||
const web3Foreign = new Web3(foreignProvider)
|
||||
|
||||
let web3Side = null
|
||||
if (ORACLE_SIDE_RPC_URL) {
|
||||
const sideUrls = ORACLE_SIDE_RPC_URL.split(' ').filter(url => url.length > 0)
|
||||
const sideOptions = {
|
||||
requestTimeout: configuredTimeout || 2000,
|
||||
retry: RETRY_CONFIG
|
||||
}
|
||||
|
||||
const sideProvider = new HttpListProvider(sideUrls, sideOptions)
|
||||
web3Side = new Web3(sideProvider)
|
||||
}
|
||||
|
||||
// secondary fallback providers are intended to be used in places where
|
||||
// it is more likely that RPC calls to the local non-archive nodes can fail
|
||||
// e.g. for checking status of the old transaction via eth_getTransactionByHash
|
||||
@@ -70,6 +83,7 @@ if (foreignUrls.length > 1) {
|
||||
module.exports = {
|
||||
web3Home,
|
||||
web3Foreign,
|
||||
web3Side,
|
||||
web3HomeRedundant,
|
||||
web3ForeignRedundant,
|
||||
web3HomeFallback,
|
||||
|
||||
114
oracle/src/shutdownManager.js
Normal file
114
oracle/src/shutdownManager.js
Normal file
@@ -0,0 +1,114 @@
|
||||
const fetch = require('node-fetch')
|
||||
const path = require('path')
|
||||
|
||||
const { EXIT_CODES } = require('./utils/constants')
|
||||
const { watchdog } = require('./utils/utils')
|
||||
const logger = require('./services/logger')
|
||||
const { redis } = require('./services/redisClient')
|
||||
const { web3Side } = require('./services/web3')
|
||||
const { getShutdownFlag, setShutdownFlag } = require('./services/shutdownState')
|
||||
|
||||
if (process.argv.length < 3) {
|
||||
logger.error('Please check the number of arguments, config file was not provided')
|
||||
process.exit(EXIT_CODES.GENERAL_ERROR)
|
||||
}
|
||||
|
||||
const config = require(path.join('../config/', process.argv[2]))
|
||||
|
||||
if (config.shutdownContractAddress && !web3Side) {
|
||||
logger.error(
|
||||
'ORACLE_SHUTDOWN_CONTRACT_ADDRESS was provided but not side chain provider was registered.' +
|
||||
' Please, specify ORACLE_SIDE_RPC_URL as well.'
|
||||
)
|
||||
process.exit(EXIT_CODES.GENERAL_ERROR)
|
||||
}
|
||||
|
||||
let shutdownCount = 0
|
||||
let okCount = 0
|
||||
|
||||
async function fetchShutdownFlag() {
|
||||
if (config.shutdownServiceURL) {
|
||||
logger.debug({ url: config.shutdownServiceURL }, 'Fetching shutdown status from external URL')
|
||||
const result = await fetch(config.shutdownServiceURL, {
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
method: 'GET',
|
||||
timeout: config.requestTimeout
|
||||
}).then(res => res.json())
|
||||
|
||||
if (result.shutdown === true) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (config.shutdownContractAddress) {
|
||||
const shutdownSelector = web3Side.eth.abi.encodeFunctionSignature(config.shutdownMethod)
|
||||
logger.debug(
|
||||
{ contract: config.shutdownContractAddress, method: config.shutdownMethod, data: shutdownSelector },
|
||||
'Fetching shutdown status from contract'
|
||||
)
|
||||
const result = await web3Side.eth.call({
|
||||
to: config.shutdownContractAddress,
|
||||
data: shutdownSelector
|
||||
})
|
||||
logger.debug({ result }, 'Obtained result from the side RPC endpoint')
|
||||
|
||||
if (result.length > 2 && web3Side.eth.abi.decodeParameter('bool', result)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
async function checkShutdownFlag() {
|
||||
const isShutdownFlag = await fetchShutdownFlag()
|
||||
const isShutdown = await getShutdownFlag(logger, config.shutdownKey)
|
||||
|
||||
if (isShutdownFlag === true && isShutdown === false) {
|
||||
shutdownCount += 1
|
||||
okCount = 0
|
||||
logger.info(
|
||||
{ shutdownCount, remainingChecks: config.checksBeforeStop - shutdownCount },
|
||||
'Received positive shutdown flag'
|
||||
)
|
||||
} else if (isShutdownFlag === false && isShutdown === true) {
|
||||
okCount += 1
|
||||
shutdownCount = 0
|
||||
logger.info({ okCount, remainingChecks: config.checksBeforeResume - okCount }, 'Received negative shutdown flag')
|
||||
} else {
|
||||
shutdownCount = 0
|
||||
okCount = 0
|
||||
logger.debug({ isShutdown, isShutdownFlag }, 'Received shutdown flag that is equal to the current state')
|
||||
}
|
||||
|
||||
if (shutdownCount >= config.checksBeforeStop) {
|
||||
await setShutdownFlag(logger, config.shutdownKey, true)
|
||||
} else if (okCount >= config.checksBeforeResume) {
|
||||
await setShutdownFlag(logger, config.shutdownKey, false)
|
||||
}
|
||||
}
|
||||
|
||||
async function initialize() {
|
||||
logger.info('Starting shutdown flag watcher')
|
||||
redis.on('connect', async () => {
|
||||
await getShutdownFlag(logger, config.shutdownKey, true)
|
||||
await main()
|
||||
})
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await watchdog(checkShutdownFlag, config.maxProcessingTime, () => {
|
||||
logger.fatal('Max processing time reached')
|
||||
process.exit(EXIT_CODES.MAX_TIME_REACHED)
|
||||
})
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
}
|
||||
|
||||
setTimeout(main, config.pollingInterval)
|
||||
}
|
||||
|
||||
initialize()
|
||||
@@ -29,24 +29,30 @@ function checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger) {
|
||||
}
|
||||
}
|
||||
|
||||
const promiseRetryForever = f => promiseRetry(f, { forever: true, factor: 1 })
|
||||
|
||||
async function waitForFunds(web3, address, minimumBalance, cb, logger) {
|
||||
promiseRetry(
|
||||
async retry => {
|
||||
logger.debug('Getting balance of validator account')
|
||||
const newBalance = web3.utils.toBN(await web3.eth.getBalance(address))
|
||||
if (newBalance.gte(web3.utils.toBN(minimumBalance.toString(10)))) {
|
||||
logger.debug({ balance: newBalance, minimumBalance }, 'Validator has minimum necessary balance')
|
||||
cb(newBalance)
|
||||
} else {
|
||||
logger.debug({ balance: newBalance, minimumBalance }, 'Balance of validator is still less than the minimum')
|
||||
retry()
|
||||
}
|
||||
},
|
||||
{
|
||||
forever: true,
|
||||
factor: 1
|
||||
promiseRetryForever(async retry => {
|
||||
logger.debug('Getting balance of validator account')
|
||||
const newBalance = web3.utils.toBN(await web3.eth.getBalance(address))
|
||||
if (newBalance.gte(web3.utils.toBN(minimumBalance.toString(10)))) {
|
||||
logger.debug({ balance: newBalance, minimumBalance }, 'Validator has minimum necessary balance')
|
||||
cb(newBalance)
|
||||
} else {
|
||||
logger.debug({ balance: newBalance, minimumBalance }, 'Balance of validator is still less than the minimum')
|
||||
retry()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
async function waitForUnsuspend(getSuspendFlag, cb) {
|
||||
promiseRetryForever(async retry => {
|
||||
if (await getSuspendFlag()) {
|
||||
retry()
|
||||
} else {
|
||||
cb()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function addExtraGas(gas, extraPercentage, maxGasLimit = Infinity) {
|
||||
@@ -58,9 +64,9 @@ function addExtraGas(gas, extraPercentage, maxGasLimit = Infinity) {
|
||||
return BigNumber.min(maxGasLimit, gasWithExtra)
|
||||
}
|
||||
|
||||
function setIntervalAndRun(f, interval) {
|
||||
async function setIntervalAndRun(f, interval) {
|
||||
const handler = setInterval(f, interval)
|
||||
f()
|
||||
await f()
|
||||
return handler
|
||||
}
|
||||
|
||||
@@ -135,6 +141,7 @@ module.exports = {
|
||||
syncForEach,
|
||||
checkHTTPS,
|
||||
waitForFunds,
|
||||
waitForUnsuspend,
|
||||
addExtraGas,
|
||||
setIntervalAndRun,
|
||||
watchdog,
|
||||
|
||||
@@ -5,6 +5,7 @@ const { connectWatcherToQueue, connection } = require('./services/amqpClient')
|
||||
const { getBlockNumber } = require('./tx/web3')
|
||||
const { redis } = require('./services/redisClient')
|
||||
const logger = require('./services/logger')
|
||||
const { getShutdownFlag } = require('./services/shutdownState')
|
||||
const { getRequiredBlockConfirmations, getEvents } = require('./tx/web3')
|
||||
const { checkHTTPS, watchdog } = require('./utils/utils')
|
||||
const { EXIT_CODES } = require('./utils/constants')
|
||||
@@ -157,6 +158,16 @@ async function isWorkerNeeded() {
|
||||
|
||||
async function main({ sendToQueue, sendToWorker }) {
|
||||
try {
|
||||
const wasShutdown = await getShutdownFlag(logger, config.shutdownKey, false)
|
||||
if (await getShutdownFlag(logger, config.shutdownKey, true)) {
|
||||
if (!wasShutdown) {
|
||||
logger.info('Oracle watcher was suspended via the remote shutdown process')
|
||||
}
|
||||
return
|
||||
} else if (wasShutdown) {
|
||||
logger.info(`Oracle watcher was unsuspended.`)
|
||||
}
|
||||
|
||||
await checkConditions()
|
||||
|
||||
const lastBlockToProcess = await getLastBlockToProcess()
|
||||
@@ -167,7 +178,8 @@ async function main({ sendToQueue, sendToWorker }) {
|
||||
}
|
||||
|
||||
const fromBlock = lastProcessedBlock.add(ONE)
|
||||
const toBlock = lastBlockToProcess
|
||||
const rangeEndBlock = config.blockPollingLimit ? fromBlock.add(config.blockPollingLimit) : lastBlockToProcess
|
||||
const toBlock = BN.min(lastBlockToProcess, rangeEndBlock)
|
||||
|
||||
const events = await getEvents({
|
||||
contract: eventContract,
|
||||
@@ -191,8 +203,8 @@ async function main({ sendToQueue, sendToWorker }) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug({ lastProcessedBlock: lastBlockToProcess.toString() }, 'Updating last processed block')
|
||||
await updateLastProcessedBlock(lastBlockToProcess)
|
||||
logger.debug({ lastProcessedBlock: toBlock.toString() }, 'Updating last processed block')
|
||||
await updateLastProcessedBlock(toBlock)
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
"commons",
|
||||
"oracle",
|
||||
"oracle-e2e",
|
||||
"ui",
|
||||
"ui-e2e",
|
||||
"monitor",
|
||||
"monitor-e2e",
|
||||
"contracts",
|
||||
@@ -35,15 +33,12 @@
|
||||
],
|
||||
"scripts": {
|
||||
"initialize": "yarn clean && git submodule update --init && yarn install --unsafe-perm --frozen-lockfile && yarn install:deploy && yarn compile:contracts",
|
||||
"build": "yarn build:ui && yarn build:alm && yarn build:plugin",
|
||||
"build:ui": "yarn workspace ui run build",
|
||||
"build": "yarn build:alm && yarn build:plugin",
|
||||
"build:alm": "yarn workspace alm run build",
|
||||
"build:plugin": "yarn workspace burner-wallet-plugin run build",
|
||||
"lint": "yarn wsrun --exclude tokenbridge-contracts lint",
|
||||
"test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e --exclude alm-e2e test",
|
||||
"test": "yarn wsrun --exclude oracle-e2e --exclude monitor-e2e --exclude alm-e2e test",
|
||||
"oracle-e2e": "./oracle-e2e/run-tests.sh",
|
||||
"ui-e2e": "./ui-e2e/run-tests.sh",
|
||||
"ui-e2e:ci": "xvfb-run yarn ui-e2e",
|
||||
"monitor-e2e": "./monitor-e2e/run-tests.sh",
|
||||
"alm-e2e": "./alm-e2e/run-tests.sh",
|
||||
"clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build ./**/**/dist",
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"plugin:node/recommended",
|
||||
"airbnb-base",
|
||||
"../.eslintrc"
|
||||
],
|
||||
"plugins": ["node"],
|
||||
"rules": {
|
||||
"node/no-unpublished-require": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"no-return-await": "off"
|
||||
},
|
||||
"env": {
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,160 +0,0 @@
|
||||
const { Key } = require('selenium-webdriver')
|
||||
const { By } = require('selenium-webdriver/lib/by')
|
||||
const { Page } = require('./Page.js')
|
||||
const { homeRPC, foreignRPC } = require('../e2e-commons/constants.json')
|
||||
|
||||
const IDMetaMask = 'hccmbhdehlhjhkenmcjnbcahkmljpife'
|
||||
const URL = 'chrome-extension://' + IDMetaMask + '//popup.html'
|
||||
const buttonSubmit = By.className('confirm btn-green')
|
||||
const buttonAccept = By.xpath('//*[@id="app-content"]/div/div[4]/div/button')
|
||||
const agreement = By.xpath('//*[@id="app-content"]/div/div[4]/div/div/div/p[1]/strong')
|
||||
const fieldNewPass = By.xpath('//*[@id="password-box"]')
|
||||
const fieldConfirmPass = By.xpath('//*[@id="password-box-confirm"]')
|
||||
const buttonCreate = By.xpath('//*[@id="app-content"]/div/div[4]/div/button')
|
||||
const buttonIveCopied = By.xpath('//*[@id="app-content"]/div/div[4]/div/button[1]')
|
||||
const popupNetwork = By.className('network-name')
|
||||
const popupAccount = By.xpath('//*[@id="app-content"]/div/div[1]/div/div[2]/span/div')
|
||||
const fieldPrivateKey = By.xpath('//*[@id="private-key-box"]')
|
||||
const pass = 'qwerty12345'
|
||||
const buttonImport = By.xpath('//*[@id="app-content"]/div/div[4]/div/div[3]/button')
|
||||
const fieldNewRPCURL = By.id('new_rpc')
|
||||
const buttonSave = By.xpath('//*[@id="app-content"]/div/div[4]/div/div[3]/div/div[2]/button')
|
||||
const arrowBackRPCURL = By.xpath('//*[@id="app-content"]/div/div[4]/div/div[1]/i')
|
||||
const iconChangeAccount = By.className('cursor-pointer color-orange accounts-selector')
|
||||
|
||||
let accountOrderNumber = 1
|
||||
const networks = [0, 3, 43, 4, 8545]
|
||||
|
||||
class MetaMask extends Page {
|
||||
constructor(driver) {
|
||||
super(driver)
|
||||
this.driver = driver
|
||||
this.URL = URL
|
||||
}
|
||||
|
||||
async clickButtonSubmitTransaction() {
|
||||
return this.clickWithWait(buttonSubmit)
|
||||
}
|
||||
|
||||
async activate() {
|
||||
return (
|
||||
(await this.switchToNextPage()) &&
|
||||
(await this.open(this.URL)) === this.URL &&
|
||||
(await this.clickWithWait(buttonAccept)) &&
|
||||
(await this.clickWithWait(agreement)) &&
|
||||
(await this.clickKey(Key.TAB, 15)) &&
|
||||
(await this.clickWithWait(buttonAccept)) &&
|
||||
(await this.waitUntilLocated(fieldNewPass)) &&
|
||||
(await this.clickWithWait(fieldNewPass)) &&
|
||||
(await this.fillWithWait(fieldNewPass, pass)) &&
|
||||
(await this.fillWithWait(fieldConfirmPass, pass)) &&
|
||||
(await this.clickWithWait(buttonCreate)) &&
|
||||
(await this.waitUntilDisplayed(buttonIveCopied)) &&
|
||||
(await this.clickWithWait(buttonIveCopied)) &&
|
||||
(await this.switchToNextPage())
|
||||
)
|
||||
}
|
||||
|
||||
async importAccount(user) {
|
||||
user.accountOrderInMetamask = accountOrderNumber - 1
|
||||
return (
|
||||
(await this.switchToNextPage()) &&
|
||||
(await this.setNetwork(user.networkID)) &&
|
||||
(await this.clickImportAccount()) &&
|
||||
(await this.fillWithWait(fieldPrivateKey, user.privateKey)) &&
|
||||
(await this.waitUntilDisplayed(buttonImport)) &&
|
||||
(await this.clickWithWait(buttonImport)) &&
|
||||
(await this.switchToNextPage())
|
||||
)
|
||||
}
|
||||
|
||||
async selectAccount(user) {
|
||||
try {
|
||||
await this.switchToNextPage()
|
||||
await this.setNetwork(user.networkID)
|
||||
await super.clickWithWait(popupAccount)
|
||||
await this.driver.executeScript(
|
||||
"document.getElementsByClassName('dropdown-menu-item')[" + user.accountOrderInMetamask + '].click();'
|
||||
)
|
||||
await this.switchToNextPage()
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async clickImportAccount() {
|
||||
try {
|
||||
await super.clickWithWait(popupAccount)
|
||||
await this.driver.executeScript(
|
||||
"document.getElementsByClassName('dropdown-menu-item')[" + (accountOrderNumber + 1) + '].click();'
|
||||
)
|
||||
accountOrderNumber++
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async signTransaction(refreshCount) {
|
||||
await this.switchToNextPage()
|
||||
let counter = 5
|
||||
if (refreshCount !== undefined) counter = refreshCount
|
||||
do {
|
||||
await this.refresh()
|
||||
await super.waitUntilLocated(iconChangeAccount)
|
||||
if (await this.isElementDisplayed(buttonSubmit)) {
|
||||
return (await this.clickButtonSubmitTransaction()) && (await this.switchToNextPage())
|
||||
}
|
||||
await this.driver.sleep(3000)
|
||||
} while (counter-- >= 0)
|
||||
|
||||
await this.switchToNextPage()
|
||||
return false
|
||||
}
|
||||
|
||||
async setNetwork(provider) {
|
||||
try {
|
||||
await super.clickWithWait(popupNetwork)
|
||||
const orderNumber = networks.indexOf(provider)
|
||||
const script = "document.getElementsByClassName('dropdown-menu-item')[" + orderNumber + '].click();'
|
||||
if (orderNumber < 0) await this.addNetwork(provider)
|
||||
else await this.driver.executeScript(script)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async addNetwork(provider) {
|
||||
let url
|
||||
switch (provider) {
|
||||
case homeRPC.ID: {
|
||||
url = 'http://localhost:8541'
|
||||
networks.push(177)
|
||||
break
|
||||
}
|
||||
case foreignRPC.ID: {
|
||||
url = 'http://localhost:8542'
|
||||
networks.push(142)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unexcpected provider ${provider}`)
|
||||
}
|
||||
}
|
||||
const index = networks.length > 8 ? 8 : networks.length
|
||||
await this.driver.executeScript(
|
||||
"document.getElementsByClassName('dropdown-menu-item')[" + (index - 1) + '].click();'
|
||||
)
|
||||
return (
|
||||
(await super.fillWithWait(fieldNewRPCURL, url)) &&
|
||||
(await super.clickWithWait(buttonSave)) &&
|
||||
(await super.clickWithWait(arrowBackRPCURL))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
MetaMask
|
||||
}
|
||||
149
ui-e2e/Page.js
149
ui-e2e/Page.js
@@ -1,149 +0,0 @@
|
||||
const webdriver = require('selenium-webdriver')
|
||||
|
||||
const Twait = 20000
|
||||
|
||||
class Page {
|
||||
constructor(driver) {
|
||||
this.driver = driver
|
||||
}
|
||||
|
||||
async waitUntilDisplayed(element, Twaiting) {
|
||||
let counter = Twaiting
|
||||
if (counter === undefined) counter = 180
|
||||
try {
|
||||
do {
|
||||
await this.driver.sleep(300)
|
||||
if (await this.isElementDisplayed(element)) return true
|
||||
} while (counter-- > 0)
|
||||
return false
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async waitUntilDisappear(element, Twaiting) {
|
||||
let counter = Twaiting
|
||||
if (counter === undefined) counter = 180
|
||||
try {
|
||||
do {
|
||||
await this.driver.sleep(300)
|
||||
if (!(await this.isElementDisplayed(element))) return true
|
||||
} while (counter-- > 0)
|
||||
return false
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async waitUntilLocated(element, Twaiting) {
|
||||
let counter = Twaiting
|
||||
if (counter === undefined) counter = 180
|
||||
try {
|
||||
do {
|
||||
await this.driver.sleep(300)
|
||||
if (await this.isElementLocated(element)) return true
|
||||
} while (counter-- > 0)
|
||||
return false
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async isElementDisplayed(element) {
|
||||
try {
|
||||
return await this.driver.findElement(element).isDisplayed()
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async isElementLocated(element) {
|
||||
return (await this.driver.findElements(element)).length > 0
|
||||
}
|
||||
|
||||
async clickWithWait(element) {
|
||||
try {
|
||||
let field
|
||||
if (element.constructor.name !== 'WebElement') {
|
||||
field = await this.driver.wait(webdriver.until.elementLocated(element), Twait)
|
||||
} else field = element
|
||||
await field.click()
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async fillWithWait(element, text) {
|
||||
try {
|
||||
let field
|
||||
if (element.constructor.name !== 'WebElement') {
|
||||
field = await this.driver.wait(webdriver.until.elementLocated(element), Twait)
|
||||
} else field = element
|
||||
await field.sendKeys(text)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async findWithWait(element) {
|
||||
try {
|
||||
await this.driver.wait(webdriver.until.elementLocated(element), Twait)
|
||||
return await this.driver.findElements(element)
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async switchToNextPage() {
|
||||
let allHandles = []
|
||||
let curHandle
|
||||
try {
|
||||
allHandles = await this.driver.getAllWindowHandles()
|
||||
curHandle = await this.driver.getWindowHandle()
|
||||
if (allHandles.length > 2) {
|
||||
allHandles = [allHandles[0], allHandles[1]]
|
||||
}
|
||||
let handle
|
||||
for (let i = 0; i < allHandles.length; i++) {
|
||||
if (curHandle !== allHandles[i]) {
|
||||
handle = allHandles[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
await this.driver.switchTo().window(handle)
|
||||
await this.driver.sleep(500)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
await this.driver.navigate().refresh()
|
||||
}
|
||||
|
||||
async getUrl() {
|
||||
return await this.driver.getCurrentUrl()
|
||||
}
|
||||
|
||||
async open(url) {
|
||||
await this.driver.get(url)
|
||||
return this.getUrl()
|
||||
}
|
||||
|
||||
async clickKey(key, times) {
|
||||
try {
|
||||
const action = this.driver.actions()
|
||||
for (let i = 0; i < times; i++) await action.sendKeys(key).perform()
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Page
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
# POA TokenBridge / UI-E2E
|
||||
|
||||
End to end tests for the POA TokenBridge [UI](../UI/README.md).
|
||||
|
||||
- Configure startURL, homeAccount, foreignAccount in ```config.json```
|
||||
|
||||
## Running
|
||||
|
||||
To run the bridge end-to-end tests, you just have to run:
|
||||
|
||||
```
|
||||
./run-tests.sh
|
||||
```
|
||||
|
||||
#### Tests
|
||||
|
||||
```
|
||||
1. User is able to open main page of bridge-ui
|
||||
2. Main page: foreign POA balance is displayed
|
||||
3. Main page: home POA balance is displayed
|
||||
|
||||
4. User is able to send tokens from Home account to Foreign account
|
||||
5. Home POA balance has correctly changed after transaction
|
||||
6. Foreign account has received correct amount of tokens after transaction
|
||||
|
||||
7. User is able to send tokens from Foreign account to Home account
|
||||
8. Foreign POA balance has correctly changed after transaction
|
||||
9. Home account has received correct amount of tokens after transaction
|
||||
```
|
||||
@@ -1,52 +0,0 @@
|
||||
const { MetaMask } = require('./MetaMask.js')
|
||||
const { MainPage } = require('./mainPage.js')
|
||||
|
||||
class User {
|
||||
constructor(driver, obj) {
|
||||
try {
|
||||
this.driver = driver
|
||||
this.account = obj.account
|
||||
this.privateKey = obj.privateKey
|
||||
this.networkID = obj.networkID
|
||||
this.accountOrderInMetamask = 'undefined' // for MetaMaskPage usage only
|
||||
} catch (err) {
|
||||
console.log('instance User was not created')
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
async transferTokens(amount) {
|
||||
const mainPage = new MainPage(this.driver)
|
||||
const metaMask = new MetaMask(this.driver)
|
||||
return (
|
||||
(await mainPage.fillFieldAmount(amount)) &&
|
||||
(await mainPage.clickButtonTransfer()) &&
|
||||
(await mainPage.waitUntilShowUpButtonTransferConfirm()) &&
|
||||
(await mainPage.clickButtonTransferConfirm()) &&
|
||||
(await metaMask.signTransaction()) &&
|
||||
(await mainPage.waitUntilTransactionDone()) &&
|
||||
(await mainPage.waitUntilShowUpButtonOk()) &&
|
||||
(await mainPage.clickButtonOk())
|
||||
)
|
||||
}
|
||||
|
||||
async setMetaMaskNetwork() {
|
||||
const metaMask = new MetaMask(this.driver)
|
||||
return (
|
||||
(await metaMask.switchToNextPage()) &&
|
||||
(await metaMask.setNetwork(this.networkID)) &&
|
||||
(await metaMask.switchToNextPage())
|
||||
)
|
||||
}
|
||||
|
||||
async setMetaMaskAccount() {
|
||||
const metaMask = new MetaMask(this.driver)
|
||||
if (this.accountOrderInMetamask === 'undefined') {
|
||||
return await metaMask.importAccount(this)
|
||||
} else return await metaMask.selectAccount(this)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
User
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
const webdriver = require('selenium-webdriver')
|
||||
const chrome = require('selenium-webdriver/chrome')
|
||||
const {
|
||||
user,
|
||||
nativeToErcBridge,
|
||||
ercToErcBridge,
|
||||
ercToNativeBridge,
|
||||
ambStakeErcToErc,
|
||||
homeRPC,
|
||||
foreignRPC
|
||||
} = require('../e2e-commons/constants.json')
|
||||
|
||||
class Utils {
|
||||
static async getHomeAccount() {
|
||||
return {
|
||||
account: user.address,
|
||||
privateKey: user.privateKey,
|
||||
networkID: homeRPC.ID
|
||||
}
|
||||
}
|
||||
|
||||
static async getForeignAccount() {
|
||||
return {
|
||||
account: user.address,
|
||||
privateKey: user.privateKey,
|
||||
networkID: foreignRPC.ID
|
||||
}
|
||||
}
|
||||
|
||||
static async getStartURL() {
|
||||
return nativeToErcBridge.ui
|
||||
}
|
||||
|
||||
static async getErc20StartURL() {
|
||||
return ercToErcBridge.ui
|
||||
}
|
||||
|
||||
static async getErc20NativeStartURL() {
|
||||
return ercToNativeBridge.ui
|
||||
}
|
||||
|
||||
static async getAMBStakeStartURL() {
|
||||
return ambStakeErcToErc.ui
|
||||
}
|
||||
|
||||
static async startBrowserWithMetamask() {
|
||||
const source = './MetaMask.crx'
|
||||
const options = new chrome.Options()
|
||||
await options.addArguments('--no-sandbox')
|
||||
await options.addArguments('--disable-gpu')
|
||||
await options.addArguments('--disable-dev-shm-usage')
|
||||
await options.addArguments('disable-popup-blocking')
|
||||
await options.addExtensions(source)
|
||||
const driver = await new webdriver.Builder().withCapabilities(options.toCapabilities()).build()
|
||||
await driver.sleep(5000)
|
||||
return driver
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Utils
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
const { By } = require('selenium-webdriver/lib/by')
|
||||
const { Page } = require('./Page.js')
|
||||
|
||||
const fieldAmount = By.id('amount')
|
||||
const buttonTransfer = By.className('bridge-form-button')
|
||||
const buttonOk = By.className('swal-button swal-button--confirm')
|
||||
const fieldsBalance = By.className('network-balance')
|
||||
const classWeb3Loaded = By.className('web3-loaded')
|
||||
const classPendingTransaction = By.className('pending-transaction')
|
||||
const loadingContainer = By.className('loading-container')
|
||||
const buttonTransferConfirm = By.className('transfer-confirm')
|
||||
const buttonDisclaimerConfirm = By.className('disclaimer-confirm')
|
||||
const checkboxDisclaimer = By.className('disclaimer-checkbox')
|
||||
const disclaimer = By.className('disclaimer-title')
|
||||
|
||||
class MainPage extends Page {
|
||||
async initFieldsBalance() {
|
||||
if (!(await this.waitUntilWeb3Loaded())) return null
|
||||
try {
|
||||
const array = await super.findWithWait(fieldsBalance)
|
||||
/* eslint-disable prefer-destructuring */
|
||||
this.fieldHomePOABalance = array[0]
|
||||
this.fieldForeignPOABalance = array[1]
|
||||
/* eslint-enable prefer-destructuring */
|
||||
return array
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async getHomePOABalance() {
|
||||
await this.initFieldsBalance()
|
||||
return parseFloat(await this.fieldHomePOABalance.getText())
|
||||
}
|
||||
|
||||
async getForeignPOABalance() {
|
||||
await this.initFieldsBalance()
|
||||
return parseFloat(await this.fieldForeignPOABalance.getText())
|
||||
}
|
||||
|
||||
async fillFieldAmount(amount) {
|
||||
try {
|
||||
await this.clickWithWait(fieldAmount)
|
||||
await this.fillWithWait(fieldAmount, amount)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async clickButtonTransfer() {
|
||||
return this.clickWithWait(buttonTransfer)
|
||||
}
|
||||
|
||||
async clickButtonOk() {
|
||||
return super.clickWithWait(buttonOk)
|
||||
}
|
||||
|
||||
async clickButtonTransferConfirm() {
|
||||
return super.clickWithWait(buttonTransferConfirm)
|
||||
}
|
||||
|
||||
async isPresentButtonOk() {
|
||||
return super.isElementDisplayed(buttonOk, 180)
|
||||
}
|
||||
|
||||
async waitUntilWeb3Loaded() {
|
||||
return this.waitUntilLocated(classWeb3Loaded, 180)
|
||||
}
|
||||
|
||||
async isPendingTransaction() {
|
||||
return super.isElementLocated(classPendingTransaction)
|
||||
}
|
||||
|
||||
async waitUntilTransactionDone() {
|
||||
return this.waitUntilDisappear(classPendingTransaction, 360)
|
||||
}
|
||||
|
||||
async waitUntilShowUpButtonOk() {
|
||||
return super.waitUntilDisplayed(buttonOk, 360)
|
||||
}
|
||||
|
||||
async waitUntilShowUpButtonTransferConfirm() {
|
||||
return super.waitUntilDisplayed(buttonTransferConfirm, 360)
|
||||
}
|
||||
|
||||
async waitUntilShowUpLoadingContainer() {
|
||||
return super.waitUntilDisplayed(loadingContainer, 180)
|
||||
}
|
||||
|
||||
async isDisplayedLoadingContainer() {
|
||||
return super.isElementDisplayed(loadingContainer)
|
||||
}
|
||||
|
||||
async confirmDisclaimer() {
|
||||
return (
|
||||
(await super.waitUntilDisplayed(disclaimer, 180)) &&
|
||||
// await this.clickCheckboxDisclaimer() &&
|
||||
(await this.clickButtonDisclaimerConfirm())
|
||||
)
|
||||
}
|
||||
|
||||
async clickButtonDisclaimerConfirm() {
|
||||
return super.clickWithWait(buttonDisclaimerConfirm)
|
||||
}
|
||||
|
||||
async clickCheckboxDisclaimer() {
|
||||
return super.clickWithWait(checkboxDisclaimer)
|
||||
}
|
||||
|
||||
async open(url) {
|
||||
let counter = 60
|
||||
do {
|
||||
await this.driver.sleep(1000)
|
||||
await super.open(url)
|
||||
} while (counter-- >= 0 && !(await this.isElementDisplayed(disclaimer)))
|
||||
return counter >= 0
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
MainPage
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "ui-e2e",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"mocha": "^5.2.0",
|
||||
"selenium-webdriver": "3.6.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . --ignore-path ../.eslintignore"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.18"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
../e2e-commons/up.sh deploy oracle ui blocks
|
||||
|
||||
yarn mocha -b ./test.js
|
||||
rc=$?
|
||||
|
||||
../e2e-commons/down.sh
|
||||
exit $rc
|
||||
401
ui-e2e/test.js
401
ui-e2e/test.js
@@ -1,401 +0,0 @@
|
||||
const test = require('selenium-webdriver/testing')
|
||||
const assert = require('assert')
|
||||
const { Utils } = require('./Utils.js')
|
||||
const { MetaMask } = require('./MetaMask.js')
|
||||
const { MainPage } = require('./mainPage.js')
|
||||
const { User } = require('./User.js')
|
||||
|
||||
test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
|
||||
this.timeout(5 * 60000)
|
||||
this.slow(1 * 60000)
|
||||
this.retries(2)
|
||||
|
||||
const maxAmountPerTransactionLimit = 1
|
||||
let startURL
|
||||
let driver
|
||||
let mainPage
|
||||
let homeAccount
|
||||
let foreignAccount
|
||||
let metaMask
|
||||
let foreignBalanceBefore
|
||||
let homeBalanceBefore
|
||||
|
||||
test.before(async () => {
|
||||
try {
|
||||
driver = await Utils.startBrowserWithMetamask()
|
||||
mainPage = new MainPage(driver)
|
||||
homeAccount = new User(driver, await Utils.getHomeAccount())
|
||||
foreignAccount = new User(driver, await Utils.getForeignAccount())
|
||||
metaMask = new MetaMask(driver)
|
||||
await metaMask.activate()
|
||||
await homeAccount.setMetaMaskAccount()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
})
|
||||
|
||||
test.after(async () => {
|
||||
try {
|
||||
await driver.quit()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
})
|
||||
|
||||
test.describe('NATIVE TO ERC', async () => {
|
||||
test.it('User is able to open main page of bridge-ui ', async () => {
|
||||
startURL = await Utils.getStartURL()
|
||||
const result = await mainPage.open(startURL)
|
||||
console.log('Test URL: ' + startURL)
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
|
||||
})
|
||||
|
||||
test.it('Home page: disclaimer is displayed ', async () => {
|
||||
const result = await mainPage.confirmDisclaimer()
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
|
||||
})
|
||||
|
||||
test.it('Main page: foreign POA balance is displayed', async () => {
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
|
||||
const result = foreignBalanceBefore === 0
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is zero or not displayed ')
|
||||
})
|
||||
|
||||
test.it('Main page: home POA balance is displayed', async () => {
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance()
|
||||
console.log('homeBalanceBefore = ' + homeBalanceBefore)
|
||||
const result = homeBalanceBefore !== 0
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is zero or not displayed ')
|
||||
})
|
||||
|
||||
test.it('User is able to send tokens from Home account to Foreign account', async () => {
|
||||
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
|
||||
return await assert.strictEqual(
|
||||
result,
|
||||
true,
|
||||
'Test FAILED. User is able send tokens from Home account to Foreign account'
|
||||
)
|
||||
})
|
||||
|
||||
test.it('Home POA balance has correctly changed after transaction', async () => {
|
||||
const newHomeBalance = await mainPage.getHomePOABalance()
|
||||
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
|
||||
console.log('newHomeBalance = ' + newHomeBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
|
||||
homeBalanceBefore = newHomeBalance
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('Foreign account has received correct amount of tokens after transaction ', async () => {
|
||||
const newForeignBalance = await mainPage.getForeignPOABalance()
|
||||
|
||||
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
|
||||
console.log('newForeignBalance = ' + newForeignBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
|
||||
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('User is able to send tokens from Foreign account to Home account', async () => {
|
||||
await foreignAccount.setMetaMaskNetwork()
|
||||
foreignBalanceBefore = await mainPage.getHomePOABalance()
|
||||
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
|
||||
return await assert.strictEqual(
|
||||
result,
|
||||
true,
|
||||
'Test FAILED. User is able send tokens from Home account to Foreign account'
|
||||
)
|
||||
})
|
||||
|
||||
test.it('Foreign POA balance has correctly changed after transaction', async () => {
|
||||
const newForeignBalance = await mainPage.getHomePOABalance()
|
||||
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
|
||||
console.log('newForeignBalance = ' + newForeignBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('Home account has received correct amount of tokens after transaction', async () => {
|
||||
const newHomeBalance = await mainPage.getForeignPOABalance()
|
||||
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
|
||||
console.log('newHomeBalance = ' + newHomeBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('ERC TO ERC', async () => {
|
||||
test.it('User is able to open main page of bridge-ui ', async () => {
|
||||
await foreignAccount.setMetaMaskNetwork()
|
||||
startURL = await Utils.getErc20StartURL()
|
||||
const result = await mainPage.open(startURL)
|
||||
console.log('Test URL: ' + startURL)
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
|
||||
})
|
||||
|
||||
test.it('Home page: disclaimer is displayed ', async () => {
|
||||
const result = await mainPage.confirmDisclaimer()
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
|
||||
})
|
||||
|
||||
test.it('Main page: foreign erc20 balance is displayed', async () => {
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
|
||||
const result = foreignBalanceBefore === 0
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Foreign erc20 balance is not zero')
|
||||
})
|
||||
|
||||
test.it('Main page: home erc20 balance is displayed', async () => {
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance()
|
||||
console.log('homeBalanceBefore = ' + homeBalanceBefore)
|
||||
const result = homeBalanceBefore !== 0
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Home erc20 balance is zero or not displayed ')
|
||||
})
|
||||
|
||||
test.it('User is able to send tokens from Foreign account to Home account ', async () => {
|
||||
homeBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
foreignBalanceBefore = await mainPage.getHomePOABalance()
|
||||
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
|
||||
return await assert.strictEqual(
|
||||
result,
|
||||
true,
|
||||
'Test FAILED. User is able send tokens from Foreign account to Home account'
|
||||
)
|
||||
})
|
||||
|
||||
test.it('Foreign POA balance has correctly changed after transaction', async () => {
|
||||
const newForeignBalance = await mainPage.getHomePOABalance()
|
||||
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
|
||||
console.log('newForeignBalance = ' + newForeignBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('Home account has received correct amount of tokens after transaction ', async () => {
|
||||
const newHomeBalance = await mainPage.getForeignPOABalance()
|
||||
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
|
||||
console.log('newHomeBalance = ' + newHomeBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
|
||||
})
|
||||
test.it('User is able to send tokens from Home account to Foreign account ', async () => {
|
||||
await homeAccount.setMetaMaskNetwork()
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance()
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
|
||||
return await assert.strictEqual(
|
||||
result,
|
||||
true,
|
||||
'Test FAILED. User is able send tokens from Home account to Foreign account'
|
||||
)
|
||||
})
|
||||
|
||||
test.it('Home POA balance has correctly changed after transaction', async () => {
|
||||
const newHomeBalance = await mainPage.getHomePOABalance()
|
||||
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
|
||||
console.log('newHomeBalance = ' + newHomeBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
|
||||
homeBalanceBefore = newHomeBalance
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('Foreign account has received correct amount of tokens after transaction ', async () => {
|
||||
const newForeignBalance = await mainPage.getForeignPOABalance()
|
||||
|
||||
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
|
||||
console.log('newForeignBalance = ' + newForeignBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
|
||||
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('ERC TO NATIVE', async () => {
|
||||
test.it('User is able to open main page of bridge-ui ', async () => {
|
||||
startURL = await Utils.getErc20NativeStartURL()
|
||||
const result = await mainPage.open(startURL)
|
||||
console.log('Test URL: ' + startURL)
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
|
||||
})
|
||||
|
||||
test.it('Home page: disclaimer is displayed ', async () => {
|
||||
const result = await mainPage.confirmDisclaimer()
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
|
||||
})
|
||||
|
||||
test.it('Main page: foreign erc20 balance is displayed', async () => {
|
||||
await foreignAccount.setMetaMaskNetwork()
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
|
||||
const result = foreignBalanceBefore !== 0
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Foreign erc20 balance is zero')
|
||||
})
|
||||
|
||||
test.it('Main page: home erc20 balance is displayed', async () => {
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance()
|
||||
console.log('homeBalanceBefore = ' + homeBalanceBefore)
|
||||
const result = homeBalanceBefore !== 0
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Home erc20 balance is zero or not displayed ')
|
||||
})
|
||||
|
||||
test.it('User is able to send tokens from Foreign account to Home account', async () => {
|
||||
homeBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
foreignBalanceBefore = await mainPage.getHomePOABalance()
|
||||
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
|
||||
return await assert.strictEqual(
|
||||
result,
|
||||
true,
|
||||
'Test FAILED. User is able send tokens from Foreign account to Home account'
|
||||
)
|
||||
})
|
||||
|
||||
test.it('Foreign POA balance has correctly changed after transaction', async () => {
|
||||
const newForeignBalance = await mainPage.getHomePOABalance()
|
||||
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
|
||||
console.log('newForeignBalance = ' + newForeignBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('Home account has received correct amount of tokens after transaction', async () => {
|
||||
const newHomeBalance = await mainPage.getForeignPOABalance()
|
||||
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
|
||||
console.log('newHomeBalance = ' + newHomeBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
|
||||
})
|
||||
test.it('User is able to send tokens from Home account to Foreign account', async () => {
|
||||
await homeAccount.setMetaMaskNetwork()
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance()
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
|
||||
return await assert.strictEqual(
|
||||
result,
|
||||
true,
|
||||
'Test FAILED. User is able send tokens from Home account to Foreign account'
|
||||
)
|
||||
})
|
||||
|
||||
test.it('Home POA balance has correctly changed after transaction', async () => {
|
||||
const newHomeBalance = await mainPage.getHomePOABalance()
|
||||
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
|
||||
console.log('newHomeBalance = ' + newHomeBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
|
||||
homeBalanceBefore = newHomeBalance
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('Foreign account has received correct amount of tokens after transaction', async () => {
|
||||
const newForeignBalance = await mainPage.getForeignPOABalance()
|
||||
|
||||
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
|
||||
console.log('newForeignBalance = ' + newForeignBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
|
||||
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('AMB-STAKE-ERC-TO-ERC', async () => {
|
||||
test.it('User is able to open main page of bridge-ui ', async () => {
|
||||
startURL = await Utils.getAMBStakeStartURL()
|
||||
const result = await mainPage.open(startURL)
|
||||
console.log('Test URL: ' + startURL)
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
|
||||
})
|
||||
|
||||
test.it('Home page: disclaimer is displayed ', async () => {
|
||||
const result = await mainPage.confirmDisclaimer()
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
|
||||
})
|
||||
|
||||
test.it('Main page: foreign erc20 balance is displayed', async () => {
|
||||
await foreignAccount.setMetaMaskNetwork()
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
|
||||
const result = foreignBalanceBefore !== 0
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Foreign erc20 balance is zero')
|
||||
})
|
||||
|
||||
test.it('Main page: home erc20 balance is displayed', async () => {
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance()
|
||||
console.log('homeBalanceBefore = ' + homeBalanceBefore)
|
||||
const result = homeBalanceBefore !== 0
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Home erc20 balance is zero or not displayed ')
|
||||
})
|
||||
|
||||
test.it('User is able to send tokens from Foreign account to Home account', async () => {
|
||||
homeBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
foreignBalanceBefore = await mainPage.getHomePOABalance()
|
||||
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
|
||||
return await assert.strictEqual(
|
||||
result,
|
||||
true,
|
||||
'Test FAILED. User is able send tokens from Foreign account to Home account'
|
||||
)
|
||||
})
|
||||
|
||||
test.it('Foreign POA balance has correctly changed after transaction', async () => {
|
||||
const newForeignBalance = await mainPage.getHomePOABalance()
|
||||
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
|
||||
console.log('newForeignBalance = ' + newForeignBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('Home account has received correct amount of tokens after transaction', async () => {
|
||||
const newHomeBalance = await mainPage.getForeignPOABalance()
|
||||
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
|
||||
console.log('newHomeBalance = ' + newHomeBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
|
||||
})
|
||||
test.it('User is able to send tokens from Home account to Foreign account', async () => {
|
||||
await homeAccount.setMetaMaskNetwork()
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance()
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance()
|
||||
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
|
||||
return await assert.strictEqual(
|
||||
result,
|
||||
true,
|
||||
'Test FAILED. User is able send tokens from Home account to Foreign account'
|
||||
)
|
||||
})
|
||||
|
||||
test.it('Home POA balance has correctly changed after transaction', async () => {
|
||||
const newHomeBalance = await mainPage.getHomePOABalance()
|
||||
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
|
||||
console.log('newHomeBalance = ' + newHomeBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
|
||||
homeBalanceBefore = newHomeBalance
|
||||
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
|
||||
})
|
||||
|
||||
test.it('Foreign account has received correct amount of tokens after transaction', async () => {
|
||||
const newForeignBalance = await mainPage.getForeignPOABalance()
|
||||
|
||||
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
|
||||
console.log('newForeignBalance = ' + newForeignBalance)
|
||||
console.log('shouldBe = ' + shouldBe)
|
||||
|
||||
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
|
||||
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
|
||||
})
|
||||
})
|
||||
})
|
||||
14
ui/.babelrc
14
ui/.babelrc
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": [
|
||||
[
|
||||
"@babel/plugin-proposal-decorators",
|
||||
{
|
||||
"legacy": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0xABb4C1399DcC28FBa3Beb76CAE2b50Be3e087353
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0xE405F6872cE38a7a4Ff63DcF946236D458c2ca3a
|
||||
|
||||
COMMON_HOME_RPC_URL=https://sokol.poa.network
|
||||
COMMON_FOREIGN_RPC_URL=https://kovan.infura.io/mew
|
||||
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
|
||||
|
||||
UI_HOME_NETWORK_DISPLAY_NAME=POA Sokol
|
||||
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
|
||||
|
||||
# Set to true if network doesn't support events
|
||||
UI_HOME_WITHOUT_EVENTS=false
|
||||
UI_FOREIGN_WITHOUT_EVENTS=false
|
||||
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR=1
|
||||
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
|
||||
# Default
|
||||
UI_TITLE=TokenBridge UI app - %c
|
||||
UI_OG_TITLE=POA Bridge UI
|
||||
UI_DESCRIPTION=The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner.
|
||||
UI_PORT=3000
|
||||
UI_PUBLIC_URL=https://bridge.poa.net
|
||||
|
||||
# RSK
|
||||
#UI_DESCRIPTION=The TokenBridge serves as a method of transferring Bancor Network tokens between the Ethereum network and the Rootstock network in a quick and cost-efficient manner.
|
||||
|
||||
# To use Ethereum-classic styles: classic
|
||||
# To use STAKE styles: stake
|
||||
# To use poa core styles: core
|
||||
UI_STYLES=core
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user