Compare commits

...

50 Commits

Author SHA1 Message Date
Alexander Kolotov
0451d6e373 Merge the develop branch to the master branch, preparation to v2.6.0 2021-01-11 20:17:53 -06:00
Kirill Fedoseev
409044b8a5 Add env variables for selective validator balance checks (#507) 2021-01-10 20:18:06 -06:00
Kirill Fedoseev
5fc52f42d7 ALM: fix blocking Blockscout/Etherscan requests (#505) 2021-01-07 13:35:23 -06:00
Alexander Kolotov
8a0d9f38b0 Added mediator endpoint in the monitor web service (#503) 2020-12-24 19:15:51 +03:00
Alexander Kolotov
1aee0a84ef Merge the develop branch to the master branch, preparation to v2.6.0-rc3 2020-12-22 10:58:25 +03:00
Alexander Kolotov
811b1a27f1 setLogger added to the RedundantHttpListProvider (#501) 2020-12-21 22:16:37 +03:00
Kirill Fedoseev
4d468ae107 Detect all AMB mediators in monitor (#493) 2020-12-20 01:19:49 +03:00
Kirill Fedoseev
4497a024b1 Fix RPC urls in the ultimate tests (#498) 2020-12-20 01:15:46 +03:00
Kirill Fedoseev
6ce98ff3dd Fetch signatures from RPC endpoint immediatly after enough signatures are collected (#499) 2020-12-20 01:13:49 +03:00
Kirill Fedoseev
04f66b243c Handle RPC error about ancient blocks (#500) 2020-12-20 01:10:47 +03:00
Kirill Fedoseev
21581b3c01 Change message status when signatures are manually submitted (#497) 2020-12-07 23:12:30 +03:00
Kirill Fedoseev
bbc68f9fa2 ALM manual signatures execution (#471) 2020-11-26 00:22:21 +03:00
Kirill Fedoseev
5327688a20 Handle JSONRPC error codes in web3 providers (#491) 2020-11-12 02:20:03 +03:00
Alexander Kolotov
1122daf9a1 Merge the develop branch to the master branch, preparation to v2.6.0-rc2 2020-11-08 15:35:47 +03:00
Kirill Fedoseev
12269d7426 Remove access lists tests from ultimate test suites (#488) 2020-11-08 01:11:21 +03:00
Alexander Kolotov
683fa0728d Update the contract's submodule to the release 5.5.0 (#486) 2020-11-07 16:22:53 +03:00
Alexander Kolotov
dd2075c351 Security Audit report by Quantstamp added (#485) 2020-11-07 13:15:35 +03:00
Kirill Fedoseev
ce29b95729 Support AMB manual lane and allowance/block lists in the monitor (#484) 2020-11-04 22:16:43 +03:00
Kirill Fedoseev
eb1069497a Cache fetched events in monitor (#482) 2020-11-04 14:24:42 +03:00
Kirill Fedoseev
0228fc7d5f Support of manual lane in the AMB oracle (#483) 2020-10-31 21:02:56 +03:00
Kirill Fedoseev
f8d85b14de Add allowance/block lists support to monitor (#477) 2020-10-29 11:25:43 +03:00
Kirill Fedoseev
5fa9d21246 Fix set-env deprecation warnings (#478) 2020-10-28 15:35:11 +03:00
Kirill Fedoseev
611b8c539d Improve workflow with several RPC URLs in oracle (#476) 2020-10-28 14:20:50 +03:00
Kirill Fedoseev
389cea3c39 Reduce time complexity of events comparison in monitor (#475) 2020-10-23 13:48:37 +03:00
Alexander Kolotov
fbce0fc035 Merge the develop branch to the master branch, preparation to v2.6.0-rc1 2020-10-14 22:29:18 +03:00
Max Alekseenko
621b20d070 Allow cors in monitor server (#472) 2020-10-12 21:16:41 +03:00
Alexander Kolotov
48752e8575 Remove nonce update for the re-send case (#470) 2020-10-08 18:51:01 +03:00
Kirill Fedoseev
4efda98f2b Do not reset nonce, if nothing was resent (#469) 2020-10-07 15:28:57 +03:00
Kirill Fedoseev
aff8b777c5 Add requiredBlockConfirmations ABI to the NATIVE_TO_ERC_V1 bridge mode (#468) 2020-10-06 21:35:30 +03:00
Kirill Fedoseev
74293959f3 Fixed resent job for failed transactions (#466) 2020-10-06 15:15:35 +03:00
Kirill Fedoseev
46daeb6815 Respect requiredBlockConfirmations parameter in the monitor (#464) 2020-10-05 15:48:36 +03:00
Alexander Kolotov
44ca0d71ce Merge the develop branch to the master branch, preparation to v2.6.0-rc0 2020-10-02 20:34:31 +03:00
Kirill Fedoseev
fbeb878cdb Fix insufficient funds error handler (#459) 2020-10-02 19:46:07 +03:00
Alexander Kolotov
d17ea2ad2b One point of handling all collected signatures requests (#457) 2020-10-02 19:44:05 +03:00
Kirill Fedoseev
4cc87ef61a Use totalExecutedPerDay instead of totalSpentPerDay for calculating quota (#460) 2020-10-02 19:07:40 +03:00
Alexander Kolotov
125b66b86d Update requirements for higher bound of gas price (#458) 2020-10-01 10:53:11 +03:00
Alexander Kolotov
7a0ed3f699 Small update of the execution waiting status description in ALM (#456) 2020-10-01 10:52:22 +03:00
Kirill Fedoseev
4e04f2ae1f Update the condition for checking if a tx was stuck (#444) 2020-09-30 19:30:51 +03:00
Kirill Fedoseev
dc377aeb9b Possibility to allow/block specific addresses in erc-to-native mode (#442) 2020-09-28 14:54:03 +03:00
Alexander Kolotov
48dd53622c Merge the develop branch to the master branch, preparation to v2.5.0 2020-09-13 11:11:29 +03:00
Kirill Fedoseev
6fe63ae9f4 Possibility to resend old pending transactions (#425) 2020-09-12 17:01:37 +03:00
Kirill Fedoseev
4954c859c3 Migrate to GitHub actions (#423) 2020-09-02 17:43:48 +03:00
Kirill Fedoseev
27f059db94 Changed used RPC URLs delimiter from comma to space (#424) 2020-09-01 23:02:33 +03:00
Andrew Gross
686c415a5c Update README.md (#422) 2020-09-01 22:56:54 +03:00
Alexander Kolotov
2e1b022512 Merge the develop branch to the master branch, preparation to v2.4.0 2020-08-24 00:28:20 +03:00
Kirill Fedoseev
f252ed2618 Add a possibility to fetch gas price from multiple sources (#420) 2020-08-23 22:56:59 +03:00
Gerardo Nardelli
bea91c0e6e Remove half duplex erc20 token support from oracle (#413) 2020-08-11 23:37:47 +03:00
Alexander Kolotov
c2f6b5e8ba Merge the develop branch to the master branch, preparation to v2.3.1 2020-08-11 20:13:58 +03:00
Alexander Kolotov
f3f226afdf Correction of the message for a transaction without the bridge requests (#416) 2020-08-06 23:43:13 +03:00
Kirill Fedoseev
1eb8a8b1dc Use requestGasLimit parameter in AMB oracle in estimateGas (#415) 2020-08-06 16:15:51 +03:00
191 changed files with 4339 additions and 3672 deletions

View File

@@ -1,382 +0,0 @@
version: 2.1
orbs:
tokenbridge-orb:
commands:
install-chrome:
steps:
- run:
name: Update dpkg
command: |
sudo apt-get clean
sudo apt-get update
sudo apt-get install dpkg
- run:
name: Install Chrome
command: |
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:
- run:
name: Install Node
command: |
export NVM_DIR="/opt/circleci/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm install 10.16.3 && nvm alias default 10.16.3
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
install-yarn:
steps:
- run:
name: Install Yarn
command: |
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get -y install yarn
wait-for-oracle:
parameters:
redis-key:
type: string
steps:
- run:
name: Install redis tools
command: sudo apt-get install -y redis-tools
- run:
name: Wait for the Oracle to start
command: |
set +e
i=0
while [[ $(redis-cli GET << parameters.redis-key >> ) ]]; do
((i++))
if [ "$i" -gt 30 ]
then
exit -1
fi
echo "Sleeping..."
sleep 3
done
init-repo:
steps:
- checkout
- run: git submodule update --init
executors:
docker-node:
docker:
- image: circleci/node:10.15
machine-with-dlc:
machine:
image: ubuntu-1604:202007-01
docker_layer_caching: true
classic-machine-without-dlc:
machine:
image: circleci/classic:latest
machine-without-dlc:
machine:
image: ubuntu-1604:202007-01
jobs:
initialize:
executor: tokenbridge-orb/docker-node
steps:
- tokenbridge-orb/init-repo
- restore_cache:
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" }}
- run: yarn install --frozen-lockfile
- save_cache:
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
- save_cache:
name: Save initialized project for subsequent jobs
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/project
initialize-root:
executor: tokenbridge-orb/docker-node
steps:
- checkout
- run: sudo su - -c 'export CI=true && cd /home/circleci/project && yarn initialize && yarn test'
build:
executor: tokenbridge-orb/docker-node
steps:
- restore_cache:
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn run build
lint:
executor: tokenbridge-orb/docker-node
steps:
- restore_cache:
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn run lint
test:
executor: tokenbridge-orb/docker-node
steps:
- restore_cache:
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn run test
build-e2e-images:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/init-repo
- run:
command: |
docker login -u ${DOCKER_LOGIN} -p ${DOCKER_PASSWORD}
cd e2e-commons
docker-compose build oracle monitor ui alm e2e molecule_runner
docker-compose push oracle monitor ui alm e2e molecule_runner
oracle-e2e:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/init-repo
- run: ./oracle-e2e/run-tests.sh
ui-e2e:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/install-node
- tokenbridge-orb/install-yarn
- tokenbridge-orb/install-chrome
- restore_cache:
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn run ui-e2e
monitor-e2e:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/init-repo
- run: ./monitor-e2e/run-tests.sh
alm-e2e:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/install-node
- tokenbridge-orb/install-yarn
- restore_cache:
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn run alm-e2e
cover:
executor: tokenbridge-orb/docker-node
steps:
- restore_cache:
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn workspace ui run coverage
- run: yarn workspace ui run coveralls
deployment-oracle:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/init-repo
- run:
name: Run the scenario
command: deployment-e2e/molecule.sh oracle
no_output_timeout: 40m
deployment-ui:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/init-repo
- run:
name: Run the scenario
command: deployment-e2e/molecule.sh ui
no_output_timeout: 40m
deployment-monitor:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/init-repo
- run:
name: Run the scenario
command: deployment-e2e/molecule.sh monitor
no_output_timeout: 40m
deployment-repo:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/init-repo
- run:
name: Run the scenario
command: deployment-e2e/molecule.sh repo
no_output_timeout: 40m
deployment-multiple:
executor: tokenbridge-orb/machine-without-dlc
steps:
- tokenbridge-orb/init-repo
- run:
name: Run the scenario
command: deployment-e2e/molecule.sh multiple
no_output_timeout: 40m
ultimate:
executor: tokenbridge-orb/classic-machine-without-dlc
parameters:
scenario-name:
description: "Molecule scenario name used to create the infrastructure"
type: string
redis-key:
description: "Redis key checked for non-emptiness to assert if Oracle is running"
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:
- tokenbridge-orb/install-node
- tokenbridge-orb/install-chrome
- tokenbridge-orb/install-yarn
- restore_cache:
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Prepare the infrastructure
command: e2e-commons/up.sh deploy << parameters.scenario-name >> blocks
no_output_timeout: 50m
- tokenbridge-orb/wait-for-oracle:
redis-key: << parameters.redis-key >>
- 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:
- initialize
- initialize-root:
filters:
branches:
only: master
- build:
requires:
- initialize
- lint:
requires:
- initialize
- test:
requires:
- initialize
- cover:
requires:
- initialize
filters:
branches:
only: master
- build-e2e-images
- oracle-e2e:
requires:
- build-e2e-images
- ui-e2e:
requires:
- build-e2e-images
- initialize
- monitor-e2e:
requires:
- build-e2e-images
- alm-e2e:
requires:
- build-e2e-images
- initialize
- deployment-oracle:
requires:
- build-e2e-images
- deployment-ui:
requires:
- build-e2e-images
- deployment-monitor:
requires:
- build-e2e-images
- deployment-repo:
requires:
- build-e2e-images
- deployment-multiple:
requires:
- build-e2e-images
- ultimate:
requires:
- build-e2e-images
- initialize
filters:
branches:
only:
- master
- develop
name: "ultimate: native to erc"
scenario-name: native-to-erc
redis-key: native-erc-collected-signatures:lastProcessedBlock
ui-e2e-grep: "NATIVE TO ERC"
- ultimate:
requires:
- build-e2e-images
- initialize
filters:
branches:
only:
- master
- develop
name: "ultimate: erc to native"
scenario-name: erc-to-native
redis-key: erc-native-collected-signatures:lastProcessedBlock
ui-e2e-grep: "ERC TO NATIVE"
- ultimate:
requires:
- build-e2e-images
- initialize
filters:
branches:
only:
- master
- develop
name: "ultimate: erc to erc"
scenario-name: erc-to-erc
redis-key: erc-erc-collected-signatures:lastProcessedBlock
ui-e2e-grep: "ERC TO ERC"
- ultimate:
requires:
- build-e2e-images
- initialize
filters:
branches:
only:
- master
- develop
name: "ultimate: amb"
scenario-name: amb
redis-key: amb-collected-signatures:lastProcessedBlock
oracle-e2e-script: "amb"
- ultimate:
requires:
- build-e2e-images
- initialize
filters:
branches:
only:
- master
- develop
name: "ultimate: amb stake erc to erc"
scenario-name: ultimate-amb-stake-erc-to-erc
redis-key: amb-collected-signatures:lastProcessedBlock
ui-e2e-grep: "AMB-STAKE-ERC-TO-ERC"

View File

@@ -8,9 +8,17 @@
**/docs
**/*.md
monitor/**/*.env*
oracle/**/*.env*
!**/.env.example
contracts/test
contracts/build
oracle/test
monitor/test
monitor/responses
monitor/cache/*
commons/test
oracle/**/*.png
oracle/**/*.jpg
audit

271
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,271 @@
name: tokenbridge
on: [push]
env:
DOCKER_REGISTRY: docker.pkg.github.com
DOCKER_REPO: poanetwork/tokenbridge
DOCKER_IMAGE_BASE: docker.pkg.github.com/poanetwork/tokenbridge
jobs:
initialize:
runs-on: ubuntu-latest
outputs:
cache_key: ${{ steps.get_cache_key.outputs.cache_key }}
steps:
- uses: actions/setup-node@v1
with:
node-version: 10
- uses: actions/checkout@v2
with:
submodules: true
- name: Set cache key
id: get_cache_key
run: |
git submodule status > submodule.status
echo "::set-output name=cache_key::cache-repo-${{ hashFiles('yarn.lock', 'package.json', 'submodule.status') }}"
- uses: actions/cache@v2
id: cache-repo
with:
path: |
**/node_modules
contracts/build
key: ${{ steps.get_cache_key.outputs.cache_key }}
- name: Install dependencies and compile contracts
if: ${{ !steps.cache-repo.outputs.cache-hit }}
run: |
yarn install --frozen-lockfile
yarn run install:deploy
yarn run compile:contracts
validate:
runs-on: ubuntu-latest
needs:
- initialize
strategy:
fail-fast: false
matrix:
task: [build, lint, test]
steps:
- uses: actions/setup-node@v1
with:
node-version: 10
- uses: actions/checkout@v2
with:
submodules: true
- uses: actions/cache@v2
id: cache-repo
with:
path: |
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- name: yarn run ${{ matrix.task }}
run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
ui-coverage:
runs-on: ubuntu-latest
needs:
- initialize
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/setup-node@v1
with:
node-version: 10
- uses: actions/checkout@v2
with:
submodules: true
- uses: actions/cache@v2
id: cache-repo
with:
path: |
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- name: yarn workspace ui run coverage
run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn workspace ui run coverage
- uses: coverallsapp/github-action@master
with:
github-token: ${{ github.token }}
path-to-lcov: ./ui/coverage/lcov.info
build-e2e-images:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Evaluate e2e docker images tags
run: |
git submodule status > submodule.status
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
- name: Rebuild and push updated images
run: |
function check_if_image_exists() {
curl -fsSlL -H 'Authorization: bearer ${{ github.token }}' "https://${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
}
updated=()
if ! check_if_image_exists e2e ${E2E_TAG}; then updated+=("e2e"); fi
if ! check_if_image_exists oracle ${ORACLE_TAG}; then updated+=("oracle"); fi
if ! check_if_image_exists monitor ${MONITOR_TAG}; then updated+=("monitor"); fi
if ! check_if_image_exists ui ${UI_TAG}; then updated+=("ui"); fi
if ! check_if_image_exists alm ${ALM_TAG}; then updated+=("alm"); fi
if [ ${#updated[@]} -gt 0 ]; then
echo "Updated services: ${updated[@]}"
cd e2e-commons
docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
docker-compose build ${updated[@]}
docker-compose push ${updated[@]}
else
echo "Nothing relevant was changed in the source"
fi
build-molecule-runner:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Evaluate e2e molecule runner tag
run: echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV
- name: Rebuild and push molecule runner e2e image
run: |
function check_if_image_exists() {
curl -fsSlL -H 'Authorization: bearer ${{ github.token }}' "https://${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
}
if check_if_image_exists molecule_runner ${MOLECULE_RUNNER_TAG}; then
echo "Image already exists"
else
cd e2e-commons
docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
docker-compose build molecule_runner
docker-compose push molecule_runner
fi
e2e:
runs-on: ubuntu-latest
needs:
- initialize
- build-e2e-images
strategy:
fail-fast: false
matrix:
task: [oracle-e2e, monitor-e2e, alm-e2e, 'ui-e2e:ci']
include:
- task: alm-e2e
use-cache: true
- task: 'ui-e2e:ci'
use-cache: true
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Evaluate e2e docker images tags
run: |
git submodule status > submodule.status
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
- if: ${{ matrix.use-cache }}
uses: actions/cache@v2
id: cache-repo
with:
path: |
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- name: Login to docker registry
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- name: yarn run ${{ matrix.task }}
run: ${{ !matrix.use-cache || steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
deployment:
runs-on: ubuntu-latest
needs:
- build-e2e-images
- build-molecule-runner
strategy:
fail-fast: false
matrix:
task: [oracle, ui, monitor, multiple, repo]
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Evaluate e2e molecule runner tag
run: echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV
- name: Login to docker registry
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- run: deployment-e2e/molecule.sh ${{ matrix.task }}
ultimate:
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags') || contains(github.event.head_commit.message, 'ultimate')
runs-on: ubuntu-latest
needs:
- initialize
- build-e2e-images
strategy:
fail-fast: false
matrix:
task: [amb, erc-to-erc, erc-to-native, native-to-erc, amb-stake-erc-to-erc]
include:
- task: erc-to-erc
ui-e2e-grep: 'ERC TO ERC'
ui-config: 'e2e-commons/components-envs/ui-erc20.env'
- task: erc-to-native
ui-e2e-grep: 'ERC TO NATIVE'
ui-config: 'e2e-commons/components-envs/ui-erc20-native.env'
- task: native-to-erc
ui-e2e-grep: 'NATIVE TO ERC'
ui-config: 'e2e-commons/components-envs/ui.env'
- task: amb-stake-erc-to-erc
ui-e2e-grep: 'AMB-STAKE-ERC-TO-ERC'
ui-config: 'e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env'
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Evaluate e2e docker images tags
run: |
git submodule status > submodule.status
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV
- uses: actions/cache@v2
id: cache-repo
with:
path: |
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- name: Login to docker registry
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- name: Deploy contracts
run: ${{ steps.cache-repo.outputs.cache-hit }} && e2e-commons/up.sh deploy blocks
- name: Pull e2e oracle image
run: |
docker-compose -f ./e2e-commons/docker-compose.yml pull oracle
docker tag ${DOCKER_IMAGE_BASE}/tokenbridge-e2e-oracle:${ORACLE_TAG} poanetwork/tokenbridge-oracle:latest
- if: ${{ matrix.ui-e2e-grep }}
name: Pull e2e ui image
run: |
docker-compose -f ./e2e-commons/docker-compose.yml pull ui
docker build \
--build-arg DOCKER_IMAGE_BASE=${DOCKER_IMAGE_BASE} \
--build-arg UI_TAG=${UI_TAG} \
--build-arg DOT_ENV_PATH=${{ matrix.ui-config }} \
-f ./e2e-commons/Dockerfile.ui \
-t ui_ui:latest \
.
- name: Deploy oracle and ui
run: deployment-e2e/molecule.sh ultimate-${{ matrix.task }}
- name: Reset docker socket permissions
run: sudo chown -R $USER:docker /var/run/docker.sock
- name: Run ui e2e tests
if: ${{ matrix.ui-e2e-grep }}
run: cd ui-e2e && xvfb-run yarn mocha -g "${{ matrix.ui-e2e-grep }}" -b ./test.js
- name: Run oracle e2e tests
if: ${{ !matrix.ui-e2e-grep }}
run: docker-compose -f ./e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run ${{ matrix.task }}

13
.gitignore vendored
View File

@@ -10,11 +10,8 @@ dist
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
*.env*
!.env.example
.idea
.nyc_output
logs/
@@ -49,5 +46,9 @@ __pycache__
#monitor
monitor/responses/*
monitor/configs/*.env
monitor/cache/*
!monitor/cache/.gitkeep
!monitor/.gitkeep
# Local Netlify folder
.netlify

View File

@@ -12,7 +12,7 @@ COMMON_HOME_GAS_PRICE_SUPPLIER_URL | The URL used to get a JSON response from th
COMMON_HOME_GAS_PRICE_SPEED_TYPE | Assuming the gas price oracle responds with the following JSON structure: `{"fast": 20.0, "block_time": 12.834, "health": true, "standard": 6.0, "block_number": 6470469, "instant": 71.0, "slow": 1.889}`, this parameter specifies the desirable transaction speed. The speed type can be omitted when `COMMON_HOME_GAS_PRICE_SUPPLIER_URL` is not used. | `instant` / `fast` / `standard` / `slow`
COMMON_HOME_GAS_PRICE_FALLBACK | The gas price (in Wei) that is used if both the oracle and the fall back gas price specified in the Home Bridge contract are not available. | integer
COMMON_HOME_GAS_PRICE_FACTOR | A value that will multiply the gas price of the oracle to convert it to gwei. If the oracle API returns gas prices in gwei then this can be set to `1`. Also, it could be used to intentionally pay more gas than suggested by the oracle to guarantee the transaction verification. E.g. `1.25` or `1.5`. | integer
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL | The URL used to get a JSON response from the gas price prediction oracle for the Foreign network. The provided gas price is used to send the validator's transactions to the RPC node. If the Foreign network is Ethereum Foundation mainnet, the oracle URL can be: https://gasprice.poa.network. Otherwise this parameter can be omitted. | URL
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL | The URL used to get a JSON response from the gas price prediction oracle for the Foreign network. The provided gas price is used to send the validator's transactions to the RPC node. If the Foreign network is Ethereum Foundation mainnet, the oracle URL can be: https://gasprice.poa.network. Otherwise this parameter can be omitted. Set to `gas-price-oracle` if you want to use npm `gas-price-oracle` package for retrieving gas price from multiple sources. | URL
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE | Assuming the gas price oracle responds with the following JSON structure: `{"fast": 20.0, "block_time": 12.834, "health": true, "standard": 6.0, "block_number": 6470469, "instant": 71.0, "slow": 1.889}`, this parameter specifies the desirable transaction speed. The speed type can be omitted when `COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL`is not used. | `instant` / `fast` / `standard` / `slow`
COMMON_FOREIGN_GAS_PRICE_FALLBACK | The gas price (in Wei) used if both the oracle and fall back gas price specified in the Foreign Bridge contract are not available. | integer
COMMON_FOREIGN_GAS_PRICE_FACTOR | A value that will multiply the gas price of the oracle to convert it to gwei. If the oracle API returns gas prices in gwei then this can be set to `1`. Also, it could be used to intentionally pay more gas than suggested by the oracle to guarantee the transaction verification. E.g. `1.25` or `1.5`. | integer
@@ -22,7 +22,7 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR | A value that will multiply the gas price of th
name | description | value
--- | --- | ---
ORACLE_BRIDGE_MODE | The bridge mode. The bridge starts listening to a different set of events based on this parameter. | NATIVE_TO_ERC / ERC_TO_ERC / ERC_TO_NATIVE
ORACLE_BRIDGE_MODE | The bridge mode. The bridge starts listening to a different set of events based on this parameter. | NATIVE_TO_ERC / ERC_TO_ERC / ERC_TO_NATIVE / ARBITRARY_MESSAGE
ORACLE_ALLOW_HTTP_FOR_RPC | **Only use in test environments - must be omitted in production environments.**. If this parameter is specified and set to `yes`, RPC URLs can be specified in form of HTTP links. A warning that the connection is insecure will be written to the logs. | `yes` / `no`
ORACLE_HOME_RPC_POLLING_INTERVAL | The interval in milliseconds used to request the RPC node in the Home network for new blocks. The interval should match the average production time for a new block. | integer
ORACLE_FOREIGN_RPC_POLLING_INTERVAL | The interval in milliseconds used to request the RPC node in the Foreign network for new blocks. The interval should match the average production time for a new block. | integer
@@ -37,6 +37,11 @@ ORACLE_MAX_PROCESSING_TIME | The workers processes will be killed if this amount
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY | The private key of the bridge validator used to sign confirmations before sending transactions to the bridge contracts. The validator account is calculated automatically from the private key. Every bridge instance (set of watchers and senders) must have its own unique private key. The specified private key is used to sign transactions on both sides of the bridge. | hexidecimal without "0x"
ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexidecimal with "0x"
ORACLE_TX_REDUNDANCY | If set to `true`, instructs oracle to send `eth_sendRawTransaction` requests through all available RPC urls defined in `COMMON_HOME_RPC_URL` and `COMMON_FOREIGN_RPC_URL` variables instead of using first available one
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST | Filename with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests will be automatically processed by the CollectedSignatures watcher. | string
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST | Filename with a list of addresses, separated by newlines. If set, determines the blocked set of accounts whose requests will not be automatically processed by the CollectedSignatures watcher. Has a lower priority than the `ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST` | string
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`
ORACLE_ALWAYS_RELAY_SIGNATURES | If set to `true`, the oracle will always relay signatures even if it was not the last who finilized the signatures collecting process. The default is `false`. | `true` / `false`
ORACLE_RPC_REQUEST_TIMEOUT | Timeout in milliseconds for a single RPC request. Default value is `ORACLE_*_RPC_POLLING_INTERVAL * 2`. | integer
## UI configuration
@@ -73,4 +78,9 @@ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT | Average gas usage of a transaction sent by
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
MONITOR_CACHE_EVENTS | If set to true, monitor will cache obtained events for other workers runs
MONITOR_CACHE_EVENTS | If set to true, monitor will cache obtained events for other workers runs | `true` / `false`
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST | File with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests should be automatically processed by the CollectedSignatures watcher. | string
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST | File with a list of addresses, separated by newlines. If set, determines the set of accounts whose requests should be marked as unclaimed. Has a lower priority than the `MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST`. | string
MONITOR_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`
MONITOR_HOME_VALIDATORS_BALANCE_ENABLE | If set, defines the list of home validator addresses for which balance should be checked. | `string`
MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE | If set, defines the list of foreign validator addresses for which balance should be checked. | `string`

View File

@@ -1,4 +1,4 @@
FROM node:8 as contracts
FROM node:10 as contracts
WORKDIR /mono
@@ -11,11 +11,12 @@ COPY ./contracts/truffle-config.js ./
COPY ./contracts/contracts ./contracts
RUN npm run compile
FROM node:8
FROM node:10
WORKDIR /mono
COPY package.json .
COPY --from=contracts /mono/contracts/build ./contracts/build
COPY commons/package.json ./commons/
COPY oracle-e2e/package.json ./oracle-e2e/
COPY monitor-e2e/package.json ./monitor-e2e/

View File

@@ -1,4 +1,4 @@
[![CircleCI](https://circleci.com/gh/poanetwork/tokenbridge.svg?style=svg)](https://circleci.com/gh/poanetwork/tokenbridge)
![tokenbridge](https://github.com/poanetwork/tokenbridge/workflows/tokenbridge/badge.svg?branch=master)
[![Gitter](https://badges.gitter.im/poanetwork/poa-bridge.svg)](https://gitter.im/poanetwork/poa-bridge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License: LGPL v3.0](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
@@ -110,4 +110,4 @@ This project is licensed under the GNU Lesser General Public License v3.0. See t
## References
* [TokenBridge Documentation](http://www.tokenbridge.net/)
* [TokenBridge Documentation](https://docs.tokenbridge.net/)

View File

@@ -19,6 +19,6 @@
"eslint-plugin-jest": "^23.18.0"
},
"engines": {
"node": ">= 8.9"
"node": ">= 10.18"
}
}

0
alm-e2e/run-tests.sh Normal file → Executable file
View File

View File

@@ -1,4 +1,4 @@
FROM node:8 as contracts
FROM node:10 as contracts
WORKDIR /mono

View File

@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ethersproject/bignumber": ">=5.0.0-beta.130",
"@react-hook/window-size": "^3.0.6",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
@@ -15,6 +16,8 @@
"@types/react-router-dom": "^5.1.5",
"@types/styled-components": "^5.1.0",
"@use-it/interval": "^0.1.3",
"@web3-react/core": "^6.1.1",
"@web3-react/injected-connector": "^6.0.7",
"customize-cra": "^1.0.0",
"date-fns": "^2.14.0",
"dotenv": "^8.2.0",
@@ -27,8 +30,9 @@
"react-scripts": "3.0.1",
"styled-components": "^5.1.1",
"typescript": "^3.5.2",
"web3": "1.2.7",
"web3-eth-contract": "1.2.7"
"web3": "1.2.11",
"web3-eth-contract": "1.2.11",
"web3-utils": "1.2.11"
},
"scripts": {
"start": "yarn createSnapshots && ./load-env.sh react-app-rewired start",

1
alm/public/_redirects Normal file
View File

@@ -0,0 +1 @@
/* /index.html 200

View File

@@ -1,14 +1,18 @@
import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import { Web3ReactProvider } from '@web3-react/core'
import Web3 from 'web3'
import { MainPage } from './components/MainPage'
import { StateProvider } from './state/StateProvider'
function App() {
return (
<BrowserRouter>
<StateProvider>
<MainPage />
</StateProvider>
<Web3ReactProvider getLibrary={provider => new Web3(provider)}>
<StateProvider>
<MainPage />
</StateProvider>
</Web3ReactProvider>
</BrowserRouter>
)
}

View File

@@ -49,7 +49,16 @@ export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }
} = useStateProvider()
const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt })
const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt })
const { confirmations, status, executionData, signatureCollected, waitingBlocksResolved } = useMessageConfirmations({
const {
confirmations,
status,
executionData,
signatureCollected,
waitingBlocksResolved,
setExecutionData,
executionEventsFetched,
setPendingExecution
} = useMessageConfirmations({
message,
receipt,
fromHome,
@@ -102,7 +111,17 @@ export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }
validatorList={validatorList}
waitingBlocksResolved={waitingBlocksResolved}
/>
{signatureCollected && <ExecutionConfirmation executionData={executionData} isHome={!fromHome} />}
{signatureCollected && (
<ExecutionConfirmation
messageData={message.data}
executionData={executionData}
isHome={!fromHome}
signatureCollected={signatureCollected}
setExecutionData={setExecutionData}
executionEventsFetched={executionEventsFetched}
setPendingExecution={setPendingExecution}
/>
)}
</StyledConfirmationContainer>
</div>
)

View File

@@ -1,24 +1,45 @@
import React from 'react'
import { formatTimestamp, formatTxHash, getExplorerTxUrl } from '../utils/networks'
import { useWindowWidth } from '@react-hook/window-size'
import { SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS, ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION } from '../config/constants'
import { SimpleLoading } from './commons/Loading'
import styled from 'styled-components'
import { ExecutionData } from '../hooks/useMessageConfirmations'
import { GreyLabel, RedLabel, SuccessLabel } from './commons/Labels'
import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { Thead, AgeTd, StatusTd } from './commons/Table'
import { ManualExecutionButton } from './ManualExecutionButton'
const StyledExecutionConfirmation = styled.div`
margin-top: 30px;
`
export interface ExecutionConfirmationParams {
messageData: string
executionData: ExecutionData
setExecutionData: Function
signatureCollected: boolean | string[]
isHome: boolean
executionEventsFetched: boolean
setPendingExecution: Function
}
export const ExecutionConfirmation = ({ executionData, isHome }: ExecutionConfirmationParams) => {
export const ExecutionConfirmation = ({
messageData,
executionData,
setExecutionData,
signatureCollected,
isHome,
executionEventsFetched,
setPendingExecution
}: ExecutionConfirmationParams) => {
const availableManualExecution =
!isHome &&
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ||
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED &&
executionEventsFetched &&
!!executionData.validator))
const requiredManualExecution = availableManualExecution && ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION
const windowWidth = useWindowWidth()
const txExplorerLink = getExplorerTxUrl(executionData.txHash, isHome)
@@ -48,26 +69,47 @@ export const ExecutionConfirmation = ({ executionData, isHome }: ExecutionConfir
<table>
<Thead>
<tr>
<th>Executed by</th>
<th>{requiredManualExecution ? 'Execution info' : 'Executed by'}</th>
<th className="text-center">Status</th>
<th className="text-center">Age</th>
{!requiredManualExecution && <th className="text-center">Age</th>}
{availableManualExecution && <th className="text-center">Actions</th>}
</tr>
</Thead>
<tbody>
<tr>
<td>{formattedValidator ? formattedValidator : <SimpleLoading />}</td>
<StatusTd className="text-center">{getExecutionStatusElement(executionData.status)}</StatusTd>
<AgeTd className="text-center">
{executionData.timestamp > 0 ? (
<ExplorerTxLink href={txExplorerLink} target="_blank">
{formatTimestamp(executionData.timestamp)}
</ExplorerTxLink>
) : executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ? (
''
<td>
{requiredManualExecution ? (
'Manual user action is required to complete the operation'
) : formattedValidator ? (
formattedValidator
) : (
SEARCHING_TX
<SimpleLoading />
)}
</AgeTd>
</td>
<StatusTd className="text-center">{getExecutionStatusElement(executionData.status)}</StatusTd>
{!requiredManualExecution && (
<AgeTd className="text-center">
{executionData.timestamp > 0 ? (
<ExplorerTxLink href={txExplorerLink} target="_blank">
{formatTimestamp(executionData.timestamp)}
</ExplorerTxLink>
) : executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ? (
''
) : (
SEARCHING_TX
)}
</AgeTd>
)}
{availableManualExecution && (
<td>
<ManualExecutionButton
messageData={messageData}
setExecutionData={setExecutionData}
signatureCollected={signatureCollected as string[]}
setPendingExecution={setPendingExecution}
/>
</td>
)}
</tr>
</tbody>
</table>

View File

@@ -8,6 +8,7 @@ import { TransactionReceipt } from 'web3-eth'
import { InfoAlert } from './commons/InfoAlert'
import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
import { ErrorAlert } from './commons/ErrorAlert'
const StyledMainPage = styled.div`
text-align: center;
@@ -51,7 +52,7 @@ export interface FormSubmitParams {
export const MainPage = () => {
const history = useHistory()
const { home, foreign } = useStateProvider()
const { home, foreign, error, setError } = useStateProvider()
const [networkName, setNetworkName] = useState('')
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
const [showInfoAlert, setShowInfoAlert] = useState(false)
@@ -131,6 +132,7 @@ export const MainPage = () => {
</AlertP>
</InfoAlert>
)}
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
<Route
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}

View File

@@ -0,0 +1,109 @@
import React, { useState, useEffect } from 'react'
import styled from 'styled-components'
import { InjectedConnector } from '@web3-react/injected-connector'
import { useWeb3React } from '@web3-react/core'
import { INCORRECT_CHAIN_ERROR, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { useStateProvider } from '../state/StateProvider'
import { signatureToVRS, packSignatures } from '../utils/signatures'
const StyledButton = styled.button`
color: var(--button-color);
border-color: var(--font-color);
margin-top: 10px;
&:focus {
outline: var(--button-color);
}
`
interface ManualExecutionButtonParams {
messageData: string
setExecutionData: Function
signatureCollected: string[]
setPendingExecution: Function
}
export const ManualExecutionButton = ({
messageData,
setExecutionData,
signatureCollected,
setPendingExecution
}: ManualExecutionButtonParams) => {
const { foreign, setError } = useStateProvider()
const { library, activate, account, active } = useWeb3React()
const [manualExecution, setManualExecution] = useState(false)
useEffect(
() => {
if (!manualExecution || !foreign.chainId) return
if (!active) {
activate(new InjectedConnector({ supportedChainIds: [foreign.chainId] }), e => {
if (e.message.includes('Unsupported chain id')) {
setError(INCORRECT_CHAIN_ERROR)
const { ethereum } = window as any
// remove the error message after chain is correctly changed to the foreign one
const listener = (chainId: string) => {
if (parseInt(chainId.slice(2), 16) === foreign.chainId) {
ethereum.removeListener('chainChanged', listener)
setError((error: string) => (error === INCORRECT_CHAIN_ERROR ? '' : error))
}
}
ethereum.on('chainChanged', listener)
} else {
setError(e.message)
}
setManualExecution(false)
})
return
}
if (!library || !foreign.bridgeContract || !signatureCollected || !signatureCollected.length) return
const signatures = packSignatures(signatureCollected.map(signatureToVRS))
const data = foreign.bridgeContract.methods.executeSignatures(messageData, signatures).encodeABI()
setManualExecution(false)
library.eth
.sendTransaction({
from: account,
to: foreign.bridgeAddress,
data
})
.on('transactionHash', (txHash: string) => {
setExecutionData({
status: VALIDATOR_CONFIRMATION_STATUS.PENDING,
validator: account,
txHash,
timestamp: Math.floor(new Date().getTime() / 1000.0),
executionResult: false
})
setPendingExecution(true)
})
.on('error', (e: Error) => setError(e.message))
},
[
manualExecution,
library,
activate,
active,
account,
foreign.chainId,
foreign.bridgeAddress,
foreign.bridgeContract,
setError,
messageData,
signatureCollected,
setExecutionData,
setPendingExecution
]
)
return (
<div className="is-center">
<StyledButton className="button outline" onClick={() => setManualExecution(true)}>
Execute
</StyledButton>
</div>
)
}

View File

@@ -64,10 +64,6 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
const displayReference = multiMessageSelected ? messages[selectedMessageId].id : txHash
const formattedMessageId = formatTxHash(displayReference)
const displayedDescription = multiMessageSelected
? getTransactionStatusDescription(TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE, timestamp)
: description
const isHome = chainId === home.chainId.toString()
const txExplorerLink = getExplorerTxUrl(txHash, isHome)
const displayExplorerLink = status !== TRANSACTION_STATUS.NOT_FOUND
@@ -75,17 +71,32 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
const displayConfirmations = status === TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE || multiMessageSelected
const messageToConfirm =
messages.length > 1 ? messages[selectedMessageId] : messages.length > 0 ? messages[0] : { id: '', data: '' }
let displayedDescription: string = multiMessageSelected
? getTransactionStatusDescription(TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE, timestamp)
: description
let link
const descArray = displayedDescription.split('%link')
if (descArray.length > 1) {
displayedDescription = descArray[0]
link = (
<ExplorerTxLink href={descArray[1]} target="_blank" rel="noopener noreferrer">
{descArray[1]}
</ExplorerTxLink>
)
}
return (
<div>
{status && (
<p>
The request{' '}
The transaction{' '}
{displayExplorerLink && (
<ExplorerTxLink href={txExplorerLink} target="_blank">
{formattedMessageId}
</ExplorerTxLink>
)}
{!displayExplorerLink && <label>{formattedMessageId}</label>} {displayedDescription}
{!displayExplorerLink && <label>{formattedMessageId}</label>} {displayedDescription} {link}
</p>
)}
{displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />}

View File

@@ -1,6 +1,6 @@
import React from 'react'
export const CloseIcon = () => (
export const CloseIcon = ({ color }: { color?: string }) => (
<svg
aria-hidden="true"
focusable="false"
@@ -10,12 +10,9 @@ export const CloseIcon = () => (
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 352 512"
fill="#1890ff"
fill={color || '#1890ff'}
height="1em"
>
<path
fill="#1890ff"
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
/>
<path d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" />
</svg>
)

View File

@@ -0,0 +1,31 @@
import React from 'react'
import styled from 'styled-components'
import { InfoIcon } from './InfoIcon'
import { CloseIcon } from './CloseIcon'
const StyledErrorAlert = styled.div`
border: 1px solid var(--failed-color);
border-radius: 4px;
margin-bottom: 20px;
padding-top: 10px;
`
const CloseIconContainer = styled.div`
cursor: pointer;
`
const TextContainer = styled.div`
flex-direction: column;
`
export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: string }) => (
<div className="row is-center">
<StyledErrorAlert className="col-10 is-vertical-align row">
<InfoIcon color="var(--failed-color)" />
<TextContainer className="col-10">{error}</TextContainer>
<CloseIconContainer className="col-1 is-vertical-align is-center" onClick={onClick}>
<CloseIcon color="var(--failed-color)" />
</CloseIconContainer>
</StyledErrorAlert>
</div>
)

View File

@@ -1,6 +1,6 @@
import React from 'react'
export const InfoIcon = () => (
export const InfoIcon = ({ color }: { color?: string }) => (
<svg
className="col-1 is-left"
viewBox="64 64 896 896"
@@ -8,7 +8,7 @@ export const InfoIcon = () => (
data-icon="info-circle"
width="1em"
height="1em"
fill="#1890ff"
fill={color || '#1890ff'}
aria-hidden="true"
>
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 010-96 48.01 48.01 0 010 96z" />

View File

@@ -13,6 +13,9 @@ export const FOREIGN_EXPLORER_TX_TEMPLATE: string = process.env.REACT_APP_ALM_FO
export const HOME_EXPLORER_API: string = process.env.REACT_APP_ALM_HOME_EXPLORER_API || ''
export const FOREIGN_EXPLORER_API: string = process.env.REACT_APP_ALM_FOREIGN_EXPLORER_API || ''
export const ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION: boolean =
(process.env.REACT_APP_ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION || '').toLowerCase() === 'true'
export const HOME_RPC_POLLING_INTERVAL: number = 5000
export const FOREIGN_RPC_POLLING_INTERVAL: number = 5000
export const BLOCK_RANGE: number = 50
@@ -61,3 +64,5 @@ export const VALIDATOR_CONFIRMATION_STATUS = {
}
export const SEARCHING_TX = 'Searching Transaction...'
export const INCORRECT_CHAIN_ERROR = `Incorrect chain chosen. Switch to ${FOREIGN_NETWORK_NAME} in the wallet.`

View File

@@ -1,8 +1,11 @@
// %t will be replaced by the time -> x minutes/hours/days ago
import { ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION } from './constants'
export const TRANSACTION_STATUS_DESCRIPTION: { [key: string]: string } = {
SUCCESS_MULTIPLE_MESSAGES: 'was initiated %t and contains several bridge messages. Specify one of them:',
SUCCESS_ONE_MESSAGE: 'was initiated %t',
SUCCESS_NO_MESSAGES: 'execution succeeded %t but it does not contain any bridge messages',
SUCCESS_NO_MESSAGES:
'successfully mined %t but it does not seem to contain any request to the bridge, \nso nothing needs to be confirmed by the validators. \nIf you are sure that the transaction should contain a request to the bridge,\ncontact to the validators by \nmessaging on %linkhttps://forum.poa.network/c/support',
FAILED: 'failed %t',
NOT_FOUND:
'Transaction not found. \n1. Check that the transaction hash is correct. \n2. Wait several blocks for the transaction to be\nmined, gas price affects mining speed.'
@@ -23,7 +26,7 @@ export const CONFIRMATIONS_STATUS_LABEL_HOME: { [key: string]: string } = {
SUCCESS_MESSAGE_FAILED: 'Success',
EXECUTION_FAILED: 'Execution failed',
EXECUTION_PENDING: 'Execution pending',
EXECUTION_WAITING: 'Execution waiting',
EXECUTION_WAITING: ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION ? 'Manual execution waiting' : 'Execution waiting',
FAILED: 'Confirmation Failed',
PENDING: 'Confirmation Pending',
WAITING_VALIDATORS: 'Confirmation Waiting',
@@ -54,11 +57,12 @@ export const CONFIRMATIONS_STATUS_DESCRIPTION_HOME: { [key: string]: string } =
SUCCESS_MESSAGE_FAILED:
'The specified transaction was included in a block,\nthe validators collected signatures and the cross-chain relay was executed correctly,\nbut the contained message execution failed.\nContact the support of the application you used to produce the transaction for the clarifications.',
EXECUTION_FAILED:
'The specified transaction was included in a block\nand the validators collected signatures. The\nvalidators transaction with collected signatures was\nsent but did not succeed. Contact to the validators by messaging\non %linkhttps://forum.poa.network/c/support',
'The specified transaction was included in a block\nand the validators collected signatures. The\n transaction with collected signatures was\nsent but did not succeed. Contact to the validators by messaging\non %linkhttps://forum.poa.network/c/support',
EXECUTION_PENDING:
'The specified transaction was included in a block\nand the validators collected signatures. The\nvalidators transaction with collected signatures was\nsent but is not yet added to a block.',
EXECUTION_WAITING:
'The specified transaction was included in a block\nand the validators collected signatures. Either\n1. One of the validators is waiting for chain finalization.\n2. A validator skipped its duty to relay signatures.\nCheck status again after a few blocks. If the issue still persists contact to the validators by messaging on %linkhttps://forum.poa.network/c/support',
'The specified transaction was included in a block\nand the validators collected signatures. The\n transaction with collected signatures was\nsent but is not yet added to a block.',
EXECUTION_WAITING: ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION
? 'The specified transaction was included in a block\nand the validators collected signatures.\nNow the manual user action is required to complete message execution.\n Please, press the "Execute" button.'
: 'The specified transaction was included in a block\nand the validators collected signatures. Either\n1. One of the validators is waiting for chain finalization.\n2. A validator skipped its duty to relay signatures.\n3. The execution transaction is still pending (e.g. due to the gas price spike).\nCheck status again after a few blocks or force execution by pressing the "Execute" button.\nIf the issue still persists contact to the validators by messaging on %linkhttps://forum.poa.network/c/support',
FAILED:
'The specified transaction was included in a block,\nbut transactions with signatures sent by a majority of\nvalidators failed. The cross-chain relay request will\nnot be processed. Contact to the validators by\nmessaging on %linkhttps://forum.poa.network/c/support',
PENDING:

View File

@@ -3,7 +3,6 @@ import { TransactionReceipt } from 'web3-eth'
import { MessageObject } from '../utils/web3'
import { useEffect, useState } from 'react'
import { EventData } from 'web3-eth-contract'
import { getAffirmationsSigned, getMessagesSigned } from '../utils/contract'
import {
BLOCK_RANGE,
CONFIRMATIONS_STATUS,
@@ -63,11 +62,12 @@ export const useMessageConfirmations = ({
blockConfirmations
}: useMessageConfirmationsParams) => {
const { home, foreign } = useStateProvider()
const [confirmations, setConfirmations] = useState<Array<ConfirmationParam>>([])
const [confirmations, setConfirmations] = useState([])
const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED)
const [waitingBlocks, setWaitingBlocks] = useState(false)
const [waitingBlocksResolved, setWaitingBlocksResolved] = useState(false)
const [signatureCollected, setSignatureCollected] = useState(false)
const [executionEventsFetched, setExecutionEventsFetched] = useState(false)
const [collectedSignaturesEvent, setCollectedSignaturesEvent] = useState<Maybe<EventData>>(null)
const [executionData, setExecutionData] = useState<ExecutionData>({
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
@@ -93,7 +93,7 @@ export const useMessageConfirmations = ({
// Check if the validators are waiting for block confirmations to verify the message
useEffect(
() => {
if (!receipt || !blockConfirmations) return
if (!receipt || !blockConfirmations || waitingBlocksResolved) return
const subscriptions: Array<number> = []
@@ -126,7 +126,16 @@ export const useMessageConfirmations = ({
blockProvider.stop()
}
},
[blockConfirmations, foreign.web3, fromHome, validatorList, home.web3, receipt]
[
blockConfirmations,
foreign.web3,
fromHome,
validatorList,
home.web3,
receipt,
setConfirmations,
waitingBlocksResolved
]
)
// The collected signature event is only fetched once the signatures are collected on tx from home to foreign, to calculate if
@@ -173,6 +182,7 @@ export const useMessageConfirmations = ({
useEffect(
() => {
if (!fromHome || !home.web3 || !receipt || !collectedSignaturesEvent || !blockConfirmations) return
if (waitingBlocksForExecutionResolved) return
const subscriptions: Array<number> = []
@@ -201,7 +211,7 @@ export const useMessageConfirmations = ({
homeBlockNumberProvider.stop()
}
},
[collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, receipt]
[collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, receipt, waitingBlocksForExecutionResolved]
)
// Checks if validators verified the message
@@ -218,14 +228,12 @@ export const useMessageConfirmations = ({
})
}
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
getConfirmationsForTx(
message.data,
home.web3,
validatorList,
home.bridgeContract,
confirmationContractMethod,
fromHome,
setConfirmations,
requiredSignatures,
setSignatureCollected,
@@ -251,7 +259,8 @@ export const useMessageConfirmations = ({
home.bridgeContract,
requiredSignatures,
waitingBlocksResolved,
timestamp
timestamp,
setConfirmations
]
)
@@ -289,7 +298,8 @@ export const useMessageConfirmations = ({
getExecutionFailedTransactionForMessage,
setFailedExecution,
getExecutionPendingTransactionsForMessage,
setPendingExecution
setPendingExecution,
setExecutionEventsFetched
)
return () => {
@@ -369,6 +379,9 @@ export const useMessageConfirmations = ({
status,
signatureCollected,
executionData,
waitingBlocksResolved
setExecutionData,
waitingBlocksResolved,
executionEventsFetched,
setPendingExecution
}
}

View File

@@ -1,4 +1,4 @@
import React, { createContext, ReactNode } from 'react'
import React, { createContext, ReactNode, useState } from 'react'
import { useNetwork } from '../hooks/useNetwork'
import {
HOME_RPC_URL,
@@ -25,6 +25,8 @@ export interface StateContext {
home: BaseNetworkParams
foreign: BaseNetworkParams
loading: boolean
error: string
setError: Function
}
const initialState = {
@@ -42,7 +44,9 @@ const initialState = {
bridgeAddress: FOREIGN_BRIDGE_ADDRESS,
bridgeContract: null
},
loading: true
loading: true,
error: '',
setError: () => {}
}
const StateContext = createContext<StateContext>(initialState)
@@ -54,6 +58,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
homeWeb3: homeNetwork.web3,
foreignWeb3: foreignNetwork.web3
})
const [error, setError] = useState('')
const value = {
home: {
@@ -68,7 +73,9 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
bridgeContract: foreignBridge,
...foreignNetwork
},
loading: homeNetwork.loading || foreignNetwork.loading
loading: homeNetwork.loading || foreignNetwork.loading,
error,
setError
}
return <StateContext.Provider value={value}>{children}</StateContext.Provider>

View File

@@ -9,7 +9,7 @@ import { BasicConfirmationParam } from '../../hooks/useMessageConfirmations'
jest.mock('../validatorConfirmationHelpers')
const getValidatorSuccessTransaction = helpers.getValidatorSuccessTransaction as jest.Mock<any>
const getSuccessExecutionTransaction = helpers.getSuccessExecutionTransaction as jest.Mock<any>
const getValidatorConfirmation = helpers.getValidatorConfirmation as jest.Mock<any>
const getValidatorFailedTransaction = helpers.getValidatorFailedTransaction as jest.Mock<any>
const getValidatorPendingTransaction = helpers.getValidatorPendingTransaction as jest.Mock<any>
@@ -24,8 +24,15 @@ const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908'
const validator2 = '0xAe8bFfc8BBc6AAa9E21ED1E4e4957fe798BEA25f'
const validator3 = '0x285A6eB779be4db94dA65e2F3518B1c5F0f71244'
const validatorList = [validator1, validator2, validator3]
const bridgeContract = {} as Contract
const confirmationContractMethod = () => {}
const signature =
'0x519d704bceed17423daa79c20531cc34fc27a4be6e53fc5069a8023019188ca4519d704bceed17423daa79c20531cc34fc27a4be6e53fc5069a8023019188ca4'
const bridgeContract = {
methods: {
signature: () => ({
call: () => signature
})
}
} as Contract
const requiredSignatures = 2
const waitingBlocksResolved = true
let subscriptions: Array<number> = []
@@ -42,7 +49,7 @@ const unsubscribe = () => {
beforeEach(() => {
// Clear all instances and calls to constructor and all methods:
getValidatorSuccessTransaction.mockClear()
getSuccessExecutionTransaction.mockClear()
getValidatorConfirmation.mockClear()
getValidatorFailedTransaction.mockClear()
getValidatorPendingTransaction.mockClear()
@@ -54,7 +61,7 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: '',
@@ -83,7 +90,7 @@ describe('getConfirmationsForTx', () => {
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
true,
setResult,
requiredSignatures,
setSignatureCollected,
@@ -102,9 +109,10 @@ describe('getConfirmationsForTx', () => {
expect(subscriptions.length).toEqual(1)
expect(setResult).toBeCalledTimes(2)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -114,14 +122,16 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false)
expect(setResult.mock.calls[0][0]()).toEqual(
const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(setResult.mock.calls[1][0]).toEqual(
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
@@ -134,7 +144,7 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator === validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: '',
@@ -163,7 +173,7 @@ describe('getConfirmationsForTx', () => {
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
true,
setResult,
requiredSignatures,
setSignatureCollected,
@@ -179,9 +189,9 @@ describe('getConfirmationsForTx', () => {
unsubscribe()
expect(setResult).toBeCalledTimes(2)
expect(setResult).toBeCalledTimes(1)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(false)
@@ -198,7 +208,7 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator !== validator3 ? '0x123' : '',
@@ -227,7 +237,7 @@ describe('getConfirmationsForTx', () => {
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
true,
setResult,
requiredSignatures,
setSignatureCollected,
@@ -244,11 +254,12 @@ describe('getConfirmationsForTx', () => {
unsubscribe()
expect(subscriptions.length).toEqual(0)
expect(setResult).toBeCalledTimes(2)
expect(setResult).toBeCalledTimes(3)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -258,14 +269,24 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false)
expect(setResult.mock.calls[0][0]()).toEqual(
const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
])
)
expect(setResult.mock.calls[1][0]).toEqual(
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
@@ -283,7 +304,7 @@ describe('getConfirmationsForTx', () => {
? VALIDATOR_CONFIRMATION_STATUS.SUCCESS
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? '0x123' : '',
@@ -315,7 +336,7 @@ describe('getConfirmationsForTx', () => {
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
true,
setResult,
requiredSignatures,
setSignatureCollected,
@@ -332,11 +353,12 @@ describe('getConfirmationsForTx', () => {
unsubscribe()
expect(subscriptions.length).toEqual(0)
expect(setResult).toBeCalledTimes(2)
expect(setResult).toBeCalledTimes(4)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -346,7 +368,19 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false)
expect(setResult.mock.calls[0][0]()).toEqual(
const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
const res4 = setResult.mock.calls[3][0](res3)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
@@ -354,7 +388,15 @@ describe('getConfirmationsForTx', () => {
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(setResult.mock.calls[1][0]).toEqual(
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
])
)
expect(res4).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
@@ -372,7 +414,7 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator === validator1 ? '0x123' : '',
@@ -407,7 +449,7 @@ describe('getConfirmationsForTx', () => {
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
true,
setResult,
requiredSignatures,
setSignatureCollected,
@@ -423,9 +465,9 @@ describe('getConfirmationsForTx', () => {
unsubscribe()
expect(setResult).toBeCalledTimes(2)
expect(setResult).toBeCalledTimes(4)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(false)
@@ -437,14 +479,32 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true)
expect(setResult.mock.calls[0][0]()).toEqual(
const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
const res4 = setResult.mock.calls[3][0](res3)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
expect(setResult.mock.calls[1][0]).toEqual(
expect(res4).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
@@ -461,7 +521,7 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getValidatorSuccessTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator === validator1 ? '0x123' : '',
@@ -493,7 +553,7 @@ describe('getConfirmationsForTx', () => {
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
true,
setResult,
requiredSignatures,
setSignatureCollected,
@@ -510,9 +570,9 @@ describe('getConfirmationsForTx', () => {
unsubscribe()
expect(subscriptions.length).toEqual(0)
expect(setResult).toBeCalledTimes(2)
expect(setResult).toBeCalledTimes(3)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(false)
@@ -524,14 +584,24 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(false)
expect(setResult.mock.calls[0][0]()).toEqual(
const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }
])
)
expect(setResult.mock.calls[1][0]).toEqual(
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
@@ -555,7 +625,7 @@ describe('getConfirmationsForTx', () => {
status:
validator !== validator2 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getValidatorSuccessTransaction
getSuccessExecutionTransaction
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
@@ -604,57 +674,7 @@ describe('getConfirmationsForTx', () => {
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
setResult,
requiredSignatures,
setSignatureCollected,
waitingBlocksResolved,
subscriptions,
timestamp,
getFailedTransactions,
setFailedConfirmations,
getPendingTransactions,
setPendingConfirmations,
getSuccessTransactions
)
unsubscribe()
expect(setResult).toBeCalledTimes(2)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getValidatorSuccessTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(false)
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
expect(setFailedConfirmations.mock.calls[0][0]).toEqual(false)
expect(getValidatorPendingTransaction).toBeCalledTimes(1)
expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true)
expect(setResult.mock.calls[0][0]()).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
expect(setResult.mock.calls[1][0]).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
await getConfirmationsForTx(
messageData,
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
true,
setResult,
requiredSignatures,
setSignatureCollected,
@@ -671,11 +691,80 @@ describe('getConfirmationsForTx', () => {
unsubscribe()
expect(setResult).toBeCalledTimes(4)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(false)
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
expect(setFailedConfirmations.mock.calls[0][0]).toEqual(false)
expect(getValidatorPendingTransaction).toBeCalledTimes(1)
expect(setPendingConfirmations).toBeCalledTimes(1)
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true)
const res1 = setResult.mock.calls[0][0]()
const res2 = setResult.mock.calls[1][0](res1)
const res3 = setResult.mock.calls[2][0](res2)
const res4 = setResult.mock.calls[3][0](res3)
expect(res1).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
expect(res4).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
await getConfirmationsForTx(
messageData,
web3,
validatorList,
bridgeContract,
true,
setResult,
requiredSignatures,
setSignatureCollected,
waitingBlocksResolved,
subscriptions,
timestamp,
getFailedTransactions,
setFailedConfirmations,
getPendingTransactions,
setPendingConfirmations,
getSuccessTransactions
)
unsubscribe()
expect(setResult).toBeCalledTimes(7)
expect(getValidatorConfirmation).toBeCalledTimes(2)
expect(getValidatorSuccessTransaction).toBeCalledTimes(2)
expect(setSignatureCollected).toBeCalledTimes(2)
expect(getSuccessExecutionTransaction).toBeCalledTimes(2)
expect(setSignatureCollected).toBeCalledTimes(3)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(false)
expect(setSignatureCollected.mock.calls[1][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[2][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(2)
expect(setFailedConfirmations).toBeCalledTimes(2)
@@ -687,14 +776,24 @@ describe('getConfirmationsForTx', () => {
expect(setPendingConfirmations.mock.calls[0][0]).toEqual(true)
expect(setPendingConfirmations.mock.calls[1][0]).toEqual(false)
expect(setResult.mock.calls[2][0]()).toEqual(
const res5 = setResult.mock.calls[4][0](res4)
const res6 = setResult.mock.calls[5][0](res5)
const res7 = setResult.mock.calls[6][0](res6)
expect(res5).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }
])
)
expect(setResult.mock.calls[3][0]).toEqual(
expect(res6).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }
])
)
expect(res7).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },

View File

@@ -61,6 +61,7 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn()
const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent(
contract,
@@ -76,7 +77,8 @@ describe('getFinalizationEvent', () => {
getFailedExecution,
setFailedExecution,
getPendingExecution,
setPendingExecution
setPendingExecution,
setExecutionEventsFetched
)
unsubscribe()
@@ -110,6 +112,7 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn()
const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent(
contract,
@@ -125,7 +128,8 @@ describe('getFinalizationEvent', () => {
getFailedExecution,
setFailedExecution,
getPendingExecution,
setPendingExecution
setPendingExecution,
setExecutionEventsFetched
)
unsubscribe()
@@ -159,6 +163,7 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn().mockResolvedValue([])
const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent(
contract,
@@ -174,7 +179,8 @@ describe('getFinalizationEvent', () => {
getFailedExecution,
setFailedExecution,
getPendingExecution,
setPendingExecution
setPendingExecution,
setExecutionEventsFetched
)
unsubscribe()
@@ -208,6 +214,7 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn().mockResolvedValue([{ hash: txHash }])
const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent(
contract,
@@ -223,7 +230,8 @@ describe('getFinalizationEvent', () => {
getFailedExecution,
setFailedExecution,
getPendingExecution,
setPendingExecution
setPendingExecution,
setExecutionEventsFetched
)
unsubscribe()
@@ -264,6 +272,7 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn().mockResolvedValue([])
const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent(
contract,
@@ -279,7 +288,8 @@ describe('getFinalizationEvent', () => {
getFailedExecution,
setFailedExecution,
getPendingExecution,
setPendingExecution
setPendingExecution,
setExecutionEventsFetched
)
unsubscribe()

View File

@@ -15,13 +15,19 @@ export const checkWaitingBlocksForExecution = async (
const currentBlock = blockProvider.get()
if (currentBlock && currentBlock >= targetBlock) {
setExecutionData({
const undefinedExecutionState = {
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
txHash: '',
timestamp: 0,
executionResult: false
})
}
setExecutionData(
(data: any) =>
data.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || data.status === VALIDATOR_CONFIRMATION_STATUS.WAITING
? undefinedExecutionState
: data
)
setWaitingBlocksForExecutionResolved(true)
setWaitingBlocksForExecution(false)
blockProvider.stop()
@@ -31,13 +37,20 @@ export const checkWaitingBlocksForExecution = async (
nextInterval = 500
} else {
setWaitingBlocksForExecution(true)
setExecutionData({
const waitingExecutionState = {
status: VALIDATOR_CONFIRMATION_STATUS.WAITING,
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
txHash: '',
timestamp: 0,
executionResult: false
})
}
setExecutionData(
(data: any) =>
data.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED ||
data.status === VALIDATOR_CONFIRMATION_STATUS.WAITING
? waitingExecutionState
: data
)
}
const timeoutId = setTimeout(
() =>

View File

@@ -7,20 +7,37 @@ import {
APIPendingTransaction,
GetPendingTransactionParams
} from './explorer'
import { getAffirmationsSigned, getMessagesSigned } from './contract'
import {
getValidatorConfirmation,
getValidatorFailedTransaction,
getValidatorPendingTransaction,
getValidatorSuccessTransaction
getSuccessExecutionTransaction
} from './validatorConfirmationHelpers'
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
const mergeConfirmations = (oldConfirmations: BasicConfirmationParam[], newConfirmations: BasicConfirmationParam[]) => {
const confirmations = [...oldConfirmations]
newConfirmations.forEach(validatorData => {
const index = confirmations.findIndex(e => e.validator === validatorData.validator)
const currentStatus = confirmations[index].status
const newStatus = validatorData.status
if (
(validatorData as ConfirmationParam).txHash ||
(newStatus !== currentStatus && newStatus !== VALIDATOR_CONFIRMATION_STATUS.UNDEFINED)
) {
confirmations[index] = validatorData
}
})
return confirmations
}
export const getConfirmationsForTx = async (
messageData: string,
web3: Maybe<Web3>,
validatorList: string[],
bridgeContract: Maybe<Contract>,
confirmationContractMethod: Function,
fromHome: boolean,
setResult: Function,
requiredSignatures: number,
setSignatureCollected: Function,
@@ -35,48 +52,47 @@ export const getConfirmationsForTx = async (
) => {
if (!web3 || !validatorList || !validatorList.length || !bridgeContract || !waitingBlocksResolved) return
// If all the information was not collected, then it should retry
let shouldRetry = false
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
const hashMsg = web3.utils.soliditySha3Raw(messageData)
let validatorConfirmations = await Promise.all(
validatorList.map(getValidatorConfirmation(web3, hashMsg, bridgeContract, confirmationContractMethod))
)
const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
setResult((prevConfirmations: ConfirmationParam[]) => {
if (prevConfirmations && prevConfirmations.length) {
successConfirmations.forEach(validatorData => {
const index = prevConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
return prevConfirmations
} else {
return validatorConfirmations
const updateConfirmations = (confirmations: BasicConfirmationParam[]) => {
if (confirmations.length === 0) {
return
}
})
validatorConfirmations = mergeConfirmations(validatorConfirmations, confirmations)
setResult((currentConfirmations: BasicConfirmationParam[]) => {
if (currentConfirmations && currentConfirmations.length) {
return mergeConfirmations(currentConfirmations, confirmations)
}
return confirmations
})
}
const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
const notSuccessConfirmations = validatorConfirmations.filter(c => c.status !== VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
const hasEnoughSignatures = successConfirmations.length === requiredSignatures
updateConfirmations(validatorConfirmations)
setSignatureCollected(hasEnoughSignatures)
// If signatures not collected, look for pending transactions
let pendingConfirmationsResult = false
if (successConfirmations.length !== requiredSignatures) {
if (!hasEnoughSignatures) {
// Check if confirmation is pending
const validatorPendingConfirmationsChecks = await Promise.all(
notSuccessConfirmations.map(getValidatorPendingTransaction(bridgeContract, messageData, getPendingTransactions))
)
const validatorPendingConfirmations = validatorPendingConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING
)
validatorPendingConfirmations.forEach(validatorData => {
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
if (validatorPendingConfirmations.length > 0) {
pendingConfirmationsResult = true
}
updateConfirmations(validatorPendingConfirmations)
setPendingConfirmations(validatorPendingConfirmations.length > 0)
} else {
setPendingConfirmations(false)
}
const undefinedConfirmations = validatorConfirmations.filter(
@@ -84,7 +100,6 @@ export const getConfirmationsForTx = async (
)
// Check if confirmation failed
let failedConfirmationsResult = false
const validatorFailedConfirmationsChecks = await Promise.all(
undefinedConfirmations.map(
getValidatorFailedTransaction(bridgeContract, messageData, timestamp, getFailedTransactions)
@@ -93,68 +108,47 @@ export const getConfirmationsForTx = async (
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
)
validatorFailedConfirmations.forEach(validatorData => {
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
const messageConfirmationsFailed = validatorFailedConfirmations.length > validatorList.length - requiredSignatures
if (messageConfirmationsFailed) {
failedConfirmationsResult = true
}
setFailedConfirmations(validatorFailedConfirmations.length > validatorList.length - requiredSignatures)
updateConfirmations(validatorFailedConfirmations)
const missingConfirmations = validatorConfirmations.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING
)
if (successConfirmations.length !== requiredSignatures && missingConfirmations.length > 0) {
shouldRetry = true
}
let signatureCollectedResult = false
if (successConfirmations.length === requiredSignatures) {
if (hasEnoughSignatures) {
// If signatures collected, it should set other signatures not found as not required
const notRequiredConfirmations = missingConfirmations.map(c => ({
validator: c.validator,
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
}))
updateConfirmations(notRequiredConfirmations)
notRequiredConfirmations.forEach(validatorData => {
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
validatorConfirmations[index] = validatorData
})
signatureCollectedResult = true
if (fromHome) {
// fetch collected signatures for possible manual processing
setSignatureCollected(
await Promise.all(
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call())
)
)
}
}
// get transactions from success signatures
const successConfirmationWithData = await Promise.all(
validatorConfirmations
.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
.map(getValidatorSuccessTransaction(bridgeContract, messageData, timestamp, getSuccessTransactions))
successConfirmations.map(
getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, timestamp, getSuccessTransactions)
)
)
const successConfirmationWithTxFound = successConfirmationWithData.filter(v => v.txHash !== '')
updateConfirmations(successConfirmationWithTxFound)
const updatedValidatorConfirmations = [...validatorConfirmations]
if (successConfirmationWithTxFound.length > 0) {
successConfirmationWithTxFound.forEach(validatorData => {
const index = updatedValidatorConfirmations.findIndex(e => e.validator === validatorData.validator)
updatedValidatorConfirmations[index] = validatorData
})
}
// Set results
setResult(updatedValidatorConfirmations)
setFailedConfirmations(failedConfirmationsResult)
setPendingConfirmations(pendingConfirmationsResult)
setSignatureCollected(signatureCollectedResult)
// Retry if not all transaction were found for validator confirmations
if (successConfirmationWithTxFound.length < successConfirmationWithData.length) {
shouldRetry = true
}
if (shouldRetry) {
// retry if not all signatures are collected and some confirmations are still missing
// or some success transactions were not fetched successfully
if (
(!hasEnoughSignatures && missingConfirmations.length > 0) ||
successConfirmationWithTxFound.length < successConfirmationWithData.length
) {
const timeoutId = setTimeout(
() =>
getConfirmationsForTx(
@@ -162,7 +156,7 @@ export const getConfirmationsForTx = async (
web3,
validatorList,
bridgeContract,
confirmationContractMethod,
fromHome,
setResult,
requiredSignatures,
setSignatureCollected,

View File

@@ -25,7 +25,8 @@ export const getFinalizationEvent = async (
getFailedExecution: (args: GetFailedTransactionParams) => Promise<APITransaction[]>,
setFailedExecution: Function,
getPendingExecution: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>,
setPendingExecution: Function
setPendingExecution: Function,
setExecutionEventsFetched: Function
) => {
if (!contract || !web3 || !waitingBlocksResolved) return
// Since it filters by the message id, only one event will be fetched
@@ -55,6 +56,7 @@ export const getFinalizationEvent = async (
executionResult: event.returnValues.status
})
} else {
setExecutionEventsFetched(true)
// If event is defined, it means it is a message from Home to Foreign
if (collectedSignaturesEvent) {
const validator = collectedSignaturesEvent.returnValues.authorityResponsibleForRelay
@@ -128,7 +130,8 @@ export const getFinalizationEvent = async (
getFailedExecution,
setFailedExecution,
getPendingExecution,
setPendingExecution
setPendingExecution,
setExecutionEventsFetched
),
interval
)

View File

@@ -0,0 +1,26 @@
import Web3 from 'web3'
function strip0x(s: string) {
return Web3.utils.isHexStrict(s) ? s.substr(2) : s
}
export interface Signature {
v: string
r: string
s: string
}
export function signatureToVRS(rawSignature: string): Signature {
const signature = strip0x(rawSignature)
const v = signature.substr(64 * 2)
const r = signature.substr(0, 32 * 2)
const s = signature.substr(32 * 2, 32 * 2)
return { v, r, s }
}
export function packSignatures(array: Array<Signature>): string {
const length = strip0x(Web3.utils.toHex(array.length))
const msgLength = length.length === 1 ? `0${length}` : length
const [v, r, s] = array.reduce(([vs, rs, ss], { v, r, s }) => [vs + v, rs + r, ss + s], ['', '', ''])
return `0x${msgLength}${v}${r}${s}`
}

View File

@@ -45,8 +45,10 @@ export const getValidatorConfirmation = (
}
}
export const getValidatorSuccessTransaction = (
export const getSuccessExecutionTransaction = (
web3: Web3,
bridgeContract: Contract,
fromHome: boolean,
messageData: string,
timestamp: number,
getSuccessTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>

View File

@@ -2600,6 +2600,11 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/mocha@^7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==
"@types/node@*", "@types/node@>= 8":
version "13.11.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b"
@@ -3215,6 +3220,11 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -3340,6 +3350,11 @@ assert@^1.1.1:
object-assign "^4.1.1"
util "0.10.3"
assertion-error@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
@@ -3435,6 +3450,13 @@ axios@^0.18.0:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
axios@^0.19.0:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
dependencies:
follow-redirects "1.5.10"
axobject-query@^2.0.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799"
@@ -3799,6 +3821,11 @@ browser-resolve@^1.11.3:
dependencies:
resolve "1.1.7"
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6:
version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
@@ -4188,6 +4215,18 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
chai@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
dependencies:
assertion-error "^1.1.0"
check-error "^1.0.2"
deep-eql "^3.0.1"
get-func-name "^2.0.0"
pathval "^1.1.0"
type-detect "^4.0.5"
chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@@ -4213,6 +4252,11 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
check-error@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
check-types@^8.0.3:
version "8.0.3"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
@@ -4446,6 +4490,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
commander@2.15.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
commander@2.17.x:
version "2.17.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
@@ -5241,6 +5290,13 @@ dedent@^0.7.0:
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
deep-eql@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==
dependencies:
type-detect "^4.0.0"
deep-equal@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
@@ -5413,6 +5469,16 @@ diff-sequences@^24.9.0:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==
diff@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@@ -7093,6 +7159,11 @@ get-caller-file@^2.0.1:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-func-name@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=
get-own-enumerable-property-symbols@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
@@ -7227,6 +7298,18 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
glob@7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
@@ -7360,6 +7443,11 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
growl@1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -7518,6 +7606,11 @@ hdkey@^1.1.1:
safe-buffer "^5.1.1"
secp256k1 "^3.0.1"
he@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
he@1.2.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
@@ -9592,6 +9685,11 @@ make-dir@^2.0.0, make-dir@^2.1.0:
pify "^4.0.1"
semver "^5.6.0"
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
make-fetch-happen@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd"
@@ -9943,6 +10041,11 @@ minimist-options@^3.0.1:
arrify "^1.0.1"
is-plain-obj "^1.1.0"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
@@ -10007,6 +10110,13 @@ mkdirp@*:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
mkdirp@0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
@@ -10021,6 +10131,23 @@ mkdirp@~0.5.0, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
mocha@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
dependencies:
browser-stdout "1.3.1"
commander "2.15.1"
debug "3.1.0"
diff "3.5.0"
escape-string-regexp "1.0.5"
glob "7.1.2"
growl "1.10.5"
he "1.1.1"
minimatch "3.0.4"
mkdirp "0.5.1"
supports-color "5.4.0"
mock-fs@^4.1.0:
version "4.11.0"
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.11.0.tgz#0828107e4b843a6ba855ecebfe3c6e073b69db92"
@@ -10967,6 +11094,11 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
pathval@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=
pbkdf2@^3.0.3:
version "3.0.17"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
@@ -13271,6 +13403,14 @@ source-map-support@0.5.6:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-support@^0.5.17:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-support@^0.5.6, source-map-support@~0.5.10, source-map-support@~0.5.12:
version "0.5.16"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
@@ -13712,6 +13852,13 @@ stylis@^3.5.0:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==
supports-color@5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
dependencies:
has-flag "^3.0.0"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -14076,6 +14223,17 @@ tryer@^1.0.1:
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
ts-node@^8.8.2:
version "8.10.2"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d"
integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==
dependencies:
arg "^4.1.0"
diff "^4.0.1"
make-error "^1.1.1"
source-map-support "^0.5.17"
yn "3.1.1"
ts-pnp@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552"
@@ -14122,6 +14280,11 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
type-detect@^4.0.0, type-detect@^4.0.5:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
type-fest@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
@@ -14167,6 +14330,11 @@ typescript@3.5.3:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
typescript@^3.5.2:
version "3.9.7"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
uglify-js@3.4.x:
version "3.4.10"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
@@ -15481,3 +15649,8 @@ yauzl@^2.4.2:
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==

View File

@@ -5,7 +5,8 @@
],
"rules": {
"no-unused-expressions": "off",
"import/no-extraneous-dependencies": "off"
"import/no-extraneous-dependencies": "off",
"no-bitwise": "off"
},
"env": {
"mocha": true

View File

@@ -13,7 +13,6 @@ 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_AMB_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeAMBErc677ToErc677').abi
const FOREIGN_AMB_ERC_TO_ERC_ABI = require('../contracts/build/contracts/ForeignAMBErc677ToErc677').abi
const HOME_STAKE_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeStakeTokenMediator').abi
@@ -136,7 +135,6 @@ module.exports = {
OLD_AMB_USER_REQUEST_FOR_AFFIRMATION_ABI,
OLD_AMB_USER_REQUEST_FOR_SIGNATURE_ABI,
BOX_ABI,
SAI_TOP,
HOME_STAKE_ERC_TO_ERC_ABI,
FOREIGN_STAKE_ERC_TO_ERC_ABI
}

View File

@@ -2,9 +2,15 @@ function strip0x(input) {
return input.replace(/^0x/, '')
}
function addTxHashToData({ encodedData, transactionHash }) {
return encodedData.slice(0, 2) + strip0x(transactionHash) + encodedData.slice(2)
}
/**
* Decodes the datatype byte from the AMB message.
* First (the most significant bit) denotes if the message should be forwarded to the manual lane.
* @param dataType: number datatype of the received AMB message.
* @return {{manualLane: boolean}}
*/
const decodeAMBDataType = dataType => ({
manualLane: (dataType & 128) === 128
})
function parseAMBMessage(message) {
message = strip0x(message)
@@ -12,16 +18,29 @@ function parseAMBMessage(message) {
const messageId = `0x${message.slice(0, 64)}`
const sender = `0x${message.slice(64, 104)}`
const executor = `0x${message.slice(104, 144)}`
const dataType = parseInt(message.slice(156, 158), 16)
return {
sender,
executor,
messageId
messageId,
dataType,
decodedDataType: decodeAMBDataType(dataType)
}
}
module.exports = {
addTxHashToData,
parseAMBMessage,
strip0x
const normalizeAMBMessageEvent = e => {
let msgData = e.returnValues.encodedData
if (!e.returnValues.messageId) {
// append tx hash to an old message, where message id was not used
// for old messages, e.messageId is a corresponding transactionHash
msgData = e.transactionHash + msgData.slice(2)
}
return parseAMBMessage(msgData)
}
module.exports = {
strip0x,
parseAMBMessage,
normalizeAMBMessageEvent
}

View File

@@ -8,6 +8,7 @@
"test": "NODE_ENV=test mocha"
},
"dependencies": {
"gas-price-oracle": "^0.1.5",
"web3-utils": "1.0.0-beta.34"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
const { BN } = require('web3-utils')
const { expect } = require('chai').use(require('bn-chai')(BN))
const { parseAMBMessage, strip0x, addTxHashToData } = require('../message')
const { parseAMBMessage, strip0x } = require('../message')
describe('strip0x', () => {
it('should remove 0x from input', () => {
@@ -24,28 +24,6 @@ describe('strip0x', () => {
expect(result).to.be.equal(input)
})
})
describe('addTxHashToData', () => {
it('should add txHash to encoded data at position 2', () => {
// Given
const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e'
const msgExecutor = '0xf4bef13f9f4f2b203faf0c3cbbaabe1afe056955'
const msgGasLimit = '000000000000000000000000000000000000000000000000000000005b877705'
const msgDataType = '00'
const msgData = '0xb1591967aed668a4b27645ff40c444892d91bf5951b382995d4d4f6ee3a2ce03'
const encodedData = `0x${strip0x(msgSender)}${strip0x(msgExecutor)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
const transactionHash = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
const message = `0x${strip0x(transactionHash)}${strip0x(msgSender)}${strip0x(
msgExecutor
)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
// When
const result = addTxHashToData({ encodedData, transactionHash })
// Then
expect(result).to.be.equal(message)
})
})
describe('parseAMBMessage', () => {
it('should parse data type 00', () => {
const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e'

View File

@@ -1,7 +1,10 @@
const { toWei, toBN } = require('web3-utils')
const { GasPriceOracle } = require('gas-price-oracle')
const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants')
const { REWARDABLE_VALIDATORS_ABI } = require('./abis')
const gasPriceOracle = new GasPriceOracle()
function decodeBridgeMode(bridgeModeHash) {
switch (bridgeModeHash) {
case '0x92a8d7fe':
@@ -161,18 +164,18 @@ const getPastEvents = async (
} catch (e) {
if (e.message.includes('query returned more than') && toBlock !== 'latest') {
const middle = toBN(fromBlock)
.add(toBlock)
.add(toBN(toBlock))
.divRound(toBN(2))
const middlePlusOne = middle.add(toBN(1))
const firstHalfEvents = await getPastEvents(contract, {
...options,
options,
event,
fromBlock,
toBlock: middle
})
const secondHalfEvents = await getPastEvents(contract, {
...options,
options,
event,
fromBlock: middlePlusOne,
toBlock
@@ -235,8 +238,13 @@ const normalizeGasPrice = (oracleGasPrice, factor, limits = null) => {
// we use built-in 'fetch' on browser side, and `node-fetch` package in Node.
const gasPriceFromSupplier = async (fetchFn, options = {}) => {
try {
const response = await fetchFn()
const json = await response.json()
let json
if (fetchFn) {
const response = await fetchFn()
json = await response.json()
} else {
json = await gasPriceOracle.fetchGasPricesOffChain()
}
const oracleGasPrice = json[options.speedType]
if (!oracleGasPrice) {

View File

@@ -65,6 +65,20 @@ const homeV1Abi = [
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: true,
inputs: [],
name: 'requiredBlockConfirmations',
outputs: [
{
name: '',
type: 'uint256'
}
],
payable: false,
stateMutability: 'view',
type: 'function'
}
]
@@ -154,6 +168,20 @@ const foreignViAbi = [
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: true,
inputs: [],
name: 'requiredBlockConfirmations',
outputs: [
{
name: '',
type: 'uint256'
}
],
payable: false,
stateMutability: 'view',
type: 'function'
}
]

View File

@@ -13,7 +13,7 @@ git push
Alternatively, if there are no changes except the playbooks, you can use the `master` branch:
```
CIRCLE_BRANCH=master ./molecule.sh <scenario_name>
./molecule.sh <scenario_name>
```
In this case `master` branch will be used as a codebase for Monitor, UI, Oracle and Contracts deployed by your local playbook.
@@ -21,7 +21,7 @@ In this case `master` branch will be used as a codebase for Monitor, UI, Oracle
## Run the tests
```
CIRCLE_BRANCH=master ./molecule.sh <scenario_name>
./molecule.sh <scenario_name>
```
Available scenarios:

View File

@@ -2,7 +2,11 @@
cd ./e2e-commons
set -e # exit when any command fails
docker-compose pull molecule_runner
if [ -z "$CI" ]; then
docker-compose build molecule_runner
else
docker-compose pull molecule_runner
fi
docker network create --driver bridge ultimate || true
while [ "$1" != "" ]; do
docker-compose run molecule_runner /bin/bash -c "molecule test --scenario-name $1"

View File

@@ -32,8 +32,6 @@ provisioner:
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"
ui-amb-stake-erc-to-erc-host:

View File

@@ -21,8 +21,6 @@ provisioner:
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:

View File

@@ -1,5 +1,3 @@
---
- import_playbook: ../../../deployment/site.yml
# The docker-compose files have to be modified, in order to join the docker containers over network with the parity containers
- import_playbook: ./oracle-docker-compose.yml
- import_playbook: ./ui-docker-compose.yml
- import_playbook: ../../../deployment/site.yml

View File

@@ -1,26 +0,0 @@
---
- 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"

View File

@@ -1,35 +1,22 @@
---
- name: Overwrite Oracle the docker-compose
- name: Prepare Oracle for ultimate tests
hosts: oracle
become: true
tasks:
- name: stop the service
shell: service poabridge stop
- name: Build current oracle image
shell: docker build -t oracle:ultimate-testing --file oracle/Dockerfile .
- name: Connect parity to oracle networks
shell: "docker network create {{ item }} && docker network connect {{ item }} parity1 && docker network connect {{ item }} parity2"
with_items:
- oracle_net_db_bridge_request
- oracle_net_db_bridge_collected
- oracle_net_db_bridge_affirmation
- oracle_net_db_bridge_transfer
- oracle_net_db_bridge_senderhome
- oracle_net_db_bridge_senderforeign
- oracle_net_rabbit_bridge_request
- oracle_net_rabbit_bridge_collected
- oracle_net_rabbit_bridge_affirmation
- oracle_net_rabbit_bridge_transfer
- oracle_net_rabbit_bridge_senderhome
- oracle_net_rabbit_bridge_senderforeign
delegate_to: 127.0.0.1
become: false
args:
chdir: "{{ lookup('env', 'PWD') }}/.."
- 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
- 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

View File

@@ -1,32 +0,0 @@
---
- name: Overwrite UI the docker-compose
hosts: ui
become: true
tasks:
- name: stop the service
shell: service tokenbridge-ui stop
- name: Slurp docker compose file
slurp:
src: "/home/poadocker/bridge/ui/docker-compose.yml"
register: docker_compose_slurp
- name: Parse docker compose file
set_fact:
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
- name: 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 UI containers to the network
set_fact:
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': ['ultimate']}}}, recursive=True) }}"
with_items: "{{ docker_compose_parsed.services }}"
- name: Write new docker-compose file
copy:
content: "{{ docker_compose_parsed | to_yaml }}"
dest: "/home/poadocker/bridge/ui/docker-compose.yml"
- name: start the service
shell: service tokenbridge-ui start

View File

@@ -32,8 +32,6 @@ provisioner:
inventory:
host_vars:
oracle-erc-to-erc-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"
ui-erc-to-erc-host:

View File

@@ -32,8 +32,6 @@ provisioner:
inventory:
host_vars:
oracle-erc-to-native-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"
ORACLE_HOME_START_BLOCK: 1

View File

@@ -32,8 +32,6 @@ provisioner:
inventory:
host_vars:
oracle-native-to-erc-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"
ui-native-to-erc-host:

View File

@@ -5,13 +5,13 @@ ORACLE_ALLOW_HTTP_FOR_RPC: yes
ORACLE_LOG_LEVEL: debug
## Home contract
COMMON_HOME_RPC_URL: "https://sokol.poa.network"
COMMON_HOME_RPC_URL: "http://parity1:8545"
UI_HOME_NETWORK_DISPLAY_NAME: "POA Sokol"
UI_HOME_WITHOUT_EVENTS: false
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
## Foreign contract
COMMON_FOREIGN_RPC_URL: "https://sokol.poa.network"
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Kovan"
UI_FOREIGN_WITHOUT_EVENTS: false
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
@@ -52,3 +52,7 @@ MONITOR_FOREIGN_START_BLOCK: 0
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
MONITOR_TX_NUMBER_THRESHOLD: 100
# disable building and pulling of docker images from the Docker Hub
skip_pull: true
skip_build: true

View File

@@ -12,6 +12,7 @@
copy:
src: ../../../../{{ item }}
dest: "{{ bridge_path }}/"
mode: '0640'
with_items:
- monorepo.tar.gz
- contracts.tar.gz

View File

@@ -3,3 +3,4 @@
shell: docker-compose pull
args:
chdir: "{{ bridge_path }}/monitor"
when: skip_pull is undefined

View File

@@ -24,6 +24,8 @@
template:
src: .env.j2
dest: "{{ bridge_path }}/monitor/.env"
owner: "{{ compose_service_user }}"
mode: '0640'
when: skip_task != true
- name: Copy docker-compose file
@@ -45,3 +47,5 @@
template:
src: config.env.j2
dest: "{{ bridge_path }}/monitor/configs/{{ MONITOR_BRIDGE_NAME }}.env"
owner: "{{ compose_service_user }}"
mode: '0640'

View File

@@ -3,3 +3,4 @@
shell: docker-compose pull
args:
chdir: "{{ bridge_path }}/oracle"
when: skip_pull is undefined

View File

@@ -9,6 +9,8 @@
template:
src: .env.j2
dest: "{{ bridge_path }}/oracle/.env"
owner: "{{ compose_service_user }}"
mode: '0640'
- name: Copy docker-compose files
copy:

View File

@@ -3,3 +3,4 @@
shell: docker-compose build
args:
chdir: "{{ bridge_path }}/ui"
when: skip_build is undefined

View File

@@ -3,3 +3,5 @@
template:
src: .env.j2
dest: "{{ bridge_path }}/ui/.env"
owner: "{{ compose_service_user }}"
mode: '0640'

View File

@@ -1,6 +1,6 @@
ARG DOCKER_LOGIN
ARG CIRCLE_BRANCH
FROM ${DOCKER_LOGIN}/tokenbridge-e2e-ui:${CIRCLE_BRANCH}
ARG DOCKER_IMAGE_BASE
ARG UI_TAG
FROM ${DOCKER_IMAGE_BASE}/tokenbridge-e2e-ui:${UI_TAG}
ARG DOT_ENV_PATH

View File

@@ -0,0 +1,2 @@
0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04
0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE

17
e2e-commons/build.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
cd $(dirname $0)
set -e # exit when any command fails
docker-compose build e2e
while [ "$1" != "" ]; do
if [ "$1" == "oracle" ]; then
docker-compose build oracle
elif [ "$1" == "monitor" ]; then
docker-compose build monitor
elif [ "$1" == "ui" ]; then
docker-compose build ui
elif [ "$1" == "alm" ]; then
docker-compose build alm
fi
shift
done

View File

@@ -23,3 +23,4 @@ ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
ORACLE_ALLOW_HTTP_FOR_RPC=yes
ORACLE_HOME_START_BLOCK=1
ORACLE_FOREIGN_START_BLOCK=1
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt

View File

@@ -23,3 +23,4 @@ ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
ORACLE_ALLOW_HTTP_FOR_RPC=yes
ORACLE_HOME_START_BLOCK=1
ORACLE_FOREIGN_START_BLOCK=1
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt

View File

@@ -19,5 +19,5 @@ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3000
UI_PORT=3003
UI_STYLES=stake

View File

@@ -19,5 +19,5 @@ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3000
UI_PORT=3002
UI_STYLES=core

View File

@@ -19,5 +19,5 @@ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3000
UI_PORT=3001
UI_STYLES=core

View File

@@ -15,6 +15,10 @@
"address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B",
"privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1"
},
"blockedUser": {
"address": "0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04",
"privateKey": "0x65df4ea787916f6ed9660f0b0fe36858a65735ad0dcd34527497f4ce32e53883"
},
"validator": {
"address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b",
"privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
@@ -50,8 +54,6 @@
"home": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
"foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
"foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9",
"halfDuplexToken": "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359",
"saiTop": "0x9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3",
"chaiToken": "0x06af07097c9eeb7fd685c692751d5c66db49c215",
"ui": "http://localhost:3002",
"monitor": "http://monitor-erc20-native:3012/bridge"
@@ -61,6 +63,7 @@
"foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
"homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"blockedHomeBox": "0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE",
"monitor": "http://monitor-amb:3013/bridge"
},
"ambStakeErcToErc": {

View File

@@ -1,16 +1,17 @@
---
version: '3'
version: '3.8'
networks:
ultimate:
external: true
services:
parity1:
container_name: parity1
build: ../parity
ports:
- "8541:8545"
networks:
- ultimate
parity2:
container_name: parity2
build:
context: ../parity
dockerfile: Dockerfile-foreign
@@ -27,7 +28,7 @@ services:
networks:
- ultimate
oracle:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-oracle:${CIRCLE_BRANCH}
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-oracle:${ORACLE_TAG:-local}
build:
context: ..
dockerfile: oracle/Dockerfile
@@ -38,10 +39,7 @@ services:
networks:
- ultimate
oracle-erc20:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-oracle:${CIRCLE_BRANCH}
build:
context: ..
dockerfile: oracle/Dockerfile
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-oracle:${ORACLE_TAG:-local}
env_file: ../e2e-commons/components-envs/oracle-erc20.env
environment:
- NODE_ENV=production
@@ -49,29 +47,29 @@ services:
networks:
- ultimate
oracle-erc20-native:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-oracle:${CIRCLE_BRANCH}
build:
context: ..
dockerfile: oracle/Dockerfile
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-oracle:${ORACLE_TAG:-local}
env_file: ../e2e-commons/components-envs/oracle-erc20-native.env
environment:
- NODE_ENV=production
command: "true"
volumes:
- '../e2e-commons/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks:
- ultimate
oracle-amb:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-oracle:${CIRCLE_BRANCH}
build:
context: ..
dockerfile: oracle/Dockerfile
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-oracle:${ORACLE_TAG:-local}
env_file: ../e2e-commons/components-envs/oracle-amb.env
environment:
- NODE_ENV=production
command: "true"
volumes:
- '../e2e-commons/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks:
- ultimate
ui:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-ui:${CIRCLE_BRANCH}
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-ui:${UI_TAG:-local}
build:
context: ..
dockerfile: ui/Dockerfile
@@ -85,8 +83,8 @@ services:
context: ..
dockerfile: e2e-commons/Dockerfile.ui
args:
DOCKER_LOGIN: ${DOCKER_LOGIN}
CIRCLE_BRANCH: ${CIRCLE_BRANCH}
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20.env
command: "true"
networks:
@@ -96,8 +94,8 @@ services:
context: ..
dockerfile: e2e-commons/Dockerfile.ui
args:
DOCKER_LOGIN: ${DOCKER_LOGIN}
CIRCLE_BRANCH: ${CIRCLE_BRANCH}
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20-native.env
command: "true"
networks:
@@ -107,14 +105,14 @@ services:
context: ..
dockerfile: e2e-commons/Dockerfile.ui
args:
DOCKER_LOGIN: ${DOCKER_LOGIN}
CIRCLE_BRANCH: ${CIRCLE_BRANCH}
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env
command: "true"
networks:
- ultimate
alm:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-alm:${CIRCLE_BRANCH}
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-alm:${ALM_TAG:-local}
build:
context: ..
dockerfile: alm/Dockerfile
@@ -124,7 +122,7 @@ services:
networks:
- ultimate
monitor:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-monitor:${CIRCLE_BRANCH}
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-monitor:${MONITOR_TAG:-local}
build:
context: ..
dockerfile: monitor/Dockerfile
@@ -135,10 +133,7 @@ services:
networks:
- ultimate
monitor-erc20:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-monitor:${CIRCLE_BRANCH}
build:
context: ..
dockerfile: monitor/Dockerfile
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-monitor:${MONITOR_TAG:-local}
env_file: ../e2e-commons/components-envs/monitor-erc20.env
entrypoint: yarn check-and-start
ports:
@@ -146,10 +141,7 @@ services:
networks:
- ultimate
monitor-erc20-native:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-monitor:${CIRCLE_BRANCH}
build:
context: ..
dockerfile: monitor/Dockerfile
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-monitor:${MONITOR_TAG:-local}
env_file: ../e2e-commons/components-envs/monitor-erc20-native.env
entrypoint: yarn check-and-start
ports:
@@ -157,10 +149,7 @@ services:
networks:
- ultimate
monitor-amb:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-monitor:${CIRCLE_BRANCH}
build:
context: ..
dockerfile: monitor/Dockerfile
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-monitor:${MONITOR_TAG:-local}
env_file: ../e2e-commons/components-envs/monitor-amb.env
entrypoint: yarn check-and-start
ports:
@@ -168,7 +157,7 @@ services:
networks:
- ultimate
e2e:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-e2e:${CIRCLE_BRANCH}
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-e2e:${E2E_TAG:-local}
build:
context: ..
dockerfile: Dockerfile.e2e
@@ -176,15 +165,12 @@ services:
networks:
- ultimate
blocks:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-e2e:${CIRCLE_BRANCH}
build:
context: ..
dockerfile: Dockerfile.e2e
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-e2e:${E2E_TAG:-local}
entrypoint: node e2e-commons/scripts/blocks.js
networks:
- ultimate
molecule_runner:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-molecule_runner:${CIRCLE_BRANCH}
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-molecule_runner:${MOLECULE_RUNNER_TAG:-local}
build:
context: ..
dockerfile: deployment-e2e/Dockerfile

View File

@@ -53,3 +53,8 @@ node deploy.js
cd - > /dev/null
node setupStakeTokens.js
cd - > /dev/null
echo -e "\n\n############ Deploying one more test contract for amb ############\n"
cd "$DEPLOY_PATH"
node src/utils/deployTestBox.js
cd - > /dev/null

View File

@@ -4,30 +4,42 @@ set -e # exit when any command fails
./down.sh
docker-compose build parity1 parity2
test -n "$NODOCKERPULL" || ./pull.sh $@
if [ -z "$CI" ]; then
./build.sh $@
else
./pull.sh $@
fi
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
if [[ -z "$MODE" || "$MODE" == native-to-erc ]]; then
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
fi
if [[ -z "$MODE" || "$MODE" == erc-to-erc ]]; then
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
fi
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
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 worker:convert-to-chai
fi
if [[ -z "$MODE" || "$MODE" == amb ]]; then
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
fi
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:home
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:foreign
}
@@ -44,27 +56,7 @@ startAMBValidator () {
while [ "$1" != "" ]; do
if [ "$1" == "oracle" ]; then
docker-compose up -d redis rabbit
docker-compose run -d oracle yarn watcher:signature-request
docker-compose run -d oracle yarn watcher:collected-signatures
docker-compose run -d oracle yarn watcher:affirmation-request
docker-compose run -d oracle-erc20 yarn watcher:signature-request
docker-compose run -d oracle-erc20 yarn watcher:collected-signatures
docker-compose run -d oracle-erc20 yarn watcher:affirmation-request
docker-compose run -d oracle-erc20 yarn watcher:transfer
docker-compose run -d oracle-erc20-native yarn watcher:signature-request
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
docker-compose run -d oracle yarn sender:home
docker-compose run -d oracle yarn sender:foreign
startValidator "" "" "" "redis" "rabbit"
fi
if [ "$1" == "oracle-validator-2" ]; then
@@ -88,9 +80,9 @@ while [ "$1" != "" ]; do
docker-compose up -d ui ui-erc20 ui-erc20-native ui-amb-stake-erc20-erc20
docker-compose run -d -p 3000:3000 ui yarn start
docker-compose run -d -p 3001:3000 ui-erc20 yarn start
docker-compose run -d -p 3002:3000 ui-erc20-native yarn start
docker-compose run -d -p 3003:3000 ui-amb-stake-erc20-erc20 yarn start
docker-compose run -d -p 3001:3001 ui-erc20 yarn start
docker-compose run -d -p 3002:3002 ui-erc20-native yarn start
docker-compose run -d -p 3003:3003 ui-amb-stake-erc20-erc20 yarn start
fi
if [ "$1" == "alm" ]; then
@@ -108,27 +100,23 @@ while [ "$1" != "" ]; do
fi
if [ "$1" == "monitor" ]; then
docker-compose up -d monitor monitor-erc20 monitor-erc20-native monitor-amb
fi
if [ "$1" == "native-to-erc" ]; then
../deployment-e2e/molecule.sh ultimate-native-to-erc
fi
if [ "$1" == "erc-to-native" ]; then
../deployment-e2e/molecule.sh ultimate-erc-to-native
fi
if [ "$1" == "erc-to-erc" ]; then
../deployment-e2e/molecule.sh ultimate-erc-to-erc
fi
if [ "$1" == "amb" ]; then
../deployment-e2e/molecule.sh ultimate-amb
fi
if [ "$1" == "ultimate-amb-stake-erc-to-erc" ]; then
../deployment-e2e/molecule.sh ultimate-amb-stake-erc-to-erc
case "$MODE" in
amb)
docker-compose up -d monitor-amb
;;
native-to-erc)
docker-compose up -d monitor
;;
erc-to-erc)
docker-compose up -d monitor-erc20
;;
erc-to-native)
docker-compose up -d monitor-erc20-native
;;
*)
docker-compose up -d monitor monitor-erc20 monitor-erc20-native monitor-amb
;;
esac
fi
if [ "$1" == "alm-e2e" ]; then

View File

@@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "mocha --timeout 30000",
"start": "mocha --timeout 120000",
"lint": "eslint . --ignore-path ../.eslintignore"
},
"author": "",
@@ -14,7 +14,7 @@
"axios": "0.19.0"
},
"engines": {
"node": ">= 8.9"
"node": ">= 10.18"
},
"devDependencies": {}
}

View File

@@ -1,7 +1,15 @@
while true; do
sleep 3
COMPOSE_INTERACTIVE_NO_CLI=1 nohup docker-compose -f ../e2e-commons/docker-compose.yml exec monitor yarn check-all
COMPOSE_INTERACTIVE_NO_CLI=1 nohup docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20 yarn check-all
COMPOSE_INTERACTIVE_NO_CLI=1 nohup docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20-native yarn check-all
COMPOSE_INTERACTIVE_NO_CLI=1 nohup docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-amb yarn check-all
docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor yarn check-all
pid1=$!
docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-erc20 yarn check-all
pid2=$!
docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-erc20-native yarn check-all
pid3=$!
docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-amb yarn check-all
pid4=$!
wait $pid1
wait $pid2
wait $pid3
wait $pid4
done

View File

@@ -1,12 +1,28 @@
cd $(dirname $0)
../e2e-commons/up.sh deploy blocks monitor
mode="$1"
case "$mode" in
amb)
script=./test/amb.js
;;
native-to-erc)
script=./test/nativeToErc.js
;;
erc-to-erc)
script=./test/ercToErc.js
;;
erc-to-native)
script=./test/ercToNative.js
;;
esac
./wait-for-monitor.sh
MODE="$mode" ../e2e-commons/up.sh deploy blocks monitor
MODE="$mode" ./wait-for-monitor.sh
nohup ./periodically-check-all.sh < /dev/null > /dev/null 2>&1 &
checkPID=$!
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace monitor-e2e run start
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace monitor-e2e run start $script
rc=$?
../e2e-commons/down.sh

View File

@@ -25,6 +25,7 @@ describe('AMB', () => {
describe('general', async () => {
it('should contain fromHomeToForeignDiff', () => assert(data.fromHomeToForeignDiff === 0))
it('should contain fromHomeToForeignPBUDiff', () => assert(data.fromHomeToForeignPBUDiff === 0))
it('should contain fromForeignToHomeDiff', () => assert(data.fromForeignToHomeDiff === 0))
it('should contain lastChecked', () => assert(data.lastChecked >= 0))
it('should contain timeDiff', () => assert(data.timeDiff >= 0))
@@ -114,7 +115,16 @@ describe('AMB', () => {
await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`))
return data.fromHomeToForeignDiff !== 0
return data.fromHomeToForeignDiff === 1 && data.fromHomeToForeignPBUDiff === 0
})
})
it('should change fromHomeToForeignPBUDiff', async () => {
// send message
await sendAMBMessage(homeRPC.URL, user, amb.homeBox, amb.home, amb.foreignBox, true)
await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`))
return data.fromHomeToForeignDiff === 1 && data.fromHomeToForeignPBUDiff === 1
})
})
it('should change validatorsMatch', async () => {

View File

@@ -37,6 +37,9 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`))
if (!data.foreign) {
return false
}
const { erc20Balance, investedErc20Balance } = data.foreign
return data.balanceDiff === 0.01 && erc20Balance === '0.01' && investedErc20Balance === undefined
})
@@ -51,12 +54,15 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
})
it('should consider chai token balance', async function() {
this.timeout(60000)
this.timeout(120000)
await initializeChaiToken(foreignRPC.URL, ercToNativeBridge.foreign)
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign)
await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`))
if (!data.foreign) {
return false
}
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
return (
data.balanceDiff === 0.02 &&
@@ -71,6 +77,9 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`))
if (!data.foreign) {
return false
}
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
return (
data.balanceDiff === 0.02 &&
@@ -85,6 +94,9 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`))
if (!data.foreign) {
return false
}
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
return (
data.balanceDiff === 0.02 &&

View File

@@ -8,7 +8,7 @@ const {
} = require('../commons')
const { validator } = require('../e2e-commons/constants')
const waitUntil = async (predicate, step = 100, timeout = 20000) => {
const waitUntil = async (predicate, step = 100, timeout = 60000) => {
const stopTime = Date.now() + timeout
while (Date.now() <= stopTime) {
const result = await predicate()
@@ -44,12 +44,16 @@ const sendTokens = async (rpcUrl, account, tokenAddress, recipientAddress) => {
})
}
const sendAMBMessage = async (rpcUrl, account, boxAddress, bridgeAddress, boxOtherSideAddress) => {
const sendAMBMessage = async (rpcUrl, account, boxAddress, bridgeAddress, boxOtherSideAddress, manualLane = false) => {
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
web3.eth.accounts.wallet.add(account.privateKey)
const homeBox = new web3.eth.Contract(BOX_ABI, boxAddress)
await homeBox.methods.setValueOnOtherNetwork(3, bridgeAddress, boxOtherSideAddress).send({
await homeBox.methods[manualLane ? 'setValueOnOtherNetworkUsingManualLane' : 'setValueOnOtherNetwork'](
3,
bridgeAddress,
boxOtherSideAddress
).send({
from: account.address,
gas: '400000'
})

View File

@@ -1,13 +1,23 @@
#!/bin/bash
FILES=(getBalances.json validators.json eventsStats.json alerts.json)
check_files_exist() {
rc=0
for f in "${FILES[@]}"; do
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
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-amb /bin/bash -c "$command") || rc=1
if [[ -z "$MODE" || "$MODE" == native-to-erc ]]; then
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor /bin/bash -c "$command") || rc=1
fi
if [[ -z "$MODE" || "$MODE" == erc-to-erc ]]; then
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-erc20 /bin/bash -c "$command") || rc=1
fi
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-erc20-native /bin/bash -c "$command") || rc=1
fi
if [[ -z "$MODE" || "$MODE" == amb ]]; then
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-amb /bin/bash -c "$command") || rc=1
fi
done
return $rc
}

View File

@@ -22,3 +22,9 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
MONITOR_TX_NUMBER_THRESHOLD=100
MONITOR_PORT=3003
MONITOR_CACHE_EVENTS=true
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST=
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST=
# MONITOR_HOME_VALIDATORS_BALANCE_ENABLE=0x... 0x... 0x...
# MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE=0x... 0x... 0x...

View File

@@ -1,4 +1,4 @@
FROM node:8 as contracts
FROM node:10 as contracts
WORKDIR /mono
@@ -11,7 +11,7 @@ COPY ./contracts/truffle-config.js ./
COPY ./contracts/contracts ./contracts
RUN npm run compile
FROM node:8
FROM node:10
WORKDIR /mono
COPY package.json .

View File

@@ -1,18 +1,9 @@
require('dotenv').config()
const Web3 = require('web3')
const logger = require('./logger')('alerts')
const eventsInfo = require('./utils/events')
const { getBlockNumber } = require('./utils/contract')
const { processedMsgNotDelivered, eventWithoutReference } = require('./utils/message')
const { BRIDGE_MODES } = require('../commons')
const { COMMON_HOME_RPC_URL, COMMON_FOREIGN_RPC_URL } = process.env
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
const foreignProvider = new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)
const web3Foreign = new Web3(foreignProvider)
const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3')
async function main() {
const {
@@ -33,7 +24,8 @@ async function main() {
xAffirmations = foreignToHomeConfirmations.filter(eventWithoutReference(foreignToHomeRequests))
}
logger.debug('building misbehavior blocks')
const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
const homeBlockNumber = await getHomeBlockNumber()
const foreignBlockNumber = await getForeignBlockNumber()
const baseRange = [false, false, false, false, false]
const xSignaturesMisbehavior = buildRangesObject(
@@ -73,21 +65,21 @@ async function main() {
/**
* Finds the location for the blockNumber in a specific range starting from currentBlockNumber
* @param {BN} currentBlockNumber
* @param {Number} currentBlockNumber
* @returns {function({blockNumber?: *}): boolean[]}
*/
const findMisbehaviorRange = currentBlockNumber => ({ blockNumber }) => {
const minus60 = currentBlockNumber.sub(Web3.utils.toBN(60))
const minus180 = currentBlockNumber.sub(Web3.utils.toBN(180))
const minus720 = currentBlockNumber.sub(Web3.utils.toBN(720))
const minus17280 = currentBlockNumber.sub(Web3.utils.toBN(17280))
const minus60 = currentBlockNumber - 60
const minus180 = currentBlockNumber - 180
const minus720 = currentBlockNumber - 720
const minus17280 = currentBlockNumber - 17280
return [
minus60.lte(blockNumber),
minus180.lte(blockNumber) && minus60.gt(blockNumber),
minus720.lte(blockNumber) && minus180.gt(blockNumber),
minus17280.lte(blockNumber) && minus720.gt(blockNumber),
minus17280.gt(blockNumber)
minus60 <= blockNumber,
minus180 <= blockNumber && minus60 > blockNumber,
minus720 <= blockNumber && minus180 > blockNumber,
minus17280 <= blockNumber && minus720 > blockNumber,
minus17280 > blockNumber
]
}

0
monitor/cache/.gitkeep vendored Normal file
View File

View File

@@ -1,20 +1,16 @@
require('dotenv').config()
const Web3 = require('web3')
const BN = require('bignumber.js')
const logger = require('./logger')('checkWorker')
const { getBridgeMode } = require('../commons')
const getBalances = require('./getBalances')
const getShortEventStats = require('./getShortEventStats')
const validators = require('./validators')
const getEventsInfo = require('./utils/events')
const { writeFile, createDir } = require('./utils/file')
const { saveCache } = require('./utils/web3Cache')
const { web3Home } = require('./utils/web3')
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 homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
const { COMMON_HOME_BRIDGE_ADDRESS, MONITOR_BRIDGE_NAME } = process.env
const { HOME_ERC_TO_ERC_ABI } = require('../commons')
@@ -24,42 +20,27 @@ async function checkWorker() {
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)
logger.debug('calling getEventsInfo()')
const eventsInfo = await getEventsInfo(bridgeMode)
logger.debug('calling getBalances()')
const balances = await getBalances(bridgeMode)
const balances = await getBalances(bridgeMode, eventsInfo)
logger.debug('calling getShortEventStats()')
const events = await getShortEventStats(bridgeMode)
const events = await getShortEventStats(bridgeMode, eventsInfo)
const home = Object.assign({}, balances.home, events.home)
const foreign = Object.assign({}, balances.foreign, events.foreign)
const status = Object.assign({}, balances, events, { home }, { foreign })
if (status.balanceDiff && status.unclaimedBalance) {
status.balanceDiff = new BN(status.balanceDiff).minus(status.unclaimedBalance).toFixed()
}
if (!status) throw new Error('status is empty: ' + JSON.stringify(status))
status.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/getBalances.json`, status)
saveCache()
logger.debug('calling validators()')
const vBalances = await validators(bridgeMode)
if (!vBalances) throw new Error('vBalances is empty: ' + JSON.stringify(vBalances))
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
vBalances.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/validators.json`, vBalances)

View File

@@ -3,6 +3,7 @@ const logger = require('./logger')('checkWorker2')
const eventsStats = require('./eventsStats')
const alerts = require('./alerts')
const { writeFile, createDir } = require('./utils/file')
const { saveCache } = require('./utils/web3Cache')
const { MONITOR_BRIDGE_NAME } = process.env
@@ -26,6 +27,7 @@ async function checkWorker2() {
_alerts.ok = !_alerts.executeAffirmations.mostRecentTxHash && !_alerts.executeSignatures.mostRecentTxHash
_alerts.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/alerts.json`, _alerts)
saveCache()
logger.debug('Done x2')
} catch (e) {
logger.error(e)

View File

@@ -1,15 +1,14 @@
require('dotenv').config()
const Web3 = require('web3')
const logger = require('./logger')('checkWorker3')
const stuckTransfers = require('./stuckTransfers')
const detectMediators = require('./detectMediators')
const { writeFile, createDir } = require('./utils/file')
const { web3Home } = require('./utils/web3')
const { saveCache } = require('./utils/web3Cache')
const { MONITOR_BRIDGE_NAME, COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL } = process.env
const { MONITOR_BRIDGE_NAME, COMMON_HOME_BRIDGE_ADDRESS } = 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 {
const homeBridge = new web3Home.eth.Contract(HOME_NATIVE_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
@@ -23,6 +22,15 @@ async function checkWorker3() {
transfers.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/stuckTransfers.json`, transfers)
logger.debug('Done')
} else if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
createDir(`/responses/${MONITOR_BRIDGE_NAME}`)
logger.debug('calling detectMediators()')
const mediators = await detectMediators(bridgeMode)
mediators.ok = true
mediators.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/mediators.json`, mediators)
saveCache()
logger.debug('Done')
}
} catch (e) {
logger.error('checkWorker3.js', e)

148
monitor/detectMediators.js Normal file
View File

@@ -0,0 +1,148 @@
require('dotenv').config()
const logger = require('./logger')('stuckTransfers.js')
const { isHomeContract, isForeignContract } = require('./utils/web3Cache')
const eventsInfo = require('./utils/events')
const { getHomeTxSender, getForeignTxSender } = require('./utils/web3Cache')
const { addExecutionStatus } = require('./utils/message')
const { normalizeAMBMessageEvent } = require('../commons')
function countInteractions(requests) {
const stats = {}
requests.forEach(msg => {
if (!stats[msg.sender]) {
stats[msg.sender] = {}
}
if (!stats[msg.sender][msg.executor]) {
stats[msg.sender][msg.executor] = 0
}
stats[msg.sender][msg.executor] += 1
})
return stats
}
const normalize = event => ({
...normalizeAMBMessageEvent(event),
txHash: event.transactionHash,
logIndex: event.transactionLogIndex
})
const flat = arrays => Array.prototype.concat.apply([], arrays)
function findPermanentMediators(homeToForeignC2C, foreignToHomeC2C) {
return flat(
Object.entries(homeToForeignC2C).map(([homeMediator, homeStats]) =>
Object.entries(foreignToHomeC2C)
.map(([foreignMediator, foreignStats]) => ({
homeMediator,
foreignMediator,
homeToForeignRequests: homeStats[foreignMediator],
foreignToHomeRequests: foreignStats[homeMediator]
}))
.filter(stats => stats.homeToForeignRequests && stats.foreignToHomeRequests)
)
)
}
function findFloatingMediators(homeToForeignC2C, foreignToHomeC2C) {
return Object.entries(homeToForeignC2C)
.map(([homeMediator, homeStats]) => {
const noResponses = ([executor]) => !foreignToHomeC2C[executor] || !foreignToHomeC2C[executor][homeMediator]
const executorRequestPairs = Object.entries(homeStats).filter(noResponses)
return {
mediator: homeMediator,
executors: executorRequestPairs.map(pair => pair[0]),
requests: executorRequestPairs.map(pair => pair[1])
}
})
.filter(stats => stats.executors.length > 0)
}
function findRemotelyControlledMediators(statsU2C) {
return Object.entries(statsU2C).map(([user, stats]) => ({
user,
executors: Object.keys(stats),
requests: Object.values(stats)
}))
}
function findUnknown(statsA2U) {
return Object.entries(statsA2U).map(([sender, stats]) => ({
sender,
executors: Object.keys(stats),
requests: Object.values(stats)
}))
}
async function main(mode) {
const {
homeToForeignRequests,
foreignToHomeRequests,
homeToForeignConfirmations,
foreignToHomeConfirmations
} = await eventsInfo(mode)
const homeToForeign = homeToForeignRequests
.map(normalize)
.map(addExecutionStatus(homeToForeignConfirmations))
.filter(x => typeof x.status === 'boolean')
const foreignToHome = foreignToHomeRequests
.map(normalize)
.map(addExecutionStatus(foreignToHomeConfirmations))
.filter(x => typeof x.status === 'boolean')
for (const event of homeToForeign) {
// AMB contract emits a single UserRequestForSignature event for every home->foreign request.
// If index of such event in logs is not equal to 0x0, then some other events occurred before it,
// meaning that the sender was a contract.
// Alternatively, the sender is a contract, if the message sender is not equal to tx.origin.
event.isSenderAContract = event.logIndex !== '0x0' || (await getHomeTxSender(event.txHash)) !== event.sender
// Executor is definitely a contract if a message execution failed, since message calls to EOA always succeed.
// Alternatively, the executor is checked to be a contract by looking at its bytecode size.
event.isExecutorAContract = !event.status || (await isForeignContract(event.executor))
}
for (const event of foreignToHome) {
// AMB contract emits a single UserRequestForAffirmation event for every foreign->home request.
// If index of such event in logs is not equal to 0x0, then some other events occurred before it,
// meaning that the sender was a contract.
// Alternatively, the sender is a contract, if the message sender is not equal to tx.origin.
event.isSenderAContract = event.logIndex !== '0x0' || (await getForeignTxSender(event.txHash)) !== event.sender
// Executor is definitely a contract if a message execution failed, since message calls to EOA always succeed.
// Alternatively, the executor is checked to be a contract by looking at its bytecode size.
event.isExecutorAContract = !event.status || (await isHomeContract(event.executor))
}
const C2C = event => event.isSenderAContract && event.isExecutorAContract
const U2C = event => !event.isSenderAContract && event.isExecutorAContract
const A2U = event => !event.isExecutorAContract
const homeToForeignC2C = countInteractions(homeToForeign.filter(C2C))
const foreignToHomeC2C = countInteractions(foreignToHome.filter(C2C))
const homeToForeignU2C = countInteractions(homeToForeign.filter(U2C))
const foreignToHomeU2C = countInteractions(foreignToHome.filter(U2C))
const homeToForeignA2U = countInteractions(homeToForeign.filter(A2U))
const foreignToHomeA2U = countInteractions(foreignToHome.filter(A2U))
const permanentMediators = findPermanentMediators(homeToForeignC2C, foreignToHomeC2C)
const floatingMediators = {
home: findFloatingMediators(homeToForeignC2C, foreignToHomeC2C),
foreign: findFloatingMediators(foreignToHomeC2C, homeToForeignC2C)
}
const remotelyControlledMediators = {
home: findRemotelyControlledMediators(homeToForeignU2C),
foreign: findRemotelyControlledMediators(foreignToHomeU2C)
}
const unknown = {
home: findUnknown(homeToForeignA2U),
foreign: findUnknown(foreignToHomeA2U)
}
logger.debug('Done')
return {
permanentMediators,
floatingMediators,
remotelyControlledMediators,
unknown,
lastChecked: Math.floor(Date.now() / 1000)
}
}
module.exports = main

View File

@@ -3,4 +3,6 @@ version: '2.4'
services:
monitor:
image: poanetwork/tokenbridge-monitor
build: .
build:
context: ..
dockerfile: ./monitor/Dockerfile

View File

@@ -4,11 +4,12 @@ services:
monitor:
image: poanetwork/tokenbridge-monitor:latest
ports:
- "${MONITOR_PORT}:${MONITOR_PORT}"
- "${MONITOR_PORT}:${MONITOR_PORT}"
env_file: ./.env
environment:
environment:
- NODE_ENV=production
volumes:
- ./responses:/mono/monitor/responses
- ./cache:/mono/monitor/cache
restart: unless-stopped
entrypoint: "yarn start"

View File

@@ -1,8 +1,20 @@
require('dotenv').config()
const eventsInfo = require('./utils/events')
const { processedMsgNotDelivered, deliveredMsgNotProcessed, eventWithoutReference } = require('./utils/message')
const {
processedMsgNotDelivered,
deliveredMsgNotProcessed,
eventWithoutReference,
unclaimedHomeToForeignRequests
} = require('./utils/message')
const { getHomeTxSender } = require('./utils/web3Cache')
const { BRIDGE_MODES } = require('../commons')
const {
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST,
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST,
MONITOR_HOME_TO_FOREIGN_CHECK_SENDER
} = process.env
async function main() {
const {
homeToForeignRequests,
@@ -33,17 +45,30 @@ async function main() {
lastChecked: Math.floor(Date.now() / 1000)
}
} else {
const onlyInHomeDeposits = homeToForeignRequests.filter(eventWithoutReference(homeToForeignConfirmations))
let onlyInHomeDeposits = homeToForeignRequests.filter(eventWithoutReference(homeToForeignConfirmations))
const onlyInForeignDeposits = homeToForeignConfirmations.filter(eventWithoutReference(homeToForeignRequests))
const onlyInHomeWithdrawals = foreignToHomeConfirmations.filter(eventWithoutReference(foreignToHomeRequests))
const onlyInForeignWithdrawals = foreignToHomeRequests.filter(eventWithoutReference(foreignToHomeConfirmations))
const unclaimedStats = {}
if (MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST || MONITOR_HOME_TO_FOREIGN_BLOCK_LIST) {
const unclaimedFilter = unclaimedHomeToForeignRequests()
if (MONITOR_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
for (let i = 0; i < onlyInHomeDeposits.length; i++) {
onlyInHomeDeposits[i].sender = await getHomeTxSender(onlyInHomeDeposits[i].transactionHash)
}
}
unclaimedStats.unclaimedHomeDeposits = onlyInHomeDeposits.filter(unclaimedFilter)
onlyInHomeDeposits = onlyInHomeDeposits.filter(e => !unclaimedFilter(e))
}
return {
onlyInHomeDeposits,
onlyInForeignDeposits,
onlyInHomeWithdrawals,
onlyInForeignWithdrawals,
...unclaimedStats,
lastChecked: Math.floor(Date.now() / 1000)
}
}

View File

@@ -1,23 +1,11 @@
require('dotenv').config()
const BN = require('bignumber.js')
const Web3 = require('web3')
const Web3Utils = require('web3').utils
const logger = require('./logger')('getBalances')
const { BRIDGE_MODES } = require('../commons')
const { web3Home, web3Foreign, getHomeBlockNumber } = require('./utils/web3')
const Web3Utils = Web3.utils
const {
COMMON_HOME_RPC_URL,
COMMON_FOREIGN_RPC_URL,
COMMON_HOME_BRIDGE_ADDRESS,
COMMON_FOREIGN_BRIDGE_ADDRESS
} = process.env
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
const foreignProvider = new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)
const web3Foreign = new Web3(foreignProvider)
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
const {
ERC20_ABI,
@@ -30,21 +18,50 @@ const {
FOREIGN_NATIVE_TO_ERC_ABI
} = require('../commons')
async function main(bridgeMode) {
async function main(bridgeMode, eventsInfo) {
const {
homeToForeignConfirmations,
foreignToHomeConfirmations,
homeDelayedBlockNumber,
foreignDelayedBlockNumber
} = eventsInfo
// Events in the ./utils/events.js are fetched for different block ranges,
// In order to be consistent with the balance values, the following values might be needed
// Foreign balance should represent all UserRequestForAffirmation events up to block `N - requiredBlockConfirmation()`
// and all RelayedMessage events up to block `N`.
// This constant tells the difference between bridge balance at block `N - requiredBlockConfirmation() + 1`
// and the actual value monitor is interested in.
const lateForeignConfirmationsTotalValue = BN.sum(
0,
...homeToForeignConfirmations.filter(e => e.blockNumber > foreignDelayedBlockNumber).map(e => e.value)
)
// Home balance should represent all UserRequestForSignature events up to block `M - requiredBlockConfirmation()`
// and all AffirmationCompleted events up to block `M`.
// This constant tells the difference between bridge balance at block `M - requiredBlockConfirmation() + 1`
// and the actual value monitor is interested in.
const lateHomeConfirmationsTotalValue = BN.sum(
0,
...foreignToHomeConfirmations.filter(e => e.blockNumber > homeDelayedBlockNumber).map(e => e.value)
)
if (bridgeMode === BRIDGE_MODES.ERC_TO_ERC) {
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const erc20Address = await foreignBridge.methods.erc20token().call()
const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
logger.debug('calling erc20Contract.methods.balanceOf')
const foreignErc20Balance = await erc20Contract.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
const foreignErc20Balance = await erc20Contract.methods
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
.call({}, foreignDelayedBlockNumber)
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
logger.debug('calling homeBridge.methods.erc677token')
const tokenAddress = await homeBridge.methods.erc677token().call()
const tokenContract = new web3Home.eth.Contract(ERC677_ABI, tokenAddress)
logger.debug('calling tokenContract.methods.totalSupply()')
const totalSupply = await tokenContract.methods.totalSupply().call()
const foreignBalanceBN = new BN(foreignErc20Balance)
const foreignTotalSupplyBN = new BN(totalSupply)
const totalSupply = await tokenContract.methods.totalSupply().call({}, homeDelayedBlockNumber)
const foreignBalanceBN = new BN(foreignErc20Balance).plus(lateForeignConfirmationsTotalValue)
const foreignTotalSupplyBN = new BN(totalSupply).plus(lateHomeConfirmationsTotalValue)
const diff = foreignBalanceBN.minus(foreignTotalSupplyBN).toString(10)
logger.debug('Done')
return {
@@ -61,12 +78,12 @@ async function main(bridgeMode) {
logger.debug('calling web3Home.eth.getBalance')
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_NATIVE_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const erc20Address = await foreignBridge.methods.erc677token().call()
const homeBalance = await web3Home.eth.getBalance(COMMON_HOME_BRIDGE_ADDRESS)
const homeBalance = await web3Home.eth.getBalance(COMMON_HOME_BRIDGE_ADDRESS, homeDelayedBlockNumber)
const tokenContract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
logger.debug('calling tokenContract.methods.totalSupply()')
const totalSupply = await tokenContract.methods.totalSupply().call()
const homeBalanceBN = new BN(homeBalance)
const foreignTotalSupplyBN = new BN(totalSupply)
const totalSupply = await tokenContract.methods.totalSupply().call({}, foreignDelayedBlockNumber)
const homeBalanceBN = new BN(homeBalance).plus(lateHomeConfirmationsTotalValue)
const foreignTotalSupplyBN = new BN(totalSupply).plus(lateForeignConfirmationsTotalValue)
const diff = homeBalanceBN.minus(foreignTotalSupplyBN).toString(10)
logger.debug('Done')
return {
@@ -103,21 +120,25 @@ async function main(bridgeMode) {
}
logger.debug('calling erc20Contract.methods.balanceOf')
const foreignErc20Balance = await erc20Contract.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
const foreignErc20Balance = await erc20Contract.methods
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
.call({}, foreignDelayedBlockNumber)
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS)
logger.debug('calling homeBridge.methods.blockRewardContract')
const blockRewardAddress = await homeBridge.methods.blockRewardContract().call()
const blockRewardContract = new web3Home.eth.Contract(BLOCK_REWARD_ABI, blockRewardAddress)
const homeBlockNumber = await getHomeBlockNumber()
logger.debug('calling blockReward.methods.mintedTotally')
const mintedCoins = await blockRewardContract.methods.mintedTotallyByBridge(COMMON_HOME_BRIDGE_ADDRESS).call()
const mintedCoins = await blockRewardContract.methods
.mintedTotallyByBridge(COMMON_HOME_BRIDGE_ADDRESS)
.call({}, homeBlockNumber)
logger.debug('calling homeBridge.methods.totalBurntCoins')
const burntCoins = await homeBridge.methods.totalBurntCoins().call()
const burntCoins = await homeBridge.methods.totalBurntCoins().call({}, homeDelayedBlockNumber)
const mintedCoinsBN = new BN(mintedCoins)
const burntCoinsBN = new BN(burntCoins)
const totalSupplyBN = mintedCoinsBN.minus(burntCoinsBN)
const foreignErc20BalanceBN = new BN(foreignErc20Balance)
const foreignErc20BalanceBN = new BN(foreignErc20Balance).plus(lateForeignConfirmationsTotalValue)
const investedAmountInDaiBN = new BN(investedAmountInDai)
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)

View File

@@ -1,18 +1,36 @@
require('dotenv').config()
const eventsInfo = require('./utils/events')
const BN = require('bignumber.js')
const Web3Utils = require('web3').utils
const {
eventWithoutReference,
deliveredMsgNotProcessed,
unclaimedHomeToForeignRequests,
manuallyProcessedAMBHomeToForeignRequests
} = require('./utils/message')
const { BRIDGE_MODES } = require('../commons')
const { getHomeTxSender } = require('./utils/web3Cache')
async function main(bridgeMode) {
const {
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST,
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST,
MONITOR_HOME_TO_FOREIGN_CHECK_SENDER
} = process.env
async function main(bridgeMode, eventsInfo) {
const {
homeToForeignConfirmations,
homeToForeignRequests,
foreignToHomeConfirmations,
foreignToHomeRequests
} = await eventsInfo(bridgeMode)
} = eventsInfo
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
const onlyInHomeRequests = homeToForeignRequests.filter(deliveredMsgNotProcessed(homeToForeignConfirmations))
const manuallyProcessedRequests = onlyInHomeRequests.filter(manuallyProcessedAMBHomeToForeignRequests())
return {
fromHomeToForeignDiff: homeToForeignRequests.length - homeToForeignConfirmations.length,
fromHomeToForeignDiff:
homeToForeignRequests.length - homeToForeignConfirmations.length - manuallyProcessedRequests.length,
fromHomeToForeignPBUDiff: manuallyProcessedRequests.length,
fromForeignToHomeDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length,
home: {
toForeign: homeToForeignRequests.length,
@@ -24,9 +42,26 @@ async function main(bridgeMode) {
}
}
} else {
return {
const stats = {
depositsDiff: homeToForeignRequests.length - homeToForeignConfirmations.length,
withdrawalDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length,
withdrawalDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length
}
if (MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST || MONITOR_HOME_TO_FOREIGN_BLOCK_LIST) {
const onlyInHomeDeposits = homeToForeignRequests.filter(eventWithoutReference(homeToForeignConfirmations))
if (MONITOR_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
for (let i = 0; i < onlyInHomeDeposits.length; i++) {
onlyInHomeDeposits[i].sender = await getHomeTxSender(onlyInHomeDeposits[i].transactionHash)
}
}
const unclaimedPool = onlyInHomeDeposits.filter(unclaimedHomeToForeignRequests())
stats.depositsDiff -= unclaimedPool.length
stats.unclaimedDiff = unclaimedPool.length
stats.unclaimedBalance = Web3Utils.fromWei(BN.sum(0, ...unclaimedPool.map(e => e.value)).toFixed())
}
return {
...stats,
home: {
deposits: homeToForeignRequests.length,
withdrawals: foreignToHomeConfirmations.length

View File

@@ -1,10 +1,13 @@
require('dotenv').config()
const express = require('express')
const cors = require('cors')
const { readFile } = require('./utils/file')
const app = express()
const bridgeRouter = express.Router({ mergeParams: true })
app.use(cors())
app.get('/favicon.ico', (req, res) => res.sendStatus(204))
app.use('/:bridgeName', bridgeRouter)
@@ -47,6 +50,16 @@ bridgeRouter.get('/alerts', async (req, res, next) => {
}
})
bridgeRouter.get('/mediators', async (req, res, next) => {
try {
const results = await readFile(`./responses/${req.params.bridgeName}/mediators.json`)
res.json(results)
} catch (e) {
// this will eventually be handled by your error handling middleware
next(e)
}
})
bridgeRouter.get('/stuckTransfers', async (req, res, next) => {
try {
const results = await readFile(`./responses/${req.params.bridgeName}/stuckTransfers.json`)

View File

@@ -14,14 +14,15 @@
"author": "",
"license": "ISC",
"dependencies": {
"bignumber.js": "^6.0.0",
"bignumber.js": "^9.0.1",
"cors": "^2.8.5",
"dotenv": "^5.0.1",
"express": "^4.16.3",
"node-fetch": "^2.1.2",
"web3": "1.0.0-beta.34"
"web3": "^1.3.0"
},
"engines": {
"node": ">=8.9"
"node": ">= 10.18"
},
"devDependencies": {
"chai": "^4.2.0"

View File

@@ -2,17 +2,21 @@
CONFIGDIR="configs"
RESPONSESDIR="responses"
ACLDIR="access-lists"
ALLOWANCEFILE="allowance_list.txt"
BLOCKFILE="block_list.txt"
CACHEDIR="cache"
IMAGETAG="latest"
cd $(dirname $0)/..
if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
tstart=`date +"%s"`
for file in ${CONFIGDIR}/*.env
do
echo "${file} handling..."
bridgename=`source ${file} && echo ${MONITOR_BRIDGE_NAME}`
reportdir=${RESPONSESDIR}"/"${bridgename}
if [ ! -d ${reportdir} ]; then
@@ -26,10 +30,18 @@ if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
fi
done
alist=`source ${file} && echo ${MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST}`
blist=`source ${file} && echo ${MONITOR_HOME_TO_FOREIGN_BLOCK_LIST}`
al_param="$(pwd)/${ACLDIR}/${bridgename}/${ALLOWANCEFILE}:/mono/monitor/access-lists/allowance_list.txt"
bl_param="$(pwd)/${ACLDIR}/${bridgename}/${BLOCKFILE}:/mono/monitor/access-lists/block_list.txt"
containername=${bridgename}"-checker"
docker container stats --no-stream ${containername} 2>/dev/null 1>&2
if [ ! "$?" == "0" ]; then
mkdir -p "$(pwd)/$CACHEDIR/$bridgename"
docker run --rm --env-file $file -v $(pwd)/${RESPONSESDIR}:/mono/monitor/responses \
${alist:+"-v"} ${alist:+"$al_param"} ${blist:+"-v"} ${blist:+"$bl_param"} \
-v $(pwd)/${CACHEDIR}/${bridgename}:/mono/monitor/cache/${bridgename} \
--name ${containername} poanetwork/tokenbridge-monitor:${IMAGETAG} \
/bin/bash -c 'yarn check-all'
shasum -a 256 -s -c ${checksumfile}
@@ -46,15 +58,15 @@ if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
else
echo "${containername} have not finished yet" >&2
fi
rm ${checksumfile}
echo "========================================"
done
tend=`date +"%s"`
tdiff=`expr ${tend} - ${tstart}`
echo "Total time to run: ${tdiff}"
else
echo "Monitor is not running, skipping checks."
fi
fi

View File

@@ -1,14 +1,12 @@
require('dotenv').config()
const Web3 = require('web3')
const logger = require('./logger')('stuckTransfers.js')
const { FOREIGN_V1_ABI } = require('../commons/abis')
const { web3Foreign, getForeignBlockNumber } = require('./utils/web3')
const { getPastEvents } = require('./utils/web3Cache')
const { COMMON_FOREIGN_RPC_URL, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
const { COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
const MONITOR_FOREIGN_START_BLOCK = Number(process.env.MONITOR_FOREIGN_START_BLOCK) || 0
const foreignProvider = new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)
const web3Foreign = new Web3(foreignProvider)
const ABITransferWithoutData = [
{
anonymous: false,
@@ -64,38 +62,39 @@ const ABIWithData = [
}
]
function compareTransfers(transfersNormal) {
return withData => {
return (
transfersNormal.filter(normal => {
return normal.transactionHash === withData.transactionHash
}).length === 0
)
}
function transferWithoutCallback(transfersNormal) {
const txHashes = new Set()
transfersNormal.forEach(transfer => txHashes.add(transfer.transactionHash))
return withData => !txHashes.has(withData.transactionHash)
}
async function main() {
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_V1_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
logger.debug('calling foreignBridge.methods.erc677token')
const erc20Address = await foreignBridge.methods.erc677token().call()
const tokenContract = new web3Foreign.eth.Contract(ABITransferWithoutData, erc20Address)
const tokenContractWithData = new web3Foreign.eth.Contract(ABIWithData, erc20Address)
logger.debug('getting last block number')
const foreignBlockNumber = await getForeignBlockNumber()
const foreignConfirmations = await foreignBridge.methods.requiredBlockConfirmations().call()
const foreignDelayedBlockNumber = foreignBlockNumber - foreignConfirmations
logger.debug('calling tokenContract.getPastEvents Transfer')
const transfersNormal = await tokenContract.getPastEvents('Transfer', {
filter: {
to: COMMON_FOREIGN_BRIDGE_ADDRESS
const options = {
event: 'Transfer',
options: {
filter: {
to: COMMON_FOREIGN_BRIDGE_ADDRESS
}
},
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: 'latest'
})
toBlock: foreignBlockNumber,
chain: 'foreign',
safeToBlock: foreignDelayedBlockNumber
}
const transfersNormal = await getPastEvents(tokenContract, options)
logger.debug('calling tokenContractWithData.getPastEvents Transfer')
const transfersWithData = await tokenContractWithData.getPastEvents('Transfer', {
filter: {
to: COMMON_FOREIGN_BRIDGE_ADDRESS
},
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: 'latest'
})
const stuckTransfers = transfersNormal.filter(compareTransfers(transfersWithData))
const transfersWithData = await getPastEvents(tokenContractWithData, options)
const stuckTransfers = transfersNormal.filter(transferWithoutCallback(transfersWithData))
logger.debug('Done')
return {
stuckTransfers,

View File

@@ -1,11 +0,0 @@
const { toBN } = require('web3').utils
const getBlockNumberCall = web3 => web3.eth.getBlockNumber()
async function getBlockNumber(web3Home, web3Foreign) {
return (await Promise.all([web3Home, web3Foreign].map(getBlockNumberCall))).map(toBN)
}
module.exports = {
getBlockNumber
}

View File

@@ -1,6 +1,4 @@
require('dotenv').config()
const Web3 = require('web3')
const { toBN } = require('web3').utils
const logger = require('../logger')('eventsUtils')
const {
BRIDGE_MODES,
@@ -11,7 +9,6 @@ const {
ERC20_ABI,
ERC677_BRIDGE_TOKEN_ABI,
getTokenType,
getPastEvents,
ZERO_ADDRESS,
OLD_AMB_USER_REQUEST_FOR_SIGNATURE_ABI,
OLD_AMB_USER_REQUEST_FOR_AFFIRMATION_ABI
@@ -19,24 +16,12 @@ const {
const { normalizeEventInformation } = require('./message')
const { filterTransferBeforeES } = require('./tokenUtils')
const { writeFile, readCacheFile } = require('./file')
const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./web3')
const { getPastEvents } = require('./web3Cache')
const {
COMMON_HOME_RPC_URL,
COMMON_FOREIGN_RPC_URL,
COMMON_HOME_BRIDGE_ADDRESS,
COMMON_FOREIGN_BRIDGE_ADDRESS,
MONITOR_CACHE_EVENTS
} = process.env
const MONITOR_HOME_START_BLOCK = toBN(Number(process.env.MONITOR_HOME_START_BLOCK) || 0)
const MONITOR_FOREIGN_START_BLOCK = toBN(Number(process.env.MONITOR_FOREIGN_START_BLOCK) || 0)
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
const foreignProvider = new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)
const web3Foreign = new Web3(foreignProvider)
const { getBlockNumber } = require('./contract')
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS, MONITOR_CACHE_EVENTS } = 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 cacheFilePath = '/tmp/cachedEvents.json'
async function main(mode) {
@@ -73,7 +58,13 @@ async function main(mode) {
}
logger.debug('getting last block numbers')
const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
const homeBlockNumber = await getHomeBlockNumber()
const foreignBlockNumber = await getForeignBlockNumber()
const homeConfirmations = await homeBridge.methods.requiredBlockConfirmations().call()
const foreignConfirmations = await foreignBridge.methods.requiredBlockConfirmations().call()
const homeDelayedBlockNumber = homeBlockNumber - homeConfirmations
const foreignDelayedBlockNumber = foreignBlockNumber - foreignConfirmations
let homeToForeignRequests = []
let foreignToHomeRequests = []
let homeMigrationBlock = MONITOR_HOME_START_BLOCK
@@ -90,22 +81,24 @@ async function main(mode) {
homeToForeignRequests = (await getPastEvents(oldHomeBridge, {
event: 'UserRequestForSignature',
fromBlock: MONITOR_HOME_START_BLOCK,
toBlock: homeBlockNumber
toBlock: homeDelayedBlockNumber,
chain: 'home'
})).map(normalizeEvent)
logger.debug(`found ${homeToForeignRequests.length} events`)
if (homeToForeignRequests.length > 0) {
homeMigrationBlock = toBN(Math.max(...homeToForeignRequests.map(x => x.blockNumber)))
homeMigrationBlock = Math.max(...homeToForeignRequests.map(x => x.blockNumber))
}
logger.debug("calling oldForeignBridge.getPastEvents('UserRequestForAffirmation(bytes)')")
foreignToHomeRequests = (await getPastEvents(oldForeignBridge, {
event: 'UserRequestForAffirmation',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: foreignBlockNumber
toBlock: foreignDelayedBlockNumber,
chain: 'foreign'
})).map(normalizeEvent)
logger.debug(`found ${foreignToHomeRequests.length} events`)
if (foreignToHomeRequests.length > 0) {
foreignMigrationBlock = toBN(Math.max(...foreignToHomeRequests.map(x => x.blockNumber)))
foreignMigrationBlock = Math.max(...foreignToHomeRequests.map(x => x.blockNumber))
}
}
@@ -113,7 +106,8 @@ async function main(mode) {
const homeToForeignRequestsNew = (await getPastEvents(homeBridge, {
event: v1Bridge ? 'Deposit' : 'UserRequestForSignature',
fromBlock: homeMigrationBlock,
toBlock: homeBlockNumber
toBlock: homeDelayedBlockNumber,
chain: 'home'
})).map(normalizeEvent)
homeToForeignRequests = [...homeToForeignRequests, ...homeToForeignRequestsNew]
@@ -121,21 +115,26 @@ async function main(mode) {
const homeToForeignConfirmations = (await getPastEvents(foreignBridge, {
event: v1Bridge ? 'Deposit' : 'RelayedMessage',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: foreignBlockNumber
toBlock: foreignBlockNumber,
chain: 'foreign',
safeToBlock: foreignDelayedBlockNumber
})).map(normalizeEvent)
logger.debug("calling homeBridge.getPastEvents('AffirmationCompleted')")
const foreignToHomeConfirmations = (await getPastEvents(homeBridge, {
event: v1Bridge ? 'Withdraw' : 'AffirmationCompleted',
fromBlock: MONITOR_HOME_START_BLOCK,
toBlock: homeBlockNumber
toBlock: homeBlockNumber,
chain: 'home',
safeToBlock: homeDelayedBlockNumber
})).map(normalizeEvent)
logger.debug("calling foreignBridge.getPastEvents('UserRequestForAffirmation')")
const foreignToHomeRequestsNew = (await getPastEvents(foreignBridge, {
event: v1Bridge ? 'Withdraw' : 'UserRequestForAffirmation',
fromBlock: foreignMigrationBlock,
toBlock: foreignBlockNumber
toBlock: foreignDelayedBlockNumber,
chain: 'foreign'
})).map(normalizeEvent)
foreignToHomeRequests = [...foreignToHomeRequests, ...foreignToHomeRequestsNew]
@@ -144,10 +143,11 @@ async function main(mode) {
let transferEvents = (await getPastEvents(erc20Contract, {
event: 'Transfer',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: foreignBlockNumber,
toBlock: foreignDelayedBlockNumber,
options: {
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
}
},
chain: 'foreign'
})).map(normalizeEvent)
let directTransfers = transferEvents
@@ -158,7 +158,9 @@ async function main(mode) {
const tokensSwappedEvents = await getPastEvents(foreignBridge, {
event: 'TokensSwapped',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: foreignBlockNumber
toBlock: foreignBlockNumber,
chain: 'foreign',
safeToBlock: foreignDelayedBlockNumber
})
// Get token swap events emitted by foreign bridge
@@ -194,10 +196,11 @@ async function main(mode) {
const halfDuplexTransferEvents = (await getPastEvents(halfDuplexTokenContract, {
event: 'Transfer',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: foreignBlockNumber,
toBlock: foreignDelayedBlockNumber,
options: {
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
}
},
chain: 'foreign'
})).map(normalizeEvent)
// Remove events after the ES
@@ -232,7 +235,9 @@ async function main(mode) {
foreignToHomeConfirmations,
foreignToHomeRequests,
isExternalErc20,
bridgeMode
bridgeMode,
homeDelayedBlockNumber,
foreignDelayedBlockNumber
}
if (MONITOR_CACHE_EVENTS === 'true') {

Some files were not shown because too many files have changed in this diff Show More