Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b4b01a79b | ||
|
|
881fafe9a8 | ||
|
|
c1ed6f21e6 | ||
|
|
8ae0fa82d5 | ||
|
|
dbf3d3d90d | ||
|
|
8977ed6d3b | ||
|
|
df0dc1c313 | ||
|
|
7b2bebbcf0 | ||
|
|
85104d67c0 | ||
|
|
2a3d7c8e08 | ||
|
|
e6052f162a | ||
|
|
52ed4e85e2 | ||
|
|
bc6dd13193 | ||
|
|
bce1e6509e | ||
|
|
52358d477b | ||
|
|
232f807e9d | ||
|
|
fe4a569e34 | ||
|
|
c408d57716 | ||
|
|
7fcb118c8c | ||
|
|
1eaf774e33 | ||
|
|
8650ba4d21 | ||
|
|
edc51c78e2 | ||
|
|
612f130544 | ||
|
|
73d5002105 | ||
|
|
65dd131107 | ||
|
|
4de24efc01 | ||
|
|
727371f251 | ||
|
|
9cb1a2041d | ||
|
|
b6d96d7f62 | ||
|
|
dc06ee8ceb | ||
|
|
8c2f58b06f | ||
|
|
3ad62d6a7f | ||
|
|
9f9638970a | ||
|
|
7054ff26a0 | ||
|
|
afb601b7f5 | ||
|
|
1736fd615d | ||
|
|
ef0a734650 | ||
|
|
6e2238fc9b | ||
|
|
0f3bea5a41 | ||
|
|
0829c95561 | ||
|
|
5bb99a7e95 | ||
|
|
3cd53f7bda | ||
|
|
0eeae74ffa | ||
|
|
8fa715089b | ||
|
|
ab2c0ea120 | ||
|
|
5583ea8b6b | ||
|
|
a4eb446f7b | ||
|
|
2d526a1454 | ||
|
|
9811c13a04 | ||
|
|
406ede9352 | ||
|
|
1360c79e69 | ||
|
|
12229e5e0b | ||
|
|
588b289bb9 | ||
|
|
b3419ccca6 | ||
|
|
ecd20890c8 | ||
|
|
b6588ff3c5 | ||
|
|
ed2de112a2 | ||
|
|
c19f48ef3f | ||
|
|
eb8de323ee | ||
|
|
c42b2f03b7 | ||
|
|
303b02f3ca | ||
|
|
98e0f8e998 | ||
|
|
ecf613954a |
@@ -11,10 +11,10 @@ orbs:
|
||||
sudo apt-get clean
|
||||
sudo apt-get update
|
||||
sudo apt-get install dpkg
|
||||
- run:
|
||||
- run:
|
||||
name: Install Chrome
|
||||
command: |
|
||||
wget -O chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
wget -O chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_77.0.3865.120-1_amd64.deb
|
||||
sudo dpkg -i chrome.deb
|
||||
install-node:
|
||||
steps:
|
||||
@@ -89,29 +89,29 @@ jobs:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- restore_cache:
|
||||
name: Restore Yarn Package Cache
|
||||
keys:
|
||||
- yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
- run: git submodule status > submodule.status
|
||||
name: Restore Yarn Package Cache
|
||||
keys:
|
||||
- yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
- run: git submodule status > submodule.status
|
||||
- restore_cache:
|
||||
name: Restore contracts submodule with compiled contracts
|
||||
keys:
|
||||
- contracts-{{ checksum "submodule.status" }}
|
||||
name: Restore contracts submodule with compiled contracts
|
||||
keys:
|
||||
- contracts-{{ checksum "submodule.status" }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
key: yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
name: Save Yarn Package Cache
|
||||
key: yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
- run: touch install_deploy.log; test -d contracts/build/contracts || yarn install:deploy &> install_deploy.log
|
||||
- store_artifacts:
|
||||
path: install_deploy.log
|
||||
- run: test -d contracts/build/contracts || yarn compile:contracts
|
||||
- save_cache:
|
||||
name: Save contracts submodule with compiled contracts
|
||||
key: contracts-{{ checksum "submodule.status" }}
|
||||
paths:
|
||||
- contracts
|
||||
name: Save contracts submodule with compiled contracts
|
||||
key: contracts-{{ checksum "submodule.status" }}
|
||||
paths:
|
||||
- contracts
|
||||
- save_cache:
|
||||
name: Save initialized project for subsequent jobs
|
||||
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
|
||||
@@ -143,11 +143,11 @@ jobs:
|
||||
oracle-e2e:
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
- run: yarn run oracle-e2e
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
- run: yarn run oracle-e2e
|
||||
ui-e2e:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
@@ -161,9 +161,9 @@ jobs:
|
||||
monitor-e2e:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: ./monitor-e2e/run-tests.sh
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: ./monitor-e2e/run-tests.sh
|
||||
cover:
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
@@ -194,7 +194,7 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run:
|
||||
- run:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh monitor
|
||||
no_output_timeout: 40m
|
||||
@@ -210,7 +210,15 @@ jobs:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh repo
|
||||
no_output_timeout: 40m
|
||||
|
||||
deployment-multiple:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh multiple
|
||||
no_output_timeout: 40m
|
||||
ultimate:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
parameters:
|
||||
@@ -222,6 +230,11 @@ jobs:
|
||||
type: string
|
||||
ui-e2e-grep:
|
||||
description: "Mocha grep string used to run ui-e2e tests specific to given type of bridge"
|
||||
default: ''
|
||||
type: string
|
||||
oracle-e2e-script:
|
||||
description: "Yarn script string used to run oracle-e2e tests specific to given type of bridge"
|
||||
default: ''
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
@@ -232,16 +245,24 @@ jobs:
|
||||
- tokenbridge-orb/yarn-install-cached-on-machine
|
||||
- run:
|
||||
name: Prepare the infrastructure
|
||||
command: e2e-commons/up.sh deploy << parameters.scenario-name >>
|
||||
command: e2e-commons/up.sh deploy << parameters.scenario-name >> blocks
|
||||
no_output_timeout: 50m
|
||||
- tokenbridge-orb/wait-for-oracle:
|
||||
redis-key: << parameters.redis-key >>
|
||||
- run:
|
||||
name: Run the ui-e2e tests
|
||||
command: |
|
||||
nvm use default;
|
||||
node ./e2e-commons/scripts/blocks.js &
|
||||
cd ui-e2e; yarn mocha -g "<< parameters.ui-e2e-grep >>" -b ./test.js
|
||||
- when:
|
||||
condition: << parameters.ui-e2e-grep >>
|
||||
steps:
|
||||
- run:
|
||||
name: Run the ui-e2e tests
|
||||
command: |
|
||||
nvm use default;
|
||||
cd ui-e2e; yarn mocha -g "<< parameters.ui-e2e-grep >>" -b ./test.js
|
||||
- when:
|
||||
condition: << parameters.oracle-e2e-script >>
|
||||
steps:
|
||||
- run:
|
||||
name: Run the oracle-e2e tests
|
||||
command: cd e2e-commons && docker-compose run e2e yarn workspace oracle-e2e run << parameters.oracle-e2e-script >>
|
||||
workflows:
|
||||
tokenbridge:
|
||||
jobs:
|
||||
@@ -272,18 +293,24 @@ workflows:
|
||||
- deployment-ui
|
||||
- deployment-monitor
|
||||
- deployment-repo
|
||||
- deployment-multiple
|
||||
- ultimate:
|
||||
name: "ultimate: native to erc"
|
||||
name: "ultimate: native to erc"
|
||||
scenario-name: native-to-erc
|
||||
redis-key: native-erc-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "NATIVE TO ERC"
|
||||
- ultimate:
|
||||
name: "ultimate: erc to native"
|
||||
name: "ultimate: erc to native"
|
||||
scenario-name: erc-to-native
|
||||
redis-key: erc-native-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "ERC TO NATIVE"
|
||||
- ultimate:
|
||||
name: "ultimate: erc to erc"
|
||||
name: "ultimate: erc to erc"
|
||||
scenario-name: erc-to-erc
|
||||
redis-key: erc-erc-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "ERC TO ERC"
|
||||
- ultimate:
|
||||
name: "ultimate: amb"
|
||||
scenario-name: amb
|
||||
redis-key: amb-collected-signatures:lastProcessedBlock
|
||||
oracle-e2e-script: "amb"
|
||||
|
||||
@@ -69,3 +69,4 @@ MONITOR_VALIDATOR_HOME_TX_LIMIT | Average gas usage of a transaction sent by a v
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT | Average gas usage of a transaction sent by a validator, it is used to estimate the number of transaction that can be paid by the validator. | integer
|
||||
MONITOR_TX_NUMBER_THRESHOLD | If estimated number of transaction is equal to or below this value, the monitor will report that the validator has less funds than it is required. | integer
|
||||
MONITOR_PORT | The port for the Monitor. | integer
|
||||
MONITOR_BRIDGE_NAME | The name to be used in the url path for the bridge | string
|
||||
|
||||
@@ -66,11 +66,13 @@ Clone the repository:
|
||||
git clone https://github.com/poanetwork/tokenbridge
|
||||
```
|
||||
|
||||
Initialize submodules, install dependencies, compile the Smart Contracts:
|
||||
If there is no need to build docker images for the TokenBridge components (oracle, monitor, UI), initialize submodules, install dependencies, compile the Smart Contracts:
|
||||
```
|
||||
yarn initialize
|
||||
```
|
||||
|
||||
Then refer to the corresponding README files to get information about particular TokenBridge component.
|
||||
|
||||
## Linting
|
||||
|
||||
Running linter for all JS projects:
|
||||
@@ -106,5 +108,4 @@ This project is licensed under the GNU Lesser General Public License v3.0. See t
|
||||
|
||||
## References
|
||||
|
||||
* [Additional Documentation](https://forum.poa.network/c/tokenbridge)
|
||||
* [POA20 Bridge FAQ](https://forum.poa.network/c/tokenbridge/poa20-bridge)
|
||||
* [TokenBridge Documentation](http://www.tokenbridge.net/)
|
||||
|
||||
Binary file not shown.
@@ -13,6 +13,7 @@ const REWARDABLE_VALIDATORS_ABI = require('../contracts/build/contracts/Rewardab
|
||||
const HOME_AMB_ABI = require('../contracts/build/contracts/HomeAMB').abi
|
||||
const FOREIGN_AMB_ABI = require('../contracts/build/contracts/ForeignAMB').abi
|
||||
const BOX_ABI = require('../contracts/build/contracts/Box').abi
|
||||
const SAI_TOP = require('../contracts/build/contracts/SaiTopMock').abi
|
||||
|
||||
const { HOME_V1_ABI, FOREIGN_V1_ABI } = require('./v1Abis')
|
||||
const { BRIDGE_MODES } = require('./constants')
|
||||
@@ -92,5 +93,6 @@ module.exports = {
|
||||
ERC20_BYTES32_ABI,
|
||||
HOME_AMB_ABI,
|
||||
FOREIGN_AMB_ABI,
|
||||
BOX_ABI
|
||||
BOX_ABI,
|
||||
SAI_TOP
|
||||
}
|
||||
|
||||
Submodule contracts updated: 20d262702d...a5946e7024
3
deployment-e2e/molecule/monitor/converge.yml
Normal file
3
deployment-e2e/molecule/monitor/converge.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
- import_playbook: ../../../deployment/site.yml
|
||||
- import_playbook: ./run-checks.yml
|
||||
@@ -29,10 +29,11 @@ provisioner:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../../../deployment/site.yml
|
||||
converge: ./converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
monitor-host:
|
||||
MONITOR_PORT: 3003
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
|
||||
7
deployment-e2e/molecule/monitor/run-checks.yml
Normal file
7
deployment-e2e/molecule/monitor/run-checks.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Generate initial data for monitor
|
||||
hosts: monitor
|
||||
become: true
|
||||
tasks:
|
||||
- name: Run monitor checks
|
||||
shell: /bin/bash -c 'cd /home/poadocker/bridge/monitor/scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err'
|
||||
@@ -34,14 +34,14 @@ def test_logging(host, filename):
|
||||
|
||||
def test_home_exists(host):
|
||||
assert host.run_test(
|
||||
'curl -s http://localhost:3003 | '
|
||||
'curl -s http://localhost:3003/bridge | '
|
||||
'grep -q -i "home"'
|
||||
)
|
||||
|
||||
|
||||
def test_foreign_exists(host):
|
||||
assert host.run_test(
|
||||
'curl -s http://localhost:3003 | '
|
||||
'curl -s http://localhost:3003/bridge | '
|
||||
'grep -q -i "foreign"'
|
||||
)
|
||||
|
||||
@@ -49,6 +49,6 @@ def test_foreign_exists(host):
|
||||
def test_no_error(host):
|
||||
assert host.run_expect(
|
||||
[1],
|
||||
'curl -s http://localhost:3003 | '
|
||||
'curl -s http://localhost:3003/bridge | '
|
||||
'grep -i -q "error"'
|
||||
)
|
||||
|
||||
14
deployment-e2e/molecule/multiple/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/multiple/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# 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
|
||||
59
deployment-e2e/molecule/multiple/molecule.yml
Normal file
59
deployment-e2e/molecule/multiple/molecule.yml
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: multiple-host
|
||||
groups:
|
||||
- example
|
||||
children:
|
||||
- oracle
|
||||
- monitor
|
||||
- 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: ../monitor/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
multiple-host:
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
MONITOR_PORT: 3003
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: multiple
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
- destroy
|
||||
32
deployment-e2e/molecule/multiple/tests/test_multiple.py
Normal file
32
deployment-e2e/molecule/multiple/tests/test_multiple.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("service", [
|
||||
("poabridge"),
|
||||
("tokenbridge-ui"),
|
||||
("tokenbridge-monitor")
|
||||
])
|
||||
def test_services(host, service):
|
||||
assert host.service(service).is_enabled
|
||||
assert host.service(service).is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", [
|
||||
("oracle_rabbit_1"),
|
||||
("oracle_redis_1"),
|
||||
("oracle_bridge_request_1"),
|
||||
("oracle_bridge_collected_1"),
|
||||
("oracle_bridge_affirmation_1"),
|
||||
("oracle_bridge_senderhome_1"),
|
||||
("oracle_bridge_senderforeign_1"),
|
||||
("ui_ui_1"),
|
||||
("monitor_monitor_1")
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
container = host.docker(name)
|
||||
assert container.is_running
|
||||
@@ -9,7 +9,6 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
def test_repo(host):
|
||||
assert host.file('/home/poadocker/bridge').exists
|
||||
assert host.file('/home/poadocker/bridge').is_directory
|
||||
assert host.file('/home/poadocker/bridge/package.json').exists
|
||||
|
||||
|
||||
def test_docker_group(host):
|
||||
|
||||
14
deployment-e2e/molecule/ultimate-amb/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/ultimate-amb/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# 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
|
||||
40
deployment-e2e/molecule/ultimate-amb/molecule.yml
Normal file
40
deployment-e2e/molecule/ultimate-amb/molecule.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
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
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../ultimate-commons/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-amb-host:
|
||||
COMMON_HOME_RPC_URL: "http://parity1:8545"
|
||||
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-amb
|
||||
test_sequence:
|
||||
- cleanup
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "/home/poadocker/bridge/oracle/{{ file }}.yml"
|
||||
register: docker_compose_slurp
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Add the external network used to connect to Parity nodes
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
|
||||
|
||||
- name: Add all Oracle containers to the network
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': docker_compose_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Expose Redis port to allow connecting from redis-cli
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}"
|
||||
|
||||
- name: Write updated docker file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "/home/poadocker/bridge/oracle/{{ file }}.yml"
|
||||
@@ -5,58 +5,31 @@
|
||||
tasks:
|
||||
- name: stop the service
|
||||
shell: service poabridge stop
|
||||
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "/home/poadocker/bridge/oracle/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: Add the external network used to connect to Parity nodes
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
|
||||
- name: Build current oracle image
|
||||
shell: docker build -t oracle:ultimate-testing --file oracle/Dockerfile .
|
||||
delegate_to: 127.0.0.1
|
||||
become: false
|
||||
args:
|
||||
chdir: "{{ lookup('env', 'PWD') }}/.."
|
||||
|
||||
- name: Add all Oracle containers to the network
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': docker_compose_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Expose Redis port to allow connecting from redis-cli
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}"
|
||||
- name: Replace oracle image
|
||||
replace:
|
||||
path: "/home/poadocker/bridge/oracle/{{ item }}.yml"
|
||||
regexp: 'poanetwork/tokenbridge-oracle:latest'
|
||||
replace: "oracle:ultimate-testing"
|
||||
with_items:
|
||||
- docker-compose
|
||||
- docker-compose-transfer
|
||||
- docker-compose-erc-native
|
||||
|
||||
- name: Write new docker-compose file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "/home/poadocker/bridge/oracle/docker-compose.yml"
|
||||
|
||||
- name: Slurp docker compose extended file
|
||||
slurp:
|
||||
src: "/home/poadocker/bridge/oracle/docker-compose-transfer.yml"
|
||||
register: docker_compose_extended_slurp
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Add the external network used to connect to Parity nodes in compose extended file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
|
||||
|
||||
- name: Add all Oracle containers to the network in compose extended file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_parsed | combine({'services': {item: {'networks': docker_compose_extended_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_extended_parsed.services }}"
|
||||
|
||||
- name: Expose Redis port to allow connecting from redis-cli in compose extended file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}"
|
||||
|
||||
- name: Write new docker-compose extended file
|
||||
copy:
|
||||
content: "{{ docker_compose_extended_parsed | to_yaml }}"
|
||||
dest: "/home/poadocker/bridge/oracle/docker-compose-transfer.yml"
|
||||
- include_tasks: oracle-add-docker-external-network.yml
|
||||
with_items:
|
||||
- docker-compose
|
||||
- docker-compose-transfer
|
||||
- docker-compose-erc-native
|
||||
loop_control:
|
||||
loop_var: file
|
||||
|
||||
- name: start the service
|
||||
shell: service poabridge start
|
||||
|
||||
@@ -78,6 +78,10 @@ Example config for installing only UI:
|
||||
1. Go to the group_vars folder.
|
||||
`cd group_vars`
|
||||
2. Note the <bridge_name> and add it to the hosts.yml configuration. For example, if a bridge file is named sokol-kovan.yml, you would change the <bridge_name> value in hosts.yml to sokol-kovan.
|
||||
|
||||
## Examples
|
||||
|
||||
[Deploy a monitor for multiple bridges](./MONITOR.md)
|
||||
|
||||
## Administrator Configurations
|
||||
|
||||
|
||||
104
deployment/MONITOR.md
Normal file
104
deployment/MONITOR.md
Normal file
@@ -0,0 +1,104 @@
|
||||
## Deploy multiple bridge monitor on the same host
|
||||
|
||||
If you want to deploy a monitor for different bridges, the [monitor variables](../monitor/.env.example) should be configured in `group_vars/<bridge_name>.yml` for each bridge.
|
||||
|
||||
For example, let's say we are going to deploy a monitor for xDai bridge and for WETC bridge.
|
||||
|
||||
#### Setup ansible configuration for xDai Bridge
|
||||
|
||||
First we create `hosts.yml` file to deploy the monitor for xdai bridge
|
||||
```yaml
|
||||
---
|
||||
xdai:
|
||||
children:
|
||||
monitor:
|
||||
hosts:
|
||||
<host_ip_A>:
|
||||
ansible_user: ubuntu
|
||||
```
|
||||
In `group_vars/xdai.yml`
|
||||
```
|
||||
---
|
||||
MONITOR_BRIDGE_NAME: "xdai"
|
||||
MONITOR_PORT: 3003
|
||||
|
||||
COMMON_HOME_RPC_URL: "https://dai.poa.network"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
|
||||
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io/v3/INFURA_KEY"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016"
|
||||
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK: 0
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
MONITOR_HOME_START_BLOCK: 759
|
||||
MONITOR_FOREIGN_START_BLOCK: 6478417
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 0
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
```
|
||||
|
||||
Run the playbook to deploy the monitor for xdai bridge
|
||||
```
|
||||
ansible-playbook -i hosts.yml site.yml
|
||||
```
|
||||
|
||||
This command will deploy the monitor component and enable statistics for xdai bridge.
|
||||
|
||||
#### Setup ansible configuration for WETC Bridge
|
||||
|
||||
Update `hosts.yml` file to deploy the monitor for WETC Bridge
|
||||
```yaml
|
||||
---
|
||||
wetc:
|
||||
children:
|
||||
monitor:
|
||||
hosts:
|
||||
<host_ip_A>:
|
||||
ansible_user: ubuntu
|
||||
```
|
||||
|
||||
In `group_vars/wetc.yml`
|
||||
```
|
||||
---
|
||||
MONITOR_BRIDGE_NAME: "wetc"
|
||||
|
||||
COMMON_HOME_RPC_URL: "https://ethereumclassic.network"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
|
||||
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io/v3/32e8e252699a4ac1b5dd5c1ef53cc301"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x0cB781EE62F815bdD9CD4c2210aE8600d43e7040"
|
||||
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice-etc.poa.network/"
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK: 15000000000
|
||||
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: 10000000000
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
MONITOR_HOME_START_BLOCK: 7703292
|
||||
MONITOR_FOREIGN_START_BLOCK: 7412459
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
```
|
||||
Given that there is a monitor component deployed in the system, the `MONITOR_PORT` variable is not needed.
|
||||
|
||||
Run the playbook to deploy the monitor for WETC Bridge
|
||||
```
|
||||
ansible-playbook -i hosts.yml site.yml
|
||||
```
|
||||
|
||||
They playbook will detect that the monitor component is already deployed in the system, so it will only generate the configuration needed to enable the WETC Bridge statistics.
|
||||
|
||||
##### Get Monitor results
|
||||
The monitor output will be available at `http://host_ip_A:MONITOR_PORT/MONITOR_BRIDGE_NAME`.
|
||||
|
||||
Given that in `xdai.env` the variable `MONITOR_BRIDGE_NAME` is set to `xdai`, the results are in the url `http://host_ip_A:3003/xdai/`.
|
||||
|
||||
Similar to the xdai case, in `wetc.env` the variable `MONITOR_BRIDGE_NAME` is set to `wetc`, so the results are in the url `http://host_ip_A:3003/wetc/`.
|
||||
5
deployment/group_vars/amb.yml
Normal file
5
deployment/group_vars/amb.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
ORACLE_BRIDGE_MODE: "ARBITRARY_MESSAGE"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0"
|
||||
MONITOR_PORT: 3013
|
||||
@@ -42,6 +42,7 @@ UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "xdai"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 759
|
||||
MONITOR_FOREIGN_START_BLOCK: 6478417
|
||||
|
||||
@@ -44,9 +44,10 @@ UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_LEFT_TX_THRESHOLD: 100
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
|
||||
@@ -41,9 +41,10 @@ UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
#montior
|
||||
#monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_LEFT_TX_THRESHOLD: 100
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
|
||||
@@ -43,6 +43,7 @@ UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "wetc"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 7703292
|
||||
MONITOR_FOREIGN_START_BLOCK: 7412459
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
group: "root"
|
||||
mode: "0755"
|
||||
|
||||
- name: Upgrade pip version
|
||||
shell: pip3 install --upgrade pip
|
||||
|
||||
- name: Install python docker library
|
||||
shell: pip3 install docker docker-compose setuptools
|
||||
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
---
|
||||
- include_tasks: dependencies.yml
|
||||
- include_tasks: repo.yml
|
||||
- include_tasks: logging.yml
|
||||
- name: Check if component is already deployed
|
||||
shell: "test -f {{ bridge_path }}/{{ component }}/.env && echo 'true'"
|
||||
ignore_errors: True
|
||||
register: already_deployed
|
||||
when: check_deployed is defined
|
||||
|
||||
- name: Set if tasks should be skipped
|
||||
set_fact: skip_task="{{ already_deployed.stdout | default('false') }}"
|
||||
|
||||
- name: Include dependencies tasks
|
||||
include_tasks: dependencies.yml
|
||||
when: skip_task != true
|
||||
|
||||
- name: Include repo tasks
|
||||
include_tasks: repo.yml
|
||||
when: skip_task != true and skip_repo is undefined
|
||||
|
||||
- name: Include logging tasks
|
||||
include_tasks: logging.yml
|
||||
when: skip_task != true
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- role: common
|
||||
- { role: common, skip_repo: true, check_deployed: true, component: 'monitor' }
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
day: "{{ monitor_cron_schedule.split(' ')[2] }}"
|
||||
month: "{{ monitor_cron_schedule.split(' ')[3] }}"
|
||||
weekday: "{{ monitor_cron_schedule.split(' ')[4] }}"
|
||||
job: "/bin/bash -c 'cd {{ bridge_path }}/monitor/scripts; ./checkDocker.sh >cronWorker.out 2>cronWorker.err'"
|
||||
job: "/bin/bash -c 'cd {{ bridge_path }}/monitor/scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err'"
|
||||
- name: Add cron entry
|
||||
cron:
|
||||
name: "RUN_MONITOR_CHECKS"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
- name: Build the containers
|
||||
shell: docker-compose build
|
||||
- name: Pull the containers images
|
||||
shell: docker-compose pull
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/monitor"
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
---
|
||||
- include_tasks: pre_config.yml
|
||||
- include_tasks: logging.yml
|
||||
- include_tasks: jumpbox.yml
|
||||
- include_tasks: servinstall.yml
|
||||
- include_tasks: cron.yml
|
||||
|
||||
- name: Include logging tasks
|
||||
include_tasks: logging.yml
|
||||
when: skip_task != true
|
||||
|
||||
- name: Include jumpbox tasks
|
||||
include_tasks: jumpbox.yml
|
||||
when: skip_task != true
|
||||
|
||||
- name: Include servinstall tasks
|
||||
include_tasks: servinstall.yml
|
||||
when: skip_task != true
|
||||
|
||||
- name: Include cron tasks
|
||||
include_tasks: cron.yml
|
||||
when: skip_task != true
|
||||
|
||||
@@ -1,5 +1,47 @@
|
||||
---
|
||||
- name: Create configs directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/monitor/configs"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Create responses directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/monitor/responses"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Create scripts directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/monitor/scripts"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Install .env config
|
||||
template:
|
||||
src: .env.j2
|
||||
dest: "{{ bridge_path }}/monitor/.env"
|
||||
when: skip_task != true
|
||||
|
||||
- name: Copy docker-compose file
|
||||
copy:
|
||||
src: ../../../../monitor/docker-compose.yml
|
||||
dest: "{{ bridge_path }}/monitor/docker-compose.yml"
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Copy script file
|
||||
copy:
|
||||
src: ../../../../monitor/scripts/getBridgeStats.sh
|
||||
dest: "{{ bridge_path }}/monitor/scripts/getBridgeStats.sh"
|
||||
owner: "{{ compose_service_user }}"
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Install bridge config env
|
||||
template:
|
||||
src: config.env.j2
|
||||
dest: "{{ bridge_path }}/monitor/configs/{{ MONITOR_BRIDGE_NAME }}.env"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# This role creates a tokenbridge-monitor service which is designed to manage docker-compose monitor deployment.
|
||||
# /etc/init.d/tokenbridge-monitor start, status, stop, restart - does what the services usually do in such cases.
|
||||
# /etc/init.d/tokenbridge-monitor rebuild - builds a new monitor deployment from scratch.
|
||||
---
|
||||
- name: "Set the service"
|
||||
template:
|
||||
|
||||
@@ -1,24 +1 @@
|
||||
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
|
||||
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
|
||||
COMMON_HOME_BRIDGE_ADDRESS={{ COMMON_HOME_BRIDGE_ADDRESS }}
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
|
||||
MONITOR_HOME_START_BLOCK={{ MONITOR_HOME_START_BLOCK }}
|
||||
MONITOR_FOREIGN_START_BLOCK={{ MONITOR_FOREIGN_START_BLOCK }}
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT={{ MONITOR_VALIDATOR_HOME_TX_LIMIT }}
|
||||
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
|
||||
{% endif %}
|
||||
{% if COMMON_HOME_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE={{ COMMON_HOME_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK={{ COMMON_HOME_GAS_PRICE_FALLBACK }}
|
||||
{% if COMMON_HOME_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_FACTOR={{ COMMON_HOME_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT={{ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT }}
|
||||
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 }}
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
|
||||
MONITOR_LEFT_TX_THRESHOLD={{ MONITOR_LEFT_TX_THRESHOLD }}
|
||||
MONITOR_PORT={{ MONITOR_PORT }}
|
||||
|
||||
34
deployment/roles/monitor/templates/config.env.j2
Normal file
34
deployment/roles/monitor/templates/config.env.j2
Normal file
@@ -0,0 +1,34 @@
|
||||
MONITOR_BRIDGE_NAME={{ MONITOR_BRIDGE_NAME }}
|
||||
|
||||
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
|
||||
COMMON_HOME_BRIDGE_ADDRESS={{ COMMON_HOME_BRIDGE_ADDRESS }}
|
||||
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
|
||||
|
||||
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
|
||||
{% endif %}
|
||||
{% if COMMON_HOME_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE={{ COMMON_HOME_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK={{ COMMON_HOME_GAS_PRICE_FALLBACK }}
|
||||
{% if COMMON_HOME_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_FACTOR={{ COMMON_HOME_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL={{ COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL }}
|
||||
{% endif %}
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE={{ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK={{ COMMON_FOREIGN_GAS_PRICE_FALLBACK }}
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
|
||||
MONITOR_HOME_START_BLOCK={{ MONITOR_HOME_START_BLOCK }}
|
||||
MONITOR_FOREIGN_START_BLOCK={{ MONITOR_FOREIGN_START_BLOCK }}
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT={{ MONITOR_VALIDATOR_HOME_TX_LIMIT }}
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT={{ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT }}
|
||||
MONITOR_TX_NUMBER_THRESHOLD={{ MONITOR_TX_NUMBER_THRESHOLD }}
|
||||
@@ -18,6 +18,7 @@ start(){
|
||||
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
|
||||
sudo -u "{{ compose_service_user }}" /bin/bash -c 'cd scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err'
|
||||
}
|
||||
|
||||
stop(){
|
||||
@@ -33,14 +34,6 @@ status(){
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose ps
|
||||
}
|
||||
|
||||
rebuild(){
|
||||
echo "Rebuild TokenBridge Monitor.."
|
||||
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
|
||||
|
||||
@@ -62,12 +55,8 @@ case "$1" in
|
||||
start
|
||||
;;
|
||||
|
||||
rebuild)
|
||||
rebuild
|
||||
;;
|
||||
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|rebuild|status}"
|
||||
echo $"Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- role: common
|
||||
- { role: common, skip_repo: true }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
- name: Build the containers
|
||||
shell: docker-compose build
|
||||
- name: Pull the containers images
|
||||
shell: docker-compose pull
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
|
||||
@@ -1,41 +1,11 @@
|
||||
---
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "{{ bridge_path }}/oracle/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 }}/oracle/docker-compose.yml"
|
||||
|
||||
- name: Slurp docker compose extended file
|
||||
slurp:
|
||||
src: "{{ bridge_path }}/oracle/docker-compose-transfer.yml"
|
||||
register: docker_compose_extended_slurp
|
||||
|
||||
- name: Parse docker compose extended file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Set logger to remote server for extended file
|
||||
set_fact:
|
||||
docker_compose_extended_parsed: "{{ docker_compose_extended_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_extended_parsed.services }}"
|
||||
|
||||
- name: Write new docker-compose-extended file
|
||||
copy:
|
||||
content: "{{ docker_compose_extended_parsed | to_yaml }}"
|
||||
dest: "{{ bridge_path }}/oracle/docker-compose-transfer.yml"
|
||||
- include_tasks: logging_by_syslog.yml
|
||||
with_items:
|
||||
- docker-compose
|
||||
- docker-compose-transfer
|
||||
- docker-compose-erc-native
|
||||
loop_control:
|
||||
loop_var: file
|
||||
|
||||
- name: Set the local container logs configuration file
|
||||
template:
|
||||
|
||||
19
deployment/roles/oracle/tasks/logging_by_syslog.yml
Normal file
19
deployment/roles/oracle/tasks/logging_by_syslog.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "{{ bridge_path }}/oracle/{{ file }}.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 updated docker file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "{{ bridge_path }}/oracle/{{ file }}.yml"
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
- name: Get blocks
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run --entrypoint "node scripts/getValidatorStartBlocks.js" bridge_affirmation
|
||||
shell: docker-compose run --rm --entrypoint "node scripts/getValidatorStartBlocks.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: BLOCKS
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
- name: Get validator address
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="{{ ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }}" --entrypoint "node scripts/privateKeyToAddress.js" bridge_affirmation
|
||||
shell: docker-compose run --rm -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="{{ ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }}" --entrypoint "node scripts/privateKeyToAddress.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: VADDRESS
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
- name: Get foreign erc type
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run --entrypoint "node scripts/initialChecks.js" bridge_affirmation
|
||||
shell: docker-compose run --rm --entrypoint "node scripts/initialChecks.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: ERCTYPE
|
||||
@@ -40,7 +40,11 @@
|
||||
|
||||
- name: Extend docker compose file
|
||||
set_fact: composefileoverride="-f docker-compose-transfer.yml"
|
||||
when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE" or ( ORACLE_BRIDGE_MODE == "ERC_TO_ERC" and FOREIGN_ERC_TYPE != "ERC677")
|
||||
when: ORACLE_BRIDGE_MODE == "ERC_TO_ERC" and FOREIGN_ERC_TYPE != "ERC677"
|
||||
|
||||
- name: Extend docker compose file for erc to native
|
||||
set_fact: composefileoverride="-f docker-compose-erc-native.yml"
|
||||
when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE"
|
||||
|
||||
- name: Install .key config
|
||||
template:
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
---
|
||||
- name: Create oracle directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/oracle"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Install .env config
|
||||
template:
|
||||
src: .env.j2
|
||||
dest: "{{ bridge_path }}/oracle/.env"
|
||||
|
||||
- name: Copy docker-compose files
|
||||
copy:
|
||||
src: ../../../../oracle/{{ item }}
|
||||
dest: "{{ bridge_path }}/oracle/"
|
||||
mode: '0755'
|
||||
with_items:
|
||||
- docker-compose.yml
|
||||
- docker-compose-transfer.yml
|
||||
- docker-compose-erc-native.yml
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# This role creates a poabridge service which is designed to manage docker-compose bridge deployment.
|
||||
# /etc/init.d/poabridge start, status, stop, restart - does what the services usually do in such cases.
|
||||
# /etc/init.d/poabridge rebuild - builds a new bridge deployment from scratch.
|
||||
---
|
||||
- name: "Set poabridge service"
|
||||
template:
|
||||
|
||||
@@ -52,14 +52,6 @@ status(){
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride ps
|
||||
}
|
||||
|
||||
rebuild(){
|
||||
echo "Rebuild bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride rm -fv
|
||||
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose $composefileoverride up --detach --force-recreate --no-deps --build
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
|
||||
@@ -81,12 +73,8 @@ case "$1" in
|
||||
start
|
||||
;;
|
||||
|
||||
rebuild)
|
||||
rebuild
|
||||
;;
|
||||
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|rebuild|status}"
|
||||
echo $"Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
|
||||
@@ -16,3 +16,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3013
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
@@ -16,3 +16,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3012
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
@@ -16,3 +16,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3011
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
@@ -16,3 +16,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3010
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
@@ -7,10 +7,26 @@
|
||||
"address": "0xcca2fb44C8C36E51f743269d6F484Fd027B9F9Aa",
|
||||
"privateKey": "0xcf954e07e6a439faf392eb474e95ddb444c2ca444847f2ad6ecc79e1a585e2b8"
|
||||
},
|
||||
"thirdUser": {
|
||||
"address": "0x441cc8537aB6cE63d060b63F3A44eE021d62e6cF",
|
||||
"privateKey": "0xd3915199f27691d7784cb01ab0c7220308053b229f95d592e97493326314a8d0"
|
||||
},
|
||||
"fourthUser": {
|
||||
"address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B",
|
||||
"privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1"
|
||||
},
|
||||
"validator": {
|
||||
"address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b",
|
||||
"privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
},
|
||||
"secondValidator": {
|
||||
"address": "0xdCC784657C78054aa61FbcFFd2605F32374816A4",
|
||||
"privateKey": "0x5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||
},
|
||||
"thirdValidator": {
|
||||
"address": "0xDcef88209a20D52165230104B245803C3269454d",
|
||||
"privateKey": "0xf877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||
},
|
||||
"blockGenerator": {
|
||||
"address": "0xB4579fd5AfEaB60B03Db3F408AAdD315035943f7",
|
||||
"privateKey": "0xd6143d390d8b28c33601bb0fe29392fb1c35c24ccfe8722c09c2bdd6ada2699f"
|
||||
@@ -20,7 +36,7 @@
|
||||
"foreign": "0x2B6871b9B02F73fa24F4864322CdC78604207769",
|
||||
"foreignToken": "0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B",
|
||||
"ui": "http://localhost:3000",
|
||||
"monitor": "http://monitor:3010"
|
||||
"monitor": "http://monitor:3010/bridge"
|
||||
},
|
||||
"ercToErcBridge": {
|
||||
"home": "0x1feB40aD9420b186F019A717c37f5546165d411E",
|
||||
@@ -28,21 +44,24 @@
|
||||
"homeToken": "0x792455a6bCb62Ed4C4362D323E0590654CA4765c",
|
||||
"foreignToken": "0x3C665A31199694Bf723fD08844AD290207B5797f",
|
||||
"ui": "http://localhost:3001",
|
||||
"monitor": "http://monitor-erc20:3011"
|
||||
"monitor": "http://monitor-erc20:3011/bridge"
|
||||
},
|
||||
"ercToNativeBridge": {
|
||||
"home": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
|
||||
"foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
|
||||
"foreignToken": "0x3C665A31199694Bf723fD08844AD290207B5797f",
|
||||
"foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9",
|
||||
"halfDuplexToken": "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359",
|
||||
"saiTop": "0x9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3",
|
||||
"chaiToken": "0x06af07097c9eeb7fd685c692751d5c66db49c215",
|
||||
"ui": "http://localhost:3002",
|
||||
"monitor": "http://monitor-erc20-native:3012"
|
||||
"monitor": "http://monitor-erc20-native:3012/bridge"
|
||||
},
|
||||
"amb": {
|
||||
"home": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
|
||||
"foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
|
||||
"homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
|
||||
"foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
|
||||
"monitor": "http://monitor-amb:3013"
|
||||
"monitor": "http://monitor-amb:3013/bridge"
|
||||
},
|
||||
"homeRPC": {
|
||||
"URL": "http://parity1:8545",
|
||||
|
||||
@@ -22,4 +22,4 @@ FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
FOREIGN_GAS_PRICE=10000000000
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
|
||||
@@ -34,8 +34,7 @@ FOREIGN_REWARDABLE=false
|
||||
ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
VALIDATORS_REWARD_ACCOUNTS=0x0000000000000000000000000000000000000000
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
BLOCK_REWARD_ADDRESS=0x0000000000000000000000000000000000000000
|
||||
DPOS_STAKING_ADDRESS=0x0000000000000000000000000000000000000000
|
||||
ERC20_EXTENDED_BY_ERC677=false
|
||||
|
||||
@@ -32,8 +32,7 @@ FOREIGN_GAS_PRICE=10000000000
|
||||
FOREIGN_REWARDABLE=false
|
||||
|
||||
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
|
||||
ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f
|
||||
ERC20_TOKEN_ADDRESS=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
VALIDATORS_REWARD_ACCOUNTS=0x0000000000000000000000000000000000000000
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
|
||||
@@ -34,6 +34,5 @@ FOREIGN_GAS_PRICE=10000000000
|
||||
FOREIGN_REWARDABLE=false
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
VALIDATORS_REWARD_ACCOUNTS=0x0000000000000000000000000000000000000000
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
BLOCK_REWARD_ADDRESS=0x0000000000000000000000000000000000000000
|
||||
|
||||
@@ -24,16 +24,13 @@ services:
|
||||
- ultimate
|
||||
rabbit:
|
||||
image: "rabbitmq:3-management"
|
||||
ports:
|
||||
- "15672:15672"
|
||||
networks:
|
||||
- ultimate
|
||||
oracle:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/oracle.env
|
||||
env_file: ../e2e-commons/components-envs/oracle.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
command: "true"
|
||||
@@ -43,8 +40,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/oracle-erc20.env
|
||||
env_file: ../e2e-commons/components-envs/oracle-erc20.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
command: "true"
|
||||
@@ -54,8 +50,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/oracle-erc20-native.env
|
||||
env_file: ../e2e-commons/components-envs/oracle-erc20-native.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
command: "true"
|
||||
@@ -65,8 +60,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/oracle-amb.env
|
||||
env_file: ../e2e-commons/components-envs/oracle-amb.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
command: "true"
|
||||
@@ -103,8 +97,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/monitor.env
|
||||
env_file: ../e2e-commons/components-envs/monitor.env
|
||||
entrypoint: yarn check-and-start
|
||||
ports:
|
||||
- "3010:3010"
|
||||
@@ -114,8 +107,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/monitor-erc20.env
|
||||
env_file: ../e2e-commons/components-envs/monitor-erc20.env
|
||||
entrypoint: yarn check-and-start
|
||||
ports:
|
||||
- "3011:3011"
|
||||
@@ -125,8 +117,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/monitor-erc20-native.env
|
||||
env_file: ../e2e-commons/components-envs/monitor-erc20-native.env
|
||||
entrypoint: yarn check-and-start
|
||||
ports:
|
||||
- "3012:3012"
|
||||
@@ -136,8 +127,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/monitor-amb.env
|
||||
env_file: ../e2e-commons/components-envs/monitor-amb.env
|
||||
entrypoint: yarn check-and-start
|
||||
ports:
|
||||
- "3013:3013"
|
||||
@@ -150,3 +140,10 @@ services:
|
||||
command: "true"
|
||||
networks:
|
||||
- ultimate
|
||||
blocks:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile.e2e
|
||||
entrypoint: node e2e-commons/scripts/blocks.js
|
||||
networks:
|
||||
- ultimate
|
||||
|
||||
@@ -5,4 +5,6 @@ if [ $CI ]; then exit $rc; fi
|
||||
|
||||
ps | grep node | grep -v grep | awk '{print "kill " $1}' | /bin/bash
|
||||
docker-compose down
|
||||
docker-compose -p validator2 down
|
||||
docker-compose -p validator3 down
|
||||
docker network rm ultimate || true
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
const Web3 = require('web3')
|
||||
const { generateNewBlock } = require('../utils')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8541'))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8542'))
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity1:8545'))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity2:8545'))
|
||||
const {user, blockGenerator} = require('../constants.json');
|
||||
|
||||
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
@@ -10,10 +9,24 @@ foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(blockGenerator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(blockGenerator.privateKey)
|
||||
|
||||
function generateNewBlock(web3, address) {
|
||||
return web3.eth.sendTransaction({
|
||||
from: address,
|
||||
to: '0x0000000000000000000000000000000000000000',
|
||||
gasPrice: '1',
|
||||
gas: '21000',
|
||||
value: '1'
|
||||
})
|
||||
}
|
||||
|
||||
function main() {
|
||||
setTimeout(async () => {
|
||||
generateNewBlock(homeWeb3, blockGenerator.address)
|
||||
generateNewBlock(foreignWeb3, blockGenerator.address)
|
||||
try {
|
||||
generateNewBlock(homeWeb3, blockGenerator.address)
|
||||
} catch {} // in case of Transaction with the same hash was already imported.
|
||||
try {
|
||||
generateNewBlock(foreignWeb3, blockGenerator.address)
|
||||
} catch {} // in case of Transaction with the same hash was already imported.
|
||||
main()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ CONTRACTS_PATH="../../contracts"
|
||||
DEPLOY_PATH="$CONTRACTS_PATH/deploy"
|
||||
ENVS_PATH="../contracts-envs"
|
||||
|
||||
# mock bridge validators contract with the one with deterministic isValidatorDuty
|
||||
mv "$CONTRACTS_PATH/build/contracts/BridgeValidatorsDeterministic.json" "$CONTRACTS_PATH/build/contracts/BridgeValidators.json"
|
||||
|
||||
echo -e "\n\n############ Deploying native-to-erc ############\n"
|
||||
cp "$ENVS_PATH/native-to-erc.env" "$DEPLOY_PATH/.env"
|
||||
cd "$DEPLOY_PATH"
|
||||
|
||||
@@ -7,6 +7,30 @@ docker-compose build
|
||||
docker network create --driver bridge ultimate || true
|
||||
docker-compose up -d parity1 parity2 e2e
|
||||
|
||||
startValidator () {
|
||||
docker-compose $1 run -d --name $4 redis
|
||||
docker-compose $1 run -d --name $5 rabbit
|
||||
docker-compose $1 run $2 $3 -d oracle yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:transfer
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:half-duplex-transfer
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:swap-tokens
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:convert-to-chai
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request
|
||||
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
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
if [ "$1" == "oracle" ]; then
|
||||
docker-compose up -d redis rabbit oracle oracle-erc20 oracle-erc20-native oracle-amb
|
||||
@@ -22,6 +46,9 @@ while [ "$1" != "" ]; do
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:collected-signatures
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:affirmation-request
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:transfer
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:half-duplex-transfer
|
||||
docker-compose run -d oracle-erc20-native yarn worker:swap-tokens
|
||||
docker-compose run -d oracle-erc20-native yarn worker:convert-to-chai
|
||||
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
|
||||
@@ -29,6 +56,20 @@ while [ "$1" != "" ]; do
|
||||
docker-compose run -d oracle yarn sender:foreign
|
||||
fi
|
||||
|
||||
if [ "$1" == "oracle-validator-2" ]; then
|
||||
oracle2name="-p validator2"
|
||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||
oracle2comp="-e ORACLE_QUEUE_URL=amqp://rabbit2 -e ORACLE_REDIS_URL=redis://redis2"
|
||||
startValidator "$oracle2name" "$oracle2Values" "$oracle2comp" "redis2" "rabbit2"
|
||||
fi
|
||||
|
||||
if [ "$1" == "oracle-validator-3" ]; then
|
||||
oracle3name="-p validator3"
|
||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||
oracle3comp="-e ORACLE_QUEUE_URL=amqp://rabbit3 -e ORACLE_REDIS_URL=redis://redis3"
|
||||
startValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3"
|
||||
fi
|
||||
|
||||
if [ "$1" == "ui" ]; then
|
||||
docker-compose up -d ui ui-erc20 ui-erc20-native
|
||||
|
||||
@@ -42,7 +83,7 @@ while [ "$1" != "" ]; do
|
||||
fi
|
||||
|
||||
if [ "$1" == "blocks" ]; then
|
||||
node ./scripts/blocks.js &
|
||||
docker-compose up -d blocks
|
||||
fi
|
||||
|
||||
if [ "$1" == "monitor" ]; then
|
||||
@@ -61,5 +102,9 @@ while [ "$1" != "" ]; do
|
||||
../deployment-e2e/molecule.sh ultimate-erc-to-erc
|
||||
fi
|
||||
|
||||
if [ "$1" == "amb" ]; then
|
||||
../deployment-e2e/molecule.sh ultimate-amb
|
||||
fi
|
||||
|
||||
shift # Shift all the parameters down by one
|
||||
done
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
function generateNewBlock(web3, address) {
|
||||
return web3.eth.sendTransaction({
|
||||
from: address,
|
||||
to: '0x0000000000000000000000000000000000000000',
|
||||
gasPrice: '1',
|
||||
gas: '21000',
|
||||
value: '1'
|
||||
const promiseRetry = require('promise-retry')
|
||||
|
||||
async function uniformRetry(f) {
|
||||
return promiseRetry(f, {
|
||||
forever: true,
|
||||
factor: 1,
|
||||
minTimeout: 500
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateNewBlock
|
||||
async function sleep(timeout) {
|
||||
return new Promise(res => setTimeout(res, timeout))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
uniformRetry,
|
||||
sleep
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cd $(dirname $0)
|
||||
|
||||
../e2e-commons/up.sh deploy monitor
|
||||
../e2e-commons/up.sh deploy blocks monitor
|
||||
|
||||
./wait-for-monitor.sh
|
||||
nohup ./periodically-check-all.sh < /dev/null > /dev/null 2>&1 &
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
const assert = require('assert')
|
||||
const axios = require('axios')
|
||||
const { ercToNativeBridge, user, foreignRPC, validator } = require('../../e2e-commons/constants.json')
|
||||
const { waitUntil, sendTokens, addValidator } = require('../utils')
|
||||
const {
|
||||
waitUntil,
|
||||
sendTokens,
|
||||
addValidator,
|
||||
initializeChaiToken,
|
||||
convertDaiToChai,
|
||||
setMinDaiTokenBalance,
|
||||
migrateToMCD
|
||||
} = require('../utils')
|
||||
|
||||
const baseUrl = ercToNativeBridge.monitor
|
||||
|
||||
@@ -24,12 +32,45 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
||||
assert((await axios.get(`${baseUrl}/validators`)).data.validatorsMatch === true)
|
||||
})
|
||||
|
||||
it('should change balanceDiff', async () => {
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign)
|
||||
it('should change balanceDiff', async function() {
|
||||
this.timeout(60000)
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.halfDuplexToken, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
return data.balanceDiff !== 0
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.01 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === undefined &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
})
|
||||
|
||||
await migrateToMCD(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.01 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0' &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
})
|
||||
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.halfDuplexToken, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.02 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,4 +81,52 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
||||
return data.validatorsMatch === false
|
||||
})
|
||||
})
|
||||
|
||||
it('should consider chai token balance', async function() {
|
||||
this.timeout(60000)
|
||||
await initializeChaiToken(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
erc20Balance === '0.02' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0' &&
|
||||
accumulatedInterest === '0.001' // value of dsrBalance() is initially defined in genesis block as 0.001
|
||||
)
|
||||
})
|
||||
|
||||
await setMinDaiTokenBalance(foreignRPC.URL, ercToNativeBridge.foreign, '0.01')
|
||||
await convertDaiToChai(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0.01' &&
|
||||
accumulatedInterest === '0.001'
|
||||
)
|
||||
})
|
||||
|
||||
await setMinDaiTokenBalance(foreignRPC.URL, ercToNativeBridge.foreign, '0.005')
|
||||
await convertDaiToChai(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
erc20Balance === '0.005' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0.015' &&
|
||||
accumulatedInterest === '0.001'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
const Web3 = require('web3')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, BRIDGE_VALIDATORS_ABI, FOREIGN_NATIVE_TO_ERC_ABI, BOX_ABI } = require('../commons')
|
||||
const {
|
||||
ERC677_BRIDGE_TOKEN_ABI,
|
||||
BRIDGE_VALIDATORS_ABI,
|
||||
FOREIGN_NATIVE_TO_ERC_ABI,
|
||||
FOREIGN_ERC_TO_NATIVE_ABI,
|
||||
BOX_ABI
|
||||
} = require('../commons')
|
||||
const { validator } = require('../e2e-commons/constants')
|
||||
|
||||
const waitUntil = async (predicate, step = 100, timeout = 10000) => {
|
||||
const waitUntil = async (predicate, step = 100, timeout = 20000) => {
|
||||
const stopTime = Date.now() + timeout
|
||||
while (Date.now() <= stopTime) {
|
||||
const result = await predicate()
|
||||
@@ -60,10 +67,57 @@ const addValidator = async (rpcUrl, account, bridgeAddress) => {
|
||||
})
|
||||
}
|
||||
|
||||
const migrateToMCD = async (rpcUrl, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
await bridgeContract.methods.migrateToMCD().send({
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
})
|
||||
}
|
||||
|
||||
const initializeChaiToken = async (rpcUrl, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
|
||||
await bridgeContract.methods.initializeChaiToken().send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
}
|
||||
|
||||
const setMinDaiTokenBalance = async (rpcUrl, bridgeAddress, limit) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
|
||||
await bridgeContract.methods.setMinDaiTokenBalance(web3.utils.toWei(limit)).send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
}
|
||||
|
||||
const convertDaiToChai = async (rpcUrl, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
|
||||
await bridgeContract.methods.convertDaiToChai().send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
waitUntil,
|
||||
sendEther,
|
||||
sendTokens,
|
||||
addValidator,
|
||||
sendAMBMessage
|
||||
sendAMBMessage,
|
||||
migrateToMCD,
|
||||
initializeChaiToken,
|
||||
setMinDaiTokenBalance,
|
||||
convertDaiToChai
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ FILES=(getBalances.json validators.json eventsStats.json alerts.json)
|
||||
check_files_exist() {
|
||||
rc=0
|
||||
for f in "${FILES[@]}"; do
|
||||
command="test -f responses/$f"
|
||||
command="test -f responses/bridge/$f"
|
||||
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor /bin/bash -c "$command") || rc=1
|
||||
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20 /bin/bash -c "$command") || rc=1
|
||||
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20-native /bin/bash -c "$command") || rc=1
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
COMMON_HOME_RPC_URL=https://sokol.poa.network
|
||||
COMMON_FOREIGN_RPC_URL=https://kovan.infura.io/mew
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0xABb4C1399DcC28FBa3Beb76CAE2b50Be3e087353
|
||||
|
||||
@@ -15,9 +15,9 @@ RUN mv ./contracts/build ./ && rm -rf ./contracts/* ./contracts/.[!.]* && mv ./b
|
||||
COPY ./commons ./commons
|
||||
|
||||
COPY ./monitor ./monitor
|
||||
ARG DOT_ENV_PATH=./monitor/.env
|
||||
COPY ${DOT_ENV_PATH} ./monitor/.env
|
||||
|
||||
WORKDIR /mono/monitor
|
||||
CMD echo "To start the monitor run:" \
|
||||
"yarn check-and-start"
|
||||
CMD echo "To start the monitor web service run:" \
|
||||
"yarn start" \
|
||||
"To run monitor scripts run:" \
|
||||
"yarn check-all"
|
||||
|
||||
@@ -4,7 +4,12 @@ Tool for checking balances and unprocessed events in bridged networks.
|
||||
## Overview
|
||||
Please refer to the [POA TokenBridge](../README.md) overview first of all.
|
||||
|
||||
- Deployed version: https://bridge-monitoring.poa.net/
|
||||
- Deployed version serves several monitor configurations:
|
||||
* https://bridge-monitoring.poa.net/poa
|
||||
* https://bridge-monitoring.poa.net/xdai
|
||||
* https://bridge-monitoring.poa.net/wetc
|
||||
* https://bridge-monitoring.poa.net/amb-dai
|
||||
* https://bridge-monitoring.poa.net/amb-poa
|
||||
|
||||
This tool allows you to spin up a NODE.JS server to monitor for health of the TokenBridge contracts: check for the balance difference, discover inconsistency in the validators list, catch unhandled transactions.
|
||||
|
||||
@@ -122,14 +127,19 @@ yarn start
|
||||
You can run web interface via [pm2](https://www.npmjs.com/package/pm2) or similar supervisor program.
|
||||
|
||||
Using Docker:
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
* to run it very first time (or after changes related to the monitor code):
|
||||
```
|
||||
docker-compose -f docker-compose-build.yml -f docker-compose.yml up -d --build
|
||||
```
|
||||
* next time (or in case of usage of an official docker image)
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
- The application will run on `http://localhost:PORT`, where `PORT` is specified in your `.env` file.
|
||||
- The application will run on `http://localhost:MONITOR_PORT/MONITOR_BRIDGE_NAME`, where `MONITOR_PORT` and `MONITOR_BRIDGE_NAME` are specified in your `.env` file.
|
||||
- To enabled debug logging, set `DEBUG=1` variable in `.env`.
|
||||
|
||||
## Check balances of contracts and validators, get unprocessed events
|
||||
## Preparing statistic about balances of bridge contracts and validators, get unprocessed events
|
||||
|
||||
Using Yarn:
|
||||
```
|
||||
@@ -138,13 +148,32 @@ yarn check-all
|
||||
|
||||
Using Docker:
|
||||
```
|
||||
docker-compose exec monitor yarn check-all
|
||||
docker run --rm --env-file .env -v $(pwd)/responses:/mono/monitor/responses \
|
||||
poanetwork/tokenbridge-monitor:latest /bin/bash -c 'yarn check-all'
|
||||
```
|
||||
|
||||
As soon as the process finishes, use the URL described above to get the statistic.
|
||||
|
||||
### Cron
|
||||
|
||||
You can create cron job to run workers (see `crontab.example` for reference):
|
||||
|
||||
## Ad-hoc monitoring
|
||||
|
||||
There is a possibility to get bridge statistics without running the web interface use the commands provided above. In this case the results will be located in the `responses` directory.
|
||||
|
||||
## Build the image without running the monitor
|
||||
|
||||
To build the image change the directory:
|
||||
```
|
||||
cd monitor
|
||||
```
|
||||
|
||||
And run the docker composer:
|
||||
```
|
||||
docker-compose -f docker-compose-build.yml build
|
||||
```
|
||||
|
||||
## Linting
|
||||
|
||||
Running linter:
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Web3 = require('web3')
|
||||
const logger = require('./logger')('checkWorker')
|
||||
const { getBridgeMode } = require('../commons')
|
||||
const getBalances = require('./getBalances')
|
||||
const getShortEventStats = require('./getShortEventStats')
|
||||
const validators = require('./validators')
|
||||
const { writeFile, createDir } = require('./utils/file')
|
||||
|
||||
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL, MONITOR_BRIDGE_NAME } = process.env
|
||||
|
||||
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
|
||||
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
|
||||
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
|
||||
|
||||
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL } = process.env
|
||||
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
|
||||
const web3Home = new Web3(homeProvider)
|
||||
|
||||
@@ -15,6 +19,7 @@ const { HOME_ERC_TO_ERC_ABI } = require('../commons')
|
||||
|
||||
async function checkWorker() {
|
||||
try {
|
||||
createDir(`/responses/${MONITOR_BRIDGE_NAME}`)
|
||||
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const bridgeMode = await getBridgeMode(homeBridge)
|
||||
logger.debug('Bridge mode:', bridgeMode)
|
||||
@@ -26,12 +31,35 @@ async function checkWorker() {
|
||||
const foreign = Object.assign({}, balances.foreign, events.foreign)
|
||||
const status = Object.assign({}, balances, events, { home }, { foreign })
|
||||
if (!status) throw new Error('status is empty: ' + JSON.stringify(status))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/getBalances.json'), JSON.stringify(status, null, 4))
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/getBalances.json`, status)
|
||||
|
||||
logger.debug('calling validators()')
|
||||
const vBalances = await validators(bridgeMode)
|
||||
if (!vBalances) throw new Error('vBalances is empty: ' + JSON.stringify(vBalances))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/validators.json'), JSON.stringify(vBalances, null, 4))
|
||||
|
||||
vBalances.homeOk = true
|
||||
vBalances.foreignOk = true
|
||||
|
||||
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
|
||||
for (const hv in vBalances.home.validators) {
|
||||
if (vBalances.home.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
|
||||
vBalances.homeOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
|
||||
for (const hv in vBalances.foreign.validators) {
|
||||
if (vBalances.foreign.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
|
||||
vBalances.foreignOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vBalances.ok = vBalances.homeOk && vBalances.foreignOk
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/validators.json`, vBalances)
|
||||
logger.debug('Done')
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const logger = require('./logger')('checkWorker2')
|
||||
const eventsStats = require('./eventsStats')
|
||||
const alerts = require('./alerts')
|
||||
const { writeFile, createDir } = require('./utils/file')
|
||||
|
||||
const { MONITOR_BRIDGE_NAME } = process.env
|
||||
|
||||
async function checkWorker2() {
|
||||
try {
|
||||
createDir(`/responses/${MONITOR_BRIDGE_NAME}`)
|
||||
logger.debug('calling eventsStats()')
|
||||
const evStats = await eventsStats()
|
||||
if (!evStats) throw new Error('evStats is empty: ' + JSON.stringify(evStats))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/eventsStats.json'), JSON.stringify(evStats, null, 4))
|
||||
evStats.ok =
|
||||
(evStats.onlyInHomeDeposits || evStats.home.deliveredMsgNotProcessedInForeign).length === 0 &&
|
||||
(evStats.onlyInForeignDeposits || evStats.home.processedMsgNotDeliveredInForeign).length === 0 &&
|
||||
(evStats.onlyInHomeWithdrawals || evStats.foreign.deliveredMsgNotProcessedInHome).length === 0 &&
|
||||
(evStats.onlyInForeignWithdrawals || evStats.foreign.processedMsgNotDeliveredInHome).length === 0
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/eventsStats.json`, evStats)
|
||||
|
||||
logger.debug('calling alerts()')
|
||||
const _alerts = await alerts()
|
||||
if (!_alerts) throw new Error('alerts is empty: ' + JSON.stringify(_alerts))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/alerts.json'), JSON.stringify(_alerts, null, 4))
|
||||
_alerts.ok = !_alerts.executeAffirmations.mostRecentTxHash && !_alerts.executeSignatures.mostRecentTxHash
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/alerts.json`, _alerts)
|
||||
logger.debug('Done x2')
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Web3 = require('web3')
|
||||
const logger = require('./logger')('checkWorker3')
|
||||
const stuckTransfers = require('./stuckTransfers')
|
||||
const { writeFile, createDir } = require('./utils/file')
|
||||
|
||||
const { MONITOR_BRIDGE_NAME, COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL } = process.env
|
||||
const { getBridgeMode, HOME_NATIVE_TO_ERC_ABI, BRIDGE_MODES } = require('../commons')
|
||||
|
||||
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
|
||||
const web3Home = new Web3(homeProvider)
|
||||
|
||||
async function checkWorker3() {
|
||||
try {
|
||||
logger.debug('calling stuckTransfers()')
|
||||
const transfers = await stuckTransfers()
|
||||
// console.log(transfers)
|
||||
if (!transfers) throw new Error('transfers is empty: ' + JSON.stringify(transfers))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/stuckTransfers.json'), JSON.stringify(transfers, null, 4))
|
||||
logger.debug('Done')
|
||||
const homeBridge = new web3Home.eth.Contract(HOME_NATIVE_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const bridgeMode = await getBridgeMode(homeBridge)
|
||||
if (bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC_V1) {
|
||||
createDir(`/responses/${MONITOR_BRIDGE_NAME}`)
|
||||
logger.debug('calling stuckTransfers()')
|
||||
const transfers = await stuckTransfers()
|
||||
if (!transfers) throw new Error('transfers is empty: ' + JSON.stringify(transfers))
|
||||
transfers.ok = transfers.total.length === 0
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/stuckTransfers.json`, transfers)
|
||||
logger.debug('Done')
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('checkWorker3.js', e)
|
||||
}
|
||||
|
||||
0
monitor/configs/.gitkeep
Normal file
0
monitor/configs/.gitkeep
Normal file
@@ -2,4 +2,4 @@
|
||||
*/4 * * * * cd $HOME/tokenbridge/monitor; yarn check-all >>cronWorker.out 2>>cronWorker.err
|
||||
|
||||
# Docker:
|
||||
*/4 * * * * cd $HOME/tokenbridge/monitor; docker-compose exec monitor yarn check-all >>cronWorker.out 2>>cronWorker.err
|
||||
*/4 * * * * cd $HOME/tokenbridge/monitor; docker run --rm --env-file .env -v $(pwd)/responses:/mono/monitor/responses poanetwork/tokenbridge-monitor:latest /bin/bash -c 'yarn check-all' >>cronWorker.out 2>>cronWorker.err
|
||||
|
||||
6
monitor/docker-compose-build.yml
Normal file
6
monitor/docker-compose-build.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
version: '2.4'
|
||||
services:
|
||||
monitor:
|
||||
image: poanetwork/tokenbridge-monitor
|
||||
build: .
|
||||
@@ -2,13 +2,13 @@
|
||||
version: '2.4'
|
||||
services:
|
||||
monitor:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
image: poanetwork/tokenbridge-monitor:latest
|
||||
ports:
|
||||
- "${MONITOR_PORT}:${MONITOR_PORT}"
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
volumes:
|
||||
- ./responses:/mono/monitor/responses
|
||||
restart: unless-stopped
|
||||
entrypoint: "yarn check-and-start"
|
||||
entrypoint: "yarn start"
|
||||
|
||||
@@ -83,6 +83,44 @@ async function main(bridgeMode) {
|
||||
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const erc20Address = await foreignBridge.methods.erc20token().call()
|
||||
const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
|
||||
let investedAmountInDai = 0
|
||||
let bridgeDsrBalance = 0
|
||||
let foreignHalfDuplexErc20Balance = 0
|
||||
let displayChaiToken = false
|
||||
let displayHalfDuplexToken = false
|
||||
let tokenSwapAllowed = false
|
||||
try {
|
||||
const halfDuplexTokenAddress = await foreignBridge.methods.halfDuplexErc20token().call()
|
||||
if (halfDuplexTokenAddress !== erc20Address) {
|
||||
const halfDuplexToken = new web3Foreign.eth.Contract(ERC20_ABI, halfDuplexTokenAddress)
|
||||
logger.debug('calling halfDuplexToken.methods.balanceOf')
|
||||
foreignHalfDuplexErc20Balance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
logger.debug('getting last block numbers')
|
||||
const block = await web3Foreign.eth.getBlock('latest')
|
||||
|
||||
logger.debug(`Checking if SCD Emergency Shutdown has happened`)
|
||||
tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call()
|
||||
displayHalfDuplexToken = true
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug('Methods for half duplex token are not present')
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug('calling foreignBridge.methods.isChaiTokenEnabled')
|
||||
if (await foreignBridge.methods.isChaiTokenEnabled().call()) {
|
||||
displayChaiToken = true
|
||||
logger.debug('calling foreignBridge.methods.investedAmountInDai')
|
||||
investedAmountInDai = await foreignBridge.methods.investedAmountInDai().call()
|
||||
logger.debug('calling foreignBridge.methods.dsrBalance')
|
||||
bridgeDsrBalance = await foreignBridge.methods.dsrBalance().call()
|
||||
} else {
|
||||
logger.debug('Chai token is currently disabled')
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug('Methods for chai token are not present')
|
||||
}
|
||||
|
||||
logger.debug('calling erc20Contract.methods.balanceOf')
|
||||
const foreignErc20Balance = await erc20Contract.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
@@ -91,7 +129,7 @@ async function main(bridgeMode) {
|
||||
const blockRewardAddress = await homeBridge.methods.blockRewardContract().call()
|
||||
const blockRewardContract = new web3Home.eth.Contract(BLOCK_REWARD_ABI, blockRewardAddress)
|
||||
logger.debug('calling blockReward.methods.mintedTotally')
|
||||
const mintedCoins = await blockRewardContract.methods.mintedTotally().call()
|
||||
const mintedCoins = await blockRewardContract.methods.mintedTotallyByBridge(COMMON_HOME_BRIDGE_ADDRESS).call()
|
||||
logger.debug('calling homeBridge.methods.totalBurntCoins')
|
||||
const burntCoins = await homeBridge.methods.totalBurntCoins().call()
|
||||
|
||||
@@ -99,16 +137,44 @@ async function main(bridgeMode) {
|
||||
const burntCoinsBN = new BN(burntCoins)
|
||||
const totalSupplyBN = mintedCoinsBN.minus(burntCoinsBN)
|
||||
const foreignErc20BalanceBN = new BN(foreignErc20Balance)
|
||||
const investedAmountInDaiBN = new BN(investedAmountInDai)
|
||||
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)
|
||||
const halfDuplexErc20BalanceBN =
|
||||
displayHalfDuplexToken && tokenSwapAllowed ? new BN(foreignHalfDuplexErc20Balance) : new BN(0)
|
||||
|
||||
const diff = foreignErc20BalanceBN
|
||||
.plus(halfDuplexErc20BalanceBN)
|
||||
.plus(investedAmountInDaiBN)
|
||||
.minus(totalSupplyBN)
|
||||
.toFixed()
|
||||
|
||||
let foreign = {
|
||||
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
|
||||
}
|
||||
|
||||
if (displayHalfDuplexToken && tokenSwapAllowed) {
|
||||
foreign = {
|
||||
...foreign,
|
||||
halfDuplexErc20Balance: Web3Utils.fromWei(foreignHalfDuplexErc20Balance)
|
||||
}
|
||||
} else if (displayHalfDuplexToken && !tokenSwapAllowed) {
|
||||
foreign = {
|
||||
...foreign,
|
||||
halfDuplexErc20BalanceAfterES: Web3Utils.fromWei(foreignHalfDuplexErc20Balance)
|
||||
}
|
||||
}
|
||||
|
||||
if (displayChaiToken) {
|
||||
foreign.investedErc20Balance = Web3Utils.fromWei(investedAmountInDai)
|
||||
foreign.accumulatedInterest = Web3Utils.fromWei(bridgeDsrBalanceBN.minus(investedAmountInDaiBN).toString(10))
|
||||
}
|
||||
|
||||
const diff = foreignErc20BalanceBN.minus(totalSupplyBN).toFixed()
|
||||
logger.debug('Done')
|
||||
return {
|
||||
home: {
|
||||
totalSupply: Web3Utils.fromWei(totalSupplyBN.toFixed())
|
||||
},
|
||||
foreign: {
|
||||
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
|
||||
},
|
||||
foreign,
|
||||
balanceDiff: Number(Web3Utils.fromWei(diff)),
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
|
||||
@@ -1,45 +1,16 @@
|
||||
require('dotenv').config()
|
||||
const express = require('express')
|
||||
const fs = require('fs')
|
||||
const { isV1Bridge } = require('./utils/serverUtils')
|
||||
const { readFile } = require('./utils/file')
|
||||
|
||||
const app = express()
|
||||
const bridgeRouter = express.Router({ mergeParams: true })
|
||||
|
||||
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
|
||||
console.log('MONITOR_TX_NUMBER_THRESHOLD = ' + MONITOR_TX_NUMBER_THRESHOLD)
|
||||
app.get('/favicon.ico', (req, res) => res.sendStatus(204))
|
||||
app.use('/:bridgeName', bridgeRouter)
|
||||
|
||||
async function readFile(path) {
|
||||
bridgeRouter.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const content = await fs.readFileSync(path)
|
||||
const json = JSON.parse(content)
|
||||
const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked
|
||||
return Object.assign({}, json, { timeDiff })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return {
|
||||
error: 'please check your worker'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initV1routes(app) {
|
||||
const exposeV1Routes = await isV1Bridge()
|
||||
if (exposeV1Routes) {
|
||||
app.get('/stuckTransfers', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/stuckTransfers.json')
|
||||
results.ok = results.total.length === 0
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/getBalances.json')
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/getBalances.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
// this will eventually be handled by your error handling middleware
|
||||
@@ -47,24 +18,9 @@ app.get('/', async (req, res, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/validators', async (req, res, next) => {
|
||||
bridgeRouter.get('/validators', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/validators.json')
|
||||
results.homeOk = true
|
||||
results.foreignOk = true
|
||||
for (const hv in results.home.validators) {
|
||||
if (results.home.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
|
||||
results.homeOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
for (const hv in results.foreign.validators) {
|
||||
if (results.foreign.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
|
||||
results.foreignOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
results.ok = results.homeOk && results.foreignOk
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/validators.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
// this will eventually be handled by your error handling middleware
|
||||
@@ -72,15 +28,9 @@ app.get('/validators', async (req, res, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
// responses/eventsStats.json
|
||||
app.get('/eventsStats', async (req, res, next) => {
|
||||
bridgeRouter.get('/eventsStats', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/eventsStats.json')
|
||||
results.ok =
|
||||
(results.onlyInHomeDeposits || results.home.deliveredMsgNotProcessedInForeign).length === 0 &&
|
||||
(results.onlyInForeignDeposits || results.home.processedMsgNotDeliveredInForeign).length === 0 &&
|
||||
(results.onlyInHomeWithdrawals || results.foreign.deliveredMsgNotProcessedInHome).length === 0 &&
|
||||
(results.onlyInForeignWithdrawals || results.foreign.processedMsgNotDeliveredInHome).length === 0
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/eventsStats.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
// this will eventually be handled by your error handling middleware
|
||||
@@ -88,17 +38,23 @@ app.get('/eventsStats', async (req, res, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/alerts', async (req, res, next) => {
|
||||
bridgeRouter.get('/alerts', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/alerts.json')
|
||||
results.ok = !results.executeAffirmations.mostRecentTxHash && !results.executeSignatures.mostRecentTxHash
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/alerts.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
|
||||
initV1routes(app)
|
||||
bridgeRouter.get('/stuckTransfers', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/stuckTransfers.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
|
||||
const port = process.env.MONITOR_PORT || 3003
|
||||
app.set('port', port)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"check-all": "timeout -s 9 5m node checkWorker.js && timeout -s 9 5m node checkWorker2.js",
|
||||
"check-all": "timeout -s 9 5m node checkWorker.js && timeout -s 9 5m node checkWorker2.js && timeout -s 9 5m node checkWorker3.js",
|
||||
"start": "node index.js",
|
||||
"check-and-start": "yarn check-all && yarn start",
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
cd $(dirname $0)/..
|
||||
|
||||
if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
|
||||
# https://github.com/docker/compose/issues/3352
|
||||
COMPOSE_INTERACTIVE_NO_CLI=1 /usr/local/bin/docker-compose exec -T monitor /bin/bash -c 'yarn check-all'
|
||||
else
|
||||
echo "Monitor is not running, skipping checks."
|
||||
fi
|
||||
11
monitor/scripts/getBridgeStats.sh
Executable file
11
monitor/scripts/getBridgeStats.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
cd $(dirname $0)/..
|
||||
|
||||
if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
|
||||
for file in configs/*.env
|
||||
do
|
||||
docker run --rm --env-file $file -v $(pwd)/responses:/mono/monitor/responses poanetwork/tokenbridge-monitor:latest /bin/bash -c 'yarn check-all'
|
||||
done
|
||||
else
|
||||
echo "Monitor is not running, skipping checks."
|
||||
fi
|
||||
@@ -15,6 +15,7 @@ const {
|
||||
ZERO_ADDRESS
|
||||
} = require('../../commons')
|
||||
const { normalizeEventInformation } = require('./message')
|
||||
const { filterTransferBeforeES } = require('./tokenUtils')
|
||||
|
||||
const {
|
||||
COMMON_HOME_RPC_URL,
|
||||
@@ -42,10 +43,11 @@ async function main(mode) {
|
||||
const v1Bridge = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC_V1
|
||||
let isExternalErc20
|
||||
let erc20Contract
|
||||
let erc20Address
|
||||
let normalizeEvent = normalizeEventInformation
|
||||
if (bridgeMode !== BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
const erc20MethodName = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC || v1Bridge ? 'erc677token' : 'erc20token'
|
||||
const erc20Address = await foreignBridge.methods[erc20MethodName]().call()
|
||||
erc20Address = await foreignBridge.methods[erc20MethodName]().call()
|
||||
const tokenType = await getTokenType(
|
||||
new web3Foreign.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, erc20Address),
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS
|
||||
@@ -110,12 +112,31 @@ async function main(mode) {
|
||||
const bridgeTokensSwappedEvents = tokensSwappedEvents.filter(e => e.address === COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
|
||||
// Get transfer events for each previous erc20
|
||||
const uniqueTokenAddresses = [...new Set(bridgeTokensSwappedEvents.map(e => e.returnValues.from))]
|
||||
const uniqueTokenAddressesSet = new Set(bridgeTokensSwappedEvents.map(e => e.returnValues.from))
|
||||
|
||||
// Exclude chai token from previous erc20
|
||||
try {
|
||||
logger.debug('calling foreignBridge.chaiToken()')
|
||||
const chaiToken = await foreignBridge.methods.chaiToken().call()
|
||||
uniqueTokenAddressesSet.delete(chaiToken)
|
||||
} catch (e) {
|
||||
logger.debug('call to foreignBridge.chaiToken() failed')
|
||||
}
|
||||
// Exclude dai token from previous erc20
|
||||
try {
|
||||
logger.debug('calling foreignBridge.erc20token()')
|
||||
const daiToken = await foreignBridge.methods.erc20token().call()
|
||||
uniqueTokenAddressesSet.delete(daiToken)
|
||||
} catch (e) {
|
||||
logger.debug('call to foreignBridge.erc20token() failed')
|
||||
}
|
||||
|
||||
const uniqueTokenAddresses = [...uniqueTokenAddressesSet]
|
||||
await Promise.all(
|
||||
uniqueTokenAddresses.map(async tokenAddress => {
|
||||
const previousERC20 = new web3Foreign.eth.Contract(ERC20_ABI, tokenAddress)
|
||||
const halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, tokenAddress)
|
||||
|
||||
const previousTransferEvents = (await getPastEvents(previousERC20, {
|
||||
const halfDuplexTransferEvents = (await getPastEvents(halfDuplexTokenContract, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber,
|
||||
@@ -123,7 +144,15 @@ async function main(mode) {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
}
|
||||
})).map(normalizeEvent)
|
||||
transferEvents = [...previousTransferEvents, ...transferEvents]
|
||||
|
||||
// Remove events after the ES
|
||||
const validHalfDuplexTransfers = await filterTransferBeforeES(
|
||||
halfDuplexTransferEvents,
|
||||
web3Foreign,
|
||||
foreignBridge
|
||||
)
|
||||
|
||||
transferEvents = [...validHalfDuplexTransfers, ...transferEvents]
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
36
monitor/utils/file.js
Normal file
36
monitor/utils/file.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
async function readFile(filePath) {
|
||||
try {
|
||||
const content = await fs.readFileSync(filePath)
|
||||
const json = JSON.parse(content)
|
||||
const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked
|
||||
return Object.assign({}, json, { timeDiff })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return {
|
||||
error: 'the bridge statistics are not available'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(filePath, object) {
|
||||
fs.writeFileSync(path.join(process.cwd(), filePath), JSON.stringify(object, null, 4))
|
||||
}
|
||||
|
||||
function createDir(dirPath) {
|
||||
try {
|
||||
fs.mkdirSync(path.join(process.cwd(), dirPath), { recursive: true })
|
||||
} catch (e) {
|
||||
if (!e.message.includes('exists')) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readFile,
|
||||
writeFile,
|
||||
createDir
|
||||
}
|
||||
40
monitor/utils/tokenUtils.js
Normal file
40
monitor/utils/tokenUtils.js
Normal file
@@ -0,0 +1,40 @@
|
||||
let beforeESBiggestBlockNumber = 0
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns true if the event was before the Emergency Shutdown.
|
||||
* The method has an optimization to avoid making request if a bigger block number is confirmed
|
||||
* to be before the ES. Events should be iterated from newer to older order to use the optimization.
|
||||
*/
|
||||
async function transferBeforeES(event, web3Foreign, foreignBridge) {
|
||||
const { blockNumber } = event
|
||||
|
||||
if (blockNumber < beforeESBiggestBlockNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
const block = await web3Foreign.eth.getBlock(blockNumber)
|
||||
|
||||
const tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call()
|
||||
if (tokenSwapAllowed) {
|
||||
beforeESBiggestBlockNumber = blockNumber
|
||||
}
|
||||
return tokenSwapAllowed
|
||||
}
|
||||
|
||||
async function filterTransferBeforeES(array, web3Foreign, foreignBridge) {
|
||||
const newArray = []
|
||||
// Iterate events from newer to older
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
const beforeES = await transferBeforeES(array[i], web3Foreign, foreignBridge)
|
||||
if (beforeES) {
|
||||
// add element to first position so the new array will have the same order
|
||||
newArray.unshift(array[i])
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
filterTransferBeforeES
|
||||
}
|
||||
@@ -10,12 +10,10 @@ const {
|
||||
COMMON_FOREIGN_RPC_URL,
|
||||
COMMON_HOME_BRIDGE_ADDRESS,
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT,
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL,
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE,
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK,
|
||||
COMMON_HOME_GAS_PRICE_FACTOR,
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT,
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL,
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE,
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK,
|
||||
@@ -23,6 +21,8 @@ const {
|
||||
} = process.env
|
||||
const MONITOR_HOME_START_BLOCK = Number(process.env.MONITOR_HOME_START_BLOCK) || 0
|
||||
const MONITOR_FOREIGN_START_BLOCK = Number(process.env.MONITOR_FOREIGN_START_BLOCK) || 0
|
||||
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
|
||||
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
|
||||
|
||||
const Web3Utils = Web3.utils
|
||||
|
||||
@@ -65,76 +65,97 @@ async function main(bridgeMode) {
|
||||
const foreignBridgeValidators = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, foreignValidatorsAddress)
|
||||
|
||||
logger.debug('calling foreignBridgeValidators getValidatorList()')
|
||||
const foreignValidators = await getValidatorList(foreignValidatorsAddress, web3Foreign.eth, {
|
||||
const foreignValidators = (await getValidatorList(foreignValidatorsAddress, web3Foreign.eth, {
|
||||
from: MONITOR_FOREIGN_START_BLOCK,
|
||||
to: foreignBlockNumber,
|
||||
logger
|
||||
})
|
||||
})).map(web3Foreign.utils.toChecksumAddress)
|
||||
|
||||
logger.debug('calling homeBridgeValidators getValidatorList()')
|
||||
const homeValidators = await getValidatorList(homeValidatorsAddress, web3Home.eth, {
|
||||
const homeValidators = (await getValidatorList(homeValidatorsAddress, web3Home.eth, {
|
||||
from: MONITOR_HOME_START_BLOCK,
|
||||
to: homeBlockNumber,
|
||||
logger
|
||||
})
|
||||
})).map(web3Home.utils.toChecksumAddress)
|
||||
|
||||
const homeBalances = {}
|
||||
logger.debug('calling asyncForEach homeValidators homeBalances')
|
||||
await asyncForEach(homeValidators, async v => {
|
||||
homeBalances[v] = Web3Utils.fromWei(await web3Home.eth.getBalance(v))
|
||||
})
|
||||
const foreignVBalances = {}
|
||||
const homeVBalances = {}
|
||||
|
||||
logger.debug('calling home getGasPrices')
|
||||
const homeGasPrice =
|
||||
(await gasPriceFromSupplier(() => fetch(COMMON_HOME_GAS_PRICE_SUPPLIER_URL), homeGasPriceSupplierOpts)) ||
|
||||
Web3Utils.toBN(COMMON_HOME_GAS_PRICE_FALLBACK)
|
||||
const homeGasPriceGwei = Web3Utils.fromWei(homeGasPrice.toString(), 'gwei')
|
||||
const homeTxCost = homeGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_HOME_TX_LIMIT))
|
||||
let homeGasPrice
|
||||
let homeGasPriceGwei
|
||||
let homeTxCost
|
||||
|
||||
logger.debug('calling foreign getGasPrices')
|
||||
const foreignGasPrice =
|
||||
(await gasPriceFromSupplier(() => fetch(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL), foreignGasPriceSupplierOpts)) ||
|
||||
Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK)
|
||||
const foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei')
|
||||
const foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT))
|
||||
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
|
||||
logger.debug('calling home getGasPrices')
|
||||
homeGasPrice =
|
||||
(await gasPriceFromSupplier(() => fetch(COMMON_HOME_GAS_PRICE_SUPPLIER_URL), homeGasPriceSupplierOpts)) ||
|
||||
Web3Utils.toBN(COMMON_HOME_GAS_PRICE_FALLBACK)
|
||||
homeGasPriceGwei = Web3Utils.fromWei(homeGasPrice.toString(), 'gwei')
|
||||
homeTxCost = homeGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_HOME_TX_LIMIT))
|
||||
}
|
||||
|
||||
let foreignGasPrice
|
||||
let foreignGasPriceGwei
|
||||
let foreignTxCost
|
||||
|
||||
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
|
||||
logger.debug('calling foreign getGasPrices')
|
||||
foreignGasPrice =
|
||||
(await gasPriceFromSupplier(() => fetch(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL), foreignGasPriceSupplierOpts)) ||
|
||||
Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK)
|
||||
foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei')
|
||||
foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT))
|
||||
}
|
||||
|
||||
let validatorsMatch = true
|
||||
logger.debug('calling asyncForEach foreignValidators foreignVBalances')
|
||||
await asyncForEach(foreignValidators, async v => {
|
||||
const balance = await web3Foreign.eth.getBalance(v)
|
||||
const leftTx = Web3Utils.toBN(balance)
|
||||
.div(foreignTxCost)
|
||||
.toString(10)
|
||||
foreignVBalances[v] = {
|
||||
balance: Web3Utils.fromWei(balance),
|
||||
leftTx: Number(leftTx),
|
||||
gasPrice: Number(foreignGasPriceGwei)
|
||||
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
|
||||
const leftTx = Web3Utils.toBN(balance)
|
||||
.div(foreignTxCost)
|
||||
.toString(10)
|
||||
foreignVBalances[v] = {
|
||||
balance: Web3Utils.fromWei(balance),
|
||||
leftTx: Number(leftTx),
|
||||
gasPrice: Number(foreignGasPriceGwei)
|
||||
}
|
||||
} else {
|
||||
foreignVBalances[v] = {
|
||||
balance: Web3Utils.fromWei(balance)
|
||||
}
|
||||
}
|
||||
|
||||
if (!homeValidators.includes(v)) {
|
||||
validatorsMatch = false
|
||||
foreignVBalances[v].onlyOnForeign = true
|
||||
}
|
||||
})
|
||||
|
||||
logger.debug('calling asyncForEach homeValidators homeVBalances')
|
||||
await asyncForEach(homeValidators, async v => {
|
||||
const balance = await web3Home.eth.getBalance(v)
|
||||
const leftTx = homeTxCost.isZero()
|
||||
? 999999
|
||||
: Web3Utils.toBN(balance)
|
||||
.div(homeTxCost)
|
||||
.toString(10)
|
||||
homeVBalances[v] = {
|
||||
balance: Web3Utils.fromWei(balance),
|
||||
leftTx: Number(leftTx),
|
||||
gasPrice: Number(homeGasPriceGwei)
|
||||
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
|
||||
const leftTx = Web3Utils.toBN(balance)
|
||||
.div(homeTxCost)
|
||||
.toString(10)
|
||||
homeVBalances[v] = {
|
||||
balance: Web3Utils.fromWei(balance),
|
||||
leftTx: Number(leftTx),
|
||||
gasPrice: Number(homeGasPriceGwei)
|
||||
}
|
||||
} else {
|
||||
homeVBalances[v] = {
|
||||
balance: Web3Utils.fromWei(balance)
|
||||
}
|
||||
}
|
||||
|
||||
if (!foreignValidators.includes(v)) {
|
||||
validatorsMatch = false
|
||||
homeVBalances[v].onlyOnHome = true
|
||||
}
|
||||
})
|
||||
|
||||
logger.debug('calling homeBridgeValidators.methods.requiredSignatures().call()')
|
||||
const reqSigHome = await homeBridgeValidators.methods.requiredSignatures().call()
|
||||
logger.debug('calling foreignBridgeValidators.methods.requiredSignatures().call()')
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"start": "mocha",
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
"native-to-erc": "mocha test/nativeToErc.js"
|
||||
"amb": "ULTIMATE=true mocha test/amb.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cd $(dirname $0)
|
||||
|
||||
../e2e-commons/up.sh deploy oracle
|
||||
../e2e-commons/up.sh deploy blocks oracle oracle-validator-2 oracle-validator-3
|
||||
|
||||
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start
|
||||
rc=$?
|
||||
|
||||
@@ -1,22 +1,55 @@
|
||||
const Web3 = require('web3')
|
||||
const assert = require('assert')
|
||||
const promiseRetry = require('promise-retry')
|
||||
const { user, homeRPC, foreignRPC, amb } = require('../../e2e-commons/constants.json')
|
||||
const { generateNewBlock } = require('../../e2e-commons/utils')
|
||||
const { BOX_ABI } = require('../../commons')
|
||||
const { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json')
|
||||
const { uniformRetry } = require('../../e2e-commons/utils')
|
||||
const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI } = require('../../commons')
|
||||
const { setRequiredSignatures } = require('./utils')
|
||||
|
||||
const { toBN } = Web3.utils
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(foreignRPC.URL))
|
||||
|
||||
const COMMON_HOME_BRIDGE_ADDRESS = amb.home
|
||||
const COMMON_FOREIGN_BRIDGE_ADDRESS = amb.foreign
|
||||
|
||||
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
|
||||
const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox)
|
||||
const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox)
|
||||
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
|
||||
describe('arbitrary message bridging', () => {
|
||||
before(async () => {
|
||||
// Only 1 validator is used in ultimate tests
|
||||
if (process.env.ULTIMATE !== 'true') {
|
||||
// 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', () => {
|
||||
it('should bridge message', async () => {
|
||||
@@ -25,7 +58,7 @@ describe('arbitrary message bridging', () => {
|
||||
const initialValue = await foreignBox.methods.value().call()
|
||||
assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value')
|
||||
|
||||
const setValueTx = await homeBox.methods
|
||||
await homeBox.methods
|
||||
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
|
||||
.send({
|
||||
from: user.address,
|
||||
@@ -35,30 +68,8 @@ describe('arbitrary message bridging', () => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
|
||||
// The bridge should create a new transaction with a CollectedSignatures
|
||||
// event so we generate another trivial transaction
|
||||
await promiseRetry(
|
||||
async retry => {
|
||||
const lastBlockNumber = await homeWeb3.eth.getBlockNumber()
|
||||
if (lastBlockNumber >= setValueTx.blockNumber + 2) {
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
} else {
|
||||
retry()
|
||||
}
|
||||
},
|
||||
{
|
||||
forever: true,
|
||||
factor: 1,
|
||||
minTimeout: 500
|
||||
}
|
||||
)
|
||||
|
||||
// check that value changed and balance decreased
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const value = await foreignBox.methods.value().call()
|
||||
if (!toBN(value).eq(toBN(newValue))) {
|
||||
retry()
|
||||
@@ -85,12 +96,8 @@ describe('arbitrary message bridging', () => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(foreignWeb3, user.address)
|
||||
|
||||
// check that value changed and balance decreased
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const value = await homeBox.methods.value().call()
|
||||
if (!toBN(value).eq(toBN(newValue))) {
|
||||
retry()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const Web3 = require('web3')
|
||||
const assert = require('assert')
|
||||
const promiseRetry = require('promise-retry')
|
||||
const { user, secondUser, ercToErcBridge, homeRPC, foreignRPC } = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||
const { generateNewBlock } = require('../../e2e-commons/utils')
|
||||
const { user, secondUser, ercToErcBridge, homeRPC, foreignRPC, validator } = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_ERC_ABI } = require('../../commons')
|
||||
const { uniformRetry } = require('../../e2e-commons/utils')
|
||||
const { setRequiredSignatures } = require('./utils')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(foreignRPC.URL))
|
||||
@@ -14,13 +14,39 @@ const COMMON_FOREIGN_BRIDGE_ADDRESS = ercToErcBridge.foreign
|
||||
const { toBN } = foreignWeb3.utils
|
||||
|
||||
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
|
||||
const erc20Token = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, ercToErcBridge.foreignToken)
|
||||
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const erc677Token = new homeWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, ercToErcBridge.homeToken)
|
||||
const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
|
||||
describe('erc to erc', () => {
|
||||
before(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 convert tokens in foreign to tokens in home', async () => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
assert(!toBN(balance).isZero(), 'Account should have tokens')
|
||||
@@ -49,12 +75,8 @@ describe('erc to erc', () => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(foreignWeb3, user.address)
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await erc677Token.methods.balanceOf(user.address).call()
|
||||
const recipientBalance = await erc677Token.methods.balanceOf(secondUser.address).call()
|
||||
assert(toBN(balance).isZero(), 'User balance should be the same')
|
||||
@@ -76,12 +98,8 @@ describe('erc to erc', () => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(foreignWeb3, user.address)
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await erc677Token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).isZero()) {
|
||||
retry()
|
||||
@@ -98,7 +116,7 @@ describe('erc to erc', () => {
|
||||
assert(!toBN(balance).isZero(), 'Account should have tokens')
|
||||
|
||||
// send transaction to home bridge
|
||||
const depositTx = await erc677Token.methods
|
||||
await erc677Token.methods
|
||||
.transferAndCall(COMMON_HOME_BRIDGE_ADDRESS, homeWeb3.utils.toWei('0.01'), '0x')
|
||||
.send({
|
||||
from: user.address,
|
||||
@@ -108,30 +126,8 @@ describe('erc to erc', () => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
|
||||
// The bridge should create a new transaction with a CollectedSignatures
|
||||
// event so we generate another trivial transaction
|
||||
await promiseRetry(
|
||||
async retry => {
|
||||
const lastBlockNumber = await homeWeb3.eth.getBlockNumber()
|
||||
if (lastBlockNumber >= depositTx.blockNumber + 2) {
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
} else {
|
||||
retry()
|
||||
}
|
||||
},
|
||||
{
|
||||
forever: true,
|
||||
factor: 1,
|
||||
minTimeout: 500
|
||||
}
|
||||
)
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).lte(toBN(originalBalance))) {
|
||||
retry()
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
const Web3 = require('web3')
|
||||
const assert = require('assert')
|
||||
const promiseRetry = require('promise-retry')
|
||||
const { user, secondUser, ercToNativeBridge, homeRPC, foreignRPC } = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||
const { generateNewBlock } = require('../../e2e-commons/utils')
|
||||
const {
|
||||
user,
|
||||
secondUser,
|
||||
validator,
|
||||
ercToNativeBridge,
|
||||
homeRPC,
|
||||
foreignRPC
|
||||
} = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, SAI_TOP, HOME_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||
const { uniformRetry, sleep } = require('../../e2e-commons/utils')
|
||||
const { setRequiredSignatures } = require('./utils')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(foreignRPC.URL))
|
||||
@@ -14,12 +22,144 @@ const COMMON_FOREIGN_BRIDGE_ADDRESS = ercToNativeBridge.foreign
|
||||
const { toBN } = foreignWeb3.utils
|
||||
|
||||
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
|
||||
const erc20Token = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, ercToNativeBridge.foreignToken)
|
||||
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
|
||||
describe('erc to native', () => {
|
||||
let halfDuplexTokenAddress
|
||||
let halfDuplexToken
|
||||
before(async () => {
|
||||
halfDuplexTokenAddress = await foreignBridge.methods.halfDuplexErc20token().call()
|
||||
halfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, halfDuplexTokenAddress)
|
||||
|
||||
// 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 continue working after migration', async () => {
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
|
||||
const transferValue = homeWeb3.utils.toWei('0.01')
|
||||
|
||||
// erc20 token address and half duplex address are the same before migration
|
||||
const tokenAddress = await foreignBridge.methods.erc20token().call()
|
||||
|
||||
const erc20AndhalfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, tokenAddress)
|
||||
|
||||
// send tokens to foreign bridge
|
||||
await erc20AndhalfDuplexToken.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async (retry, number) => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
// retry at least 4 times to check transfer is not double processed by the two watchers
|
||||
if (toBN(balance).lte(toBN(originalBalanceOnHome)) || number < 4) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(balance).eq(toBN(originalBalanceOnHome).add(toBN(transferValue))),
|
||||
'User balance should be increased only by second transfer'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// call migration
|
||||
await foreignBridge.methods.migrateToMCD().send({
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
})
|
||||
|
||||
// update min threshold for swap
|
||||
await foreignBridge.methods.setMinHDTokenBalance(foreignWeb3.utils.toWei('2', 'ether')).send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
const AfterMigrateBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
|
||||
// send tokens to foreign bridge
|
||||
await erc20Token.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async (retry, number) => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
// retry at least 4 times to check transfer is not double processed by the two watchers
|
||||
if (toBN(balance).lte(toBN(AfterMigrateBalance)) || number < 4) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(balance).eq(toBN(AfterMigrateBalance).add(toBN(transferValue))),
|
||||
'User balance should be increased only by second transfer'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const afterMigrateAndTransferBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
|
||||
// send tokens to foreign bridge
|
||||
await halfDuplexToken.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async (retry, number) => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
// retry at least 4 times to check transfer is not double processed by the two watchers
|
||||
if (toBN(balance).lte(toBN(afterMigrateAndTransferBalance)) || number < 4) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(balance).eq(toBN(afterMigrateAndTransferBalance).add(toBN(transferValue))),
|
||||
'User balance should be increased only by second transfer'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should convert tokens in foreign to coins in home', async () => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
@@ -48,12 +188,8 @@ describe('erc to native', () => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(foreignWeb3, user.address)
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
const secondUserbalance = await homeWeb3.eth.getBalance(secondUser.address)
|
||||
assert(toBN(balance).lte(toBN(originalBalanceOnHome)), 'User balance should be the same')
|
||||
@@ -75,12 +211,8 @@ describe('erc to native', () => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(foreignWeb3, user.address)
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
if (toBN(balance).lte(toBN(originalBalanceOnHome))) {
|
||||
retry()
|
||||
@@ -92,6 +224,223 @@ describe('erc to native', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should convert half duplex token in foreign to native token in home', async () => {
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
const bridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether')
|
||||
|
||||
// this transfer won't trigger a call to swap tokens
|
||||
await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
if (toBN(balance).lte(toBN(originalBalanceOnHome))) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(balance).eq(toBN(originalBalanceOnHome).add(toBN(valueToTransfer))),
|
||||
'User balance should be increased by the half duplex token transfer'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const updatedBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const updatedBridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(
|
||||
toBN(updatedBridgeHalfDuplexBalance).eq(toBN(bridgeHalfDuplexBalance).add(toBN(valueToTransfer))),
|
||||
'Bridge balance should reflect the transfer value'
|
||||
)
|
||||
|
||||
// this transfer will trigger call to swap tokens
|
||||
await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await sleep(2000)
|
||||
|
||||
await uniformRetry(async retry => {
|
||||
const userBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
if (
|
||||
toBN(userBalance).lte(toBN(updatedBalanceOnHome)) ||
|
||||
toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance))
|
||||
) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(userBalance).eq(toBN(updatedBalanceOnHome).add(toBN(valueToTransfer))),
|
||||
'User balance should be increased by the half duplex token transfer'
|
||||
)
|
||||
const updatedBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(toBN(updatedBalance).isZero(), 'Half duplex bridge balance should be zero')
|
||||
assert(
|
||||
toBN(updatedBridgeErc20TokenBalance).eq(
|
||||
toBN(bridgeErc20TokenBalance)
|
||||
.add(toBN(bridgeHalfDuplexBalance))
|
||||
.add(toBN(foreignWeb3.utils.toWei('2', 'ether')))
|
||||
),
|
||||
'Erc20 token balance should be correctly increased by the token swap'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should convert half duplex token in foreign to native token in home for alternative receiver ', async () => {
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const initialBalanceSecondUser = await homeWeb3.eth.getBalance(secondUser.address)
|
||||
const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether')
|
||||
|
||||
// approve tokens to foreign bridge
|
||||
await halfDuplexToken.methods
|
||||
.approve(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// call bridge method to transfer tokens to a different recipient
|
||||
await foreignBridge.methods['relayTokens(address,uint256,address)'](
|
||||
secondUser.address,
|
||||
valueToTransfer,
|
||||
halfDuplexTokenAddress
|
||||
)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
const secondUserbalance = await homeWeb3.eth.getBalance(secondUser.address)
|
||||
const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
const userbalance = await homeWeb3.eth.getBalance(user.address)
|
||||
assert(toBN(userbalance).lte(toBN(originalBalanceOnHome)), 'User balance should be the same')
|
||||
if (
|
||||
toBN(secondUserbalance).lte(toBN(initialBalanceSecondUser)) ||
|
||||
toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance))
|
||||
) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(secondUserbalance).eq(toBN(initialBalanceSecondUser).add(toBN(valueToTransfer))),
|
||||
'User balance should be increased by the half duplex token transfer'
|
||||
)
|
||||
const updatedHDBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(toBN(updatedHDBalance).isZero(), 'Half duplex bridge balance should be zero')
|
||||
assert(
|
||||
toBN(updatedBridgeErc20TokenBalance).eq(toBN(bridgeErc20TokenBalance).add(toBN(valueToTransfer))),
|
||||
'Erc20 token balance should be correctly increased by the token swap'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should not relay half duplex token transfer after Emergency Shutdown', async () => {
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
const bridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
const block = await foreignWeb3.eth.getBlock('latest')
|
||||
const saiTop = new foreignWeb3.eth.Contract(SAI_TOP, ercToNativeBridge.saiTop)
|
||||
|
||||
// Trigger Emergency Shutdown
|
||||
await saiTop.methods
|
||||
.setCaged(block.timestamp)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether')
|
||||
|
||||
await sleep(2000)
|
||||
|
||||
// this transfer won't trigger a call to swap tokens
|
||||
await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
// check that transfer and swap are not processed in the next blocks.
|
||||
await promiseRetry(async (retry, number) => {
|
||||
const balanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const currentBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
const currentBridgeHalfDuplexBalance = await halfDuplexToken.methods
|
||||
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
.call()
|
||||
|
||||
assert(toBN(balanceOnHome).eq(toBN(originalBalanceOnHome)), 'User balance should be the same')
|
||||
assert(
|
||||
toBN(currentBridgeHalfDuplexBalance).eq(toBN(bridgeHalfDuplexBalance).add(toBN(valueToTransfer))),
|
||||
'Half duplex balance should be the value of transfer'
|
||||
)
|
||||
assert(toBN(currentBridgeErc20TokenBalance).eq(toBN(bridgeErc20TokenBalance)), 'erc20 balance should not change')
|
||||
|
||||
// after several retries, the state is corrects
|
||||
if (number < 4) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
|
||||
// let's undo the Emergency Shutdown to check that the oracle is still working
|
||||
await saiTop.methods.setCaged('0').send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
const newValueToTransfer = foreignWeb3.utils.toWei('2', 'ether')
|
||||
|
||||
await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, newValueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await sleep(2000)
|
||||
|
||||
await uniformRetry(async retry => {
|
||||
const userBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
if (
|
||||
toBN(userBalance).lte(toBN(originalBalanceOnHome)) ||
|
||||
toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance))
|
||||
) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(userBalance).eq(toBN(originalBalanceOnHome).add(toBN(newValueToTransfer))),
|
||||
'User balance should be increased by the half duplex token transfer'
|
||||
)
|
||||
const updatedHDBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(toBN(updatedHDBalance).isZero(), 'Half duplex bridge balance should be zero')
|
||||
assert(
|
||||
toBN(updatedBridgeErc20TokenBalance).eq(
|
||||
toBN(bridgeErc20TokenBalance)
|
||||
.add(toBN(valueToTransfer))
|
||||
.add(toBN(newValueToTransfer))
|
||||
),
|
||||
'Erc20 token balance should be correctly increased by the token swap'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should convert coins in home to tokens in foreign', async () => {
|
||||
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
|
||||
@@ -100,7 +449,7 @@ describe('erc to native', () => {
|
||||
assert(!toBN(balance).isZero(), 'Account should have tokens')
|
||||
|
||||
// send transaction to home bridge
|
||||
const depositTx = await homeWeb3.eth.sendTransaction({
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: user.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
@@ -108,34 +457,217 @@ describe('erc to native', () => {
|
||||
value: homeWeb3.utils.toWei('0.01')
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
|
||||
// The bridge should create a new transaction with a CollectedSignatures
|
||||
// event so we generate another trivial transaction
|
||||
await promiseRetry(
|
||||
async retry => {
|
||||
const lastBlockNumber = await homeWeb3.eth.getBlockNumber()
|
||||
if (lastBlockNumber >= depositTx.blockNumber + 2) {
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
} else {
|
||||
retry()
|
||||
}
|
||||
},
|
||||
{
|
||||
forever: true,
|
||||
factor: 1,
|
||||
minTimeout: 500
|
||||
}
|
||||
)
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).lte(toBN(originalBalance))) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should not invest dai when chai token is disabled', async () => {
|
||||
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
await foreignBridge.methods.setMinDaiTokenBalance(foreignWeb3.utils.toWei('2', 'ether')).send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
}) // set min limit for automatic investment to 2*2 dai
|
||||
|
||||
const valueToTransfer = foreignWeb3.utils.toWei('5', 'ether')
|
||||
|
||||
// this transfer won't trigger a call to convert to chai
|
||||
await erc20Token.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await promiseRetry(async (retry, number) => {
|
||||
if (number < 4) {
|
||||
retry()
|
||||
} else {
|
||||
const updatedBridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(
|
||||
toBN(bridgeDaiTokenBalance)
|
||||
.add(toBN(valueToTransfer))
|
||||
.eq(toBN(updatedBridgeDaiTokenBalance)),
|
||||
'Dai tokens should not be when chai is disabled'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should invest dai after enough tokens are collected on bridge account', async () => {
|
||||
await foreignBridge.methods.initializeChaiToken().send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
}) // initialize chai token
|
||||
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
|
||||
await foreignBridge.methods.setMinDaiTokenBalance(foreignWeb3.utils.toWei('2', 'ether')).send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
}) // set investing limit to 2 dai, automatically invest should happen after 4 dai
|
||||
|
||||
const valueToTransfer = foreignWeb3.utils.toWei('3', 'ether')
|
||||
|
||||
// this transfer won't trigger a call to convert to chai
|
||||
await erc20Token.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await promiseRetry(async (retry, number) => {
|
||||
if (number < 4) {
|
||||
retry()
|
||||
} else {
|
||||
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(
|
||||
valueToTransfer === bridgeDaiTokenBalance,
|
||||
'Dai tokens should not be invested automatically before twice limit is reached'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// this transfer will trigger call to convert to chai
|
||||
await erc20Token.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await promiseRetry(async retry => {
|
||||
const updatedBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
if (toBN(updatedBalance).gte(toBN(valueToTransfer).add(toBN(valueToTransfer)))) {
|
||||
retry()
|
||||
} else {
|
||||
const updatedBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(
|
||||
toBN(updatedBalance).eq(toBN(foreignWeb3.utils.toWei('2', 'ether'))),
|
||||
'Dai bridge balance should be equal to limit'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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'
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1 +1 @@
|
||||
--timeout 30000
|
||||
--timeout 120000
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
const Web3 = require('web3')
|
||||
const assert = require('assert')
|
||||
const promiseRetry = require('promise-retry')
|
||||
const {
|
||||
user,
|
||||
validator,
|
||||
secondValidator,
|
||||
thirdValidator,
|
||||
nativeToErcBridge,
|
||||
secondUser,
|
||||
thirdUser,
|
||||
fourthUser,
|
||||
homeRPC,
|
||||
foreignRPC
|
||||
} = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI } = require('../../commons')
|
||||
const { generateNewBlock } = require('../../e2e-commons/utils')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, HOME_NATIVE_TO_ERC_ABI, FOREIGN_NATIVE_TO_ERC_ABI } = require('../../commons')
|
||||
const { uniformRetry, sleep } = require('../../e2e-commons/utils')
|
||||
const { setRequiredSignatures } = require('./utils')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(foreignRPC.URL))
|
||||
@@ -19,25 +23,58 @@ const { toBN } = foreignWeb3.utils
|
||||
const COMMON_HOME_BRIDGE_ADDRESS = nativeToErcBridge.home
|
||||
const COMMON_FOREIGN_BRIDGE_ADDRESS = nativeToErcBridge.foreign
|
||||
|
||||
const validatorAddresses = [validator.address, secondValidator.address, thirdValidator.address]
|
||||
|
||||
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(secondUser.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(secondValidator.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(thirdValidator.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(thirdUser.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(fourthUser.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(secondUser.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(secondValidator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(thirdValidator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(thirdUser.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(fourthUser.privateKey)
|
||||
|
||||
const token = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, nativeToErcBridge.foreignToken)
|
||||
|
||||
const sleep = timeout => new Promise(res => setTimeout(res, timeout))
|
||||
const homeBridge = new homeWeb3.eth.Contract(HOME_NATIVE_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_NATIVE_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
|
||||
describe('native to erc', () => {
|
||||
before(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 convert eth in home to tokens in foreign', async () => {
|
||||
// check that account has zero tokens in the foreign chain
|
||||
const balance = await token.methods.balanceOf(user.address).call()
|
||||
assert(toBN(balance).isZero(), 'Account should not have tokens yet')
|
||||
|
||||
// send transaction to home chain
|
||||
const depositTx = await homeWeb3.eth.sendTransaction({
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: user.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
@@ -45,30 +82,8 @@ describe('native to erc', () => {
|
||||
value: '1000000000000000000'
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
|
||||
// The bridge should create a new transaction with a CollectedSignatures
|
||||
// event so we generate another trivial transaction
|
||||
await promiseRetry(
|
||||
async retry => {
|
||||
const lastBlockNumber = await homeWeb3.eth.getBlockNumber()
|
||||
if (lastBlockNumber >= depositTx.blockNumber + 2) {
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
} else {
|
||||
retry()
|
||||
}
|
||||
},
|
||||
{
|
||||
forever: true,
|
||||
factor: 1,
|
||||
minTimeout: 500
|
||||
}
|
||||
)
|
||||
|
||||
// check that account has tokens in the foreign chain
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).isZero()) {
|
||||
retry()
|
||||
@@ -90,12 +105,8 @@ describe('native to erc', () => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// Send a trivial transaction to generate a new block since the watcher
|
||||
// is configured to wait 1 confirmation block
|
||||
await generateNewBlock(foreignWeb3, user.address)
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
if (toBN(balance).lte(toBN(originalBalance))) {
|
||||
retry()
|
||||
@@ -109,9 +120,12 @@ describe('native to erc', () => {
|
||||
|
||||
// empty validator funds
|
||||
await sendAllBalance(homeWeb3, validator.address, secondUser.address)
|
||||
await sendAllBalance(homeWeb3, secondValidator.address, thirdUser.address)
|
||||
await sendAllBalance(homeWeb3, thirdValidator.address, fourthUser.address)
|
||||
|
||||
const nonces = await Promise.all(validatorAddresses.map(homeWeb3.eth.getTransactionCount))
|
||||
// send transaction to home chain
|
||||
const depositTx = await homeWeb3.eth.sendTransaction({
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
from: user.address,
|
||||
to: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
gasPrice: '1',
|
||||
@@ -119,39 +133,20 @@ describe('native to erc', () => {
|
||||
value: '1000000000000000000'
|
||||
})
|
||||
|
||||
// Send a Tx to generate a new block
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
|
||||
// wait two seconds, no new blocks should have been generated
|
||||
await sleep(2000)
|
||||
const lastBlockNumber = await homeWeb3.eth.getBlockNumber()
|
||||
const newNonces = await Promise.all(validatorAddresses.map(homeWeb3.eth.getTransactionCount))
|
||||
const balance = toBN(await token.methods.balanceOf(user.address).call())
|
||||
assert(lastBlockNumber === depositTx.blockNumber + 1, "Shouldn't have emitted a new block")
|
||||
assert.deepStrictEqual(nonces, newNonces, "Shouldn't sent new tx")
|
||||
assert(originalBalance.eq(balance), "Token balance shouldn't have changed")
|
||||
|
||||
// send funds back to validator
|
||||
const sendBalanceBackTx = await sendAllBalance(homeWeb3, secondUser.address, validator.address)
|
||||
|
||||
// expect Deposit event to be processed
|
||||
await promiseRetry(
|
||||
async retry => {
|
||||
const lastBlockNumber = await homeWeb3.eth.getBlockNumber()
|
||||
// check that a new block was created since the last transaction
|
||||
if (lastBlockNumber === sendBalanceBackTx.blockNumber + 1) {
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
} else {
|
||||
retry()
|
||||
}
|
||||
},
|
||||
{
|
||||
forever: true,
|
||||
factor: 1,
|
||||
minTimeout: 500
|
||||
}
|
||||
)
|
||||
await sendAllBalance(homeWeb3, secondUser.address, validator.address)
|
||||
await sendAllBalance(homeWeb3, thirdUser.address, secondValidator.address)
|
||||
await sendAllBalance(homeWeb3, fourthUser.address, thirdValidator.address)
|
||||
|
||||
// check that token balance was incremented in foreign chain
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = toBN(await token.methods.balanceOf(user.address).call())
|
||||
if (!balance.gt(originalBalance)) {
|
||||
retry()
|
||||
@@ -165,7 +160,9 @@ describe('native to erc', () => {
|
||||
|
||||
// empty foreign validator funds
|
||||
await sendAllBalance(foreignWeb3, validator.address, secondUser.address)
|
||||
const foreignBlockNumber = await foreignWeb3.eth.getBlockNumber()
|
||||
await sendAllBalance(foreignWeb3, secondValidator.address, thirdUser.address)
|
||||
await sendAllBalance(foreignWeb3, thirdValidator.address, fourthUser.address)
|
||||
const nonces = await Promise.all(validatorAddresses.map(foreignWeb3.eth.getTransactionCount))
|
||||
|
||||
// send transaction to home chain
|
||||
await homeWeb3.eth.sendTransaction({
|
||||
@@ -176,36 +173,18 @@ describe('native to erc', () => {
|
||||
value: '1000000000000000000'
|
||||
})
|
||||
|
||||
// Send a Tx to generate a new block
|
||||
const lastHomeTx = await generateNewBlock(homeWeb3, user.address)
|
||||
|
||||
// wait for the deposit to be processed
|
||||
await promiseRetry(
|
||||
async retry => {
|
||||
const lastBlockNumber = await homeWeb3.eth.getBlockNumber()
|
||||
if (lastBlockNumber === lastHomeTx.blockNumber + 1) {
|
||||
await generateNewBlock(homeWeb3, user.address)
|
||||
} else {
|
||||
retry()
|
||||
}
|
||||
},
|
||||
{
|
||||
forever: true,
|
||||
factor: 1,
|
||||
minTimeout: 500
|
||||
}
|
||||
)
|
||||
|
||||
// tokens shouldn't be generated in the foreign chain because the validator doesn't have funds
|
||||
await sleep(2000)
|
||||
const lastForeignBlockNumber = await foreignWeb3.eth.getBlockNumber()
|
||||
assert(lastForeignBlockNumber === foreignBlockNumber, "Shouldn't have emitted a new block")
|
||||
const newNonces = await Promise.all(validatorAddresses.map(foreignWeb3.eth.getTransactionCount))
|
||||
assert.deepStrictEqual(nonces, newNonces, "Shouldn't sent new tx")
|
||||
|
||||
// send funds back to validator
|
||||
await sendAllBalance(foreignWeb3, secondUser.address, validator.address)
|
||||
await sendAllBalance(foreignWeb3, thirdUser.address, secondValidator.address)
|
||||
await sendAllBalance(foreignWeb3, fourthUser.address, thirdValidator.address)
|
||||
|
||||
// check that account has tokens in the foreign chain
|
||||
await promiseRetry(async retry => {
|
||||
await uniformRetry(async retry => {
|
||||
const balance = toBN(await token.methods.balanceOf(user.address).call())
|
||||
if (balance.eq(originalBalance)) {
|
||||
retry()
|
||||
|
||||
12
oracle-e2e/test/utils.js
Normal file
12
oracle-e2e/test/utils.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const { BRIDGE_VALIDATORS_ABI } = require('../../commons')
|
||||
|
||||
const setRequiredSignatures = async ({ bridgeContract, web3, requiredSignatures, options }) => {
|
||||
const validatorAddress = await bridgeContract.methods.validatorContract().call()
|
||||
const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorAddress)
|
||||
|
||||
return validatorContract.methods.setRequiredSignatures(requiredSignatures).send(options)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setRequiredSignatures
|
||||
}
|
||||
@@ -22,8 +22,6 @@ RUN mv ./contracts/build ./ && rm -rf ./contracts/* ./contracts/.[!.]* && mv ./b
|
||||
COPY ./commons ./commons
|
||||
|
||||
COPY ./oracle ./oracle
|
||||
ARG DOT_ENV_PATH=./oracle/.env
|
||||
COPY ${DOT_ENV_PATH} ./oracle/.env
|
||||
|
||||
WORKDIR /mono/oracle
|
||||
CMD echo "To start a bridge process run:" \
|
||||
|
||||
@@ -117,8 +117,18 @@ There are two options to run the TokenBridge processes:
|
||||
|
||||
### Docker
|
||||
|
||||
- While running the bridge containers for the first time use `ORACLE_VALIDATOR_ADDRESS=<validator address> ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=<validator address private key> docker-compose up -d --build`
|
||||
- For further launches use `ORACLE_VALIDATOR_ADDRESS=<validator address> ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=<validator address private key> docker-compose up --detach`
|
||||
- While running the bridge containers for the first time use
|
||||
```
|
||||
env ORACLE_VALIDATOR_ADDRESS=<validator address> \
|
||||
env ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=<validator address private key> \
|
||||
docker-compose -f docker-compose-build.yml -f docker-compose.yml up -d --build
|
||||
```
|
||||
- For further launches (or in case of usage an official docker image) use
|
||||
```
|
||||
env ORACLE_VALIDATOR_ADDRESS=<validator address> \
|
||||
env ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=<validator address private key> \
|
||||
docker-compose up --d
|
||||
```
|
||||
|
||||
All [watcher](#watcher) & [sender](#sender) services launch when `docker-compose` is called.
|
||||
|
||||
@@ -144,6 +154,18 @@ In case you need to reset your bridge or setup a new one (with different configu
|
||||
|
||||
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:
|
||||
```
|
||||
cd oracle
|
||||
```
|
||||
|
||||
And run the docker composer:
|
||||
```
|
||||
docker-compose -f docker-compose-build.yml build
|
||||
```
|
||||
|
||||
## Rollback the Last Processed Block in Redis
|
||||
|
||||
If the bridge does not handle an event properly (i.e. a transaction stalls due to a low gas price), the Redis DB can be rolled back. You must identify which watcher needs to re-run. For example, if the validator signatures were collected but the transaction with signatures was not sent to the Foreign network, the `collected-signatures` watcher must look at the block where the corresponding `CollectedSignatures` event was raised.
|
||||
|
||||
20
oracle/config/convert-to-chai-worker.config.js
Normal file
20
oracle/config/convert-to-chai-worker.config.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const baseConfig = require('./base.config')
|
||||
const { EXIT_CODES } = require('../src/utils/constants')
|
||||
|
||||
const id = `${baseConfig.id}-convert-to-chai`
|
||||
|
||||
const workerRequired = baseConfig.id === 'erc-native'
|
||||
|
||||
if (!workerRequired) {
|
||||
console.error(`Convert to chai tokens worker not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`)
|
||||
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
workerQueue: 'convert-to-chai',
|
||||
senderQueue: 'foreign',
|
||||
name: `worker-${id}`,
|
||||
id
|
||||
}
|
||||
39
oracle/config/half-duplex-transfer-watcher.config.js
Normal file
39
oracle/config/half-duplex-transfer-watcher.config.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const baseConfig = require('./base.config')
|
||||
const { ERC20_ABI } = require('../../commons')
|
||||
const { EXIT_CODES } = require('../src/utils/constants')
|
||||
|
||||
const initialChecksJson = process.argv[3]
|
||||
|
||||
if (!initialChecksJson) {
|
||||
throw new Error('initial check parameter was not provided.')
|
||||
}
|
||||
|
||||
let initialChecks
|
||||
try {
|
||||
initialChecks = JSON.parse(initialChecksJson)
|
||||
} catch (e) {
|
||||
throw new Error('Error on decoding values from initial checks.')
|
||||
}
|
||||
|
||||
const id = `${baseConfig.id}-half-duplex-transfer`
|
||||
|
||||
const transferWatcherRequired = baseConfig.id === 'erc-native'
|
||||
|
||||
if (!transferWatcherRequired) {
|
||||
console.error(`Transfer watcher not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`)
|
||||
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
event: 'Transfer',
|
||||
eventContractAddress: initialChecks.halfDuplexTokenAddress,
|
||||
eventAbi: ERC20_ABI,
|
||||
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
||||
queue: 'home',
|
||||
workerQueue: 'swap-tokens',
|
||||
name: `watcher-${id}`,
|
||||
id,
|
||||
idle: initialChecks.idle
|
||||
}
|
||||
20
oracle/config/swap-tokens-worker.config.js
Normal file
20
oracle/config/swap-tokens-worker.config.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const baseConfig = require('./base.config')
|
||||
const { EXIT_CODES } = require('../src/utils/constants')
|
||||
|
||||
const id = `${baseConfig.id}-swap-tokens`
|
||||
|
||||
const workerRequired = baseConfig.id === 'erc-native'
|
||||
|
||||
if (!workerRequired) {
|
||||
console.error(`Swap tokens worker not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`)
|
||||
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
workerQueue: 'swap-tokens',
|
||||
senderQueue: 'foreign',
|
||||
name: `worker-${id}`,
|
||||
id
|
||||
}
|
||||
@@ -29,6 +29,11 @@ if (!transferWatcherRequired) {
|
||||
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
|
||||
}
|
||||
|
||||
const workerQueueConfig = {}
|
||||
if (baseConfig.id === 'erc-native') {
|
||||
workerQueueConfig.workerQueue = 'convert-to-chai'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
@@ -37,6 +42,7 @@ module.exports = {
|
||||
eventAbi: ERC20_ABI,
|
||||
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
||||
queue: 'home',
|
||||
...workerQueueConfig,
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
}
|
||||
|
||||
6
oracle/docker-compose-build.yml
Normal file
6
oracle/docker-compose-build.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
version: '2.4'
|
||||
services:
|
||||
oracle:
|
||||
image: poanetwork/tokenbridge-oracle
|
||||
build: .
|
||||
138
oracle/docker-compose-erc-native.yml
Normal file
138
oracle/docker-compose-erc-native.yml
Normal file
@@ -0,0 +1,138 @@
|
||||
---
|
||||
version: '2.4'
|
||||
services:
|
||||
rabbit:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: rabbit
|
||||
networks:
|
||||
- net_rabbit_bridge_transfer
|
||||
- net_rabbit_bridge_half_duplex_transfer
|
||||
- net_rabbit_bridge_swap_tokens_worker
|
||||
- net_rabbit_bridge_convert_to_chai_worker
|
||||
redis:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: redis
|
||||
networks:
|
||||
- net_db_bridge_transfer
|
||||
- net_db_bridge_half_duplex_transfer
|
||||
bridge_request:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: bridge_request
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_rabbit_bridge_request
|
||||
bridge_collected:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: bridge_collected
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_rabbit_bridge_request
|
||||
bridge_affirmation:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: bridge_affirmation
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_rabbit_bridge_request
|
||||
bridge_transfer:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn watcher:transfer
|
||||
networks:
|
||||
- net_db_bridge_transfer
|
||||
- net_rabbit_bridge_transfer
|
||||
bridge_half_duplex_transfer:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn watcher:half-duplex-transfer
|
||||
networks:
|
||||
- net_db_bridge_half_duplex_transfer
|
||||
- net_rabbit_bridge_half_duplex_transfer
|
||||
bridge_swap_tokens_worker:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn worker:swap-tokens
|
||||
networks:
|
||||
- net_rabbit_bridge_swap_tokens_worker
|
||||
bridge_convert_to_chai_worker:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn worker:convert-to-chai
|
||||
networks:
|
||||
- net_rabbit_bridge_convert_to_chai_worker
|
||||
bridge_senderhome:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: bridge_senderhome
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_rabbit_bridge_request
|
||||
bridge_senderforeign:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: bridge_senderforeign
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_rabbit_bridge_request
|
||||
|
||||
networks:
|
||||
net_db_bridge_request:
|
||||
driver: bridge
|
||||
net_db_bridge_collected:
|
||||
driver: bridge
|
||||
net_db_bridge_affirmation:
|
||||
driver: bridge
|
||||
net_db_bridge_transfer:
|
||||
driver: bridge
|
||||
net_db_bridge_half_duplex_transfer:
|
||||
driver: bridge
|
||||
net_db_bridge_senderhome:
|
||||
driver: bridge
|
||||
net_db_bridge_senderforeign:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_request:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_collected:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_affirmation:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_transfer:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_half_duplex_transfer:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_swap_tokens_worker:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_senderhome:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_senderforeign:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_convert_to_chai_worker:
|
||||
driver: bridge
|
||||
@@ -37,9 +37,7 @@ services:
|
||||
bridge_transfer:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
|
||||
@@ -32,9 +32,7 @@ services:
|
||||
bridge_request:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
@@ -47,9 +45,7 @@ services:
|
||||
bridge_collected:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
@@ -62,9 +58,7 @@ services:
|
||||
bridge_affirmation:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
@@ -77,9 +71,7 @@ services:
|
||||
bridge_senderhome:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
@@ -92,9 +84,7 @@ services:
|
||||
bridge_senderforeign:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user