Compare commits

...

26 Commits

Author SHA1 Message Date
Alexander Kolotov
1122daf9a1 Merge the develop branch to the master branch, preparation to v2.6.0-rc2 2020-11-08 15:35:47 +03:00
Kirill Fedoseev
12269d7426 Remove access lists tests from ultimate test suites (#488) 2020-11-08 01:11:21 +03:00
Alexander Kolotov
683fa0728d Update the contract's submodule to the release 5.5.0 (#486) 2020-11-07 16:22:53 +03:00
Alexander Kolotov
dd2075c351 Security Audit report by Quantstamp added (#485) 2020-11-07 13:15:35 +03:00
Kirill Fedoseev
ce29b95729 Support AMB manual lane and allowance/block lists in the monitor (#484) 2020-11-04 22:16:43 +03:00
Kirill Fedoseev
eb1069497a Cache fetched events in monitor (#482) 2020-11-04 14:24:42 +03:00
Kirill Fedoseev
0228fc7d5f Support of manual lane in the AMB oracle (#483) 2020-10-31 21:02:56 +03:00
Kirill Fedoseev
f8d85b14de Add allowance/block lists support to monitor (#477) 2020-10-29 11:25:43 +03:00
Kirill Fedoseev
5fa9d21246 Fix set-env deprecation warnings (#478) 2020-10-28 15:35:11 +03:00
Kirill Fedoseev
611b8c539d Improve workflow with several RPC URLs in oracle (#476) 2020-10-28 14:20:50 +03:00
Kirill Fedoseev
389cea3c39 Reduce time complexity of events comparison in monitor (#475) 2020-10-23 13:48:37 +03:00
Alexander Kolotov
fbce0fc035 Merge the develop branch to the master branch, preparation to v2.6.0-rc1 2020-10-14 22:29:18 +03:00
Max Alekseenko
621b20d070 Allow cors in monitor server (#472) 2020-10-12 21:16:41 +03:00
Alexander Kolotov
48752e8575 Remove nonce update for the re-send case (#470) 2020-10-08 18:51:01 +03:00
Kirill Fedoseev
4efda98f2b Do not reset nonce, if nothing was resent (#469) 2020-10-07 15:28:57 +03:00
Kirill Fedoseev
aff8b777c5 Add requiredBlockConfirmations ABI to the NATIVE_TO_ERC_V1 bridge mode (#468) 2020-10-06 21:35:30 +03:00
Kirill Fedoseev
74293959f3 Fixed resent job for failed transactions (#466) 2020-10-06 15:15:35 +03:00
Kirill Fedoseev
46daeb6815 Respect requiredBlockConfirmations parameter in the monitor (#464) 2020-10-05 15:48:36 +03:00
Alexander Kolotov
44ca0d71ce Merge the develop branch to the master branch, preparation to v2.6.0-rc0 2020-10-02 20:34:31 +03:00
Kirill Fedoseev
fbeb878cdb Fix insufficient funds error handler (#459) 2020-10-02 19:46:07 +03:00
Alexander Kolotov
d17ea2ad2b One point of handling all collected signatures requests (#457) 2020-10-02 19:44:05 +03:00
Kirill Fedoseev
4cc87ef61a Use totalExecutedPerDay instead of totalSpentPerDay for calculating quota (#460) 2020-10-02 19:07:40 +03:00
Alexander Kolotov
125b66b86d Update requirements for higher bound of gas price (#458) 2020-10-01 10:53:11 +03:00
Alexander Kolotov
7a0ed3f699 Small update of the execution waiting status description in ALM (#456) 2020-10-01 10:52:22 +03:00
Kirill Fedoseev
4e04f2ae1f Update the condition for checking if a tx was stuck (#444) 2020-09-30 19:30:51 +03:00
Kirill Fedoseev
dc377aeb9b Possibility to allow/block specific addresses in erc-to-native mode (#442) 2020-09-28 14:54:03 +03:00
107 changed files with 2550 additions and 1772 deletions

View File

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

View File

@@ -19,9 +19,11 @@ jobs:
- 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') }}"
- name: Set cache key
id: get_cache_key
run: |
git submodule status > submodule.status
echo "::set-output name=cache_key::cache-repo-${{ hashFiles('yarn.lock', 'package.json', 'submodule.status') }}"
- uses: actions/cache@v2
id: cache-repo
with:
@@ -29,7 +31,8 @@ jobs:
**/node_modules
contracts/build
key: ${{ steps.get_cache_key.outputs.cache_key }}
- if: ${{ !steps.cache-repo.outputs.cache-hit }}
- name: Install dependencies and compile contracts
if: ${{ !steps.cache-repo.outputs.cache-hit }}
run: |
yarn install --frozen-lockfile
yarn run install:deploy
@@ -56,7 +59,8 @@ jobs:
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
- name: yarn run ${{ matrix.task }}
run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
ui-coverage:
runs-on: ubuntu-latest
needs:
@@ -76,7 +80,8 @@ jobs:
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn workspace ui run coverage
- name: yarn workspace ui run coverage
run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn workspace ui run coverage
- uses: coverallsapp/github-action@master
with:
github-token: ${{ github.token }}
@@ -87,14 +92,16 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: true
- run: |
- name: Evaluate e2e docker images tags
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: |
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
- name: Rebuild and push updated images
run: |
function check_if_image_exists() {
curl -fsSlL -H 'Authorization: bearer ${{ github.token }}' "https://${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
}
@@ -119,8 +126,10 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: true
- run: echo "::set-env name=MOLECULE_RUNNER_TAG::${{ hashFiles('./deployment-e2e/Dockerfile') }}"
- run: |
- name: Evaluate e2e molecule runner tag
run: echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV
- name: Rebuild and push molecule runner e2e image
run: |
function check_if_image_exists() {
curl -fsSlL -H 'Authorization: bearer ${{ github.token }}' "https://${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
}
@@ -150,14 +159,15 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: true
- run: |
- name: Evaluate e2e docker images tags
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 }}
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
- if: ${{ matrix.use-cache }}
uses: actions/cache@v2
id: cache-repo
with:
@@ -165,8 +175,10 @@ jobs:
**/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 }}
- name: Login to docker registry
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- name: yarn run ${{ matrix.task }}
run: ${{ !matrix.use-cache || steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
deployment:
runs-on: ubuntu-latest
needs:
@@ -179,8 +191,10 @@ jobs:
- 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 }}
- name: Evaluate e2e molecule runner tag
run: echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV
- name: Login to docker registry
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- run: deployment-e2e/molecule.sh ${{ matrix.task }}
ultimate:
runs-on: ubuntu-latest
@@ -205,14 +219,15 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: true
- run: |
- name: Evaluate e2e docker images tags
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') }}"
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV
- uses: actions/cache@v2
id: cache-repo
with:
@@ -220,12 +235,19 @@ jobs:
**/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 }}
- name: Login to docker registry
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- name: Deploy contracts
run: ${{ steps.cache-repo.outputs.cache-hit }} && e2e-commons/up.sh deploy blocks
- name: Pull e2e oracle image
run: docker-compose -f ./e2e-commons/docker-compose.yml pull oracle
- name: Deploy oracle and ui
run: deployment-e2e/molecule.sh ultimate-${{ matrix.task }}
- name: Reset docker socket permissions
run: sudo chown -R $USER:docker /var/run/docker.sock
- name: Run ui e2e tests
if: ${{ matrix.ui-e2e-grep }}
run: cd ui-e2e && xvfb-run yarn mocha -g "${{ matrix.ui-e2e-grep }}" -b ./test.js
- if: ${{ !matrix.ui-e2e-grep }}
- name: Run oracle e2e tests
if: ${{ !matrix.ui-e2e-grep }}
run: docker-compose -f ./e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run ${{ matrix.task }}

10
.gitignore vendored
View File

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

View File

@@ -22,7 +22,7 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR | A value that will multiply the gas price of th
name | description | value
--- | --- | ---
ORACLE_BRIDGE_MODE | The bridge mode. The bridge starts listening to a different set of events based on this parameter. | NATIVE_TO_ERC / ERC_TO_ERC / ERC_TO_NATIVE
ORACLE_BRIDGE_MODE | The bridge mode. The bridge starts listening to a different set of events based on this parameter. | NATIVE_TO_ERC / ERC_TO_ERC / ERC_TO_NATIVE / ARBITRARY_MESSAGE
ORACLE_ALLOW_HTTP_FOR_RPC | **Only use in test environments - must be omitted in production environments.**. If this parameter is specified and set to `yes`, RPC URLs can be specified in form of HTTP links. A warning that the connection is insecure will be written to the logs. | `yes` / `no`
ORACLE_HOME_RPC_POLLING_INTERVAL | The interval in milliseconds used to request the RPC node in the Home network for new blocks. The interval should match the average production time for a new block. | integer
ORACLE_FOREIGN_RPC_POLLING_INTERVAL | The interval in milliseconds used to request the RPC node in the Foreign network for new blocks. The interval should match the average production time for a new block. | integer
@@ -37,6 +37,11 @@ ORACLE_MAX_PROCESSING_TIME | The workers processes will be killed if this amount
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY | The private key of the bridge validator used to sign confirmations before sending transactions to the bridge contracts. The validator account is calculated automatically from the private key. Every bridge instance (set of watchers and senders) must have its own unique private key. The specified private key is used to sign transactions on both sides of the bridge. | hexidecimal without "0x"
ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexidecimal with "0x"
ORACLE_TX_REDUNDANCY | If set to `true`, instructs oracle to send `eth_sendRawTransaction` requests through all available RPC urls defined in `COMMON_HOME_RPC_URL` and `COMMON_FOREIGN_RPC_URL` variables instead of using first available one
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST | Filename with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests will be automatically processed by the CollectedSignatures watcher. | string
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST | Filename with a list of addresses, separated by newlines. If set, determines the blocked set of accounts whose requests will not be automatically processed by the CollectedSignatures watcher. Has a lower priority than the `ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST` | string
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`
ORACLE_ALWAYS_RELAY_SIGNATURES | If set to `true`, the oracle will always relay signatures even if it was not the last who finilized the signatures collecting process. The default is `false`. | `true` / `false`
ORACLE_RPC_REQUEST_TIMEOUT | Timeout in milliseconds for a single RPC request. Default value is `ORACLE_*_RPC_POLLING_INTERVAL * 2`. | integer
## UI configuration
@@ -73,4 +78,7 @@ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT | Average gas usage of a transaction sent by
MONITOR_TX_NUMBER_THRESHOLD | If estimated number of transaction is equal to or below this value, the monitor will report that the validator has less funds than it is required. | integer
MONITOR_PORT | The port for the Monitor. | integer
MONITOR_BRIDGE_NAME | The name to be used in the url path for the bridge | string
MONITOR_CACHE_EVENTS | If set to true, monitor will cache obtained events for other workers runs
MONITOR_CACHE_EVENTS | If set to true, monitor will cache obtained events for other workers runs | `true` / `false`
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST | File with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests should be automatically processed by the CollectedSignatures watcher. | string
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST | File with a list of addresses, separated by newlines. If set, determines the set of accounts whose requests should be marked as unclaimed. Has a lower priority than the `MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST`. | string
MONITOR_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`

View File

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

View File

@@ -59,7 +59,7 @@ export const CONFIRMATIONS_STATUS_DESCRIPTION_HOME: { [key: string]: string } =
EXECUTION_PENDING:
'The specified transaction was included in a block\nand the validators collected signatures. The\nvalidators transaction with collected signatures was\nsent but is not yet added to a block.',
EXECUTION_WAITING:
'The specified transaction was included in a block\nand the validators collected signatures. Either\n1. One of the validators is waiting for chain finalization.\n2. A validator skipped its duty to relay signatures.\nCheck status again after a few blocks. If the issue still persists contact to the validators by messaging on %linkhttps://forum.poa.network/c/support',
'The specified transaction was included in a block\nand the validators collected signatures. 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:
'The specified transaction was included in a block,\nbut transactions with signatures sent by a majority of\nvalidators failed. The cross-chain relay request will\nnot be processed. Contact to the validators by\nmessaging on %linkhttps://forum.poa.network/c/support',
PENDING:

View File

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

View File

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

View File

@@ -6,17 +6,30 @@ function addTxHashToData({ encodedData, transactionHash }) {
return encodedData.slice(0, 2) + strip0x(transactionHash) + encodedData.slice(2)
}
/**
* Decodes the datatype byte from the AMB message.
* First (the most significant bit) denotes if the message should be forwarded to the manual lane.
* @param dataType: number datatype of the received AMB message.
* @return {{manualLane: boolean}}
*/
const decodeAMBDataType = dataType => ({
manualLane: (dataType & 128) === 128
})
function parseAMBMessage(message) {
message = strip0x(message)
const messageId = `0x${message.slice(0, 64)}`
const sender = `0x${message.slice(64, 104)}`
const executor = `0x${message.slice(104, 144)}`
const dataType = parseInt(message.slice(156, 158), 16)
return {
sender,
executor,
messageId
messageId,
dataType,
decodedDataType: decodeAMBDataType(dataType)
}
}

View File

@@ -164,18 +164,18 @@ const getPastEvents = async (
} catch (e) {
if (e.message.includes('query returned more than') && toBlock !== 'latest') {
const middle = toBN(fromBlock)
.add(toBlock)
.add(toBN(toBlock))
.divRound(toBN(2))
const middlePlusOne = middle.add(toBN(1))
const firstHalfEvents = await getPastEvents(contract, {
...options,
options,
event,
fromBlock,
toBlock: middle
})
const secondHalfEvents = await getPastEvents(contract, {
...options,
options,
event,
fromBlock: middlePlusOne,
toBlock

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,6 +15,10 @@
"address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B",
"privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1"
},
"blockedUser": {
"address": "0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04",
"privateKey": "0x65df4ea787916f6ed9660f0b0fe36858a65735ad0dcd34527497f4ce32e53883"
},
"validator": {
"address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b",
"privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
@@ -59,6 +63,7 @@
"foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
"homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"blockedHomeBox": "0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE",
"monitor": "http://monitor-amb:3013/bridge"
},
"ambStakeErcToErc": {

View File

@@ -50,6 +50,9 @@ services:
environment:
- NODE_ENV=production
command: "true"
volumes:
- '../e2e-commons/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks:
- ultimate
oracle-amb:
@@ -58,6 +61,9 @@ services:
environment:
- NODE_ENV=production
command: "true"
volumes:
- '../e2e-commons/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks:
- ultimate
ui:

View File

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

View File

@@ -17,21 +17,29 @@ docker-compose up -d parity1 parity2 e2e
startValidator () {
docker-compose $1 run -d --name $4 redis
docker-compose $1 run -d --name $5 rabbit
docker-compose $1 run $2 $3 -d oracle yarn watcher:signature-request
docker-compose $1 run $2 $3 -d oracle yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle yarn watcher:affirmation-request
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:signature-request
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:affirmation-request
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:transfer
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:signature-request
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:convert-to-chai
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:signature-request
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request
if [[ -z "$MODE" || "$MODE" == native-to-erc ]]; then
docker-compose $1 run $2 $3 -d oracle yarn watcher:signature-request
docker-compose $1 run $2 $3 -d oracle yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle yarn watcher:affirmation-request
fi
if [[ -z "$MODE" || "$MODE" == erc-to-erc ]]; then
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:signature-request
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:affirmation-request
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:transfer
fi
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:signature-request
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:convert-to-chai
fi
if [[ -z "$MODE" || "$MODE" == amb ]]; then
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:signature-request
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request
fi
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:home
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:foreign
}
@@ -48,25 +56,7 @@ startAMBValidator () {
while [ "$1" != "" ]; do
if [ "$1" == "oracle" ]; then
docker-compose up -d redis rabbit
docker-compose run -d oracle yarn watcher:signature-request
docker-compose run -d oracle yarn watcher:collected-signatures
docker-compose run -d oracle yarn watcher:affirmation-request
docker-compose run -d oracle-erc20 yarn watcher:signature-request
docker-compose run -d oracle-erc20 yarn watcher:collected-signatures
docker-compose run -d oracle-erc20 yarn watcher:affirmation-request
docker-compose run -d oracle-erc20 yarn watcher:transfer
docker-compose run -d oracle-erc20-native yarn watcher:signature-request
docker-compose run -d oracle-erc20-native yarn watcher:collected-signatures
docker-compose run -d oracle-erc20-native yarn watcher:affirmation-request
docker-compose run -d oracle-erc20-native yarn watcher:transfer
docker-compose run -d oracle-erc20-native yarn worker:convert-to-chai
docker-compose run -d oracle-amb yarn watcher:signature-request
docker-compose run -d oracle-amb yarn watcher:collected-signatures
docker-compose run -d oracle-amb yarn watcher:affirmation-request
docker-compose run -d oracle yarn sender:home
docker-compose run -d oracle yarn sender:foreign
startValidator "" "" "" "redis" "rabbit"
fi
if [ "$1" == "oracle-validator-2" ]; then
@@ -110,7 +100,23 @@ while [ "$1" != "" ]; do
fi
if [ "$1" == "monitor" ]; then
docker-compose up -d monitor monitor-erc20 monitor-erc20-native monitor-amb
case "$MODE" in
amb)
docker-compose up -d monitor-amb
;;
native-to-erc)
docker-compose up -d monitor
;;
erc-to-erc)
docker-compose up -d monitor-erc20
;;
erc-to-native)
docker-compose up -d monitor-erc20-native
;;
*)
docker-compose up -d monitor monitor-erc20 monitor-erc20-native monitor-amb
;;
esac
fi
if [ "$1" == "alm-e2e" ]; then

View File

@@ -14,7 +14,7 @@
"axios": "0.19.0"
},
"engines": {
"node": ">= 8.9"
"node": ">= 10.18"
},
"devDependencies": {}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -44,12 +44,16 @@ const sendTokens = async (rpcUrl, account, tokenAddress, recipientAddress) => {
})
}
const sendAMBMessage = async (rpcUrl, account, boxAddress, bridgeAddress, boxOtherSideAddress) => {
const sendAMBMessage = async (rpcUrl, account, boxAddress, bridgeAddress, boxOtherSideAddress, manualLane = false) => {
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
web3.eth.accounts.wallet.add(account.privateKey)
const homeBox = new web3.eth.Contract(BOX_ABI, boxAddress)
await homeBox.methods.setValueOnOtherNetwork(3, bridgeAddress, boxOtherSideAddress).send({
await homeBox.methods[manualLane ? 'setValueOnOtherNetworkUsingManualLane' : 'setValueOnOtherNetwork'](
3,
bridgeAddress,
boxOtherSideAddress
).send({
from: account.address,
gas: '400000'
})

View File

@@ -6,10 +6,18 @@ check_files_exist() {
rc=0
for f in "${FILES[@]}"; do
command="test -f responses/bridge/$f"
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor /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 -T monitor-erc20-native /bin/bash -c "$command") || rc=1
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-amb /bin/bash -c "$command") || rc=1
if [[ -z "$MODE" || "$MODE" == native-to-erc ]]; then
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor /bin/bash -c "$command") || rc=1
fi
if [[ -z "$MODE" || "$MODE" == erc-to-erc ]]; then
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-erc20 /bin/bash -c "$command") || rc=1
fi
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-erc20-native /bin/bash -c "$command") || rc=1
fi
if [[ -z "$MODE" || "$MODE" == amb ]]; then
(docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-amb /bin/bash -c "$command") || rc=1
fi
done
return $rc
}

View File

@@ -22,3 +22,6 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
MONITOR_TX_NUMBER_THRESHOLD=100
MONITOR_PORT=3003
MONITOR_CACHE_EVENTS=true
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST=
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST=

View File

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

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

View File

@@ -1,21 +1,21 @@
require('dotenv').config()
const Web3 = require('web3')
const BN = require('bignumber.js')
const logger = require('./logger')('checkWorker')
const { getBridgeMode } = require('../commons')
const getBalances = require('./getBalances')
const getShortEventStats = require('./getShortEventStats')
const validators = require('./validators')
const getEventsInfo = require('./utils/events')
const { writeFile, createDir } = require('./utils/file')
const { saveCache } = require('./utils/web3Cache')
const { web3Home } = require('./utils/web3')
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL, MONITOR_BRIDGE_NAME } = process.env
const { COMMON_HOME_BRIDGE_ADDRESS, MONITOR_BRIDGE_NAME } = process.env
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
const { HOME_ERC_TO_ERC_ABI } = require('../commons')
async function checkWorker() {
@@ -24,16 +24,22 @@ async function checkWorker() {
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
const bridgeMode = await getBridgeMode(homeBridge)
logger.debug('Bridge mode:', bridgeMode)
logger.debug('calling getEventsInfo()')
const eventsInfo = await getEventsInfo(bridgeMode)
logger.debug('calling getBalances()')
const balances = await getBalances(bridgeMode)
const balances = await getBalances(bridgeMode, eventsInfo)
logger.debug('calling getShortEventStats()')
const events = await getShortEventStats(bridgeMode)
const events = await getShortEventStats(bridgeMode, eventsInfo)
const home = Object.assign({}, balances.home, events.home)
const foreign = Object.assign({}, balances.foreign, events.foreign)
const status = Object.assign({}, balances, events, { home }, { foreign })
if (status.balanceDiff && status.unclaimedBalance) {
status.balanceDiff = new BN(status.balanceDiff).minus(status.unclaimedBalance).toFixed()
}
if (!status) throw new Error('status is empty: ' + JSON.stringify(status))
status.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/getBalances.json`, status)
saveCache()
logger.debug('calling validators()')
const vBalances = await validators(bridgeMode)

View File

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

View File

@@ -1,15 +1,12 @@
require('dotenv').config()
const Web3 = require('web3')
const logger = require('./logger')('checkWorker3')
const stuckTransfers = require('./stuckTransfers')
const { writeFile, createDir } = require('./utils/file')
const { web3Home } = require('./utils/web3')
const { MONITOR_BRIDGE_NAME, COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL } = process.env
const { MONITOR_BRIDGE_NAME, COMMON_HOME_BRIDGE_ADDRESS } = process.env
const { getBridgeMode, HOME_NATIVE_TO_ERC_ABI, BRIDGE_MODES } = require('../commons')
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
async function checkWorker3() {
try {
const homeBridge = new web3Home.eth.Contract(HOME_NATIVE_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,13 @@
require('dotenv').config()
const express = require('express')
const cors = require('cors')
const { readFile } = require('./utils/file')
const app = express()
const bridgeRouter = express.Router({ mergeParams: true })
app.use(cors())
app.get('/favicon.ico', (req, res) => res.sendStatus(204))
app.use('/:bridgeName', bridgeRouter)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,9 +38,25 @@ function readCacheFile(filePath) {
}
}
function writeCacheFile(filePath, object) {
fs.mkdirSync(path.dirname(filePath), { recursive: true })
fs.writeFileSync(filePath, JSON.stringify(object))
}
function readAccessListFile(filePath) {
const data = fs.readFileSync(filePath)
return data
.toString()
.split('\n')
.map(addr => addr.trim().toLowerCase())
.filter(addr => addr.length === 42)
}
module.exports = {
readFile,
writeFile,
createDir,
readCacheFile
readCacheFile,
writeCacheFile,
readAccessListFile
}

View File

@@ -0,0 +1,93 @@
const { REWARDABLE_VALIDATORS_ABI, processValidatorsEvents } = require('../../commons')
const { getPastEvents } = require('./web3Cache')
const VALIDATORS_INDEXED_EVENTS_ABI = [
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'validator',
type: 'address'
}
],
name: 'ValidatorRemoved',
type: 'event'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'validator',
type: 'address'
}
],
name: 'ValidatorAdded',
type: 'event'
},
{
anonymous: false,
inputs: [
{
indexed: true,
name: 'validator',
type: 'address'
},
{
indexed: true,
name: 'reward',
type: 'address'
}
],
name: 'ValidatorAdded',
type: 'event'
}
]
const tryCall = async (method, fallbackValue) => {
try {
return await method.call()
} catch (e) {
return fallbackValue
}
}
const getValidatorList = async (address, eth, options) => {
const { logger } = options
logger.debug('getting validatorList')
const validatorsContract = new eth.Contract(REWARDABLE_VALIDATORS_ABI, address) // in monitor, BRIDGE_VALIDATORS_ABI was used
const validators = await tryCall(validatorsContract.methods.validatorList(), [])
if (validators.length) {
return validators
}
logger.debug('getting validatorsEvents')
options.fromBlock = Number(await tryCall(validatorsContract.methods.deployedAtBlock(), 0))
const contract = new eth.Contract(VALIDATORS_INDEXED_EVENTS_ABI, address)
const validatorsEvents = [
...(await getPastEvents(contract, {
event: 'ValidatorAdded(address)',
...options
})),
...(await getPastEvents(contract, {
event: 'ValidatorAdded(address,address)',
...options
})),
...(await getPastEvents(contract, {
event: 'ValidatorRemoved(address)',
...options
}))
].sort((a, b) => a.blockNumber - b.blockNumber || a.transactionIndex - b.transactionIndex)
return processValidatorsEvents(validatorsEvents)
}
module.exports = {
getValidatorList
}

View File

@@ -1,44 +1,30 @@
const web3Utils = require('web3').utils
const { parseAMBMessage } = require('../../commons')
const { readAccessListFile } = require('./file')
const { MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST, MONITOR_HOME_TO_FOREIGN_BLOCK_LIST } = process.env
const keyAMB = e => [e.messageId, e.sender, e.executor].join(',').toLowerCase()
const normalizeAMBMessage = e => {
let msgData = e.returnValues.encodedData
if (!e.returnValues.messageId) {
// append tx hash to an old message, where message id was not used
// for old messages, e.messageId is a corresponding transactionHash
msgData = e.transactionHash + msgData.slice(2)
}
return parseAMBMessage(msgData)
}
function deliveredMsgNotProcessed(processedList) {
return deliveredMsg => {
let msgData = deliveredMsg.returnValues.encodedData
if (!deliveredMsg.returnValues.messageId) {
// append tx hash to an old message, where message id was not used
msgData = deliveredMsg.transactionHash + msgData.slice(2)
}
const msg = parseAMBMessage(msgData)
return (
processedList.filter(processedMsg => {
return messageEqualsEvent(msg, processedMsg.returnValues)
}).length === 0
)
}
const keys = new Set()
processedList.forEach(processedMsg => keys.add(keyAMB(processedMsg.returnValues)))
return deliveredMsg => !keys.has(keyAMB(normalizeAMBMessage(deliveredMsg)))
}
function processedMsgNotDelivered(deliveredList) {
return processedMsg => {
return (
deliveredList.filter(deliveredMsg => {
let msgData = deliveredMsg.returnValues.encodedData
if (!deliveredMsg.returnValues.messageId) {
// append tx hash to an old message, where message id was not used
msgData = deliveredMsg.transactionHash + msgData.slice(2)
}
const msg = parseAMBMessage(msgData)
return messageEqualsEvent(msg, processedMsg.returnValues)
}).length === 0
)
}
}
function messageEqualsEvent(parsedMsg, event) {
return (
web3Utils.toChecksumAddress(parsedMsg.sender) === event.sender &&
web3Utils.toChecksumAddress(parsedMsg.executor) === event.executor &&
parsedMsg.messageId === event.messageId // for an old messages, event.messageId is actually a transactionHash
)
const keys = new Set()
deliveredList.forEach(deliveredMsg => keys.add(keyAMB(normalizeAMBMessage(deliveredMsg))))
return processedMsg => !keys.has(keyAMB(processedMsg.returnValues))
}
/**
@@ -60,13 +46,49 @@ const normalizeEventInformation = event => ({
value: event.returnValues.value
})
const eventWithoutReference = otherSideEvents => e =>
otherSideEvents.filter(a => a.referenceTx === e.referenceTx && a.recipient === e.recipient && a.value === e.value)
.length === 0
const key = e => [e.referenceTx, e.recipient, e.value].join(',').toLowerCase()
const eventWithoutReference = otherSideEvents => {
const keys = new Set()
otherSideEvents.forEach(e => keys.add(key(e)))
return e => !keys.has(key(e))
}
const unclaimedHomeToForeignRequests = () => {
if (MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST) {
const allowanceList = readAccessListFile(MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST)
return e => !allowanceList.includes(e.recipient.toLowerCase()) && !(e.sender && allowanceList.includes(e.sender))
} else if (MONITOR_HOME_TO_FOREIGN_BLOCK_LIST) {
const blockList = readAccessListFile(MONITOR_HOME_TO_FOREIGN_BLOCK_LIST)
return e => blockList.includes(e.recipient.toLowerCase()) || (e.sender && blockList.includes(e.sender))
} else {
return () => false
}
}
const manuallyProcessedAMBHomeToForeignRequests = () => {
if (MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST) {
const allowanceList = readAccessListFile(MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST)
return e => {
const { sender, executor, decodedDataType } = normalizeAMBMessage(e)
return (!allowanceList.includes(sender) && !allowanceList.includes(executor)) || decodedDataType.manualLane
}
} else if (MONITOR_HOME_TO_FOREIGN_BLOCK_LIST) {
const blockList = readAccessListFile(MONITOR_HOME_TO_FOREIGN_BLOCK_LIST)
return e => {
const { sender, executor, decodedDataType } = normalizeAMBMessage(e)
return blockList.includes(sender) || blockList.includes(executor) || decodedDataType.manualLane
}
} else {
return e => normalizeAMBMessage(e).decodedDataType.manualLane
}
}
module.exports = {
deliveredMsgNotProcessed,
processedMsgNotDelivered,
normalizeEventInformation,
eventWithoutReference
eventWithoutReference,
unclaimedHomeToForeignRequests,
manuallyProcessedAMBHomeToForeignRequests
}

View File

@@ -1,16 +0,0 @@
const Web3 = require('web3')
const { BRIDGE_MODES, getBridgeMode, HOME_ERC_TO_ERC_ABI } = require('../../commons')
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL } = process.env
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
async function isV1Bridge() {
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
const bridgeMode = await getBridgeMode(homeBridge)
return bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC_V1
}
module.exports = {
isV1Bridge
}

27
monitor/utils/web3.js Normal file
View File

@@ -0,0 +1,27 @@
require('dotenv').config()
const Web3 = require('web3')
const { COMMON_HOME_RPC_URL, COMMON_FOREIGN_RPC_URL } = process.env
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
const foreignProvider = new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)
const web3Foreign = new Web3(foreignProvider)
function blockNumberWrapper(web3) {
let blockNumber = null
return async () => {
if (!blockNumber) {
blockNumber = await web3.eth.getBlockNumber()
}
return blockNumber
}
}
module.exports = {
web3Home,
web3Foreign,
getHomeBlockNumber: blockNumberWrapper(web3Home),
getForeignBlockNumber: blockNumberWrapper(web3Foreign)
}

133
monitor/utils/web3Cache.js Normal file
View File

@@ -0,0 +1,133 @@
const logger = require('../logger')('web3Cache')
const { readCacheFile, writeCacheFile } = require('./file')
const { web3Home } = require('./web3')
const { getPastEvents: commonGetPastEvents } = require('../../commons')
const { MONITOR_BRIDGE_NAME, MONITOR_CACHE_EVENTS } = process.env
let isDirty = false
const homeTxSendersCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/home/txSenders.json`
const cachedHomeTxSenders = readCacheFile(homeTxSendersCacheFile) || {}
async function getHomeTxSender(txHash) {
if (!cachedHomeTxSenders[txHash]) {
logger.debug(`Fetching sender for tx ${txHash}`)
cachedHomeTxSenders[txHash] = (await web3Home.eth.getTransaction(txHash)).from.toLowerCase()
isDirty = true
}
return cachedHomeTxSenders[txHash]
}
async function getPastEvents(contract, options) {
if (MONITOR_CACHE_EVENTS !== 'true') {
return commonGetPastEvents(contract, options)
}
const contractAddr = contract.options.address
let eventSignature
if (options.event.includes('(')) {
eventSignature = options.event
options.event = web3Home.utils.sha3(eventSignature)
const eventABI = contract.options.jsonInterface.find(e => e.type === 'event' && e.signature === options.event)
if (!eventABI) {
throw new Error(`Event ${eventSignature} not found`)
}
} else {
const eventABI = contract.options.jsonInterface.find(
e => e.type === 'event' && (e.name === options.event || e.signature === options.event)
)
if (!eventABI) {
throw new Error(`Event ${options.event} not found`)
}
eventSignature = `${eventABI.name}(${eventABI.inputs.map(i => i.type).join(',')})`
}
const cacheFile = `./cache/${MONITOR_BRIDGE_NAME}/${options.chain}/${contractAddr}/${eventSignature}.json`
const { fromBlock, toBlock } = options
const { fromBlock: cachedFromBlock, toBlock: cachedToBlock, events: cachedEvents } = readCacheFile(cacheFile) || {
fromBlock: 0,
toBlock: 0,
events: []
}
let result
if (cachedFromBlock > toBlock || fromBlock > cachedToBlock) {
// requested: A...B
// cached: C...D
// OR
// requested: A...B
// cached: C...D
logger.debug(`Fetching events for blocks ${fromBlock}...${toBlock}`)
result = await commonGetPastEvents(contract, options)
} else if (fromBlock < cachedFromBlock && toBlock <= cachedToBlock) {
// requested: A...B
// cached: C...D
logger.debug(`Cache hit for blocks ${cachedFromBlock}...${toBlock}`)
logger.debug(`Fetching events for blocks ${fromBlock}...${cachedFromBlock - 1}`)
result = [
...(await commonGetPastEvents(contract, { ...options, toBlock: cachedFromBlock - 1 })),
...cachedEvents.filter(e => e.blockNumber <= toBlock)
]
} else if (fromBlock < cachedFromBlock && cachedToBlock < toBlock) {
// requested: A.....B
// cached: C.D
logger.debug(`Cache hit for blocks ${cachedFromBlock}...${cachedToBlock}`)
logger.debug(`Fetching events for blocks ${fromBlock}...${cachedFromBlock - 1}`)
logger.debug(`Fetching events for blocks ${cachedToBlock + 1}...${toBlock}`)
result = [
...(await commonGetPastEvents(contract, { ...options, toBlock: cachedFromBlock - 1 })),
...cachedEvents,
...(await commonGetPastEvents(contract, { ...options, fromBlock: cachedToBlock + 1 }))
]
} else if (cachedFromBlock <= fromBlock && toBlock <= cachedToBlock) {
// requested: A.B
// cached: C.....D
logger.debug(`Cache hit for blocks ${fromBlock}...${toBlock}`)
result = cachedEvents.filter(e => fromBlock <= e.blockNumber && e.blockNumber <= toBlock)
} else if (fromBlock >= cachedFromBlock && toBlock > cachedToBlock) {
// requested: A...B
// cached: C...D
logger.debug(`Cache hit for blocks ${fromBlock}...${cachedToBlock}`)
logger.debug(`Fetching events for blocks ${cachedToBlock + 1}...${toBlock}`)
result = [
...cachedEvents.filter(e => e.blockNumber >= fromBlock),
...(await commonGetPastEvents(contract, { ...options, fromBlock: cachedToBlock + 1 }))
]
} else {
throw new Error(
`Something is broken with cache resolution for getPastEvents,
requested blocks ${fromBlock}...${toBlock},
cached blocks ${cachedFromBlock}...${cachedToBlock}`
)
}
// it is not safe to cache events with too low block confirmations
// so, only events from finalized blocks are included into the cache
const safeToBlock = options.safeToBlock || toBlock
const cacheToSave = result.filter(e => e.blockNumber <= safeToBlock)
logger.debug(
`Saving events cache for ${MONITOR_BRIDGE_NAME}/${options.chain}/${contractAddr}/${eventSignature} on disk`
)
writeCacheFile(cacheFile, {
fromBlock,
toBlock: safeToBlock,
events: cacheToSave
})
return result
}
function saveCache() {
if (isDirty) {
logger.debug('Saving cache on disk')
writeCacheFile(homeTxSendersCacheFile, cachedHomeTxSenders)
}
}
module.exports = {
getHomeTxSender,
getPastEvents,
saveCache
}

View File

@@ -1,13 +1,12 @@
require('dotenv').config()
const Web3 = require('web3')
const Web3Utils = require('web3').utils
const fetch = require('node-fetch')
const logger = require('./logger')('validators')
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, getValidatorList, gasPriceFromSupplier } = require('../commons')
const { getBlockNumber } = require('./utils/contract')
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, gasPriceFromSupplier } = require('../commons')
const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3')
const { getValidatorList } = require('./utils/getValidatorsList')
const {
COMMON_HOME_RPC_URL,
COMMON_FOREIGN_RPC_URL,
COMMON_HOME_BRIDGE_ADDRESS,
COMMON_FOREIGN_BRIDGE_ADDRESS,
COMMON_HOME_GAS_PRICE_SUPPLIER_URL,
@@ -19,19 +18,9 @@ const {
COMMON_FOREIGN_GAS_PRICE_FALLBACK,
COMMON_FOREIGN_GAS_PRICE_FACTOR
} = process.env
const MONITOR_HOME_START_BLOCK = Number(process.env.MONITOR_HOME_START_BLOCK) || 0
const MONITOR_FOREIGN_START_BLOCK = Number(process.env.MONITOR_FOREIGN_START_BLOCK) || 0
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
const Web3Utils = Web3.utils
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
const foreignProvider = new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)
const web3Foreign = new Web3(foreignProvider)
const homeGasPriceSupplierOpts = {
speedType: COMMON_HOME_GAS_PRICE_SPEED_TYPE,
factor: COMMON_HOME_GAS_PRICE_FACTOR,
@@ -58,7 +47,12 @@ async function main(bridgeMode) {
const homeBridgeValidators = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, homeValidatorsAddress)
logger.debug('getting last block numbers')
const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
const homeBlockNumber = await getHomeBlockNumber()
const foreignBlockNumber = await getForeignBlockNumber()
const homeConfirmations = await homeBridge.methods.requiredBlockConfirmations().call()
const foreignConfirmations = await foreignBridge.methods.requiredBlockConfirmations().call()
const homeDelayedBlockNumber = homeBlockNumber - homeConfirmations
const foreignDelayedBlockNumber = foreignBlockNumber - foreignConfirmations
logger.debug('calling foreignBridge.methods.validatorContract().call()')
const foreignValidatorsAddress = await foreignBridge.methods.validatorContract().call()
@@ -66,16 +60,18 @@ async function main(bridgeMode) {
logger.debug('calling foreignBridgeValidators getValidatorList()')
const foreignValidators = (await getValidatorList(foreignValidatorsAddress, web3Foreign.eth, {
from: MONITOR_FOREIGN_START_BLOCK,
to: foreignBlockNumber,
logger
toBlock: foreignBlockNumber,
logger,
chain: 'foreign',
safeToBlock: foreignDelayedBlockNumber
})).map(web3Foreign.utils.toChecksumAddress)
logger.debug('calling homeBridgeValidators getValidatorList()')
const homeValidators = (await getValidatorList(homeValidatorsAddress, web3Home.eth, {
from: MONITOR_HOME_START_BLOCK,
to: homeBlockNumber,
logger
toBlock: homeBlockNumber,
logger,
chain: 'home',
safeToBlock: homeDelayedBlockNumber
})).map(web3Home.utils.toChecksumAddress)
const foreignVBalances = {}

View File

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

View File

@@ -1,8 +1,24 @@
cd $(dirname $0)
../e2e-commons/up.sh deploy blocks oracle oracle-validator-2 oracle-validator-3
mode="$1"
case "$mode" in
amb)
script=./test/amb.js
;;
native-to-erc)
script=./test/nativeToErc.js
;;
erc-to-erc)
script=./test/ercToErc.js
;;
erc-to-native)
script=./test/ercToNative.js
;;
esac
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start
MODE="$mode" ../e2e-commons/up.sh deploy blocks oracle oracle-validator-2 oracle-validator-3
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start $script
rc=$?
../e2e-commons/down.sh

View File

@@ -3,7 +3,7 @@ const assert = require('assert')
const { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json')
const { uniformRetry } = require('../../e2e-commons/utils')
const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI } = require('../../commons')
const { setRequiredSignatures } = require('./utils')
const { delay, setRequiredSignatures } = require('./utils')
const { toBN } = Web3.utils
@@ -19,14 +19,17 @@ foreignWeb3.eth.accounts.wallet.add(user.privateKey)
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox)
const blockHomeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.blockedHomeBox)
const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox)
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, COMMON_HOME_BRIDGE_ADDRESS)
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
describe('arbitrary message bridging', () => {
let requiredSignatures = 1
before(async () => {
// Only 1 validator is used in ultimate tests
if (process.env.ULTIMATE !== 'true') {
requiredSignatures = 2
// Set 2 required signatures for home bridge
await setRequiredSignatures({
bridgeContract: homeBridge,
@@ -76,6 +79,85 @@ describe('arbitrary message bridging', () => {
}
})
})
// allowance/block lists files are not mounted to the host during the ultimate test
if (process.env.ULTIMATE !== 'true') {
it('should confirm but not relay message from blocked contract', async () => {
const newValue = 4
const initialValue = await foreignBox.methods.value().call()
assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value')
const signatures = await homeBridge.getPastEvents('SignedForUserRequest', {
fromBlock: 0,
toBlock: 'latest'
})
await blockHomeBox.methods
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
.send({
from: user.address,
gas: '400000'
})
.catch(e => {
console.error(e)
})
await delay(5000)
const newSignatures = await homeBridge.getPastEvents('SignedForUserRequest', {
fromBlock: 0,
toBlock: 'latest'
})
assert(
newSignatures.length === signatures.length + requiredSignatures,
`Incorrect amount of signatures submitted, got ${newSignatures.length}, expected ${signatures.length +
requiredSignatures}`
)
const value = await foreignBox.methods.value().call()
assert(!toBN(value).eq(toBN(newValue)), 'Message should not be relayed by oracle automatically')
})
}
it('should confirm but not relay message from manual lane', async () => {
const newValue = 5
const initialValue = await foreignBox.methods.value().call()
assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value')
const signatures = await homeBridge.getPastEvents('SignedForUserRequest', {
fromBlock: 0,
toBlock: 'latest'
})
await homeBox.methods
.setValueOnOtherNetworkUsingManualLane(newValue, amb.home, amb.foreignBox)
.send({
from: user.address,
gas: '400000'
})
.catch(e => {
console.error(e)
})
await delay(5000)
const newSignatures = await homeBridge.getPastEvents('SignedForUserRequest', {
fromBlock: 0,
toBlock: 'latest'
})
assert(
newSignatures.length === signatures.length + requiredSignatures,
`Incorrect amount of signatures submitted, got ${newSignatures.length}, expected ${signatures.length +
requiredSignatures}`
)
const value = await foreignBox.methods.value().call()
assert(!toBN(value).eq(toBN(newValue)), 'Message should not be relayed by oracle automatically')
})
})
})
describe('Foreign to Home', () => {

View File

@@ -4,13 +4,14 @@ const promiseRetry = require('promise-retry')
const {
user,
secondUser,
blockedUser,
validator,
ercToNativeBridge,
homeRPC,
foreignRPC
} = require('../../e2e-commons/constants.json')
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 homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
@@ -22,6 +23,7 @@ const COMMON_FOREIGN_BRIDGE_ADDRESS = ercToNativeBridge.foreign
const { toBN } = foreignWeb3.utils
homeWeb3.eth.accounts.wallet.add(user.privateKey)
homeWeb3.eth.accounts.wallet.add(blockedUser.privateKey)
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
@@ -142,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 () => {
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()

View File

@@ -1,5 +1,9 @@
const { BRIDGE_VALIDATORS_ABI } = require('../../commons')
async function delay(ms) {
return new Promise(res => setTimeout(res, ms))
}
const setRequiredSignatures = async ({ bridgeContract, web3, requiredSignatures, options }) => {
const validatorAddress = await bridgeContract.methods.validatorContract().call()
const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorAddress)
@@ -8,5 +12,6 @@ const setRequiredSignatures = async ({ bridgeContract, web3, requiredSignatures,
}
module.exports = {
delay,
setRequiredSignatures
}

View File

@@ -29,6 +29,11 @@ ORACLE_FOREIGN_START_BLOCK=
ORACLE_LOG_LEVEL=debug
ORACLE_MAX_PROCESSING_TIME=20000
ORACLE_RPC_REQUEST_TIMEOUT=5000
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
#USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370

View File

@@ -1,6 +1,6 @@
const baseConfig = require('./base.config')
const { web3Foreign } = require('../src/services/web3')
const { web3Foreign, web3ForeignRedundant } = require('../src/services/web3')
module.exports = {
...baseConfig.bridgeConfig,
@@ -8,5 +8,6 @@ module.exports = {
oldQueue: 'foreign',
id: 'foreign',
name: 'sender-foreign',
web3: web3Foreign
web3: web3Foreign,
web3Redundant: web3ForeignRedundant
}

View File

@@ -1,6 +1,6 @@
const baseConfig = require('./base.config')
const { web3Home } = require('../src/services/web3')
const { web3Home, web3HomeRedundant } = require('../src/services/web3')
module.exports = {
...baseConfig.bridgeConfig,
@@ -8,5 +8,6 @@ module.exports = {
oldQueue: 'home',
id: 'home',
name: 'sender-home',
web3: web3Home
web3: web3Home,
web3Redundant: web3HomeRedundant
}

View File

@@ -25,6 +25,9 @@ services:
extends:
file: docker-compose.yml
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:
- net_db_bridge_request
- net_rabbit_bridge_request

View File

@@ -26,15 +26,13 @@
"amqplib": "^0.5.2",
"bignumber.js": "^7.2.1",
"dotenv": "^5.0.1",
"http-list-provider": "0.0.5",
"ioredis": "^3.2.2",
"node-fetch": "^2.1.2",
"pino": "^4.17.3",
"pino-pretty": "^2.0.1",
"promise-limit": "^2.7.0",
"promise-retry": "^1.1.1",
"web3": "1.0.0-beta.34",
"web3-utils": "1.0.0-beta.34"
"web3": "^1.3.0"
},
"devDependencies": {
"bn-chai": "^1.0.1",
@@ -48,6 +46,6 @@
"sinon": "^6.1.0"
},
"engines": {
"node": ">= 8.9"
"node": ">= 10.18"
}
}

View File

@@ -1,8 +1,7 @@
require('../../env')
const Web3 = require('web3')
const Web3Utils = require('web3-utils')
const rpcUrlsManager = require('../../src/services/getRpcUrlsManager')
const { sendTx, sendRawTx } = require('../../src/tx/sendTx')
const { toWei } = require('web3').utils
const { web3Foreign } = require('../../src/services/web3')
const { sendTx } = require('../../src/tx/sendTx')
const { ERC20_ABI } = require('../../../commons')
const {
@@ -17,37 +16,23 @@ const NUMBER_OF_DEPOSITS_TO_SEND = process.argv[2] || process.env.NUMBER_OF_DEPO
const { FOREIGN_ERC_TO_ERC_ABI } = require('../../../commons')
const foreignRpcUrl = rpcUrlsManager.foreignUrls[0]
const foreignProvider = new Web3.providers.HttpProvider(foreignRpcUrl)
const web3Foreign = new Web3(foreignProvider)
async function main() {
const bridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const bridgeableTokenAddress = await bridge.methods.erc20token().call()
const poa20 = new web3Foreign.eth.Contract(ERC20_ABI, bridgeableTokenAddress)
try {
const foreignChainId = await sendRawTx({
chain: 'foreign',
params: [],
method: 'net_version'
})
let nonce = await sendRawTx({
chain: 'foreign',
method: 'eth_getTransactionCount',
params: [USER_ADDRESS, 'latest']
})
nonce = Web3Utils.hexToNumber(nonce)
const foreignChainId = await web3Foreign.eth.getChainId()
let nonce = await web3Foreign.eth.getTransactionCount(USER_ADDRESS)
let actualSent = 0
for (let i = 0; i < Number(NUMBER_OF_DEPOSITS_TO_SEND); i++) {
const gasLimit = await poa20.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, Web3Utils.toWei(FOREIGN_MIN_AMOUNT_PER_TX))
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, toWei(FOREIGN_MIN_AMOUNT_PER_TX))
.estimateGas({ from: USER_ADDRESS })
const data = await poa20.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, Web3Utils.toWei(FOREIGN_MIN_AMOUNT_PER_TX))
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, toWei(FOREIGN_MIN_AMOUNT_PER_TX))
.encodeABI({ from: USER_ADDRESS })
const txHash = await sendTx({
chain: 'foreign',
privateKey: USER_ADDRESS_PRIVATE_KEY,
data,
nonce,

View File

@@ -1,8 +1,7 @@
require('../../env')
const Web3 = require('web3')
const Web3Utils = require('web3-utils')
const rpcUrlsManager = require('../../src/services/getRpcUrlsManager')
const { sendTx, sendRawTx } = require('../../src/tx/sendTx')
const { toWei } = require('web3').utils
const { web3Home } = require('../../src/services/web3')
const { sendTx } = require('../../src/tx/sendTx')
const { isValidAmount } = require('../utils/utils')
const { HOME_ERC_TO_ERC_ABI } = require('../../../commons')
@@ -46,10 +45,6 @@ const BRIDGEABLE_TOKEN_ABI = [
}
]
const homeRpcUrl = rpcUrlsManager.homeUrls[0]
const homeProvider = new Web3.providers.HttpProvider(homeRpcUrl)
const web3Home = new Web3(homeProvider)
async function main() {
const bridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
const BRIDGEABLE_TOKEN_ADDRESS = await bridge.methods.erc677token().call()
@@ -58,27 +53,17 @@ async function main() {
try {
await isValidAmount(HOME_MIN_AMOUNT_PER_TX, bridge)
const homeChainId = await sendRawTx({
chain: 'home',
params: [],
method: 'net_version'
})
let nonce = await sendRawTx({
chain: 'home',
method: 'eth_getTransactionCount',
params: [USER_ADDRESS, 'latest']
})
nonce = Web3Utils.hexToNumber(nonce)
const homeChainId = await web3Home.eth.getChainId()
let nonce = await web3Home.eth.getTransactionCount(USER_ADDRESS)
let actualSent = 0
for (let i = 0; i < Number(NUMBER_OF_WITHDRAWALS_TO_SEND); i++) {
const gasLimit = await erc677.methods
.transferAndCall(COMMON_HOME_BRIDGE_ADDRESS, Web3Utils.toWei(HOME_MIN_AMOUNT_PER_TX), '0x')
.transferAndCall(COMMON_HOME_BRIDGE_ADDRESS, toWei(HOME_MIN_AMOUNT_PER_TX), '0x')
.estimateGas({ from: USER_ADDRESS })
const data = await erc677.methods
.transferAndCall(COMMON_HOME_BRIDGE_ADDRESS, Web3Utils.toWei(HOME_MIN_AMOUNT_PER_TX), '0x')
.transferAndCall(COMMON_HOME_BRIDGE_ADDRESS, toWei(HOME_MIN_AMOUNT_PER_TX), '0x')
.encodeABI({ from: USER_ADDRESS })
const txHash = await sendTx({
chain: 'home',
privateKey: USER_ADDRESS_PRIVATE_KEY,
data,
nonce,

View File

@@ -1,8 +1,7 @@
require('../../env')
const Web3 = require('web3')
const Web3Utils = require('web3-utils')
const rpcUrlsManager = require('../../src/services/getRpcUrlsManager')
const { sendTx, sendRawTx } = require('../../src/tx/sendTx')
const { toWei } = require('web3').utils
const { web3Foreign } = require('../../src/services/web3')
const { sendTx } = require('../../src/tx/sendTx')
const {
USER_ADDRESS,
@@ -16,37 +15,23 @@ const NUMBER_OF_DEPOSITS_TO_SEND = process.argv[2] || process.env.NUMBER_OF_DEPO
const { ERC20_ABI, FOREIGN_ERC_TO_NATIVE_ABI } = require('../../../commons')
const foreignRpcUrl = rpcUrlsManager.foreignUrls[0]
const foreignProvider = new Web3.providers.HttpProvider(foreignRpcUrl)
const web3Foreign = new Web3(foreignProvider)
async function main() {
const bridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const bridgeableTokenAddress = await bridge.methods.erc20token().call()
const poa20 = new web3Foreign.eth.Contract(ERC20_ABI, bridgeableTokenAddress)
try {
const foreignChainId = await sendRawTx({
chain: 'foreign',
params: [],
method: 'net_version'
})
let nonce = await sendRawTx({
chain: 'foreign',
method: 'eth_getTransactionCount',
params: [USER_ADDRESS, 'latest']
})
nonce = Web3Utils.hexToNumber(nonce)
const foreignChainId = await web3Foreign.eth.getChainId()
let nonce = await web3Foreign.eth.getTransactionCount(USER_ADDRESS)
let actualSent = 0
for (let i = 0; i < Number(NUMBER_OF_DEPOSITS_TO_SEND); i++) {
const gasLimit = await poa20.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, Web3Utils.toWei(FOREIGN_MIN_AMOUNT_PER_TX))
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, toWei(FOREIGN_MIN_AMOUNT_PER_TX))
.estimateGas({ from: USER_ADDRESS })
const data = await poa20.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, Web3Utils.toWei(FOREIGN_MIN_AMOUNT_PER_TX))
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, toWei(FOREIGN_MIN_AMOUNT_PER_TX))
.encodeABI({ from: USER_ADDRESS })
const txHash = await sendTx({
chain: 'foreign',
privateKey: USER_ADDRESS_PRIVATE_KEY,
data,
nonce,

View File

@@ -1,7 +1,6 @@
require('../../env')
const Web3Utils = require('web3-utils')
const { web3Home } = require('../../src/services/web3')
const { sendTx, sendRawTx } = require('../../src/tx/sendTx')
const { sendTx } = require('../../src/tx/sendTx')
const { isValidAmount } = require('../utils/utils')
const { HOME_ERC_TO_NATIVE_ABI } = require('../../../commons')
@@ -21,21 +20,11 @@ async function main() {
try {
await isValidAmount(HOME_MIN_AMOUNT_PER_TX, bridge)
const homeChainId = await sendRawTx({
chain: 'home',
params: [],
method: 'net_version'
})
let nonce = await sendRawTx({
chain: 'home',
method: 'eth_getTransactionCount',
params: [USER_ADDRESS, 'latest']
})
nonce = Web3Utils.hexToNumber(nonce)
const homeChainId = await web3Home.eth.getChainId()
let nonce = await web3Home.eth.getTransactionCount(USER_ADDRESS)
let actualSent = 0
for (let i = 0; i < Number(NUMBER_OF_DEPOSITS_TO_SEND); i++) {
const txHash = await sendTx({
chain: 'home',
privateKey: USER_ADDRESS_PRIVATE_KEY,
data: '0x',
nonce,

View File

@@ -1,23 +1,20 @@
require('../env')
const Web3 = require('web3')
const { BRIDGE_VALIDATORS_ABI } = require('../../commons')
const rpcUrlsManager = require('../src/services/getRpcUrlsManager')
const { web3Home, web3Foreign } = require('../src/services/web3')
const { bridgeConfig } = require('../config/base.config')
const homeABI = bridgeConfig.homeBridgeAbi
const foreignABI = bridgeConfig.foreignBridgeAbi
async function getStartBlock(rpcUrl, bridgeAddress, bridgeAbi) {
async function getStartBlock(web3, bridgeAddress, bridgeAbi) {
try {
const web3Provider = new Web3.providers.HttpProvider(rpcUrl)
const web3Instance = new Web3(web3Provider)
const bridgeContract = new web3Instance.eth.Contract(bridgeAbi, bridgeAddress)
const bridgeContract = new web3.eth.Contract(bridgeAbi, bridgeAddress)
const deployedAtBlock = await bridgeContract.methods.deployedAtBlock().call()
const validatorContractAddress = await bridgeContract.methods.validatorContract().call()
const validatorContract = new web3Instance.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
const validatorDeployedAtBlock = await validatorContract.methods.deployedAtBlock().call()
@@ -35,10 +32,8 @@ async function getStartBlock(rpcUrl, bridgeAddress, bridgeAbi) {
async function main() {
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
const homeRpcUrl = rpcUrlsManager.homeUrls[0]
const foreignRpcUrl = rpcUrlsManager.foreignUrls[0]
const homeStartBlock = await getStartBlock(homeRpcUrl, COMMON_HOME_BRIDGE_ADDRESS, homeABI)
const foreignStartBlock = await getStartBlock(foreignRpcUrl, COMMON_FOREIGN_BRIDGE_ADDRESS, foreignABI)
const homeStartBlock = await getStartBlock(web3Home, COMMON_HOME_BRIDGE_ADDRESS, homeABI)
const foreignStartBlock = await getStartBlock(web3Foreign, COMMON_FOREIGN_BRIDGE_ADDRESS, foreignABI)
const result = {
homeStartBlock,
foreignStartBlock

View File

@@ -1,5 +1,4 @@
require('../env')
const Web3 = require('web3')
const { getTokensState } = require('../src/utils/tokenState')
const {
ERC677_BRIDGE_TOKEN_ABI,
@@ -7,6 +6,7 @@ const {
FOREIGN_ERC_TO_NATIVE_ABI,
getTokenType
} = require('../../commons')
const { web3Foreign } = require('../src/services/web3')
const emptyLogger = {
debug: () => {},
@@ -14,26 +14,25 @@ const emptyLogger = {
}
async function initialChecks() {
const { ORACLE_BRIDGE_MODE, COMMON_FOREIGN_RPC_URL, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
const { ORACLE_BRIDGE_MODE, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
let result = {}
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL))
if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') {
const bridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const bridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
result.bridgeableTokenAddress = await bridge.methods.erc20token().call()
} else if (ORACLE_BRIDGE_MODE === 'ERC_TO_NATIVE') {
const bridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const bridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
result = await getTokensState(bridge, emptyLogger)
}
if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') {
const bridgeTokenContract = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, result.bridgeableTokenAddress)
const bridgeTokenContract = new web3Foreign.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, result.bridgeableTokenAddress)
result.foreignERC = await getTokenType(bridgeTokenContract, COMMON_FOREIGN_BRIDGE_ADDRESS)
}
console.log(JSON.stringify(result))
return result
}
initialChecks()
const result = initialChecks()
module.exports = initialChecks
module.exports = result

View File

@@ -1,7 +1,7 @@
require('../../env')
const Web3Utils = require('web3-utils')
const { toWei } = require('web3').utils
const { web3Foreign } = require('../../src/services/web3')
const { sendTx, sendRawTx } = require('../../src/tx/sendTx')
const { sendTx } = require('../../src/tx/sendTx')
const { isValidAmount } = require('../utils/utils')
const { FOREIGN_NATIVE_TO_ERC_ABI } = require('../../../commons')
@@ -53,27 +53,17 @@ async function main() {
try {
await isValidAmount(FOREIGN_MIN_AMOUNT_PER_TX, bridge)
const foreignChainId = await sendRawTx({
chain: 'foreign',
params: [],
method: 'net_version'
})
let nonce = await sendRawTx({
chain: 'foreign',
method: 'eth_getTransactionCount',
params: [USER_ADDRESS, 'latest']
})
nonce = Web3Utils.hexToNumber(nonce)
const foreignChainId = await web3Foreign.eth.getChainId()
let nonce = await web3Foreign.eth.getTransactionCount(USER_ADDRESS)
let actualSent = 0
for (let i = 0; i < Number(NUMBER_OF_WITHDRAWALS_TO_SEND); i++) {
const gasLimit = await poa20.methods
.transferAndCall(COMMON_FOREIGN_BRIDGE_ADDRESS, Web3Utils.toWei(FOREIGN_MIN_AMOUNT_PER_TX), '0x')
.transferAndCall(COMMON_FOREIGN_BRIDGE_ADDRESS, toWei(FOREIGN_MIN_AMOUNT_PER_TX), '0x')
.estimateGas({ from: USER_ADDRESS })
const data = await poa20.methods
.transferAndCall(COMMON_FOREIGN_BRIDGE_ADDRESS, Web3Utils.toWei(FOREIGN_MIN_AMOUNT_PER_TX), '0x')
.transferAndCall(COMMON_FOREIGN_BRIDGE_ADDRESS, toWei(FOREIGN_MIN_AMOUNT_PER_TX), '0x')
.encodeABI({ from: USER_ADDRESS })
const txHash = await sendTx({
chain: 'foreign',
privateKey: USER_ADDRESS_PRIVATE_KEY,
data,
nonce,

View File

@@ -1,7 +1,6 @@
require('../../env')
const Web3Utils = require('web3-utils')
const { web3Home } = require('../../src/services/web3')
const { sendTx, sendRawTx } = require('../../src/tx/sendTx')
const { sendTx } = require('../../src/tx/sendTx')
const { isValidAmount } = require('../utils/utils')
const { HOME_NATIVE_TO_ERC_ABI } = require('../../../commons')
@@ -21,21 +20,11 @@ async function main() {
try {
await isValidAmount(HOME_MIN_AMOUNT_PER_TX, bridge)
const homeChainId = await sendRawTx({
chain: 'home',
params: [],
method: 'net_version'
})
let nonce = await sendRawTx({
chain: 'home',
method: 'eth_getTransactionCount',
params: [USER_ADDRESS, 'latest']
})
nonce = Web3Utils.hexToNumber(nonce)
const homeChainId = await web3Home.eth.getChainId()
let nonce = await web3Home.eth.getTransactionCount(USER_ADDRESS)
let actualSent = 0
for (let i = 0; i < Number(NUMBER_OF_DEPOSITS_TO_SEND); i++) {
const txHash = await sendTx({
chain: 'home',
privateKey: USER_ADDRESS_PRIVATE_KEY,
data: '0x',
nonce,

View File

@@ -1,8 +1,8 @@
const Web3Utils = require('web3-utils')
const { fromWei } = require('web3').utils
async function getMinPerTxLimit(bridge) {
const minPerTx = await bridge.methods.minPerTx().call()
return Web3Utils.fromWei(minPerTx)
return fromWei(minPerTx)
}
async function isValidAmount(amount, bridge) {

View File

@@ -3,7 +3,6 @@ const path = require('path')
const { isAttached, connectWatcherToQueue, connection } = require('./services/amqpClient')
const logger = require('./services/logger')
const GasPrice = require('./services/gasPrice')
const rpcUrlsManager = require('./services/getRpcUrlsManager')
const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3')
const { sendTx } = require('./tx/sendTx')
const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils')
@@ -37,8 +36,7 @@ async function initialize() {
try {
const checkHttps = checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger)
rpcUrlsManager.homeUrls.forEach(checkHttps('home'))
rpcUrlsManager.foreignUrls.forEach(checkHttps('foreign'))
web3Instance.currentProvider.urls.forEach(checkHttps(config.chain))
attached = await isAttached()
if (attached) {
@@ -139,7 +137,7 @@ async function main({ sendJob, txHash }) {
async function sendJobTx(jobs) {
const gasPrice = await GasPrice.start(config.chain, true)
const chainId = await getChainId(config.chain)
const chainId = await getChainId(web3Instance)
let nonce = await getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
await syncForEach(jobs, async job => {
@@ -153,7 +151,6 @@ async function sendJobTx(jobs) {
try {
logger.info(`Sending transaction with nonce ${nonce}`)
const txHash = await sendTx({
chain: config.chain,
data: job.data,
nonce,
gasPrice: gasPrice.toString(10),
@@ -177,7 +174,7 @@ async function sendJobTx(jobs) {
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 minimumBalance = gasLimit.multipliedBy(gasPrice)
logger.error(

View File

@@ -1,4 +1,4 @@
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
const logger = require('../../services/logger').child({
module: 'processAffirmationRequests:estimateGas'

View File

@@ -1,6 +1,6 @@
require('dotenv').config()
const { HttpListProviderError } = require('http-list-provider')
const promiseLimit = require('promise-limit')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const rootLogger = require('../../services/logger')
const { web3Home } = require('../../services/web3')
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi

View File

@@ -1,5 +1,5 @@
const Web3 = require('web3')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
const logger = require('../../services/logger').child({
module: 'processCollectedSignatures:estimateGas'

View File

@@ -1,10 +1,11 @@
require('dotenv').config()
const promiseLimit = require('promise-limit')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
const rootLogger = require('../../services/logger')
const { web3Home, web3Foreign } = require('../../services/web3')
const { signatureToVRS, packSignatures } = require('../../utils/message')
const { readAccessListFile } = require('../../utils/utils')
const { parseAMBMessage } = require('../../../../commons')
const estimateGas = require('./estimateGas')
const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
@@ -12,6 +13,12 @@ const { MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/const
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
const {
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST,
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST,
ORACLE_ALWAYS_RELAY_SIGNATURES
} = process.env
let validatorContract = null
function processCollectedSignaturesBuilder(config) {
@@ -39,13 +46,50 @@ function processCollectedSignaturesBuilder(config) {
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}`)
return
}
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
const message = await homeBridge.methods.message(messageHash).call()
const parsedMessage = parseAMBMessage(message)
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
const sender = parsedMessage.sender.toLowerCase()
const executor = parsedMessage.executor.toLowerCase()
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST) {
const allowanceList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST, logger)
if (!allowanceList.includes(executor) && !allowanceList.includes(sender)) {
logger.info(
{ sender, executor },
'Validator skips a message. Neither sender nor executor addresses are 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.includes(executor)) {
logger.info({ executor }, 'Validator skips a message. Executor address is in the block list.')
return
}
if (blockList.includes(sender)) {
logger.info({ sender }, 'Validator skips a message. Sender address is in the block list.')
return
}
}
}
if (parsedMessage.decodedDataType.manualLane) {
logger.info(
{ dataType: parsedMessage.dataType },
'Validator skips a message. Message was forwarded to the manual lane by the extension'
)
return
}
logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get')

View File

@@ -1,6 +1,6 @@
require('dotenv').config()
const promiseLimit = require('promise-limit')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
const rootLogger = require('../../services/logger')
const { web3Home } = require('../../services/web3')

View File

@@ -1,4 +1,4 @@
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
const logger = require('../../services/logger').child({
module: 'processAffirmationRequests:estimateGas'

View File

@@ -1,6 +1,6 @@
require('../../../env')
const promiseLimit = require('promise-limit')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const rootLogger = require('../../services/logger')
const { web3Home } = require('../../services/web3')

View File

@@ -1,5 +1,5 @@
const Web3 = require('web3')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
const { parseMessage } = require('../../utils/message')
const logger = require('../../services/logger').child({

View File

@@ -1,14 +1,22 @@
require('../../../env')
const promiseLimit = require('promise-limit')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
const rootLogger = require('../../services/logger')
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 { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
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)
let validatorContract = null
@@ -38,7 +46,9 @@ function processCollectedSignaturesBuilder(config) {
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}`)
return
}
@@ -46,6 +56,49 @@ function processCollectedSignaturesBuilder(config) {
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
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')
const requiredSignatures = []

View File

@@ -1,4 +1,4 @@
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
const logger = require('../../services/logger').child({
module: 'processSignatureRequests:estimateGas'

View File

@@ -1,6 +1,6 @@
require('../../../env')
const promiseLimit = require('promise-limit')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
const rootLogger = require('../../services/logger')
const { web3Home } = require('../../services/web3')

View File

@@ -1,6 +1,6 @@
require('../../../env')
const promiseLimit = require('promise-limit')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../../services/HttpListProvider')
const { BRIDGE_VALIDATORS_ABI, ZERO_ADDRESS } = require('../../../../commons')
const rootLogger = require('../../services/logger')
const { web3Home, web3Foreign } = require('../../services/web3')

View File

@@ -1,11 +1,9 @@
require('../env')
const path = require('path')
const { toBN } = require('web3-utils')
const { connectSenderToQueue } = require('./services/amqpClient')
const { redis } = require('./services/redisClient')
const GasPrice = require('./services/gasPrice')
const logger = require('./services/logger')
const rpcUrlsManager = require('./services/getRpcUrlsManager')
const { sendTx } = require('./tx/sendTx')
const { getNonce, getChainId } = require('./tx/web3')
const {
@@ -19,7 +17,7 @@ const {
} = require('./utils/utils')
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, ORACLE_TX_REDUNDANCY } = process.env
const ORACLE_VALIDATOR_ADDRESS = privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY)
@@ -31,6 +29,7 @@ if (process.argv.length < 3) {
const config = require(path.join('../config/', process.argv[2]))
const web3Instance = config.web3
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.web3Redundant : config.web3
const nonceKey = `${config.id}:nonce`
let chainId = 0
@@ -38,12 +37,11 @@ async function initialize() {
try {
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
rpcUrlsManager.homeUrls.forEach(checkHttps('home'))
rpcUrlsManager.foreignUrls.forEach(checkHttps('foreign'))
web3Instance.currentProvider.urls.forEach(checkHttps(config.chain))
GasPrice.start(config.id)
chainId = await getChainId(config.id)
chainId = await getChainId(web3Instance)
connectSenderToQueue({
queueName: config.queue,
oldQueueName: config.oldQueue,
@@ -81,13 +79,17 @@ async function readNonce(forceUpdate) {
logger.debug({ nonce }, 'Nonce found in the DB')
return Number(nonce)
} else {
logger.debug("Nonce wasn't found in the DB")
logger.warn("Nonce wasn't found in the DB")
return getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
}
}
function updateNonce(nonce) {
return redis.set(nonceKey, nonce)
if (typeof nonce !== 'number') {
logger.warn('Given nonce value is not a valid number. Nothing will be updated in the DB.')
} else {
redis.set(nonceKey, nonce)
}
}
async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleTransactionResend }) {
@@ -98,21 +100,23 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
}
const txArray = JSON.parse(msg.content)
logger.info(`Msg received with ${txArray.length} Tx to send`)
const gasPrice = GasPrice.getPrice()
logger.debug(`Msg received with ${txArray.length} Tx to send`)
const gasPrice = GasPrice.getPrice().toString(10)
let nonce = await readNonce()
let nonce
let insufficientFunds = false
let minimumBalance = null
const failedTx = []
const sentTx = []
const resendJobs = []
const isResend = txArray.length > 0 && !!txArray[0].txHash
if (isResend) {
logger.debug(`Checking status of ${txArray.length} transactions`)
logger.info(`Checking status of ${txArray.length} transactions`)
nonce = null
} else {
logger.debug(`Sending ${txArray.length} transactions`)
logger.info(`Sending ${txArray.length} transactions`)
nonce = await readNonce()
}
await syncForEach(txArray, async job => {
let gasLimit
@@ -123,50 +127,40 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
}
try {
let txNonce
if (isResend) {
const tx = await web3Instance.eth.getTransaction(job.txHash)
if (tx === null) {
logger.info(`Transaction ${job.txHash} was not found, dropping it`)
return
}
if (tx.blockNumber !== null) {
logger.info(`Transaction ${job.txHash} was successfully mined`)
if (tx && tx.blockNumber !== null) {
logger.debug(`Transaction ${job.txHash} was successfully mined`)
return
}
logger.info(
`Previously sent transaction is stuck, updating gasPrice: ${tx.gasPrice} -> ${gasPrice.toString(10)}`
)
if (toBN(tx.gasPrice).gte(toBN(gasPrice))) {
logger.info("Gas price returned from the oracle didn't increase, will reinspect this transaction later")
sentTx.push(job)
return
if (nonce === null) {
nonce = await readNonce(true)
}
txNonce = tx.nonce
} else {
txNonce = nonce++
logger.info(`Transaction ${job.txHash} was not mined, updating gasPrice: ${job.gasPrice} -> ${gasPrice}`)
}
logger.info(`Sending transaction with nonce ${txNonce}`)
logger.info(`Sending transaction with nonce ${nonce}`)
const txHash = await sendTx({
chain: config.id,
data: job.data,
nonce: txNonce,
gasPrice: gasPrice.toString(10),
nonce,
gasPrice,
amount: '0',
gasLimit,
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
to: job.to,
chainId,
web3: web3Instance
web3: web3Redundant
})
sentTx.push({
const resendJob = {
...job,
txHash
})
txHash,
gasPrice
}
resendJobs.push(resendJob)
nonce++
logger.info(
{ eventTransactionHash: job.transactionReference, generatedTransactionHash: txHash },
`Tx generated ${txHash} for event Tx ${job.transactionReference}`
@@ -177,11 +171,15 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
`Tx Failed for event Tx ${job.transactionReference}.`,
e.message
)
if (!e.message.includes('Transaction with the same hash was already imported')) {
failedTx.push(job)
if (!e.message.toLowerCase().includes('transaction with the same hash was already imported')) {
if (isResend) {
resendJobs.push(job)
} else {
failedTx.push(job)
}
}
if (e.message.includes('Insufficient funds')) {
if (e.message.toLowerCase().includes('insufficient funds')) {
insufficientFunds = true
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
minimumBalance = gasLimit.multipliedBy(gasPrice)
@@ -194,16 +192,18 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
}
})
logger.debug('Updating nonce')
await updateNonce(nonce)
if (typeof nonce === 'number') {
logger.debug('Updating nonce')
await updateNonce(nonce)
}
if (failedTx.length) {
logger.info(`Sending ${failedTx.length} Failed Tx to Queue`)
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)
if (resendJobs.length) {
logger.info(`Sending ${resendJobs.length} Tx Delayed Resend Requests to Queue`)
await scheduleTransactionResend(resendJobs)
}
ackMsg(msg)
logger.debug(`Finished processing msg`)

View File

@@ -0,0 +1,89 @@
const fetch = require('node-fetch')
const promiseRetry = require('promise-retry')
const defaultOptions = {
requestTimeout: 0,
retry: {
retries: 0
}
}
class HttpListProviderError extends Error {
constructor(message, errors) {
super(message)
this.errors = errors
}
}
function HttpListProvider(urls, options = {}) {
if (!(this instanceof HttpListProvider)) {
return new HttpListProvider(urls)
}
if (!urls || !urls.length) {
throw new TypeError(`Invalid URLs: '${urls}'`)
}
this.urls = urls
this.options = { ...defaultOptions, ...options }
this.currentIndex = 0
}
HttpListProvider.prototype.send = async function send(payload, callback) {
// save the currentIndex to avoid race condition
const { currentIndex } = this
try {
const [result, index] = await promiseRetry(retry => {
return trySend(payload, this.urls, currentIndex, this.options).catch(retry)
}, this.options.retry)
this.currentIndex = index
callback(null, result)
} catch (e) {
callback(e)
}
}
function send(url, payload, options) {
return fetch(url, {
headers: {
'Content-type': 'application/json'
},
method: 'POST',
body: JSON.stringify(payload),
timeout: options.requestTimeout
})
.then(response => {
if (response.ok) {
return response
} else {
throw new Error(response.statusText)
}
})
.then(response => response.json())
}
async function trySend(payload, urls, initialIndex, options) {
const errors = []
let index = initialIndex
for (let count = 0; count < urls.length; count++) {
const url = urls[index]
try {
const result = await send(url, payload, options)
return [result, index]
} catch (e) {
errors.push(e)
}
index = (index + 1) % urls.length
}
throw new HttpListProviderError('Request failed for all urls', errors)
}
module.exports = {
HttpListProvider,
HttpListProviderError,
defaultOptions,
send
}

View File

@@ -0,0 +1,40 @@
const promiseRetry = require('promise-retry')
const { promiseAny } = require('../utils/utils')
const { defaultOptions, HttpListProviderError, send } = require('./HttpListProvider')
function RedundantHttpListProvider(urls, options = {}) {
if (!(this instanceof RedundantHttpListProvider)) {
return new RedundantHttpListProvider(urls)
}
if (!urls || !urls.length) {
throw new TypeError(`Invalid URLs: '${urls}'`)
}
this.urls = urls
this.options = { ...defaultOptions, ...options }
this.currentIndex = 0
}
RedundantHttpListProvider.prototype.send = async function send(payload, callback) {
try {
const result = await promiseRetry(retry => {
return trySend(payload, this.urls, this.options).catch(retry)
}, this.options.retry)
callback(null, result)
} catch (e) {
callback(e)
}
}
async function trySend(payload, urls, options) {
try {
return await promiseAny(urls.map(url => send(url, payload, options)))
} catch (errors) {
throw new HttpListProviderError('Request failed for all urls', errors)
}
}
module.exports = {
RedundantHttpListProvider
}

View File

@@ -1,50 +0,0 @@
const promiseRetry = require('promise-retry')
const tryEach = require('../utils/tryEach')
const { RETRY_CONFIG } = require('../utils/constants')
const { promiseAny } = require('../utils/utils')
function RpcUrlsManager(homeUrls, foreignUrls) {
if (!homeUrls) {
throw new Error(`Invalid homeUrls: '${homeUrls}'`)
}
if (!foreignUrls) {
throw new Error(`Invalid foreignUrls: '${foreignUrls}'`)
}
this.homeUrls = homeUrls.split(' ')
this.foreignUrls = foreignUrls.split(' ')
}
RpcUrlsManager.prototype.tryEach = async function(chain, f, redundant = false) {
if (chain !== 'home' && chain !== 'foreign') {
throw new Error(`Invalid argument chain: '${chain}'`)
}
// save urls to avoid race condition
const urls = chain === 'home' ? [...this.homeUrls] : [...this.foreignUrls]
if (redundant) {
// result from first responded node will be returned immediately
// remaining nodes will continue to retry queries in separate promises
// promiseAny will throw only if all urls reached max retry number
return promiseAny(urls.map(url => promiseRetry(retry => f(url).catch(retry), RETRY_CONFIG)))
}
const [result, index] = await promiseRetry(retry => tryEach(urls, f).catch(retry), RETRY_CONFIG)
if (index > 0) {
// rotate urls
const failed = urls.splice(0, index)
urls.push(...failed)
}
if (chain === 'home') {
this.homeUrls = urls
} else {
this.foreignUrls = urls
}
return result
}
module.exports = RpcUrlsManager

View File

@@ -1,3 +0,0 @@
const RpcUrlsManager = require('./RpcUrlsManager')
module.exports = new RpcUrlsManager(process.env.COMMON_HOME_RPC_URL, process.env.COMMON_FOREIGN_RPC_URL)

View File

@@ -1,19 +1,55 @@
const HttpListProvider = require('http-list-provider')
const Web3 = require('web3')
const rpcUrlsManager = require('./getRpcUrlsManager')
const { HttpListProvider } = require('./HttpListProvider')
const { RedundantHttpListProvider } = require('./RedundantHttpListProvider')
const { RETRY_CONFIG } = require('../utils/constants')
const homeProvider = new HttpListProvider(rpcUrlsManager.homeUrls, {
const {
COMMON_HOME_RPC_URL,
COMMON_FOREIGN_RPC_URL,
ORACLE_RPC_REQUEST_TIMEOUT,
ORACLE_HOME_RPC_POLLING_INTERVAL,
ORACLE_FOREIGN_RPC_POLLING_INTERVAL
} = process.env
if (!COMMON_HOME_RPC_URL) {
throw new Error(`Invalid homeUrls: '${COMMON_HOME_RPC_URL}'`)
}
if (!COMMON_FOREIGN_RPC_URL) {
throw new Error(`Invalid foreignUrls: '${COMMON_FOREIGN_RPC_URL}'`)
}
const homeUrls = COMMON_HOME_RPC_URL.split(' ').filter(url => url.length > 0)
const foreignUrls = COMMON_FOREIGN_RPC_URL.split(' ').filter(url => url.length > 0)
const homeDefaultTimeout = parseInt(ORACLE_HOME_RPC_POLLING_INTERVAL, 10) * 2
const foreignDefaultTimeout = parseInt(ORACLE_FOREIGN_RPC_POLLING_INTERVAL, 10) * 2
const configuredTimeout = parseInt(ORACLE_RPC_REQUEST_TIMEOUT, 10)
const homeOptions = {
requestTimeout: configuredTimeout || homeDefaultTimeout,
retry: RETRY_CONFIG
})
}
const foreignOptions = {
requestTimeout: configuredTimeout || foreignDefaultTimeout,
retry: RETRY_CONFIG
}
const homeProvider = new HttpListProvider(homeUrls, homeOptions)
const web3Home = new Web3(homeProvider)
const foreignProvider = new HttpListProvider(rpcUrlsManager.foreignUrls, {
retry: RETRY_CONFIG
})
const foreignProvider = new HttpListProvider(foreignUrls, foreignOptions)
const web3Foreign = new Web3(foreignProvider)
const redundantHomeProvider = new RedundantHttpListProvider(homeUrls, homeOptions)
const web3HomeRedundant = new Web3(redundantHomeProvider)
const redundantForeignProvider = new RedundantHttpListProvider(foreignUrls, foreignOptions)
const web3ForeignRedundant = new Web3(redundantForeignProvider)
module.exports = {
web3Home,
web3Foreign
web3Foreign,
web3HomeRedundant,
web3ForeignRedundant
}

View File

@@ -1,67 +1,27 @@
const Web3Utils = require('web3-utils')
const fetch = require('node-fetch')
const rpcUrlsManager = require('../services/getRpcUrlsManager')
const { toWei } = require('web3').utils
const { ORACLE_TX_REDUNDANCY } = process.env
// eslint-disable-next-line consistent-return
async function sendTx({ chain, privateKey, data, nonce, gasPrice, amount, gasLimit, to, chainId, web3 }) {
async function sendTx({ privateKey, data, nonce, gasPrice, amount, gasLimit, to, chainId, web3 }) {
const serializedTx = await web3.eth.accounts.signTransaction(
{
nonce: Number(nonce),
chainId,
to,
data,
value: Web3Utils.toWei(amount),
value: toWei(amount),
gasPrice,
gas: gasLimit
},
`0x${privateKey}`
)
return sendRawTx({
chain,
method: 'eth_sendRawTransaction',
params: [serializedTx.rawTransaction]
})
}
// eslint-disable-next-line consistent-return
async function sendRawTx({ chain, params, method }) {
const result = await rpcUrlsManager.tryEach(
chain,
async url => {
// curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":[{see above}],"id":1}'
const response = await fetch(url, {
headers: {
'Content-type': 'application/json'
},
method: 'POST',
body: JSON.stringify({
jsonrpc: '2.0',
method,
params,
id: Math.floor(Math.random() * 100) + 1
})
})
if (!response.ok) {
throw new Error(response.statusText)
}
return response
},
ORACLE_TX_REDUNDANCY === 'true' && method === 'eth_sendRawTransaction'
return new Promise((res, rej) =>
web3.eth
.sendSignedTransaction(serializedTx.rawTransaction)
.once('transactionHash', res)
.once('error', rej)
)
const json = await result.json()
if (json.error) {
throw json.error
}
return json.result
}
module.exports = {
sendTx,
sendRawTx
sendTx
}

View File

@@ -1,8 +1,6 @@
const { hexToNumber } = require('web3-utils')
const logger = require('../services/logger').child({
module: 'web3'
})
const { sendRawTx } = require('./sendTx')
async function getNonce(web3, address) {
try {
@@ -26,15 +24,10 @@ async function getBlockNumber(web3) {
}
}
async function getChainId(chain) {
async function getChainId(web3) {
try {
logger.debug('Getting chain id')
const chainIdHex = await sendRawTx({
chain,
method: 'eth_chainId',
params: []
})
const chainId = hexToNumber(chainIdHex)
const chainId = await web3.eth.getChainId()
logger.debug({ chainId }, 'Chain id obtained')
return chainId
} catch (e) {

View File

@@ -21,7 +21,7 @@ module.exports = {
},
GAS_PRICE_BOUNDARIES: {
MIN: 1,
MAX: 250
MAX: 1000
},
TRANSACTION_RESEND_TIMEOUT: 20 * 60 * 1000,
SENDER_QUEUE_MAX_PRIORITY: 10,

View File

@@ -1,13 +1,13 @@
const assert = require('assert')
const Web3Utils = require('web3-utils')
const { toHex, numberToHex, padLeft } = require('web3').utils
const { strip0x } = require('../../../commons')
function createMessage({ recipient, value, transactionHash, bridgeAddress, expectedMessageLength }) {
recipient = strip0x(recipient)
assert.strictEqual(recipient.length, 20 * 2)
value = Web3Utils.numberToHex(value)
value = Web3Utils.padLeft(value, 32 * 2)
value = numberToHex(value)
value = padLeft(value, 32 * 2)
value = strip0x(value)
assert.strictEqual(value.length, 64)
@@ -60,7 +60,7 @@ function signatureToVRS(rawSignature) {
}
function packSignatures(array) {
const length = strip0x(Web3Utils.toHex(array.length))
const length = strip0x(toHex(array.length))
const msgLength = length.length === 1 ? `0${length}` : length
let v = ''
let r = ''

View File

@@ -1,3 +1,4 @@
const fs = require('fs')
const BigNumber = require('bignumber.js')
const promiseRetry = require('promise-retry')
const Web3 = require('web3')
@@ -93,10 +94,11 @@ function privateKeyToAddress(privateKey) {
}
function nonceError(e) {
const message = e.message.toLowerCase()
return (
e.message.includes('Transaction nonce is too low') ||
e.message.includes('nonce too low') ||
e.message.includes('transaction with same nonce in the queue')
message.includes('transaction nonce is too low') ||
message.includes('nonce too low') ||
message.includes('transaction with same nonce in the queue')
)
}
@@ -105,6 +107,30 @@ function nonceError(e) {
const invert = p => new Promise((res, rej) => p.then(rej, res))
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, ${readAccessLists[fileName].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 = {
syncForEach,
checkHTTPS,
@@ -115,5 +141,6 @@ module.exports = {
privateKeyToAddress,
nonceError,
getRetrySequence,
promiseAny
promiseAny,
readAccessListFile
}

View File

@@ -5,7 +5,6 @@ const { connectWatcherToQueue, connection } = require('./services/amqpClient')
const { getBlockNumber } = require('./tx/web3')
const { redis } = require('./services/redisClient')
const logger = require('./services/logger')
const rpcUrlsManager = require('./services/getRpcUrlsManager')
const { getRequiredBlockConfirmations, getEvents } = require('./tx/web3')
const { checkHTTPS, watchdog } = require('./utils/utils')
const { EXIT_CODES } = require('./utils/constants')
@@ -42,8 +41,7 @@ async function initialize() {
try {
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
rpcUrlsManager.homeUrls.forEach(checkHttps('home'))
rpcUrlsManager.foreignUrls.forEach(checkHttps('foreign'))
web3Instance.currentProvider.urls.forEach(checkHttps(config.chain))
await getLastProcessedBlock()
connectWatcherToQueue({

View File

@@ -1,6 +1,5 @@
const path = require('path')
const logger = require('./services/logger')
const rpcUrlsManager = require('./services/getRpcUrlsManager')
const { checkHTTPS, watchdog } = require('./utils/utils')
const { EXIT_CODES } = require('./utils/constants')
const { connectWorkerToQueue } = require('./services/amqpClient')
@@ -9,12 +8,13 @@ const config = require(path.join('../config/', process.argv[2]))
const convertToChai = require('./workers/convertToChai')(config)
const web3Instance = config.web3
async function initialize() {
try {
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
rpcUrlsManager.homeUrls.forEach(checkHttps('home'))
rpcUrlsManager.foreignUrls.forEach(checkHttps('foreign'))
web3Instance.currentProvider.urls.forEach(checkHttps(config.chain))
connectWorkerToQueue({
queueName: config.workerQueue,

View File

@@ -1,5 +1,5 @@
require('../../env')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../services/HttpListProvider')
const rootLogger = require('../services/logger')
const { web3Foreign } = require('../services/web3')

View File

@@ -2,7 +2,7 @@ const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const sinon = require('sinon')
const Web3 = require('web3')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../src/services/HttpListProvider')
const estimateGas = require('../src/events/processAffirmationRequests/estimateGas')
const errors = require('../src/utils/errors')

View File

@@ -1,7 +1,7 @@
const { expect } = require('chai').use(require('chai-as-promised'))
const sinon = require('sinon')
const Web3 = require('web3')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../src/services/HttpListProvider')
const { createMessage, signatureToVRS } = require('../src/utils/message')
const estimateGas = require('../src/events/processCollectedSignatures/estimateGas')
const errors = require('../src/utils/errors')

View File

@@ -2,7 +2,7 @@ const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const sinon = require('sinon')
const Web3 = require('web3')
const { HttpListProviderError } = require('http-list-provider')
const { HttpListProviderError } = require('../src/services/HttpListProvider')
const estimateGas = require('../src/events/processSignatureRequests/estimateGas')
const errors = require('../src/utils/errors')

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