Compare commits

...

20 Commits

Author SHA1 Message Date
Kirill Fedoseev
c02ecad8fd Fix isBridgeContract handler 2020-10-02 21:21:02 +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
94 changed files with 884 additions and 1232 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

@@ -11,6 +11,9 @@
contracts/test contracts/test
contracts/build contracts/build
oracle/test oracle/test
monitor/test
monitor/responses
commons/test
oracle/**/*.png oracle/**/*.png
oracle/**/*.jpg oracle/**/*.jpg
audit audit

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

@@ -0,0 +1,231 @@
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
- run: git submodule status > submodule.status
- id: get_cache_key
run: 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 }}
- 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 }}
- 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 }}
- 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
- run: |
git submodule status > submodule.status
echo "::set-env name=E2E_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}"
echo "::set-env name=ORACLE_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}"
echo "::set-env name=MONITOR_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}"
echo "::set-env name=UI_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}"
echo "::set-env name=ALM_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}"
- 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
- run: echo "::set-env name=MOLECULE_RUNNER_TAG::${{ hashFiles('./deployment-e2e/Dockerfile') }}"
- 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
- run: |
git submodule status > submodule.status
echo "::set-env name=E2E_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}"
echo "::set-env name=ORACLE_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}"
echo "::set-env name=MONITOR_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}"
echo "::set-env name=UI_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}"
echo "::set-env name=ALM_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}"
- if: ${{ matrix.use-cache }}
uses: actions/cache@v2
id: cache-repo
with:
path: |
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- run: ${{ !matrix.use-cache || steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
deployment:
runs-on: ubuntu-latest
needs:
- build-molecule-runner
strategy:
fail-fast: false
matrix:
task: [oracle, ui, monitor, multiple, repo]
steps:
- uses: actions/checkout@v2
with:
submodules: true
- run: echo "::set-env name=MOLECULE_RUNNER_TAG::${{ hashFiles('./deployment-e2e/Dockerfile') }}"
- run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- run: deployment-e2e/molecule.sh ${{ matrix.task }}
ultimate:
runs-on: ubuntu-latest
needs:
- initialize
- build-e2e-images
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags')
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'
- task: erc-to-native
ui-e2e-grep: 'ERC TO NATIVE'
- task: native-to-erc
ui-e2e-grep: 'NATIVE TO ERC'
- task: amb-stake-erc-to-erc
ui-e2e-grep: 'AMB-STAKE-ERC-TO-ERC'
steps:
- uses: actions/checkout@v2
with:
submodules: true
- run: |
git submodule status > submodule.status
echo "::set-env name=E2E_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}"
echo "::set-env name=ORACLE_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}"
echo "::set-env name=MONITOR_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}"
echo "::set-env name=UI_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}"
echo "::set-env name=ALM_TAG::${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}"
echo "::set-env name=MOLECULE_RUNNER_TAG::${{ hashFiles('./deployment-e2e/Dockerfile') }}"
- uses: actions/cache@v2
id: cache-repo
with:
path: |
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- run: ${{ steps.cache-repo.outputs.cache-hit }} && e2e-commons/up.sh deploy blocks
- run: docker-compose -f ./e2e-commons/docker-compose.yml pull oracle
- run: deployment-e2e/molecule.sh ultimate-${{ matrix.task }}
- run: sudo chown -R $USER:docker /var/run/docker.sock
- if: ${{ matrix.ui-e2e-grep }}
run: cd ui-e2e && xvfb-run yarn mocha -g "${{ matrix.ui-e2e-grep }}" -b ./test.js
- if: ${{ !matrix.ui-e2e-grep }}
run: docker-compose -f ./e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run ${{ matrix.task }}

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_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_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_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_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_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 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 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_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_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 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,10 @@ 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_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_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_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`
## UI configuration ## UI configuration

View File

@@ -1,4 +1,4 @@
FROM node:8 as contracts FROM node:10 as contracts
WORKDIR /mono WORKDIR /mono
@@ -11,11 +11,12 @@ COPY ./contracts/truffle-config.js ./
COPY ./contracts/contracts ./contracts COPY ./contracts/contracts ./contracts
RUN npm run compile RUN npm run compile
FROM node:8 FROM node:10
WORKDIR /mono WORKDIR /mono
COPY package.json . COPY package.json .
COPY --from=contracts /mono/contracts/build ./contracts/build COPY --from=contracts /mono/contracts/build ./contracts/build
COPY commons/package.json ./commons/
COPY oracle-e2e/package.json ./oracle-e2e/ COPY oracle-e2e/package.json ./oracle-e2e/
COPY monitor-e2e/package.json ./monitor-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) [![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) [![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 ## 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" "eslint-plugin-jest": "^23.18.0"
}, },
"engines": { "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 WORKDIR /mono

View File

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

View File

@@ -2,7 +2,8 @@
export const TRANSACTION_STATUS_DESCRIPTION: { [key: string]: string } = { export const TRANSACTION_STATUS_DESCRIPTION: { [key: string]: string } = {
SUCCESS_MULTIPLE_MESSAGES: 'was initiated %t and contains several bridge messages. Specify one of them:', SUCCESS_MULTIPLE_MESSAGES: 'was initiated %t and contains several bridge messages. Specify one of them:',
SUCCESS_ONE_MESSAGE: 'was initiated %t', 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', FAILED: 'failed %t',
NOT_FOUND: 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.' '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.'
@@ -58,7 +59,7 @@ export const CONFIRMATIONS_STATUS_DESCRIPTION_HOME: { [key: string]: string } =
EXECUTION_PENDING: 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.', '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: 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. 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. If the issue still persists contact to the validators by messaging on %linkhttps://forum.poa.network/c/support',
FAILED: 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', '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: PENDING:

View File

@@ -351,7 +351,7 @@ describe('getConfirmationsForTx', () => {
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
]) ])
) )
expect(setResult.mock.calls[1][0]).toEqual( expect(setResult.mock.calls[1][0]).toEqual(

View File

@@ -51,6 +51,9 @@ export const isBridgeContract = async (contract: Contract, allowedModes?: string
} }
return allowedModes.includes(mode) return allowedModes.includes(mode)
} catch (e) { } catch (e) {
if (e.message.includes("Returned values aren't valid")) {
return false return false
} }
throw e
}
} }

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 HOME_AMB_ABI = require('../contracts/build/contracts/HomeAMB').abi
const FOREIGN_AMB_ABI = require('../contracts/build/contracts/ForeignAMB').abi const FOREIGN_AMB_ABI = require('../contracts/build/contracts/ForeignAMB').abi
const BOX_ABI = require('../contracts/build/contracts/Box').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 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 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 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_AFFIRMATION_ABI,
OLD_AMB_USER_REQUEST_FOR_SIGNATURE_ABI, OLD_AMB_USER_REQUEST_FOR_SIGNATURE_ABI,
BOX_ABI, BOX_ABI,
SAI_TOP,
HOME_STAKE_ERC_TO_ERC_ABI, HOME_STAKE_ERC_TO_ERC_ABI,
FOREIGN_STAKE_ERC_TO_ERC_ABI FOREIGN_STAKE_ERC_TO_ERC_ABI
} }

View File

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

View File

@@ -1,7 +1,10 @@
const { toWei, toBN } = require('web3-utils') const { toWei, toBN } = require('web3-utils')
const { GasPriceOracle } = require('gas-price-oracle')
const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants') const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants')
const { REWARDABLE_VALIDATORS_ABI } = require('./abis') const { REWARDABLE_VALIDATORS_ABI } = require('./abis')
const gasPriceOracle = new GasPriceOracle()
function decodeBridgeMode(bridgeModeHash) { function decodeBridgeMode(bridgeModeHash) {
switch (bridgeModeHash) { switch (bridgeModeHash) {
case '0x92a8d7fe': case '0x92a8d7fe':
@@ -235,8 +238,13 @@ const normalizeGasPrice = (oracleGasPrice, factor, limits = null) => {
// we use built-in 'fetch' on browser side, and `node-fetch` package in Node. // we use built-in 'fetch' on browser side, and `node-fetch` package in Node.
const gasPriceFromSupplier = async (fetchFn, options = {}) => { const gasPriceFromSupplier = async (fetchFn, options = {}) => {
try { try {
let json
if (fetchFn) {
const response = await fetchFn() const response = await fetchFn()
const json = await response.json() json = await response.json()
} else {
json = await gasPriceOracle.fetchGasPricesOffChain()
}
const oracleGasPrice = json[options.speedType] const oracleGasPrice = json[options.speedType]
if (!oracleGasPrice) { if (!oracleGasPrice) {

View File

@@ -13,7 +13,7 @@ git push
Alternatively, if there are no changes except the playbooks, you can use the `master` branch: 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. 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 ## Run the tests
``` ```
CIRCLE_BRANCH=master ./molecule.sh <scenario_name> ./molecule.sh <scenario_name>
``` ```
Available scenarios: Available scenarios:

View File

@@ -2,7 +2,11 @@
cd ./e2e-commons cd ./e2e-commons
set -e # exit when any command fails 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 docker network create --driver bridge ultimate || true
while [ "$1" != "" ]; do while [ "$1" != "" ]; do
docker-compose run molecule_runner /bin/bash -c "molecule test --scenario-name $1" docker-compose run molecule_runner /bin/bash -c "molecule test --scenario-name $1"

View File

@@ -6,12 +6,10 @@
- name: stop the service - name: stop the service
shell: service poabridge stop shell: service poabridge stop
- name: Build current oracle image - name: ReTag current oracle image
shell: docker build -t oracle:ultimate-testing --file oracle/Dockerfile . shell: docker tag $(docker images --format '{{ '{{' }}.Repository{{ '}}' }}:{{ '{{' }}.Tag{{ '}}' }}' | grep -m 1 tokenbridge-e2e-oracle) oracle:ultimate-testing
delegate_to: 127.0.0.1 delegate_to: 127.0.0.1
become: false become: false
args:
chdir: "{{ lookup('env', 'PWD') }}/.."
- name: Replace oracle image - name: Replace oracle image
replace: replace:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04

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_ALLOW_HTTP_FOR_RPC=yes
ORACLE_HOME_START_BLOCK=1 ORACLE_HOME_START_BLOCK=1
ORACLE_FOREIGN_START_BLOCK=1 ORACLE_FOREIGN_START_BLOCK=1
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt

View File

@@ -15,6 +15,10 @@
"address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B", "address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B",
"privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1" "privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1"
}, },
"blockedUser": {
"address": "0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04",
"privateKey": "0x65df4ea787916f6ed9660f0b0fe36858a65735ad0dcd34527497f4ce32e53883"
},
"validator": { "validator": {
"address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b", "address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b",
"privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" "privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
@@ -50,8 +54,6 @@
"home": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda", "home": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
"foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda", "foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
"foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9", "foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9",
"halfDuplexToken": "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359",
"saiTop": "0x9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3",
"chaiToken": "0x06af07097c9eeb7fd685c692751d5c66db49c215", "chaiToken": "0x06af07097c9eeb7fd685c692751d5c66db49c215",
"ui": "http://localhost:3002", "ui": "http://localhost:3002",
"monitor": "http://monitor-erc20-native:3012/bridge" "monitor": "http://monitor-erc20-native:3012/bridge"

View File

@@ -1,5 +1,4 @@
--- version: '3.8'
version: '3'
networks: networks:
ultimate: ultimate:
external: true external: true
@@ -27,7 +26,7 @@ services:
networks: networks:
- ultimate - ultimate
oracle: oracle:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-oracle:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-oracle:${ORACLE_TAG:-local}
build: build:
context: .. context: ..
dockerfile: oracle/Dockerfile dockerfile: oracle/Dockerfile
@@ -38,10 +37,7 @@ services:
networks: networks:
- ultimate - ultimate
oracle-erc20: oracle-erc20:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-oracle:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-oracle:${ORACLE_TAG:-local}
build:
context: ..
dockerfile: oracle/Dockerfile
env_file: ../e2e-commons/components-envs/oracle-erc20.env env_file: ../e2e-commons/components-envs/oracle-erc20.env
environment: environment:
- NODE_ENV=production - NODE_ENV=production
@@ -49,21 +45,18 @@ services:
networks: networks:
- ultimate - ultimate
oracle-erc20-native: oracle-erc20-native:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-oracle:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-oracle:${ORACLE_TAG:-local}
build:
context: ..
dockerfile: oracle/Dockerfile
env_file: ../e2e-commons/components-envs/oracle-erc20-native.env env_file: ../e2e-commons/components-envs/oracle-erc20-native.env
environment: environment:
- NODE_ENV=production - NODE_ENV=production
command: "true" 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: networks:
- ultimate - ultimate
oracle-amb: oracle-amb:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-oracle:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-oracle:${ORACLE_TAG:-local}
build:
context: ..
dockerfile: oracle/Dockerfile
env_file: ../e2e-commons/components-envs/oracle-amb.env env_file: ../e2e-commons/components-envs/oracle-amb.env
environment: environment:
- NODE_ENV=production - NODE_ENV=production
@@ -71,7 +64,7 @@ services:
networks: networks:
- ultimate - ultimate
ui: ui:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-ui:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-ui:${UI_TAG:-local}
build: build:
context: .. context: ..
dockerfile: ui/Dockerfile dockerfile: ui/Dockerfile
@@ -85,8 +78,8 @@ services:
context: .. context: ..
dockerfile: e2e-commons/Dockerfile.ui dockerfile: e2e-commons/Dockerfile.ui
args: args:
DOCKER_LOGIN: ${DOCKER_LOGIN} DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
CIRCLE_BRANCH: ${CIRCLE_BRANCH} UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20.env DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20.env
command: "true" command: "true"
networks: networks:
@@ -96,8 +89,8 @@ services:
context: .. context: ..
dockerfile: e2e-commons/Dockerfile.ui dockerfile: e2e-commons/Dockerfile.ui
args: args:
DOCKER_LOGIN: ${DOCKER_LOGIN} DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
CIRCLE_BRANCH: ${CIRCLE_BRANCH} UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20-native.env DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20-native.env
command: "true" command: "true"
networks: networks:
@@ -107,14 +100,14 @@ services:
context: .. context: ..
dockerfile: e2e-commons/Dockerfile.ui dockerfile: e2e-commons/Dockerfile.ui
args: args:
DOCKER_LOGIN: ${DOCKER_LOGIN} DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
CIRCLE_BRANCH: ${CIRCLE_BRANCH} UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env DOT_ENV_PATH: e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env
command: "true" command: "true"
networks: networks:
- ultimate - ultimate
alm: alm:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-alm:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-alm:${ALM_TAG:-local}
build: build:
context: .. context: ..
dockerfile: alm/Dockerfile dockerfile: alm/Dockerfile
@@ -124,7 +117,7 @@ services:
networks: networks:
- ultimate - ultimate
monitor: monitor:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-monitor:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-monitor:${MONITOR_TAG:-local}
build: build:
context: .. context: ..
dockerfile: monitor/Dockerfile dockerfile: monitor/Dockerfile
@@ -135,10 +128,7 @@ services:
networks: networks:
- ultimate - ultimate
monitor-erc20: monitor-erc20:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-monitor:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-monitor:${MONITOR_TAG:-local}
build:
context: ..
dockerfile: monitor/Dockerfile
env_file: ../e2e-commons/components-envs/monitor-erc20.env env_file: ../e2e-commons/components-envs/monitor-erc20.env
entrypoint: yarn check-and-start entrypoint: yarn check-and-start
ports: ports:
@@ -146,10 +136,7 @@ services:
networks: networks:
- ultimate - ultimate
monitor-erc20-native: monitor-erc20-native:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-monitor:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-monitor:${MONITOR_TAG:-local}
build:
context: ..
dockerfile: monitor/Dockerfile
env_file: ../e2e-commons/components-envs/monitor-erc20-native.env env_file: ../e2e-commons/components-envs/monitor-erc20-native.env
entrypoint: yarn check-and-start entrypoint: yarn check-and-start
ports: ports:
@@ -157,10 +144,7 @@ services:
networks: networks:
- ultimate - ultimate
monitor-amb: monitor-amb:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-monitor:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-monitor:${MONITOR_TAG:-local}
build:
context: ..
dockerfile: monitor/Dockerfile
env_file: ../e2e-commons/components-envs/monitor-amb.env env_file: ../e2e-commons/components-envs/monitor-amb.env
entrypoint: yarn check-and-start entrypoint: yarn check-and-start
ports: ports:
@@ -168,7 +152,7 @@ services:
networks: networks:
- ultimate - ultimate
e2e: e2e:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-e2e:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-e2e:${E2E_TAG:-local}
build: build:
context: .. context: ..
dockerfile: Dockerfile.e2e dockerfile: Dockerfile.e2e
@@ -176,15 +160,12 @@ services:
networks: networks:
- ultimate - ultimate
blocks: blocks:
image: ${DOCKER_LOGIN}/tokenbridge-e2e-e2e:${CIRCLE_BRANCH} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-e2e:${E2E_TAG:-local}
build:
context: ..
dockerfile: Dockerfile.e2e
entrypoint: node e2e-commons/scripts/blocks.js entrypoint: node e2e-commons/scripts/blocks.js
networks: networks:
- ultimate - ultimate
molecule_runner: 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: build:
context: .. context: ..
dockerfile: deployment-e2e/Dockerfile dockerfile: deployment-e2e/Dockerfile

View File

@@ -4,7 +4,13 @@ set -e # exit when any command fails
./down.sh ./down.sh
docker-compose build parity1 parity2 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 network create --driver bridge ultimate || true
docker-compose up -d parity1 parity2 e2e docker-compose up -d parity1 parity2 e2e
@@ -22,8 +28,6 @@ startValidator () {
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: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: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: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-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: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:collected-signatures
@@ -57,8 +61,6 @@ while [ "$1" != "" ]; do
docker-compose run -d oracle-erc20-native yarn watcher:collected-signatures 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:affirmation-request
docker-compose run -d oracle-erc20-native yarn watcher:transfer 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-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:signature-request
docker-compose run -d oracle-amb yarn watcher:collected-signatures docker-compose run -d oracle-amb yarn watcher:collected-signatures
@@ -111,26 +113,6 @@ while [ "$1" != "" ]; do
docker-compose up -d monitor monitor-erc20 monitor-erc20-native monitor-amb docker-compose up -d monitor monitor-erc20 monitor-erc20-native monitor-amb
fi 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
fi
if [ "$1" == "alm-e2e" ]; then if [ "$1" == "alm-e2e" ]; then
docker-compose up -d redis rabbit docker-compose up -d redis rabbit

View File

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

View File

@@ -1,7 +1,7 @@
while true; do while true; do
sleep 3 sleep 5
COMPOSE_INTERACTIVE_NO_CLI=1 nohup docker-compose -f ../e2e-commons/docker-compose.yml exec monitor yarn check-all docker-compose -f ../e2e-commons/docker-compose.yml exec -d monitor yarn check-all
COMPOSE_INTERACTIVE_NO_CLI=1 nohup docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20 yarn check-all docker-compose -f ../e2e-commons/docker-compose.yml exec -d 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 docker-compose -f ../e2e-commons/docker-compose.yml exec -d 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 -d monitor-amb yarn check-all
done done

View File

@@ -37,6 +37,9 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => { await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`)) ;({ data } = await axios.get(`${baseUrl}`))
if (!data.foreign) {
return false
}
const { erc20Balance, investedErc20Balance } = data.foreign const { erc20Balance, investedErc20Balance } = data.foreign
return data.balanceDiff === 0.01 && erc20Balance === '0.01' && investedErc20Balance === undefined return data.balanceDiff === 0.01 && erc20Balance === '0.01' && investedErc20Balance === undefined
}) })
@@ -58,6 +61,9 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => { await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`)) ;({ data } = await axios.get(`${baseUrl}`))
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
if (!data.foreign) {
return false
}
return ( return (
data.balanceDiff === 0.02 && data.balanceDiff === 0.02 &&
erc20Balance === '0.02' && erc20Balance === '0.02' &&
@@ -72,6 +78,9 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => { await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`)) ;({ data } = await axios.get(`${baseUrl}`))
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
if (!data.foreign) {
return false
}
return ( return (
data.balanceDiff === 0.02 && data.balanceDiff === 0.02 &&
erc20Balance === '0.01' && erc20Balance === '0.01' &&
@@ -86,6 +95,9 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => { await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`)) ;({ data } = await axios.get(`${baseUrl}`))
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
if (!data.foreign) {
return false
}
return ( return (
data.balanceDiff === 0.02 && data.balanceDiff === 0.02 &&
erc20Balance === '0.005' && erc20Balance === '0.005' &&

View File

@@ -8,7 +8,7 @@ const {
} = require('../commons') } = require('../commons')
const { validator } = require('../e2e-commons/constants') 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 const stopTime = Date.now() + timeout
while (Date.now() <= stopTime) { while (Date.now() <= stopTime) {
const result = await predicate() const result = await predicate()

View File

@@ -1,13 +1,15 @@
#!/bin/bash
FILES=(getBalances.json validators.json eventsStats.json alerts.json) FILES=(getBalances.json validators.json eventsStats.json alerts.json)
check_files_exist() { check_files_exist() {
rc=0 rc=0
for f in "${FILES[@]}"; do for f in "${FILES[@]}"; do
command="test -f responses/bridge/$f" command="test -f responses/bridge/$f"
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor /bin/bash -c "$command") || rc=1 (docker-compose -f ../e2e-commons/docker-compose.yml exec -T 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 -T 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 -T 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 (docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-amb /bin/bash -c "$command") || rc=1
done done
return $rc return $rc
} }

View File

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

View File

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

View File

@@ -21,7 +21,7 @@
"web3": "1.0.0-beta.34" "web3": "1.0.0-beta.34"
}, },
"engines": { "engines": {
"node": ">=8.9" "node": ">= 10.18"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.2.0" "chai": "^4.2.0"

View File

@@ -100,8 +100,13 @@ async function main(bridgeMode) {
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) { if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
logger.debug('calling foreign getGasPrices') logger.debug('calling foreign getGasPrices')
const fetchFn =
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL === 'gas-price-oracle'
? null
: () => fetch(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL)
foreignGasPrice = foreignGasPrice =
(await gasPriceFromSupplier(() => fetch(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL), foreignGasPriceSupplierOpts)) || (await gasPriceFromSupplier(fetchFn, foreignGasPriceSupplierOpts)) ||
Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK) Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK)
foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei') foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei')
foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT)) foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT))

View File

@@ -22,7 +22,7 @@
"websocket": "^1.0.28" "websocket": "^1.0.28"
}, },
"engines": { "engines": {
"node": ">= 8.9" "node": ">= 10.18"
}, },
"devDependencies": {} "devDependencies": {}
} }

View File

@@ -4,13 +4,14 @@ const promiseRetry = require('promise-retry')
const { const {
user, user,
secondUser, secondUser,
blockedUser,
validator, validator,
ercToNativeBridge, ercToNativeBridge,
homeRPC, homeRPC,
foreignRPC foreignRPC
} = require('../../e2e-commons/constants.json') } = require('../../e2e-commons/constants.json')
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_NATIVE_ABI } = require('../../commons') const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_NATIVE_ABI } = require('../../commons')
const { uniformRetry } = require('../../e2e-commons/utils') const { uniformRetry, sleep } = require('../../e2e-commons/utils')
const { setRequiredSignatures } = require('./utils') const { setRequiredSignatures } = require('./utils')
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL)) const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
@@ -22,6 +23,7 @@ const COMMON_FOREIGN_BRIDGE_ADDRESS = ercToNativeBridge.foreign
const { toBN } = foreignWeb3.utils const { toBN } = foreignWeb3.utils
homeWeb3.eth.accounts.wallet.add(user.privateKey) homeWeb3.eth.accounts.wallet.add(user.privateKey)
homeWeb3.eth.accounts.wallet.add(blockedUser.privateKey)
homeWeb3.eth.accounts.wallet.add(validator.privateKey) homeWeb3.eth.accounts.wallet.add(validator.privateKey)
foreignWeb3.eth.accounts.wallet.add(user.privateKey) foreignWeb3.eth.accounts.wallet.add(user.privateKey)
foreignWeb3.eth.accounts.wallet.add(validator.privateKey) foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
@@ -31,12 +33,7 @@ const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, CO
const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS) const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS)
describe('erc to native', () => { describe('erc to native', () => {
let halfDuplexTokenAddress
let halfDuplexToken
before(async () => { before(async () => {
halfDuplexTokenAddress = await foreignBridge.methods.halfDuplexErc20token().call()
halfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, halfDuplexTokenAddress)
// Set 2 required signatures for home bridge // Set 2 required signatures for home bridge
await setRequiredSignatures({ await setRequiredSignatures({
bridgeContract: homeBridge, bridgeContract: homeBridge,
@@ -59,85 +56,6 @@ describe('erc to native', () => {
} }
}) })
}) })
it('should not convert half duplex tokens to native tokens in home', async () => {
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
const transferValue = homeWeb3.utils.toWei('0.01')
// send tokens to foreign bridge
await halfDuplexToken.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
.send({
from: user.address,
gas: '1000000'
})
.catch(e => {
console.error(e)
})
// check that balance does not increases
await promiseRetry(async (retry, number) => {
const balance = await homeWeb3.eth.getBalance(user.address)
// retry at least 4 times to check transfer is not processed
if (toBN(balance).eq(toBN(originalBalanceOnHome)) && number < 4) {
retry()
} else {
assert(toBN(balance).eq(toBN(originalBalanceOnHome)), 'User balance should not be increased')
}
})
// send tokens to foreign bridge
await erc20Token.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
.send({
from: user.address,
gas: '1000000'
})
.catch(e => {
console.error(e)
})
// check that balance increases
await promiseRetry(async (retry, number) => {
const balance = await homeWeb3.eth.getBalance(user.address)
// retry at least 4 times to check transfer is not double processed by the two watchers
if (toBN(balance).lte(toBN(originalBalanceOnHome)) || number < 4) {
retry()
} else {
assert(
toBN(balance).eq(toBN(originalBalanceOnHome).add(toBN(transferValue))),
'User balance should be increased only by second transfer'
)
}
})
const afterTransferBalance = await homeWeb3.eth.getBalance(user.address)
// send tokens to foreign bridge
await erc20Token.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
.send({
from: user.address,
gas: '1000000'
})
.catch(e => {
console.error(e)
})
// check that balance increases
await promiseRetry(async (retry, number) => {
const balance = await homeWeb3.eth.getBalance(user.address)
// retry at least 4 times to check transfer is not double processed by the two watchers
if (toBN(balance).lte(toBN(afterTransferBalance)) || number < 4) {
retry()
} else {
assert(
toBN(balance).eq(toBN(afterTransferBalance).add(toBN(transferValue))),
'User balance should be increased'
)
}
})
})
it('should convert tokens in foreign to coins in home', async () => { it('should convert tokens in foreign to coins in home', async () => {
const balance = await erc20Token.methods.balanceOf(user.address).call() const balance = await erc20Token.methods.balanceOf(user.address).call()
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address) const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
@@ -226,6 +144,47 @@ describe('erc to native', () => {
} }
}) })
}) })
it('should not process transaction from blocked users', async () => {
const originalBalance1 = await erc20Token.methods.balanceOf(user.address).call()
const originalBalance2 = await erc20Token.methods.balanceOf(blockedUser.address).call()
// check that account has tokens in home chain
const balance1 = await homeWeb3.eth.getBalance(user.address)
const balance2 = await homeWeb3.eth.getBalance(blockedUser.address)
assert(!toBN(balance1).isZero(), 'Account should have tokens')
assert(!toBN(balance2).isZero(), 'Account should have tokens')
// send transaction to home bridge
await homeWeb3.eth.sendTransaction({
from: user.address,
to: COMMON_HOME_BRIDGE_ADDRESS,
gasPrice: '1',
gas: '1000000',
value: homeWeb3.utils.toWei('0.01')
})
// send transaction to home bridge
await homeWeb3.eth.sendTransaction({
from: blockedUser.address,
to: COMMON_HOME_BRIDGE_ADDRESS,
gasPrice: '1',
gas: '1000000',
value: homeWeb3.utils.toWei('0.01')
})
// check that balance increases
await uniformRetry(async retry => {
const balance = await erc20Token.methods.balanceOf(user.address).call()
if (toBN(balance).lte(toBN(originalBalance1))) {
retry()
}
})
await sleep(3000)
const balance = await erc20Token.methods.balanceOf(blockedUser.address).call()
assert(toBN(balance).eq(toBN(originalBalance2)), 'Bridge should not process collected signatures from blocked user')
})
it('should not invest dai when chai token is disabled', async () => { it('should not invest dai when chai token is disabled', async () => {
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()

View File

@@ -30,6 +30,10 @@ ORACLE_FOREIGN_START_BLOCK=
ORACLE_LOG_LEVEL=debug ORACLE_LOG_LEVEL=debug
ORACLE_MAX_PROCESSING_TIME=20000 ORACLE_MAX_PROCESSING_TIME=20000
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST=access-lists/allowance_list.txt
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=access-lists/block_list.txt
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER=false
#Uncomment these lines only if you are going to send transaction by testing scripts #Uncomment these lines only if you are going to send transaction by testing scripts
#USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370 #USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370
#USER_ADDRESS_PRIVATE_KEY= #USER_ADDRESS_PRIVATE_KEY=

View File

@@ -1,4 +1,4 @@
FROM node:8 as contracts FROM node:10 as contracts
WORKDIR /mono WORKDIR /mono
@@ -11,14 +11,11 @@ COPY ./contracts/truffle-config.js ./
COPY ./contracts/contracts ./contracts COPY ./contracts/contracts ./contracts
RUN npm run compile RUN npm run compile
FROM node:8 FROM node:10
RUN apt-get update RUN apt-get update && \
RUN apt-get install -y build-essential apt-get install -y build-essential libc6-dev libc6-dev-i386 wget && \
RUN apt-get install -y libc6-dev apt-get clean
RUN apt-get install -y libc6-dev-i386
RUN apt-get install -y wget
RUN apt-get clean
WORKDIR /mono WORKDIR /mono
COPY package.json . COPY package.json .

View File

@@ -24,7 +24,7 @@ module.exports = {
...baseConfig.bridgeConfig, ...baseConfig.bridgeConfig,
...baseConfig.foreignConfig, ...baseConfig.foreignConfig,
event: 'UserRequestForAffirmation', event: 'UserRequestForAffirmation',
queue: 'home', queue: 'home-prioritized',
name: `watcher-${id}`, name: `watcher-${id}`,
id id
} }

View File

@@ -73,6 +73,7 @@ const bridgeConfig = {
} }
const homeConfig = { const homeConfig = {
chain: 'home',
eventContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS, eventContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS,
eventAbi: homeAbi, eventAbi: homeAbi,
bridgeContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS, bridgeContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS,
@@ -83,6 +84,7 @@ const homeConfig = {
} }
const foreignConfig = { const foreignConfig = {
chain: 'foreign',
eventContractAddress: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS, eventContractAddress: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS,
eventAbi: foreignAbi, eventAbi: foreignAbi,
bridgeContractAddress: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS, bridgeContractAddress: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS,

View File

@@ -6,7 +6,7 @@ module.exports = {
...baseConfig.bridgeConfig, ...baseConfig.bridgeConfig,
...baseConfig.homeConfig, ...baseConfig.homeConfig,
event: 'CollectedSignatures', event: 'CollectedSignatures',
queue: 'foreign', queue: 'foreign-prioritized',
name: `watcher-${id}`, name: `watcher-${id}`,
id id
} }

View File

@@ -14,7 +14,7 @@ module.exports = {
...baseConfig.bridgeConfig, ...baseConfig.bridgeConfig,
...baseConfig.foreignConfig, ...baseConfig.foreignConfig,
workerQueue: 'convert-to-chai', workerQueue: 'convert-to-chai',
senderQueue: 'foreign', senderQueue: 'foreign-prioritized',
name: `worker-${id}`, name: `worker-${id}`,
id id
} }

View File

@@ -4,7 +4,8 @@ const { web3Foreign } = require('../src/services/web3')
module.exports = { module.exports = {
...baseConfig.bridgeConfig, ...baseConfig.bridgeConfig,
queue: 'foreign', queue: 'foreign-prioritized',
oldQueue: 'foreign',
id: 'foreign', id: 'foreign',
name: 'sender-foreign', name: 'sender-foreign',
web3: web3Foreign web3: web3Foreign

View File

@@ -1,39 +0,0 @@
const baseConfig = require('./base.config')
const { ERC20_ABI } = require('../../commons')
const { EXIT_CODES } = require('../src/utils/constants')
const initialChecksJson = process.argv[3]
if (!initialChecksJson) {
throw new Error('initial check parameter was not provided.')
}
let initialChecks
try {
initialChecks = JSON.parse(initialChecksJson)
} catch (e) {
throw new Error('Error on decoding values from initial checks.')
}
const id = `${baseConfig.id}-half-duplex-transfer`
const transferWatcherRequired = baseConfig.id === 'erc-native'
if (!transferWatcherRequired) {
console.error(`Transfer watcher not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`)
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
}
module.exports = {
...baseConfig.bridgeConfig,
...baseConfig.foreignConfig,
event: 'Transfer',
eventContractAddress: initialChecks.halfDuplexTokenAddress,
eventAbi: ERC20_ABI,
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
queue: 'home',
workerQueue: 'swap-tokens',
name: `watcher-${id}`,
id,
idle: initialChecks.idle
}

View File

@@ -4,7 +4,8 @@ const { web3Home } = require('../src/services/web3')
module.exports = { module.exports = {
...baseConfig.bridgeConfig, ...baseConfig.bridgeConfig,
queue: 'home', queue: 'home-prioritized',
oldQueue: 'home',
id: 'home', id: 'home',
name: 'sender-home', name: 'sender-home',
web3: web3Home web3: web3Home

View File

@@ -6,7 +6,7 @@ module.exports = {
...baseConfig.bridgeConfig, ...baseConfig.bridgeConfig,
...baseConfig.homeConfig, ...baseConfig.homeConfig,
event: 'UserRequestForSignature', event: 'UserRequestForSignature',
queue: 'home', queue: 'home-prioritized',
name: `watcher-${id}`, name: `watcher-${id}`,
id id
} }

View File

@@ -1,20 +0,0 @@
const baseConfig = require('./base.config')
const { EXIT_CODES } = require('../src/utils/constants')
const id = `${baseConfig.id}-swap-tokens`
const workerRequired = baseConfig.id === 'erc-native'
if (!workerRequired) {
console.error(`Swap tokens worker not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`)
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
}
module.exports = {
...baseConfig.bridgeConfig,
...baseConfig.foreignConfig,
workerQueue: 'swap-tokens',
senderQueue: 'foreign',
name: `worker-${id}`,
id
}

View File

@@ -41,7 +41,7 @@ module.exports = {
eventContractAddress: initialChecks.bridgeableTokenAddress, eventContractAddress: initialChecks.bridgeableTokenAddress,
eventAbi: ERC20_ABI, eventAbi: ERC20_ABI,
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS }, eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
queue: 'home', queue: 'home-prioritized',
...workerQueueConfig, ...workerQueueConfig,
name: `watcher-${id}`, name: `watcher-${id}`,
id id

View File

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

View File

@@ -7,8 +7,6 @@ services:
service: rabbit service: rabbit
networks: networks:
- net_rabbit_bridge_transfer - net_rabbit_bridge_transfer
- net_rabbit_bridge_half_duplex_transfer
- net_rabbit_bridge_swap_tokens_worker
- net_rabbit_bridge_convert_to_chai_worker - net_rabbit_bridge_convert_to_chai_worker
redis: redis:
extends: extends:
@@ -16,7 +14,6 @@ services:
service: redis service: redis
networks: networks:
- net_db_bridge_transfer - net_db_bridge_transfer
- net_db_bridge_half_duplex_transfer
bridge_request: bridge_request:
extends: extends:
file: docker-compose.yml file: docker-compose.yml
@@ -28,6 +25,9 @@ services:
extends: extends:
file: docker-compose.yml file: docker-compose.yml
service: bridge_collected service: bridge_collected
volumes:
- '~/bridge_data/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
- '~/bridge_data/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks: networks:
- net_db_bridge_request - net_db_bridge_request
- net_rabbit_bridge_request - net_rabbit_bridge_request
@@ -51,31 +51,6 @@ services:
networks: networks:
- net_db_bridge_transfer - net_db_bridge_transfer
- net_rabbit_bridge_transfer - net_rabbit_bridge_transfer
bridge_half_duplex_transfer:
cpus: 0.1
mem_limit: 500m
image: poanetwork/tokenbridge-oracle:latest
env_file: ./.env
environment:
- NODE_ENV=production
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
restart: unless-stopped
entrypoint: yarn watcher:half-duplex-transfer
networks:
- net_db_bridge_half_duplex_transfer
- net_rabbit_bridge_half_duplex_transfer
bridge_swap_tokens_worker:
cpus: 0.1
mem_limit: 500m
image: poanetwork/tokenbridge-oracle:latest
env_file: ./.env
environment:
- NODE_ENV=production
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
restart: unless-stopped
entrypoint: yarn worker:swap-tokens
networks:
- net_rabbit_bridge_swap_tokens_worker
bridge_convert_to_chai_worker: bridge_convert_to_chai_worker:
cpus: 0.1 cpus: 0.1
mem_limit: 500m mem_limit: 500m
@@ -112,8 +87,6 @@ networks:
driver: bridge driver: bridge
net_db_bridge_transfer: net_db_bridge_transfer:
driver: bridge driver: bridge
net_db_bridge_half_duplex_transfer:
driver: bridge
net_db_bridge_senderhome: net_db_bridge_senderhome:
driver: bridge driver: bridge
net_db_bridge_senderforeign: net_db_bridge_senderforeign:
@@ -126,10 +99,6 @@ networks:
driver: bridge driver: bridge
net_rabbit_bridge_transfer: net_rabbit_bridge_transfer:
driver: bridge driver: bridge
net_rabbit_bridge_half_duplex_transfer:
driver: bridge
net_rabbit_bridge_swap_tokens_worker:
driver: bridge
net_rabbit_bridge_senderhome: net_rabbit_bridge_senderhome:
driver: bridge driver: bridge
net_rabbit_bridge_senderforeign: net_rabbit_bridge_senderforeign:

View File

@@ -9,13 +9,11 @@
"watcher:collected-signatures": "./scripts/start-worker.sh watcher collected-signatures-watcher", "watcher:collected-signatures": "./scripts/start-worker.sh watcher collected-signatures-watcher",
"watcher:affirmation-request": "./scripts/start-worker.sh watcher affirmation-request-watcher", "watcher:affirmation-request": "./scripts/start-worker.sh watcher affirmation-request-watcher",
"watcher:transfer": "./scripts/start-worker.sh watcher transfer-watcher", "watcher:transfer": "./scripts/start-worker.sh watcher transfer-watcher",
"watcher:half-duplex-transfer": "./scripts/start-worker.sh watcher half-duplex-transfer-watcher",
"worker:swap-tokens": "./scripts/start-worker.sh worker swap-tokens-worker",
"worker:convert-to-chai": "./scripts/start-worker.sh worker convert-to-chai-worker", "worker:convert-to-chai": "./scripts/start-worker.sh worker convert-to-chai-worker",
"sender:home": "./scripts/start-worker.sh sender home-sender", "sender:home": "./scripts/start-worker.sh sender home-sender",
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender", "sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
"confirm:transfer": "./scripts/start-worker.sh confirmRelay transfer-watcher", "confirm:transfer": "./scripts/start-worker.sh confirmRelay transfer-watcher",
"dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer,watcher:half-duplex-transfer, worker:swap-tokens, sender:home,sender:foreign' -c 'red,green,yellow,blue,white,gray,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn watcher:half-duplex-transfer' 'yarn worker:swap-tokens' 'yarn sender:home' 'yarn sender:foreign'", "dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer, sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn sender:home' 'yarn sender:foreign'",
"test": "NODE_ENV=test mocha", "test": "NODE_ENV=test mocha",
"test:watch": "NODE_ENV=test mocha --watch --reporter=min", "test:watch": "NODE_ENV=test mocha --watch --reporter=min",
"coverage": "NODE_ENV=test nyc --reporter=text --reporter=html mocha", "coverage": "NODE_ENV=test nyc --reporter=text --reporter=html mocha",
@@ -50,6 +48,6 @@
"sinon": "^6.1.0" "sinon": "^6.1.0"
}, },
"engines": { "engines": {
"node": ">= 8.9" "node": ">= 10.18"
} }
} }

View File

@@ -7,7 +7,7 @@ const rpcUrlsManager = require('./services/getRpcUrlsManager')
const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3') const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3')
const { sendTx } = require('./tx/sendTx') const { sendTx } = require('./tx/sendTx')
const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils') const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE } = require('./utils/constants') const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants')
const { ORACLE_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_ALLOW_HTTP_FOR_RPC } = process.env const { ORACLE_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_ALLOW_HTTP_FOR_RPC } = process.env
@@ -138,17 +138,22 @@ async function main({ sendJob, txHash }) {
} }
async function sendJobTx(jobs) { async function sendJobTx(jobs) {
const gasPrice = await GasPrice.start(config.queue, true) const gasPrice = await GasPrice.start(config.chain, true)
const chainId = await getChainId(config.queue) const chainId = await getChainId(config.chain)
let nonce = await getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS) let nonce = await getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
await syncForEach(jobs, async job => { await syncForEach(jobs, async job => {
const gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE) let gasLimit
if (typeof job.extraGas === 'number') {
gasLimit = addExtraGas(job.gasEstimate + job.extraGas, 0, MAX_GAS_LIMIT)
} else {
gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT)
}
try { try {
logger.info(`Sending transaction with nonce ${nonce}`) logger.info(`Sending transaction with nonce ${nonce}`)
const txHash = await sendTx({ const txHash = await sendTx({
chain: config.queue, chain: config.chain,
data: job.data, data: job.data,
nonce, nonce,
gasPrice: gasPrice.toString(10), gasPrice: gasPrice.toString(10),
@@ -172,7 +177,7 @@ async function sendJobTx(jobs) {
e.message e.message
) )
if (e.message.includes('Insufficient funds')) { if (e.message.toLowerCase().includes('insufficient funds')) {
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS) const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
const minimumBalance = gasLimit.multipliedBy(gasPrice) const minimumBalance = gasLimit.multipliedBy(gasPrice)
logger.error( logger.error(

View File

@@ -3,14 +3,23 @@ const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = req
const logger = require('../../services/logger').child({ const logger = require('../../services/logger').child({
module: 'processAffirmationRequests:estimateGas' module: 'processAffirmationRequests:estimateGas'
}) })
const { parseAMBHeader } = require('../../utils/message')
const { strip0x } = require('../../../../commons')
const {
AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: estimateExtraGas,
MIN_AMB_HEADER_LENGTH
} = require('../../utils/constants')
async function estimateGas({ web3, homeBridge, validatorContract, message, address }) { async function estimateGas({ web3, homeBridge, validatorContract, message, address }) {
try { try {
const gasEstimate = await homeBridge.methods.executeAffirmation(message).estimateGas({ const gasEstimate = await homeBridge.methods.executeAffirmation(message).estimateGas({
from: address from: address
}) })
const msgGasLimit = parseAMBHeader(message).gasLimit
// message length in bytes
const len = strip0x(message).length / 2 - MIN_AMB_HEADER_LENGTH
return gasEstimate return gasEstimate + msgGasLimit + estimateExtraGas(len)
} catch (e) { } catch (e) {
if (e instanceof HttpListProviderError) { if (e instanceof HttpListProviderError) {
throw e throw e

View File

@@ -4,7 +4,7 @@ const promiseLimit = require('promise-limit')
const rootLogger = require('../../services/logger') const rootLogger = require('../../services/logger')
const { web3Home } = require('../../services/web3') const { web3Home } = require('../../services/web3')
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
const estimateGas = require('./estimateGas') const estimateGas = require('./estimateGas')
const { parseAMBMessage } = require('../../../../commons') const { parseAMBMessage } = require('../../../../commons')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
@@ -75,6 +75,7 @@ function processAffirmationRequestsBuilder(config) {
txToSend.push({ txToSend.push({
data, data,
gasEstimate, gasEstimate,
extraGas: EXTRA_GAS_ABSOLUTE,
transactionReference: affirmationRequest.transactionHash, transactionReference: affirmationRequest.transactionHash,
to: config.homeBridgeAddress to: config.homeBridgeAddress
}) })

View File

@@ -4,6 +4,7 @@ const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError
const logger = require('../../services/logger').child({ const logger = require('../../services/logger').child({
module: 'processCollectedSignatures:estimateGas' module: 'processCollectedSignatures:estimateGas'
}) })
const { parseAMBHeader } = require('../../utils/message')
const web3 = new Web3() const web3 = new Web3()
const { toBN } = Web3.utils const { toBN } = Web3.utils
@@ -24,7 +25,12 @@ async function estimateGas({
const gasEstimate = await foreignBridge.methods.executeSignatures(message, signatures).estimateGas({ const gasEstimate = await foreignBridge.methods.executeSignatures(message, signatures).estimateGas({
from: address from: address
}) })
return gasEstimate const msgGasLimit = parseAMBHeader(message).gasLimit
// + estimateExtraGas(len)
// is not needed here, since estimateGas will already take into account gas
// needed for memory expansion, message processing, etc.
return gasEstimate + msgGasLimit
} catch (e) { } catch (e) {
if (e instanceof HttpListProviderError) { if (e instanceof HttpListProviderError) {
throw e throw e

View File

@@ -8,10 +8,12 @@ const { signatureToVRS, packSignatures } = require('../../utils/message')
const { parseAMBMessage } = require('../../../../commons') const { parseAMBMessage } = require('../../../../commons')
const estimateGas = require('./estimateGas') const estimateGas = require('./estimateGas')
const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors') const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const { MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
const limit = promiseLimit(MAX_CONCURRENT_EVENTS) const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
const { ORACLE_ALWAYS_RELAY_SIGNATURES } = process.env
let validatorContract = null let validatorContract = null
function processCollectedSignaturesBuilder(config) { function processCollectedSignaturesBuilder(config) {
@@ -39,7 +41,9 @@ function processCollectedSignaturesBuilder(config) {
eventTransactionHash: colSignature.transactionHash eventTransactionHash: colSignature.transactionHash
}) })
if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) { if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
logger.debug('Validator handles all CollectedSignature requests')
} else if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) {
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`) logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
return return
} }
@@ -107,6 +111,7 @@ function processCollectedSignaturesBuilder(config) {
txToSend.push({ txToSend.push({
data, data,
gasEstimate, gasEstimate,
extraGas: EXTRA_GAS_ABSOLUTE,
transactionReference: colSignature.transactionHash, transactionReference: colSignature.transactionHash,
to: config.foreignBridgeAddress to: config.foreignBridgeAddress
}) })

View File

@@ -4,11 +4,19 @@ const { HttpListProviderError } = require('http-list-provider')
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons') const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
const rootLogger = require('../../services/logger') const rootLogger = require('../../services/logger')
const { web3Home, web3Foreign } = require('../../services/web3') const { web3Home, web3Foreign } = require('../../services/web3')
const { signatureToVRS, packSignatures } = require('../../utils/message') const { signatureToVRS, packSignatures, parseMessage } = require('../../utils/message')
const { readAccessListFile } = require('../../utils/utils')
const estimateGas = require('./estimateGas') const estimateGas = require('./estimateGas')
const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors') const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const {
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST,
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST,
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER,
ORACLE_ALWAYS_RELAY_SIGNATURES
} = process.env
const limit = promiseLimit(MAX_CONCURRENT_EVENTS) const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
let validatorContract = null let validatorContract = null
@@ -38,7 +46,9 @@ function processCollectedSignaturesBuilder(config) {
eventTransactionHash: colSignature.transactionHash eventTransactionHash: colSignature.transactionHash
}) })
if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) { if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
logger.debug('Validator handles all CollectedSignature requests')
} else if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) {
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`) logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
return return
} }
@@ -46,6 +56,49 @@ function processCollectedSignaturesBuilder(config) {
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`) logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
const message = await homeBridge.methods.message(messageHash).call() const message = await homeBridge.methods.message(messageHash).call()
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
const parsedMessage = parseMessage(message)
const recipient = parsedMessage.recipient.toLowerCase()
const originalTxHash = parsedMessage.txHash
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST) {
const allowanceList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST, logger)
if (allowanceList.indexOf(recipient) === -1) {
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
if (allowanceList.indexOf(sender) === -1) {
logger.info(
{ sender, recipient },
'Validator skips a transaction. Neither sender nor recipient addresses are in the allowance list.'
)
return
}
} else {
logger.info(
{ recipient },
'Validator skips a transaction. Recipient address is not in the allowance list.'
)
return
}
}
} else if (ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
const blockList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_BLOCK_LIST, logger)
if (blockList.indexOf(recipient) > -1) {
logger.info({ recipient }, 'Validator skips a transaction. Recipient address is in the block list.')
return
}
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
if (blockList.indexOf(sender) > -1) {
logger.info({ sender }, 'Validator skips a transaction. Sender address is in the block list.')
return
}
}
}
}
logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get') logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get')
const requiredSignatures = [] const requiredSignatures = []

View File

@@ -1,134 +0,0 @@
require('../../../env')
const promiseLimit = require('promise-limit')
const { HttpListProviderError } = require('http-list-provider')
const { BRIDGE_VALIDATORS_ABI, ZERO_ADDRESS } = require('../../../../commons')
const rootLogger = require('../../services/logger')
const { web3Home, web3Foreign } = require('../../services/web3')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const estimateGas = require('../processAffirmationRequests/estimateGas')
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
let validatorContract = null
function processTransfersBuilder(config) {
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
const foreignBridge = new web3Foreign.eth.Contract(config.foreignBridgeAbi, config.foreignBridgeAddress)
const userRequestForAffirmationAbi = config.foreignBridgeAbi.filter(
e => e.type === 'event' && e.name === 'UserRequestForAffirmation'
)[0]
const tokensSwappedAbi = config.foreignBridgeAbi.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0]
const userRequestForAffirmationHash = web3Home.eth.abi.encodeEventSignature(userRequestForAffirmationAbi)
const tokensSwappedHash = tokensSwappedAbi ? web3Home.eth.abi.encodeEventSignature(tokensSwappedAbi) : '0x'
return async function processTransfers(transfers, blockNumber) {
const txToSend = []
if (validatorContract === null) {
rootLogger.debug('Getting validator contract address')
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
validatorContract = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
}
rootLogger.debug(`Processing ${transfers.length} Transfer events`)
const callbacks = transfers
.map(transfer => async () => {
const { from, value } = transfer.returnValues
const logger = rootLogger.child({
eventTransactionHash: transfer.transactionHash
})
logger.info({ from, value }, `Processing transfer ${transfer.transactionHash}`)
const block = await web3Foreign.eth.getBlock(blockNumber)
logger.debug({ blockNumber, timestamp: block.timestamp }, `Block obtained`)
const tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call()
if (!tokenSwapAllowed) {
logger.info(
`Transfer event discarded because SCD Emergency Shutdown has happened ${transfer.transactionHash}`
)
return
}
const receipt = await web3Foreign.eth.getTransactionReceipt(transfer.transactionHash)
const existsAffirmationEvent = receipt.logs.some(
e => e.address === config.foreignBridgeAddress && e.topics[0] === userRequestForAffirmationHash
)
if (existsAffirmationEvent) {
logger.info(
`Transfer event discarded because a transaction with alternative receiver detected in transaction ${
transfer.transactionHash
}`
)
return
}
const existsTokensSwappedEvent = tokensSwappedAbi
? receipt.logs.some(e => e.address === config.foreignBridgeAddress && e.topics[0] === tokensSwappedHash)
: false
if (from === ZERO_ADDRESS && existsTokensSwappedEvent) {
logger.info(
`Transfer event discarded because token swap is detected in transaction ${transfer.transactionHash}`
)
return
}
let gasEstimate
try {
logger.debug('Estimate gas')
gasEstimate = await estimateGas({
web3: web3Home,
homeBridge,
validatorContract,
recipient: from,
value,
txHash: transfer.transactionHash,
address: config.validatorAddress
})
logger.debug({ gasEstimate }, 'Gas estimated')
} catch (e) {
if (e instanceof HttpListProviderError) {
throw new Error('RPC Connection Error: submitSignature Gas Estimate cannot be obtained.')
} else if (e instanceof InvalidValidatorError) {
logger.fatal({ address: config.validatorAddress }, 'Invalid validator')
process.exit(EXIT_CODES.INCOMPATIBILITY)
} else if (e instanceof AlreadySignedError) {
logger.info(`Already signed transfer ${transfer.transactionHash}`)
return
} else if (e instanceof AlreadyProcessedError) {
logger.info(`transfer ${transfer.transactionHash} was already processed by other validators`)
return
} else {
logger.error(e, 'Unknown error while processing transaction')
throw e
}
}
const data = await homeBridge.methods
.executeAffirmation(from, value, transfer.transactionHash)
.encodeABI({ from: config.validatorAddress })
txToSend.push({
data,
gasEstimate,
transactionReference: transfer.transactionHash,
to: config.homeBridgeAddress
})
})
.map(promise => limit(promise))
await Promise.all(callbacks)
return txToSend
}
}
module.exports = processTransfersBuilder

View File

@@ -16,7 +16,7 @@ const {
watchdog, watchdog,
nonceError nonceError
} = require('./utils/utils') } = require('./utils/utils')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE } = require('./utils/constants') const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants')
const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
@@ -45,6 +45,7 @@ async function initialize() {
chainId = await getChainId(config.id) chainId = await getChainId(config.id)
connectSenderToQueue({ connectSenderToQueue({
queueName: config.queue, queueName: config.queue,
oldQueueName: config.oldQueue,
cb: options => { cb: options => {
if (config.maxProcessingTime) { if (config.maxProcessingTime) {
return watchdog(() => main(options), config.maxProcessingTime, () => { return watchdog(() => main(options), config.maxProcessingTime, () => {
@@ -88,7 +89,7 @@ function updateNonce(nonce) {
return redis.set(nonceKey, nonce) return redis.set(nonceKey, nonce)
} }
async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry }) { async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleTransactionResend }) {
try { try {
if (redis.status !== 'ready') { if (redis.status !== 'ready') {
nackMsg(msg) nackMsg(msg)
@@ -96,25 +97,56 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry }) {
} }
const txArray = JSON.parse(msg.content) const txArray = JSON.parse(msg.content)
logger.info(`Msg received with ${txArray.length} Tx to send`) logger.debug(`Msg received with ${txArray.length} Tx to send`)
const gasPrice = GasPrice.getPrice() const gasPrice = GasPrice.getPrice()
let nonce = await readNonce() let nonce
let insufficientFunds = false let insufficientFunds = false
let minimumBalance = null let minimumBalance = null
const failedTx = [] const failedTx = []
const sentTx = []
logger.debug(`Sending ${txArray.length} transactions`) const isResend = txArray.length > 0 && !!txArray[0].txHash
if (isResend) {
logger.info(`Checking status of ${txArray.length} transactions`)
nonce = null
} else {
logger.info(`Sending ${txArray.length} transactions`)
nonce = await readNonce()
}
await syncForEach(txArray, async job => { await syncForEach(txArray, async job => {
const gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE) let gasLimit
if (typeof job.extraGas === 'number') {
gasLimit = addExtraGas(job.gasEstimate + job.extraGas, 0, MAX_GAS_LIMIT)
} else {
gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT)
}
try { try {
if (isResend) {
const tx = await web3Instance.eth.getTransaction(job.txHash)
if (tx && tx.blockNumber !== null) {
logger.debug(`Transaction ${job.txHash} was successfully mined`)
return
}
if (nonce === null) {
nonce = await readNonce(true)
}
logger.info(
`Transaction ${job.txHash} was not mined, updating gasPrice: ${job.gasPrice} -> ${gasPrice.toString(10)}`
)
}
logger.info(`Sending transaction with nonce ${nonce}`) logger.info(`Sending transaction with nonce ${nonce}`)
const txHash = await sendTx({ job.gasPrice = gasPrice.toString(10)
job.txHash = await sendTx({
chain: config.id, chain: config.id,
data: job.data, data: job.data,
nonce, nonce,
gasPrice: gasPrice.toString(10), gasPrice: job.gasPrice,
amount: '0', amount: '0',
gasLimit, gasLimit,
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
@@ -122,11 +154,12 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry }) {
chainId, chainId,
web3: web3Instance web3: web3Instance
}) })
sentTx.push(job)
nonce++ nonce++
logger.info( logger.info(
{ eventTransactionHash: job.transactionReference, generatedTransactionHash: txHash }, { eventTransactionHash: job.transactionReference, generatedTransactionHash: job.txHash },
`Tx generated ${txHash} for event Tx ${job.transactionReference}` `Tx generated ${job.txHash} for event Tx ${job.transactionReference}`
) )
} catch (e) { } catch (e) {
logger.error( logger.error(
@@ -134,11 +167,11 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry }) {
`Tx Failed for event Tx ${job.transactionReference}.`, `Tx Failed for event Tx ${job.transactionReference}.`,
e.message e.message
) )
if (!e.message.includes('Transaction with the same hash was already imported')) { if (!e.message.toLowerCase().includes('transaction with the same hash was already imported')) {
failedTx.push(job) failedTx.push(job)
} }
if (e.message.includes('Insufficient funds')) { if (e.message.toLowerCase().includes('insufficient funds')) {
insufficientFunds = true insufficientFunds = true
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS) const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
minimumBalance = gasLimit.multipliedBy(gasPrice) minimumBalance = gasLimit.multipliedBy(gasPrice)
@@ -158,6 +191,10 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry }) {
logger.info(`Sending ${failedTx.length} Failed Tx to Queue`) logger.info(`Sending ${failedTx.length} Failed Tx to Queue`)
await scheduleForRetry(failedTx, msg.properties.headers['x-retries']) await scheduleForRetry(failedTx, msg.properties.headers['x-retries'])
} }
if (sentTx.length) {
logger.info(`Sending ${sentTx.length} Tx Delayed Resend Requests to Queue`)
await scheduleTransactionResend(sentTx)
}
ackMsg(msg) ackMsg(msg)
logger.debug(`Finished processing msg`) logger.debug(`Finished processing msg`)

View File

@@ -11,8 +11,8 @@ function RpcUrlsManager(homeUrls, foreignUrls) {
throw new Error(`Invalid foreignUrls: '${foreignUrls}'`) throw new Error(`Invalid foreignUrls: '${foreignUrls}'`)
} }
this.homeUrls = homeUrls.split(',') this.homeUrls = homeUrls.split(' ')
this.foreignUrls = foreignUrls.split(',') this.foreignUrls = foreignUrls.split(' ')
} }
RpcUrlsManager.prototype.tryEach = async function(chain, f, redundant = false) { RpcUrlsManager.prototype.tryEach = async function(chain, f, redundant = false) {

View File

@@ -4,6 +4,12 @@ const dns = require('dns')
const connection = require('amqp-connection-manager').connect(process.env.ORACLE_QUEUE_URL) const connection = require('amqp-connection-manager').connect(process.env.ORACLE_QUEUE_URL)
const logger = require('./logger') const logger = require('./logger')
const { getRetrySequence } = require('../utils/utils') const { getRetrySequence } = require('../utils/utils')
const {
TRANSACTION_RESEND_TIMEOUT,
SENDER_QUEUE_MAX_PRIORITY,
SENDER_QUEUE_SEND_PRIORITY,
SENDER_QUEUE_CHECK_STATUS_PRIORITY
} = require('../utils/constants')
connection.on('connect', () => { connection.on('connect', () => {
logger.info('Connected to amqp Broker') logger.info('Connected to amqp Broker')
@@ -22,16 +28,18 @@ async function isAttached() {
} }
function connectWatcherToQueue({ queueName, workerQueue, cb }) { function connectWatcherToQueue({ queueName, workerQueue, cb }) {
const queueList = workerQueue ? [queueName, workerQueue] : [queueName]
const channelWrapper = connection.createChannel({ const channelWrapper = connection.createChannel({
json: true, json: true,
setup(channel) { async setup(channel) {
return Promise.all(queueList.map(queue => channel.assertQueue(queue, { durable: true }))) await channel.assertQueue(queueName, { durable: true, maxPriority: SENDER_QUEUE_MAX_PRIORITY })
if (workerQueue) {
await channel.assertQueue(workerQueue, { durable: true })
}
} }
}) })
const sendToQueue = data => channelWrapper.sendToQueue(queueName, data, { persistent: true }) const sendToQueue = data =>
channelWrapper.sendToQueue(queueName, data, { persistent: true, priority: SENDER_QUEUE_SEND_PRIORITY })
let sendToWorker let sendToWorker
if (workerQueue) { if (workerQueue) {
sendToWorker = data => channelWrapper.sendToQueue(workerQueue, data, { persistent: true }) sendToWorker = data => channelWrapper.sendToQueue(workerQueue, data, { persistent: true })
@@ -40,20 +48,34 @@ function connectWatcherToQueue({ queueName, workerQueue, cb }) {
cb({ sendToQueue, sendToWorker, channel: channelWrapper }) cb({ sendToQueue, sendToWorker, channel: channelWrapper })
} }
function connectSenderToQueue({ queueName, cb }) { function connectSenderToQueue({ queueName, oldQueueName, cb }) {
const deadLetterExchange = `${queueName}-retry` const deadLetterExchange = `${queueName}-retry`
async function resendMessagesToNewQueue(channel) {
logger.info(`Trying to check messages in the old non-priority queue ${queueName}`)
while (true) {
const msg = await channel.get(oldQueueName)
if (msg === false) {
logger.info(`No messages in the old queue ${oldQueueName} left`)
break
}
logger.debug(`Message in the old queue ${oldQueueName} was found, redirecting it to the new queue ${queueName}`)
await channel.sendToQueue(queueName, msg.content, { persistent: true, priority: SENDER_QUEUE_SEND_PRIORITY })
await channel.ack(msg)
}
}
const channelWrapper = connection.createChannel({ const channelWrapper = connection.createChannel({
json: true json: true
}) })
channelWrapper.addSetup(channel => { channelWrapper.addSetup(async channel => {
return Promise.all([ await channel.assertExchange(deadLetterExchange, 'fanout', { durable: true })
channel.assertExchange(deadLetterExchange, 'fanout', { durable: true }), await channel.assertQueue(queueName, { durable: true, maxPriority: SENDER_QUEUE_MAX_PRIORITY })
channel.assertQueue(queueName, { durable: true }), await channel.assertQueue(oldQueueName, { durable: true }).then(() => resendMessagesToNewQueue(channel))
channel.bindQueue(queueName, deadLetterExchange), await channel.bindQueue(queueName, deadLetterExchange)
channel.prefetch(1), await channel.prefetch(1)
channel.consume(queueName, msg => await channel.consume(queueName, msg =>
cb({ cb({
msg, msg,
channel: channelWrapper, channel: channelWrapper,
@@ -68,10 +90,18 @@ function connectSenderToQueue({ queueName, cb }) {
queueName, queueName,
deadLetterExchange deadLetterExchange
}) })
},
scheduleTransactionResend: async data => {
await generateTransactionResend({
data,
channelWrapper,
channel,
queueName,
deadLetterExchange
})
} }
}) })
) )
])
}) })
} }
@@ -82,20 +112,20 @@ function connectWorkerToQueue({ queueName, senderQueue, cb }) {
json: true json: true
}) })
channelWrapper.addSetup(channel => { channelWrapper.addSetup(async channel => {
return Promise.all([ await channel.assertExchange(deadLetterExchange, 'fanout', { durable: true })
channel.assertExchange(deadLetterExchange, 'fanout', { durable: true }), await channel.assertQueue(queueName, { durable: true })
channel.assertQueue(queueName, { durable: true }), await channel.assertQueue(senderQueue, { durable: true, maxPriority: SENDER_QUEUE_MAX_PRIORITY })
channel.assertQueue(senderQueue, { durable: true }), await channel.bindQueue(queueName, deadLetterExchange)
channel.bindQueue(queueName, deadLetterExchange), await channel.prefetch(1)
channel.prefetch(1), await channel.consume(queueName, msg =>
channel.consume(queueName, msg =>
cb({ cb({
msg, msg,
channel: channelWrapper, channel: channelWrapper,
ackMsg: job => channelWrapper.ack(job), ackMsg: job => channelWrapper.ack(job),
nackMsg: job => channelWrapper.nack(job, false, true), nackMsg: job => channelWrapper.nack(job, false, true),
sendToSenderQueue: data => channelWrapper.sendToQueue(senderQueue, data, { persistent: true }), sendToSenderQueue: data =>
channelWrapper.sendToQueue(senderQueue, data, { persistent: true, priority: SENDER_QUEUE_SEND_PRIORITY }),
scheduleForRetry: async (data, msgRetries = 0) => { scheduleForRetry: async (data, msgRetries = 0) => {
await generateRetry({ await generateRetry({
data, data,
@@ -108,26 +138,47 @@ function connectWorkerToQueue({ queueName, senderQueue, cb }) {
} }
}) })
) )
])
}) })
} }
async function generateRetry({ data, msgRetries, channelWrapper, channel, queueName, deadLetterExchange }) { async function generateRetry({ data, msgRetries, channelWrapper, channel, queueName, deadLetterExchange }) {
const retries = msgRetries + 1 const retries = msgRetries + 1
const delay = getRetrySequence(retries) * 1000 const delay = getRetrySequence(retries) * 1000
// New retry queue is created, and one message is send to it.
// Nobody consumes messages from this queue, so eventually the message will be dropped.
// `messageTtl` defines a timeout after which the message will be dropped out of the queue.
// When message is dropped, it will be resend into the specified `deadLetterExchange` with the updated `x-retries` header.
const retryQueue = `${queueName}-retry-${delay}` const retryQueue = `${queueName}-retry-${delay}`
await channel.assertQueue(retryQueue, { await channel.assertQueue(retryQueue, {
durable: true, durable: true,
deadLetterExchange, deadLetterExchange,
messageTtl: delay, messageTtl: delay,
expires: delay * 10 expires: delay * 10,
maxPriority: SENDER_QUEUE_MAX_PRIORITY
}) })
await channelWrapper.sendToQueue(retryQueue, data, { await channelWrapper.sendToQueue(retryQueue, data, {
persistent: true, persistent: true,
priority: SENDER_QUEUE_SEND_PRIORITY,
headers: { 'x-retries': retries } headers: { 'x-retries': retries }
}) })
} }
async function generateTransactionResend({ data, channelWrapper, channel, queueName, deadLetterExchange }) {
const retryQueue = `${queueName}-check-tx-status`
await channel.assertQueue(retryQueue, {
durable: true,
deadLetterExchange,
messageTtl: TRANSACTION_RESEND_TIMEOUT,
expires: TRANSACTION_RESEND_TIMEOUT * 10,
maxPriority: SENDER_QUEUE_MAX_PRIORITY
})
await channelWrapper.sendToQueue(retryQueue, data, {
priority: SENDER_QUEUE_CHECK_STATUS_PRIORITY,
persistent: true
})
}
module.exports = { module.exports = {
isAttached, isAttached,
connectWatcherToQueue, connectWatcherToQueue,

View File

@@ -73,13 +73,14 @@ async function start(chainId, fetchOnce) {
throw new Error(`Unrecognized chainId '${chainId}'`) throw new Error(`Unrecognized chainId '${chainId}'`)
} }
const fetchFn = gasPriceSupplierUrl === 'gas-price-oracle' ? null : () => fetch(gasPriceSupplierUrl)
if (fetchOnce) { if (fetchOnce) {
await fetchGasPrice(speedType, factor, bridgeContract, () => fetch(gasPriceSupplierUrl)) await fetchGasPrice(speedType, factor, bridgeContract, fetchFn)
return getPrice() return getPrice()
} }
fetchGasPriceInterval = setIntervalAndRun( fetchGasPriceInterval = setIntervalAndRun(
() => fetchGasPrice(speedType, factor, bridgeContract, () => fetch(gasPriceSupplierUrl)), () => fetchGasPrice(speedType, factor, bridgeContract, fetchFn),
updateInterval updateInterval
) )
return null return null

View File

@@ -1,5 +1,9 @@
module.exports = { module.exports = {
EXTRA_GAS_PERCENTAGE: 4, EXTRA_GAS_PERCENTAGE: 4,
EXTRA_GAS_ABSOLUTE: 200000,
AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: len => Math.floor(0.0035 * len ** 2 + 40 * len),
MIN_AMB_HEADER_LENGTH: 32 + 20 + 20 + 4 + 2 + 1 + 2,
MAX_GAS_LIMIT: 10000000,
MAX_CONCURRENT_EVENTS: 50, MAX_CONCURRENT_EVENTS: 50,
RETRY_CONFIG: { RETRY_CONFIG: {
retries: 20, retries: 20,
@@ -17,6 +21,10 @@ module.exports = {
}, },
GAS_PRICE_BOUNDARIES: { GAS_PRICE_BOUNDARIES: {
MIN: 1, MIN: 1,
MAX: 250 MAX: 1000
} },
TRANSACTION_RESEND_TIMEOUT: 20 * 60 * 1000,
SENDER_QUEUE_MAX_PRIORITY: 10,
SENDER_QUEUE_SEND_PRIORITY: 5,
SENDER_QUEUE_CHECK_STATUS_PRIORITY: 1
} }

View File

@@ -73,9 +73,37 @@ function packSignatures(array) {
return `0x${msgLength}${v}${r}${s}` return `0x${msgLength}${v}${r}${s}`
} }
function parseAMBHeader(message) {
message = strip0x(message)
const messageIdStart = 0
const messageIdLength = 32 * 2
const messageId = `0x${message.slice(messageIdStart, messageIdStart + messageIdLength)}`
const senderStart = messageIdStart + messageIdLength
const senderLength = 20 * 2
const sender = `0x${message.slice(senderStart, senderStart + senderLength)}`
const executorStart = senderStart + senderLength
const executorLength = 20 * 2
const executor = `0x${message.slice(executorStart, executorStart + executorLength)}`
const gasLimitStart = executorStart + executorLength
const gasLimitLength = 4 * 2
const gasLimit = parseInt(message.slice(gasLimitStart, gasLimitStart + gasLimitLength), 16)
return {
messageId,
sender,
executor,
gasLimit
}
}
module.exports = { module.exports = {
createMessage, createMessage,
parseMessage, parseMessage,
signatureToVRS, signatureToVRS,
packSignatures packSignatures,
parseAMBHeader
} }

View File

@@ -8,21 +8,6 @@ async function getTokensState(bridgeContract, logger) {
throw new Error(`Bridgeable token address cannot be obtained`) throw new Error(`Bridgeable token address cannot be obtained`)
} }
try {
logger.debug('Getting Half Duplex token address')
const halfDuplexErc20tokenAddress = await bridgeContract.methods.halfDuplexErc20token().call()
logger.debug({ address: halfDuplexErc20tokenAddress }, 'Half Duplex token address obtained')
if (halfDuplexErc20tokenAddress !== context.bridgeableTokenAddress) {
context.halfDuplexTokenAddress = halfDuplexErc20tokenAddress
} else {
logger.info('Migration to support two tokens was not applied')
context.idle = true
}
} catch (e) {
logger.info('Old version of contracts is used')
context.idle = true
}
return context return context
} }

View File

@@ -1,3 +1,4 @@
const fs = require('fs')
const BigNumber = require('bignumber.js') const BigNumber = require('bignumber.js')
const promiseRetry = require('promise-retry') const promiseRetry = require('promise-retry')
const Web3 = require('web3') const Web3 = require('web3')
@@ -33,7 +34,7 @@ async function waitForFunds(web3, address, minimumBalance, cb, logger) {
async retry => { async retry => {
logger.debug('Getting balance of validator account') logger.debug('Getting balance of validator account')
const newBalance = web3.utils.toBN(await web3.eth.getBalance(address)) const newBalance = web3.utils.toBN(await web3.eth.getBalance(address))
if (newBalance.gte(minimumBalance)) { if (newBalance.gte(web3.utils.toBN(minimumBalance.toString(10)))) {
logger.debug({ balance: newBalance, minimumBalance }, 'Validator has minimum necessary balance') logger.debug({ balance: newBalance, minimumBalance }, 'Validator has minimum necessary balance')
cb(newBalance) cb(newBalance)
} else { } else {
@@ -48,13 +49,13 @@ async function waitForFunds(web3, address, minimumBalance, cb, logger) {
) )
} }
function addExtraGas(gas, extraPercentage) { function addExtraGas(gas, extraPercentage, maxGasLimit = Infinity) {
gas = BigNumber(gas) gas = BigNumber(gas)
extraPercentage = BigNumber(1 + extraPercentage) extraPercentage = BigNumber(1 + extraPercentage)
const gasWithExtra = gas.multipliedBy(extraPercentage).toFixed(0) const gasWithExtra = gas.multipliedBy(extraPercentage).toFixed(0)
return BigNumber(gasWithExtra) return BigNumber.min(maxGasLimit, gasWithExtra)
} }
function setIntervalAndRun(f, interval) { function setIntervalAndRun(f, interval) {
@@ -93,10 +94,11 @@ function privateKeyToAddress(privateKey) {
} }
function nonceError(e) { function nonceError(e) {
const message = e.message.toLowerCase()
return ( return (
e.message.includes('Transaction nonce is too low') || message.includes('transaction nonce is too low') ||
e.message.includes('nonce too low') || message.includes('nonce too low') ||
e.message.includes('transaction with same nonce in the queue') message.includes('transaction with same nonce in the queue')
) )
} }
@@ -105,6 +107,27 @@ function nonceError(e) {
const invert = p => new Promise((res, rej) => p.then(rej, res)) const invert = p => new Promise((res, rej) => p.then(rej, res))
const promiseAny = ps => invert(Promise.all(ps.map(invert))) const promiseAny = ps => invert(Promise.all(ps.map(invert)))
const readAccessLists = {}
async function readAccessListFile(fileName, logger) {
if (!readAccessLists[fileName]) {
logger.debug({ fileName }, 'Access list file read requested')
try {
const data = await fs.promises.readFile(fileName)
readAccessLists[fileName] = data
.toString()
.split('\n')
.map(addr => addr.trim().toLowerCase())
.filter(addr => addr.length === 42)
logger.info({ fileName }, `Access list was read successfully, ${data.length} addresses found`)
logger.debug({ addresses: readAccessLists[fileName] }, `Read addresses from the file`)
} catch (e) {
readAccessLists[fileName] = []
logger.error({ fileName, error: e }, `Failed to read access list from the file`)
}
}
return readAccessLists[fileName]
}
module.exports = { module.exports = {
syncForEach, syncForEach,
checkHTTPS, checkHTTPS,
@@ -115,5 +138,6 @@ module.exports = {
privateKeyToAddress, privateKeyToAddress,
nonceError, nonceError,
getRetrySequence, getRetrySequence,
promiseAny promiseAny,
readAccessListFile
} }

View File

@@ -22,7 +22,6 @@ const processSignatureRequests = require('./events/processSignatureRequests')(co
const processCollectedSignatures = require('./events/processCollectedSignatures')(config) const processCollectedSignatures = require('./events/processCollectedSignatures')(config)
const processAffirmationRequests = require('./events/processAffirmationRequests')(config) const processAffirmationRequests = require('./events/processAffirmationRequests')(config)
const processTransfers = require('./events/processTransfers')(config) const processTransfers = require('./events/processTransfers')(config)
const processHalfDuplexTransfers = require('./events/processHalfDuplexTransfers')(config)
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config) const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config) const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config) const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
@@ -36,7 +35,6 @@ const web3Instance = config.web3
const bridgeContract = new web3Instance.eth.Contract(config.bridgeAbi, config.bridgeContractAddress) const bridgeContract = new web3Instance.eth.Contract(config.bridgeAbi, config.bridgeContractAddress)
let { eventContractAddress } = config let { eventContractAddress } = config
let eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress) let eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress)
let skipEvents = config.idle
const lastBlockRedisKey = `${config.id}:lastProcessedBlock` const lastBlockRedisKey = `${config.id}:lastProcessedBlock`
let lastProcessedBlock = BN.max(config.startBlock.sub(ONE), ZERO) let lastProcessedBlock = BN.max(config.startBlock.sub(ONE), ZERO)
@@ -91,7 +89,7 @@ function updateLastProcessedBlock(lastBlockNumber) {
return redis.set(lastBlockRedisKey, lastProcessedBlock.toString()) return redis.set(lastBlockRedisKey, lastProcessedBlock.toString())
} }
function processEvents(events, blockNumber) { function processEvents(events) {
switch (config.id) { switch (config.id) {
case 'native-erc-signature-request': case 'native-erc-signature-request':
case 'erc-erc-signature-request': case 'erc-erc-signature-request':
@@ -109,8 +107,6 @@ function processEvents(events, blockNumber) {
case 'erc-erc-transfer': case 'erc-erc-transfer':
case 'erc-native-transfer': case 'erc-native-transfer':
return processTransfers(events) return processTransfers(events)
case 'erc-native-half-duplex-transfer':
return processHalfDuplexTransfers(events, blockNumber)
case 'amb-signature-request': case 'amb-signature-request':
return processAMBSignatureRequests(events) return processAMBSignatureRequests(events)
case 'amb-collected-signatures': case 'amb-collected-signatures':
@@ -130,12 +126,6 @@ async function checkConditions() {
state = await getTokensState(bridgeContract, logger) state = await getTokensState(bridgeContract, logger)
updateEventContract(state.bridgeableTokenAddress) updateEventContract(state.bridgeableTokenAddress)
break break
case 'erc-native-half-duplex-transfer':
logger.debug('Getting Half Duplex token address to listen Transfer events')
state = await getTokensState(bridgeContract, logger)
skipEvents = state.idle
updateEventContract(state.halfDuplexTokenAddress)
break
default: default:
} }
} }
@@ -171,11 +161,6 @@ async function main({ sendToQueue, sendToWorker }) {
try { try {
await checkConditions() await checkConditions()
if (skipEvents) {
logger.debug('Watcher in idle mode, skipping getting events')
return
}
const lastBlockToProcess = await getLastBlockToProcess() const lastBlockToProcess = await getLastBlockToProcess()
if (lastBlockToProcess.lte(lastProcessedBlock)) { if (lastBlockToProcess.lte(lastProcessedBlock)) {
@@ -200,7 +185,7 @@ async function main({ sendToQueue, sendToWorker }) {
await sendToWorker({ blockNumber: toBlock.toString() }) await sendToWorker({ blockNumber: toBlock.toString() })
} }
const job = await processEvents(events, toBlock.toString()) const job = await processEvents(events)
logger.info('Transactions to send:', job.length) logger.info('Transactions to send:', job.length)
if (job.length) { if (job.length) {

View File

@@ -7,7 +7,6 @@ const { connectWorkerToQueue } = require('./services/amqpClient')
const config = require(path.join('../config/', process.argv[2])) const config = require(path.join('../config/', process.argv[2]))
const swapTokens = require('./workers/swapTokens')(config)
const convertToChai = require('./workers/convertToChai')(config) const convertToChai = require('./workers/convertToChai')(config)
async function initialize() { async function initialize() {
@@ -38,9 +37,7 @@ async function initialize() {
} }
async function run(blockNumber) { async function run(blockNumber) {
if (config.id === 'erc-native-swap-tokens') { if (config.id === 'erc-native-convert-to-chai') {
return swapTokens(blockNumber)
} else if (config.id === 'erc-native-convert-to-chai') {
return convertToChai(blockNumber) return convertToChai(blockNumber)
} else { } else {
return [] return []

View File

@@ -1,111 +0,0 @@
require('../../env')
const { HttpListProviderError } = require('http-list-provider')
const rootLogger = require('../services/logger')
const { web3Foreign } = require('../services/web3')
const { BRIDGE_VALIDATORS_ABI, ERC20_ABI } = require('../../../commons')
let validatorContract = null
let halfDuplexTokenContract = null
function swapTokensBuilder(config) {
const foreignBridge = new web3Foreign.eth.Contract(config.foreignBridgeAbi, config.foreignBridgeAddress)
return async function swapTokens(blockNumber) {
const txToSend = []
const logger = rootLogger.child({
blockNumber: blockNumber.toString()
})
logger.debug(`Starting swap tokens operation`)
if (validatorContract === null) {
logger.debug('Getting validator contract address')
const validatorContractAddress = await foreignBridge.methods.validatorContract().call()
logger.debug({ validatorContractAddress }, 'Validator contract address obtained')
validatorContract = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
}
logger.debug(`Checking if is validator duty`)
const validatorDuty = await validatorContract.methods.isValidatorDuty(config.validatorAddress).call()
if (!validatorDuty) {
logger.info(`Token swap discarded because is not validator duty`)
return txToSend
}
logger.debug(`Checking if half duplex token balance is above the threshold`)
const hdTokenBalanceAboveMinBalance = await foreignBridge.methods.isHDTokenBalanceAboveMinBalance().call()
if (!hdTokenBalanceAboveMinBalance) {
logger.info(`Token swap discarded because half duplex balance is below the threshold`)
return txToSend
}
const block = await web3Foreign.eth.getBlock(blockNumber)
logger.debug({ timestamp: block.timestamp }, `Block obtained`)
logger.debug(`Checking if SCD Emergency Shutdown has happened`)
const tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call()
if (!tokenSwapAllowed) {
logger.info(`Token swap discarded because SCD Emergency Shutdown has happened`)
return txToSend
}
let gasEstimate
try {
logger.debug(`Estimate gas`)
gasEstimate = await foreignBridge.methods.swapTokens().estimateGas({
from: config.validatorAddress
})
logger.debug({ gasEstimate }, 'Gas estimated')
} catch (e) {
if (e instanceof HttpListProviderError) {
const errorMsg = 'RPC Connection Error: swapTokens Gas Estimate cannot be obtained.'
logger.error(e, errorMsg)
throw new Error(errorMsg)
} else {
if (halfDuplexTokenContract === null) {
logger.debug('Getting half duplex token contract address')
const halfDuplexErc20Token = await foreignBridge.methods.halfDuplexErc20token().call()
logger.debug({ halfDuplexErc20Token }, 'Half duplex token contract address obtained')
halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, halfDuplexErc20Token)
}
const balance = web3Foreign.utils.toBN(
await halfDuplexTokenContract.methods.balanceOf(config.foreignBridgeAddress).call()
)
logger.debug({ balance: balance.toString() }, 'Half duplex token bridge balance obtained')
if (balance.isZero()) {
logger.info(`Gas estimate failed because half duplex token balance is zero. Tokens swap is discarded.`)
return txToSend
}
logger.error(e, 'Unknown error while processing transaction')
throw e
}
}
// generate data
const data = await foreignBridge.methods.swapTokens().encodeABI()
// push to job
txToSend.push({
data,
gasEstimate,
transactionReference: `swap tokens operation for block number ${blockNumber.toString()}`,
to: config.foreignBridgeAddress
})
return txToSend
}
}
module.exports = swapTokensBuilder

View File

@@ -73,7 +73,7 @@ describe('gasPrice', () => {
await gasPrice.start('home') await gasPrice.start('home')
// when // when
await gasPrice.fetchGasPrice('standard', 1, null, null) await gasPrice.fetchGasPrice('standard', 1, null, () => null)
// then // then
expect(gasPrice.getPrice()).to.equal('101000000000') expect(gasPrice.getPrice()).to.equal('101000000000')
@@ -113,7 +113,7 @@ describe('gasPrice', () => {
} }
// when // when
await gasPrice.fetchGasPrice('standard', 1, bridgeContractMock, null) await gasPrice.fetchGasPrice('standard', 1, bridgeContractMock, () => {})
// then // then
expect(gasPrice.getPrice().toString()).to.equal('102000000000') expect(gasPrice.getPrice().toString()).to.equal('102000000000')
@@ -156,7 +156,7 @@ describe('gasPrice', () => {
await gasPrice.start('home') await gasPrice.start('home')
// when // when
await gasPrice.fetchGasPrice('standard', 1, null, null) await gasPrice.fetchGasPrice('standard', 1, null, () => {})
// then // then
expect(fakeLogger.error.calledTwice).to.equal(true) // two errors expect(fakeLogger.error.calledTwice).to.equal(true) // two errors

View File

@@ -1,6 +1,6 @@
const { BN, toBN } = require('web3').utils const { BN, toBN } = require('web3').utils
const { expect } = require('chai').use(require('bn-chai')(BN)) const { expect } = require('chai').use(require('bn-chai')(BN))
const { createMessage, parseMessage, signatureToVRS } = require('../src/utils/message') const { createMessage, parseMessage, signatureToVRS, parseAMBHeader } = require('../src/utils/message')
describe('message utils', () => { describe('message utils', () => {
const expectedMessageLength = 104 const expectedMessageLength = 104
@@ -288,4 +288,19 @@ describe('message utils', () => {
expect(signatureThunk).to.throw() expect(signatureThunk).to.throw()
}) })
}) })
describe('parseAMBHeader', () => {
it('should return correct values for parsed headers', () => {
// given
const message =
'0x000500009a6ff99b356dd998260582be7d95a4d08b2132600000000000000061339d0e6f308a410f18888932bdf661636a0f538f34718200957aeadd6bece186e61b95618e73a6dc000f42400101002a4d2fb102cf00000000000000000000000081c770bbe8f5f41b4642ed575e630c911c94e4070000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002e516d543162324178466b6643456a6f715861547148734370666f724a4c66765667434853516e513847575a347662000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e516d5a7543586f693378487338486e4c325a423539316674466f4c454471516b473655746e47324d6d513147614e000000000000000000000000000000000000'
const { messageId, sender, executor, gasLimit } = parseAMBHeader(message)
// then
expect(messageId).to.equal('0x000500009a6ff99b356dd998260582be7d95a4d08b2132600000000000000061')
expect(sender).to.equal('0x339d0e6f308a410f18888932bdf661636a0f538f')
expect(executor).to.equal('0x34718200957aeadd6bece186e61b95618e73a6dc')
expect(gasLimit).to.equal(1000000)
})
})
}) })

View File

@@ -34,6 +34,14 @@ describe('utils', () => {
expect(result.toString()).to.equal('225') expect(result.toString()).to.equal('225')
}) })
it('should handle maxGasLimit', () => {
const result1 = addExtraGas(new BigNumber(100), 0.25, 110)
const result2 = addExtraGas(new BigNumber(100), 0.25, 150)
expect(result1.toString()).to.equal('110')
expect(result2.toString()).to.equal('125')
})
}) })
describe('checkHTTPS', () => { describe('checkHTTPS', () => {

View File

@@ -43,6 +43,7 @@
"test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e --exclude alm-e2e test", "test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e --exclude alm-e2e test",
"oracle-e2e": "./oracle-e2e/run-tests.sh", "oracle-e2e": "./oracle-e2e/run-tests.sh",
"ui-e2e": "./ui-e2e/run-tests.sh", "ui-e2e": "./ui-e2e/run-tests.sh",
"ui-e2e:ci": "xvfb-run yarn ui-e2e",
"monitor-e2e": "./monitor-e2e/run-tests.sh", "monitor-e2e": "./monitor-e2e/run-tests.sh",
"alm-e2e": "./alm-e2e/run-tests.sh", "alm-e2e": "./alm-e2e/run-tests.sh",
"clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build ./**/**/dist", "clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build ./**/**/dist",

View File

@@ -51,6 +51,7 @@
"Dcef88209a20D52165230104B245803C3269454d": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, "Dcef88209a20D52165230104B245803C3269454d": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
"bb140FbA6242a1c3887A7823F7750a73101383e3": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, "bb140FbA6242a1c3887A7823F7750a73101383e3": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
"7FC1442AB55Da569940Eb750AaD2BAA63DA4010E": { "balance": "500000000000000000000" }, "7FC1442AB55Da569940Eb750AaD2BAA63DA4010E": { "balance": "500000000000000000000" },
"B4579fd5AfEaB60B03Db3F408AAdD315035943f7": { "balance": "500000000000000000000" } "B4579fd5AfEaB60B03Db3F408AAdD315035943f7": { "balance": "500000000000000000000" },
"c9e38bfdB9c635F0796ad83CC8705dc379F41c04": { "balance": "500000000000000000000" }
} }
} }

Binary file not shown.

View File

@@ -3,7 +3,7 @@ const { By } = require('selenium-webdriver/lib/by')
const { Page } = require('./Page.js') const { Page } = require('./Page.js')
const { homeRPC, foreignRPC } = require('../e2e-commons/constants.json') const { homeRPC, foreignRPC } = require('../e2e-commons/constants.json')
const IDMetaMask = 'dapggmdndodedfoaljbglbkaicfpmkkm' const IDMetaMask = 'hccmbhdehlhjhkenmcjnbcahkmljpife'
const URL = 'chrome-extension://' + IDMetaMask + '//popup.html' const URL = 'chrome-extension://' + IDMetaMask + '//popup.html'
const buttonSubmit = By.className('confirm btn-green') const buttonSubmit = By.className('confirm btn-green')
const buttonAccept = By.xpath('//*[@id="app-content"]/div/div[4]/div/button') const buttonAccept = By.xpath('//*[@id="app-content"]/div/div[4]/div/button')

View File

@@ -46,8 +46,11 @@ class Utils {
static async startBrowserWithMetamask() { static async startBrowserWithMetamask() {
const source = './MetaMask.crx' const source = './MetaMask.crx'
const options = new chrome.Options() const options = new chrome.Options()
await options.addExtensions(source) await options.addArguments('--no-sandbox')
await options.addArguments('--disable-gpu')
await options.addArguments('--disable-dev-shm-usage')
await options.addArguments('disable-popup-blocking') await options.addArguments('disable-popup-blocking')
await options.addExtensions(source)
const driver = await new webdriver.Builder().withCapabilities(options.toCapabilities()).build() const driver = await new webdriver.Builder().withCapabilities(options.toCapabilities()).build()
await driver.sleep(5000) await driver.sleep(5000)
return driver return driver

View File

@@ -4,7 +4,6 @@
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"chromedriver": "^77.0.0",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"selenium-webdriver": "3.6.0" "selenium-webdriver": "3.6.0"
}, },
@@ -12,6 +11,6 @@
"lint": "eslint . --ignore-path ../.eslintignore" "lint": "eslint . --ignore-path ../.eslintignore"
}, },
"engines": { "engines": {
"node": ">= 8.9" "node": ">= 10.18"
} }
} }

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cd $(dirname $0) cd $(dirname $0)
../e2e-commons/up.sh deploy oracle ui blocks ../e2e-commons/up.sh deploy oracle ui blocks

View File

@@ -83,7 +83,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance) console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
homeBalanceBefore = newHomeBalance homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
}) })
@@ -95,7 +95,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
console.log('newForeignBalance = ' + newForeignBalance) console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
}) })
@@ -115,7 +115,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance) console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
}) })
@@ -124,7 +124,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance) console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
}) })
}) })
@@ -173,7 +173,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance) console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
}) })
@@ -182,7 +182,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance) console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
}) })
test.it('User is able to send tokens from Home account to Foreign account ', async () => { test.it('User is able to send tokens from Home account to Foreign account ', async () => {
@@ -202,7 +202,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance) console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
homeBalanceBefore = newHomeBalance homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
}) })
@@ -214,7 +214,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
console.log('newForeignBalance = ' + newForeignBalance) console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
}) })
}) })
@@ -263,7 +263,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance) console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
}) })
@@ -272,7 +272,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance) console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
}) })
test.it('User is able to send tokens from Home account to Foreign account', async () => { test.it('User is able to send tokens from Home account to Foreign account', async () => {
@@ -292,7 +292,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance) console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
homeBalanceBefore = newHomeBalance homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
}) })
@@ -304,7 +304,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
console.log('newForeignBalance = ' + newForeignBalance) console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
}) })
}) })
@@ -353,7 +353,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance) console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
}) })
@@ -362,7 +362,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance) console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
}) })
test.it('User is able to send tokens from Home account to Foreign account', async () => { test.it('User is able to send tokens from Home account to Foreign account', async () => {
@@ -382,7 +382,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance) console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
homeBalanceBefore = newHomeBalance homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
}) })
@@ -394,7 +394,7 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
console.log('newForeignBalance = ' + newForeignBalance) console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe) console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100 const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction') return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
}) })
}) })

View File

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

View File

@@ -5,7 +5,6 @@
"dependencies": { "dependencies": {
"@babel/plugin-proposal-decorators": "^7.4.0", "@babel/plugin-proposal-decorators": "^7.4.0",
"bignumber.js": "^6.0.0", "bignumber.js": "^6.0.0",
"coveralls": "^3.0.0",
"customize-cra": "^0.2.12", "customize-cra": "^0.2.12",
"date-fns": "^2.13.0", "date-fns": "^2.13.0",
"dotenv": "^7.0.0", "dotenv": "^7.0.0",
@@ -37,7 +36,6 @@
"test": "react-app-rewired test --env=jsdom --no-watch", "test": "react-app-rewired test --env=jsdom --no-watch",
"test:watch": "react-app-rewired test --env=jsdom", "test:watch": "react-app-rewired test --env=jsdom",
"coverage": "react-app-rewired test --env=jsdom --coverage", "coverage": "react-app-rewired test --env=jsdom --coverage",
"coveralls": "cat ./coverage/lcov.info | node node_modules/.bin/coveralls",
"eject": "react-app-rewired eject", "eject": "react-app-rewired eject",
"postinstall": "(cp lib/web3-eth/index.js ../node_modules/web3-eth/src; cp lib/web3-eth/index.js ./node_modules/web3-eth/src) || :" "postinstall": "(cp lib/web3-eth/index.js ../node_modules/web3-eth/src; cp lib/web3-eth/index.js ./node_modules/web3-eth/src) || :"
}, },

View File

@@ -325,7 +325,7 @@ class ForeignStore {
@action @action
async getCurrentLimit() { async getCurrentLimit() {
try { try {
const result = await getCurrentLimit(this.foreignBridge, this.tokenDecimals) const result = await getCurrentLimit(this.foreignBridge, this.homeStore.homeBridge, this.tokenDecimals)
this.maxCurrentDeposit = result.maxCurrentDeposit this.maxCurrentDeposit = result.maxCurrentDeposit
this.dailyLimit = result.dailyLimit this.dailyLimit = result.dailyLimit
this.totalSpentPerDay = result.totalSpentPerDay this.totalSpentPerDay = result.totalSpentPerDay

View File

@@ -53,7 +53,8 @@ class GasPriceStore {
} }
const oracleOptions = { speedType: this.speedType, factor: this.factor, logger: console } const oracleOptions = { speedType: this.speedType, factor: this.factor, logger: console }
this.gasPrice = (await gasPriceFromSupplier(() => fetch(this.gasPriceSupplierUrl), oracleOptions)) || this.gasPrice const fetchFn = this.gasPriceSupplierUrl === 'gas-price-oracle' ? null : () => fetch(this.gasPriceSupplierUrl)
this.gasPrice = (await gasPriceFromSupplier(fetchFn, oracleOptions)) || this.gasPrice
setTimeout(() => this.updateGasPrice(), this.updateInterval) setTimeout(() => this.updateGasPrice(), this.updateInterval)
} }

View File

@@ -429,7 +429,11 @@ class HomeStore {
@action @action
async getCurrentLimit() { async getCurrentLimit() {
try { try {
const result = await getCurrentLimit(this.homeBridge, this.tokenDecimals) const result = await getCurrentLimit(
this.homeBridge,
this.rootStore.foreignStore.foreignBridge,
this.tokenDecimals
)
this.maxCurrentDeposit = result.maxCurrentDeposit this.maxCurrentDeposit = result.maxCurrentDeposit
this.dailyLimit = result.dailyLimit this.dailyLimit = result.dailyLimit
this.totalSpentPerDay = result.totalSpentPerDay this.totalSpentPerDay = result.totalSpentPerDay

View File

@@ -21,10 +21,10 @@ export const getMinPerTxLimit = async (contract, decimals) => {
return fromDecimals(minPerTx, decimals) return fromDecimals(minPerTx, decimals)
} }
export const getCurrentLimit = async (contract, decimals) => { export const getCurrentLimit = async (contract, otherContract, decimals) => {
const currentDay = await contract.methods.getCurrentDay().call() const currentDay = await contract.methods.getCurrentDay().call()
const dailyLimit = await contract.methods.dailyLimit().call() const dailyLimit = await contract.methods.dailyLimit().call()
const totalSpentPerDay = await contract.methods.totalSpentPerDay(currentDay).call() const totalSpentPerDay = await otherContract.methods.totalExecutedPerDay(currentDay).call()
const maxCurrentDeposit = new BN(dailyLimit).minus(new BN(totalSpentPerDay)).toString(10) const maxCurrentDeposit = new BN(dailyLimit).minus(new BN(totalSpentPerDay)).toString(10)
return { return {
maxCurrentDeposit: fromDecimals(maxCurrentDeposit, decimals), maxCurrentDeposit: fromDecimals(maxCurrentDeposit, decimals),

140
yarn.lock
View File

@@ -4209,6 +4209,13 @@ axios@0.19.0:
follow-redirects "1.5.10" follow-redirects "1.5.10"
is-buffer "^2.0.2" is-buffer "^2.0.2"
axios@^0.19.2:
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: axobject-query@^2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9"
@@ -5840,17 +5847,6 @@ chrome-trace-event@^1.0.0:
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
chromedriver@^77.0.0:
version "77.0.0"
resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-77.0.0.tgz#bd916cc87a0ccb7a6e4fb4b43cb2368bc54db6a0"
integrity sha512-mZa1IVx4HD8rDaItWbnS470mmypgiWsDiu98r0NkiT4uLm3qrANl4vOU6no6vtWtLQiW5kt1POcIbjeNpsLbXA==
dependencies:
del "^4.1.1"
extract-zip "^1.6.7"
mkdirp "^0.5.1"
request "^2.88.0"
tcp-port-used "^1.0.1"
ci-info@^1.5.0: ci-info@^1.5.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
@@ -6235,7 +6231,7 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.5.1, concat-stream@^1.6.0, concat-stream@^1.6.2: concat-stream@^1.5.0, concat-stream@^1.5.1, concat-stream@^1.6.0, concat-stream@^1.6.2:
version "1.6.2" version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
@@ -6550,18 +6546,6 @@ cosmiconfig@^5.0.0, cosmiconfig@^5.0.7, cosmiconfig@^5.1.0, cosmiconfig@^5.2.0:
js-yaml "^3.13.1" js-yaml "^3.13.1"
parse-json "^4.0.0" parse-json "^4.0.0"
coveralls@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.3.tgz#83b1c64aea1c6afa69beaf50b55ac1bc4d13e2b8"
integrity sha512-viNfeGlda2zJr8Gj1zqXpDMRjw9uM54p7wzZdvLRyOgnAfCe974Dq4veZkjJdxQXbmdppu6flEajFYseHYaUhg==
dependencies:
growl "~> 1.10.0"
js-yaml "^3.11.0"
lcov-parse "^0.0.10"
log-driver "^1.2.7"
minimist "^1.2.0"
request "^2.86.0"
coveralls@^3.0.6: coveralls@^3.0.6:
version "3.0.11" version "3.0.11"
resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.11.tgz#e141da0922b632fcc66620f334460c3f0026a4ce" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.11.tgz#e141da0922b632fcc66620f334460c3f0026a4ce"
@@ -7054,13 +7038,6 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87"
integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==
dependencies:
ms "^2.1.1"
debuglog@^1.0.1: debuglog@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
@@ -7192,7 +7169,7 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
deep-is@^0.1.3, deep-is@~0.1.3: deep-is@~0.1.3:
version "0.1.3" version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
@@ -7302,19 +7279,6 @@ del@^3.0.0:
pify "^3.0.0" pify "^3.0.0"
rimraf "^2.2.8" rimraf "^2.2.8"
del@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4"
integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==
dependencies:
"@types/glob" "^7.1.1"
globby "^6.1.0"
is-path-cwd "^2.0.0"
is-path-in-cwd "^2.0.0"
p-map "^2.0.0"
pify "^4.0.1"
rimraf "^2.6.3"
delayed-stream@~1.0.0: delayed-stream@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@@ -9101,16 +9065,6 @@ extract-comments@^1.1.0:
esprima-extract-comments "^1.1.0" esprima-extract-comments "^1.1.0"
parse-code-context "^1.0.0" parse-code-context "^1.0.0"
extract-zip@^1.6.7:
version "1.6.7"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=
dependencies:
concat-stream "1.6.2"
debug "2.6.9"
mkdirp "0.5.1"
yauzl "2.4.1"
extract-zip@^2.0.0: extract-zip@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
@@ -9227,13 +9181,6 @@ fb-watchman@^2.0.0:
dependencies: dependencies:
bser "^2.0.0" bser "^2.0.0"
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=
dependencies:
pend "~1.2.0"
fd-slicer@~1.1.0: fd-slicer@~1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
@@ -9822,6 +9769,14 @@ ganache-core@^2.6.0:
ethereumjs-wallet "0.6.3" ethereumjs-wallet "0.6.3"
web3 "1.2.4" web3 "1.2.4"
gas-price-oracle@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/gas-price-oracle/-/gas-price-oracle-0.1.5.tgz#09dd0d9806465c2f5e63b682e6742f96f6eb525c"
integrity sha512-fkaTXnxJcSVco/tMPEcN5gieoUNs8O6JYMXflGLN2+3YeGZAucUI0fgCliazM3nRVAk//bBEm9819/Zb83xhrw==
dependencies:
axios "^0.19.2"
bignumber.js "^9.0.0"
gauge@~2.7.3: gauge@~2.7.3:
version "2.7.4" version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -10316,7 +10271,7 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.6:
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
growl@1.10.5, "growl@~> 1.10.0": growl@1.10.5:
version "1.10.5" version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
@@ -11556,11 +11511,6 @@ is-path-cwd@^1.0.0:
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
is-path-cwd@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
is-path-in-cwd@^1.0.0: is-path-in-cwd@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
@@ -11568,13 +11518,6 @@ is-path-in-cwd@^1.0.0:
dependencies: dependencies:
is-path-inside "^1.0.0" is-path-inside "^1.0.0"
is-path-in-cwd@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb"
integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==
dependencies:
is-path-inside "^2.1.0"
is-path-inside@^1.0.0: is-path-inside@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
@@ -11582,13 +11525,6 @@ is-path-inside@^1.0.0:
dependencies: dependencies:
path-is-inside "^1.0.1" path-is-inside "^1.0.1"
is-path-inside@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2"
integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==
dependencies:
path-is-inside "^1.0.2"
is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
@@ -11719,11 +11655,6 @@ is-unc-path@^1.0.0:
dependencies: dependencies:
unc-path-regex "^0.1.2" unc-path-regex "^0.1.2"
is-url@^1.2.2:
version "1.2.4"
resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
is-utf8@^0.2.0, is-utf8@^0.2.1: is-utf8@^0.2.0, is-utf8@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
@@ -11749,15 +11680,6 @@ is-wsl@^1.1.0:
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
is2@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.1.tgz#8ac355644840921ce435d94f05d3a94634d3481a"
integrity sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==
dependencies:
deep-is "^0.1.3"
ip-regex "^2.1.0"
is-url "^1.2.2"
isarray@0.0.1: isarray@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -12423,7 +12345,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@3.x, js-yaml@^3.11.0, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.9.0: js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.9.0:
version "3.13.1" version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
@@ -12811,11 +12733,6 @@ lcid@^2.0.0:
dependencies: dependencies:
invert-kv "^2.0.0" invert-kv "^2.0.0"
lcov-parse@^0.0.10:
version "0.0.10"
resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3"
integrity sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=
lcov-parse@^1.0.0: lcov-parse@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0"
@@ -15065,7 +14982,7 @@ p-map@^1.1.1:
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
p-map@^2.0.0, p-map@^2.1.0: p-map@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
@@ -17525,7 +17442,7 @@ request@^2.67.0, request@^2.85.0, request@^2.87.0, request@^2.88.0:
tunnel-agent "^0.6.0" tunnel-agent "^0.6.0"
uuid "^3.3.2" uuid "^3.3.2"
request@^2.79.0, request@^2.86.0: request@^2.79.0:
version "2.88.0" version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
@@ -19367,14 +19284,6 @@ tar@^4.0.2, tar@^4.4.10, tar@^4.4.12, tar@^4.4.8:
safe-buffer "^5.1.2" safe-buffer "^5.1.2"
yallist "^3.0.3" yallist "^3.0.3"
tcp-port-used@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.1.tgz#46061078e2d38c73979a2c2c12b5a674e6689d70"
integrity sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q==
dependencies:
debug "4.1.0"
is2 "2.0.1"
temp-dir@^1.0.0: temp-dir@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
@@ -22418,13 +22327,6 @@ yargs@^7.0.0, yargs@^7.1.0:
y18n "^3.2.1" y18n "^3.2.1"
yargs-parser "^5.0.0" yargs-parser "^5.0.0"
yauzl@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=
dependencies:
fd-slicer "~1.0.1"
yauzl@^2.10.0, yauzl@^2.4.2: yauzl@^2.10.0, yauzl@^2.4.2:
version "2.10.0" version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"