Compare commits

..

1 Commits

Author SHA1 Message Date
Kirill Fedoseev
c02ecad8fd Fix isBridgeContract handler 2020-10-02 21:21:02 +03:00
146 changed files with 2774 additions and 4100 deletions

View File

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

View File

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

13
.gitignore vendored
View File

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

View File

@@ -41,7 +41,6 @@ ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST | Filename with a list of addresses, separ
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_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_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_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 ## UI configuration
@@ -78,9 +77,4 @@ 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_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_PORT | The port for the Monitor. | integer
MONITOR_BRIDGE_NAME | The name to be used in the url path for the bridge | string 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 | `true` / `false` MONITOR_CACHE_EVENTS | If set to true, monitor will cache obtained events for other workers runs
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST | File with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests should be automatically processed by the CollectedSignatures watcher. | string
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST | File with a list of addresses, separated by newlines. If set, determines the set of accounts whose requests should be marked as unclaimed. Has a lower priority than the `MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST`. | string
MONITOR_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`
MONITOR_HOME_VALIDATORS_BALANCE_ENABLE | If set, defines the list of home validator addresses for which balance should be checked. | `string`
MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE | If set, defines the list of foreign validator addresses for which balance should be checked. | `string`

View File

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

View File

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

View File

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

View File

@@ -39,38 +39,21 @@ export interface ConfirmationsContainerParams {
message: MessageObject message: MessageObject
receipt: Maybe<TransactionReceipt> receipt: Maybe<TransactionReceipt>
fromHome: boolean fromHome: boolean
homeStartBlock: Maybe<number> timestamp: number
foreignStartBlock: Maybe<number>
} }
export const ConfirmationsContainer = ({ export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }: ConfirmationsContainerParams) => {
message,
receipt,
fromHome,
homeStartBlock,
foreignStartBlock
}: ConfirmationsContainerParams) => {
const { const {
home: { name: homeName }, home: { name: homeName },
foreign: { name: foreignName } foreign: { name: foreignName }
} = useStateProvider() } = useStateProvider()
const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt }) const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt })
const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt }) const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt })
const { const { confirmations, status, executionData, signatureCollected, waitingBlocksResolved } = useMessageConfirmations({
confirmations,
status,
executionData,
signatureCollected,
waitingBlocksResolved,
setExecutionData,
executionEventsFetched,
setPendingExecution
} = useMessageConfirmations({
message, message,
receipt, receipt,
fromHome, fromHome,
homeStartBlock, timestamp,
foreignStartBlock,
requiredSignatures, requiredSignatures,
validatorList, validatorList,
blockConfirmations blockConfirmations
@@ -119,17 +102,7 @@ export const ConfirmationsContainer = ({
validatorList={validatorList} validatorList={validatorList}
waitingBlocksResolved={waitingBlocksResolved} waitingBlocksResolved={waitingBlocksResolved}
/> />
{signatureCollected && ( {signatureCollected && <ExecutionConfirmation executionData={executionData} isHome={!fromHome} />}
<ExecutionConfirmation
messageData={message.data}
executionData={executionData}
isHome={!fromHome}
signatureCollected={signatureCollected}
setExecutionData={setExecutionData}
executionEventsFetched={executionEventsFetched}
setPendingExecution={setPendingExecution}
/>
)}
</StyledConfirmationContainer> </StyledConfirmationContainer>
</div> </div>
) )

View File

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

View File

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

View File

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

View File

@@ -10,7 +10,6 @@ import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { ConfirmationsContainer } from './ConfirmationsContainer' import { ConfirmationsContainer } from './ConfirmationsContainer'
import { TransactionReceipt } from 'web3-eth' import { TransactionReceipt } from 'web3-eth'
import { BackButton } from './commons/BackButton' import { BackButton } from './commons/BackButton'
import { useClosestBlock } from '../hooks/useClosestBlock'
export interface StatusContainerParam { export interface StatusContainerParam {
onBackToMain: () => void onBackToMain: () => void
@@ -24,15 +23,12 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
const { chainId, txHash, messageIdParam } = useParams() const { chainId, txHash, messageIdParam } = useParams()
const validChainId = chainId === home.chainId.toString() || chainId === foreign.chainId.toString() const validChainId = chainId === home.chainId.toString() || chainId === foreign.chainId.toString()
const validParameters = validChainId && validTxHash(txHash) const validParameters = validChainId && validTxHash(txHash)
const isHome = chainId === home.chainId.toString()
const { messages, receipt, status, description, timestamp, loading } = useTransactionStatus({ const { messages, receipt, status, description, timestamp, loading } = useTransactionStatus({
txHash: validParameters ? txHash : '', txHash: validParameters ? txHash : '',
chainId: validParameters ? parseInt(chainId) : 0, chainId: validParameters ? parseInt(chainId) : 0,
receiptParam receiptParam
}) })
const homeStartBlock = useClosestBlock(true, isHome, receipt, timestamp)
const foreignStartBlock = useClosestBlock(false, isHome, receipt, timestamp)
const selectedMessageId = messageIdParam === undefined || messages[messageIdParam] === undefined ? -1 : messageIdParam const selectedMessageId = messageIdParam === undefined || messages[messageIdParam] === undefined ? -1 : messageIdParam
@@ -68,6 +64,7 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
const displayReference = multiMessageSelected ? messages[selectedMessageId].id : txHash const displayReference = multiMessageSelected ? messages[selectedMessageId].id : txHash
const formattedMessageId = formatTxHash(displayReference) const formattedMessageId = formatTxHash(displayReference)
const isHome = chainId === home.chainId.toString()
const txExplorerLink = getExplorerTxUrl(txHash, isHome) const txExplorerLink = getExplorerTxUrl(txHash, isHome)
const displayExplorerLink = status !== TRANSACTION_STATUS.NOT_FOUND const displayExplorerLink = status !== TRANSACTION_STATUS.NOT_FOUND
@@ -104,13 +101,7 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
)} )}
{displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />} {displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />}
{displayConfirmations && ( {displayConfirmations && (
<ConfirmationsContainer <ConfirmationsContainer message={messageToConfirm} receipt={receipt} fromHome={isHome} timestamp={timestamp} />
message={messageToConfirm}
receipt={receipt}
fromHome={isHome}
homeStartBlock={homeStartBlock}
foreignStartBlock={foreignStartBlock}
/>
)} )}
<BackButton onBackToMain={onBackToMain} /> <BackButton onBackToMain={onBackToMain} />
</div> </div>

View File

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

View File

@@ -1,48 +0,0 @@
import React from 'react'
import styled from 'styled-components'
import { InfoIcon } from './InfoIcon'
import { CloseIcon } from './CloseIcon'
import { ExplorerTxLink } from './ExplorerTxLink'
const StyledErrorAlert = styled.div`
border: 1px solid var(--failed-color);
border-radius: 4px;
margin-bottom: 20px;
padding-top: 10px;
`
const CloseIconContainer = styled.div`
cursor: pointer;
`
const TextContainer = styled.div`
white-space: pre-wrap;
flex-direction: column;
`
export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: string }) => {
const errorArray = error.split('%link')
const text = errorArray[0]
let link
if (errorArray.length > 1) {
link = (
<ExplorerTxLink href={errorArray[1]} target="_blank" rel="noopener noreferrer">
{errorArray[1]}
</ExplorerTxLink>
)
}
return (
<div className="row is-center">
<StyledErrorAlert className="col-10 is-vertical-align row">
<InfoIcon color="var(--failed-color)" />
<TextContainer className="col-10">
{text}
{link}
</TextContainer>
<CloseIconContainer className="col-1 is-vertical-align is-center" onClick={onClick}>
<CloseIcon color="var(--failed-color)" />
</CloseIconContainer>
</StyledErrorAlert>
</div>
)
}

View File

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

View File

@@ -13,13 +13,11 @@ export const FOREIGN_EXPLORER_TX_TEMPLATE: string = process.env.REACT_APP_ALM_FO
export const HOME_EXPLORER_API: string = process.env.REACT_APP_ALM_HOME_EXPLORER_API || '' export const HOME_EXPLORER_API: string = process.env.REACT_APP_ALM_HOME_EXPLORER_API || ''
export const FOREIGN_EXPLORER_API: string = process.env.REACT_APP_ALM_FOREIGN_EXPLORER_API || '' export const FOREIGN_EXPLORER_API: string = process.env.REACT_APP_ALM_FOREIGN_EXPLORER_API || ''
export const ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION: boolean =
(process.env.REACT_APP_ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION || '').toLowerCase() === 'true'
export const HOME_RPC_POLLING_INTERVAL: number = 5000 export const HOME_RPC_POLLING_INTERVAL: number = 5000
export const FOREIGN_RPC_POLLING_INTERVAL: number = 5000 export const FOREIGN_RPC_POLLING_INTERVAL: number = 5000
export const BLOCK_RANGE: number = 500 export const BLOCK_RANGE: number = 50
export const MAX_TX_SEARCH_BLOCK_RANGE: number = 10000 export const ONE_DAY_TIMESTAMP: number = 86400
export const THREE_DAYS_TIMESTAMP: number = 259200
export const EXECUTE_AFFIRMATION_HASH = 'e7a2c01f' export const EXECUTE_AFFIRMATION_HASH = 'e7a2c01f'
export const SUBMIT_SIGNATURE_HASH = '630cea8e' export const SUBMIT_SIGNATURE_HASH = '630cea8e'
@@ -63,14 +61,3 @@ export const VALIDATOR_CONFIRMATION_STATUS = {
} }
export const SEARCHING_TX = 'Searching Transaction...' export const SEARCHING_TX = 'Searching Transaction...'
export const INCORRECT_CHAIN_ERROR = `Incorrect chain chosen. Switch to ${FOREIGN_NETWORK_NAME} in the wallet.`
export const DOUBLE_EXECUTION_ATTEMPT_ERROR = `Your execution transaction has been reverted.
However, the execution completed successfully in the transaction sent by a different party.`
export const EXECUTION_FAILED_ERROR = `Your execution transaction has been reverted.
Please, contact the support by messaging on %linkhttps://forum.poa.network/c/support`
export const EXECUTION_OUT_OF_GAS_ERROR = `Your execution transaction has been reverted due to Out-of-Gas error.
Please, resend the transaction and provide more gas to it.`

View File

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

View File

@@ -1,68 +0,0 @@
import { useEffect, useState } from 'react'
import { TransactionReceipt } from 'web3-eth'
import { useStateProvider } from '../state/StateProvider'
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
import { getClosestBlockByTimestamp } from '../utils/explorer'
export function useClosestBlock(
searchHome: boolean,
fromHome: boolean,
receipt: Maybe<TransactionReceipt>,
timestamp: number
) {
const { home, foreign } = useStateProvider()
const [blockNumber, setBlockNumber] = useState<number | null>(null)
useEffect(
() => {
if (!receipt || blockNumber || !timestamp) return
if (fromHome === searchHome) {
setBlockNumber(receipt.blockNumber)
return
}
const web3 = searchHome ? home.web3 : foreign.web3
if (!web3) return
const getBlock = async () => {
// try to fast-fetch closest block number from the chain explorer
try {
const api = searchHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API
setBlockNumber(await getClosestBlockByTimestamp(api, timestamp))
return
} catch {}
const lastBlock = await web3.eth.getBlock('latest')
if (lastBlock.timestamp <= timestamp) {
setBlockNumber(lastBlock.number)
return
}
const oldBlock = await web3.eth.getBlock(Math.max(lastBlock.number - 10000, 1))
const blockDiff = lastBlock.number - oldBlock.number
const timeDiff = (lastBlock.timestamp as number) - (oldBlock.timestamp as number)
const averageBlockTime = timeDiff / blockDiff
let currentBlock = lastBlock
let prevBlockDiff = Infinity
while (true) {
const timeDiff = (currentBlock.timestamp as number) - timestamp
const blockDiff = Math.ceil(timeDiff / averageBlockTime)
if (Math.abs(blockDiff) < 5 || Math.abs(blockDiff) >= Math.abs(prevBlockDiff)) {
setBlockNumber(currentBlock.number - blockDiff - 5)
break
}
prevBlockDiff = blockDiff
currentBlock = await web3.eth.getBlock(currentBlock.number - blockDiff)
}
}
getBlock()
},
[blockNumber, foreign.web3, fromHome, home.web3, receipt, searchHome, timestamp]
)
return blockNumber
}

View File

@@ -3,6 +3,7 @@ import { TransactionReceipt } from 'web3-eth'
import { MessageObject } from '../utils/web3' import { MessageObject } from '../utils/web3'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { EventData } from 'web3-eth-contract' import { EventData } from 'web3-eth-contract'
import { getAffirmationsSigned, getMessagesSigned } from '../utils/contract'
import { import {
BLOCK_RANGE, BLOCK_RANGE,
CONFIRMATIONS_STATUS, CONFIRMATIONS_STATUS,
@@ -28,8 +29,7 @@ export interface useMessageConfirmationsParams {
message: MessageObject message: MessageObject
receipt: Maybe<TransactionReceipt> receipt: Maybe<TransactionReceipt>
fromHome: boolean fromHome: boolean
homeStartBlock: Maybe<number> timestamp: number
foreignStartBlock: Maybe<number>
requiredSignatures: number requiredSignatures: number
validatorList: string[] validatorList: string[]
blockConfirmations: number blockConfirmations: number
@@ -57,19 +57,17 @@ export const useMessageConfirmations = ({
message, message,
receipt, receipt,
fromHome, fromHome,
homeStartBlock, timestamp,
foreignStartBlock,
requiredSignatures, requiredSignatures,
validatorList, validatorList,
blockConfirmations blockConfirmations
}: useMessageConfirmationsParams) => { }: useMessageConfirmationsParams) => {
const { home, foreign } = useStateProvider() const { home, foreign } = useStateProvider()
const [confirmations, setConfirmations] = useState([]) const [confirmations, setConfirmations] = useState<Array<ConfirmationParam>>([])
const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED) const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED)
const [waitingBlocks, setWaitingBlocks] = useState(false) const [waitingBlocks, setWaitingBlocks] = useState(false)
const [waitingBlocksResolved, setWaitingBlocksResolved] = useState(false) const [waitingBlocksResolved, setWaitingBlocksResolved] = useState(false)
const [signatureCollected, setSignatureCollected] = useState(false) const [signatureCollected, setSignatureCollected] = useState(false)
const [executionEventsFetched, setExecutionEventsFetched] = useState(false)
const [collectedSignaturesEvent, setCollectedSignaturesEvent] = useState<Maybe<EventData>>(null) const [collectedSignaturesEvent, setCollectedSignaturesEvent] = useState<Maybe<EventData>>(null)
const [executionData, setExecutionData] = useState<ExecutionData>({ const [executionData, setExecutionData] = useState<ExecutionData>({
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
@@ -92,21 +90,10 @@ export const useMessageConfirmations = ({
return filteredList.length > 0 return filteredList.length > 0
} }
// start watching blocks at the start
useEffect(
() => {
if (!home.web3 || !foreign.web3) return
homeBlockNumberProvider.start(home.web3)
foreignBlockNumberProvider.start(foreign.web3)
},
[foreign.web3, home.web3]
)
// Check if the validators are waiting for block confirmations to verify the message // Check if the validators are waiting for block confirmations to verify the message
useEffect( useEffect(
() => { () => {
if (!receipt || !blockConfirmations || waitingBlocksResolved) return if (!receipt || !blockConfirmations) return
const subscriptions: Array<number> = [] const subscriptions: Array<number> = []
@@ -118,6 +105,9 @@ export const useMessageConfirmations = ({
const blockProvider = fromHome ? homeBlockNumberProvider : foreignBlockNumberProvider const blockProvider = fromHome ? homeBlockNumberProvider : foreignBlockNumberProvider
const interval = fromHome ? HOME_RPC_POLLING_INTERVAL : FOREIGN_RPC_POLLING_INTERVAL const interval = fromHome ? HOME_RPC_POLLING_INTERVAL : FOREIGN_RPC_POLLING_INTERVAL
const web3 = fromHome ? home.web3 : foreign.web3
blockProvider.start(web3)
const targetBlock = receipt.blockNumber + blockConfirmations const targetBlock = receipt.blockNumber + blockConfirmations
checkSignaturesWaitingForBLocks( checkSignaturesWaitingForBLocks(
@@ -133,18 +123,10 @@ export const useMessageConfirmations = ({
return () => { return () => {
unsubscribe() unsubscribe()
blockProvider.stop()
} }
}, },
[ [blockConfirmations, foreign.web3, fromHome, validatorList, home.web3, receipt]
blockConfirmations,
foreign.web3,
fromHome,
validatorList,
home.web3,
receipt,
setConfirmations,
waitingBlocksResolved
]
) )
// The collected signature event is only fetched once the signatures are collected on tx from home to foreign, to calculate if // The collected signature event is only fetched once the signatures are collected on tx from home to foreign, to calculate if
@@ -162,6 +144,8 @@ export const useMessageConfirmations = ({
}) })
} }
homeBlockNumberProvider.start(home.web3)
const fromBlock = receipt.blockNumber const fromBlock = receipt.blockNumber
const toBlock = fromBlock + BLOCK_RANGE const toBlock = fromBlock + BLOCK_RANGE
const messageHash = home.web3.utils.soliditySha3Raw(message.data) const messageHash = home.web3.utils.soliditySha3Raw(message.data)
@@ -178,6 +162,7 @@ export const useMessageConfirmations = ({
return () => { return () => {
unsubscribe() unsubscribe()
homeBlockNumberProvider.stop()
} }
}, },
[fromHome, home.bridgeContract, home.web3, message.data, receipt, signatureCollected] [fromHome, home.bridgeContract, home.web3, message.data, receipt, signatureCollected]
@@ -188,7 +173,6 @@ export const useMessageConfirmations = ({
useEffect( useEffect(
() => { () => {
if (!fromHome || !home.web3 || !receipt || !collectedSignaturesEvent || !blockConfirmations) return if (!fromHome || !home.web3 || !receipt || !collectedSignaturesEvent || !blockConfirmations) return
if (waitingBlocksForExecutionResolved) return
const subscriptions: Array<number> = [] const subscriptions: Array<number> = []
@@ -198,6 +182,7 @@ export const useMessageConfirmations = ({
}) })
} }
homeBlockNumberProvider.start(home.web3)
const targetBlock = collectedSignaturesEvent.blockNumber + blockConfirmations const targetBlock = collectedSignaturesEvent.blockNumber + blockConfirmations
checkWaitingBlocksForExecution( checkWaitingBlocksForExecution(
@@ -213,16 +198,17 @@ export const useMessageConfirmations = ({
return () => { return () => {
unsubscribe() unsubscribe()
homeBlockNumberProvider.stop()
} }
}, },
[collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, receipt, waitingBlocksForExecutionResolved] [collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, receipt]
) )
// Checks if validators verified the message // Checks if validators verified the message
// To avoid making extra requests, this is only executed when validators finished waiting for blocks confirmations // To avoid making extra requests, this is only executed when validators finished waiting for blocks confirmations
useEffect( useEffect(
() => { () => {
if (!waitingBlocksResolved || !homeStartBlock || !requiredSignatures) return if (!waitingBlocksResolved || !timestamp || !requiredSignatures) return
const subscriptions: Array<number> = [] const subscriptions: Array<number> = []
@@ -232,18 +218,20 @@ export const useMessageConfirmations = ({
}) })
} }
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
getConfirmationsForTx( getConfirmationsForTx(
message.data, message.data,
home.web3, home.web3,
validatorList, validatorList,
home.bridgeContract, home.bridgeContract,
fromHome, confirmationContractMethod,
setConfirmations, setConfirmations,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, waitingBlocksResolved,
subscriptions, subscriptions,
homeStartBlock, timestamp,
getValidatorFailedTransactionsForMessage, getValidatorFailedTransactionsForMessage,
setFailedConfirmations, setFailedConfirmations,
getValidatorPendingTransactionsForMessage, getValidatorPendingTransactionsForMessage,
@@ -263,8 +251,7 @@ export const useMessageConfirmations = ({
home.bridgeContract, home.bridgeContract,
requiredSignatures, requiredSignatures,
waitingBlocksResolved, waitingBlocksResolved,
homeStartBlock, timestamp
setConfirmations
] ]
) )
@@ -274,8 +261,6 @@ export const useMessageConfirmations = ({
useEffect( useEffect(
() => { () => {
if ((fromHome && !waitingBlocksForExecutionResolved) || (!fromHome && !waitingBlocksResolved)) return if ((fromHome && !waitingBlocksForExecutionResolved) || (!fromHome && !waitingBlocksResolved)) return
const startBlock = fromHome ? foreignStartBlock : homeStartBlock
if (!startBlock) return
const subscriptions: Array<number> = [] const subscriptions: Array<number> = []
@@ -291,7 +276,6 @@ export const useMessageConfirmations = ({
const interval = fromHome ? FOREIGN_RPC_POLLING_INTERVAL : HOME_RPC_POLLING_INTERVAL const interval = fromHome ? FOREIGN_RPC_POLLING_INTERVAL : HOME_RPC_POLLING_INTERVAL
getFinalizationEvent( getFinalizationEvent(
fromHome,
bridgeContract, bridgeContract,
contractEvent, contractEvent,
providedWeb3, providedWeb3,
@@ -300,13 +284,12 @@ export const useMessageConfirmations = ({
message, message,
interval, interval,
subscriptions, subscriptions,
startBlock, timestamp,
collectedSignaturesEvent, collectedSignaturesEvent,
getExecutionFailedTransactionForMessage, getExecutionFailedTransactionForMessage,
setFailedExecution, setFailedExecution,
getExecutionPendingTransactionsForMessage, getExecutionPendingTransactionsForMessage,
setPendingExecution, setPendingExecution
setExecutionEventsFetched
) )
return () => { return () => {
@@ -322,9 +305,8 @@ export const useMessageConfirmations = ({
home.web3, home.web3,
waitingBlocksResolved, waitingBlocksResolved,
waitingBlocksForExecutionResolved, waitingBlocksForExecutionResolved,
collectedSignaturesEvent, timestamp,
foreignStartBlock, collectedSignaturesEvent
homeStartBlock
] ]
) )
@@ -336,9 +318,6 @@ export const useMessageConfirmations = ({
? CONFIRMATIONS_STATUS.SUCCESS ? CONFIRMATIONS_STATUS.SUCCESS
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED : CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED
setStatus(newStatus) setStatus(newStatus)
foreignBlockNumberProvider.stop()
homeBlockNumberProvider.stop()
} else if (signatureCollected) { } else if (signatureCollected) {
if (fromHome) { if (fromHome) {
if (waitingBlocksForExecution) { if (waitingBlocksForExecution) {
@@ -390,9 +369,6 @@ export const useMessageConfirmations = ({
status, status,
signatureCollected, signatureCollected,
executionData, executionData,
setExecutionData, waitingBlocksResolved
waitingBlocksResolved,
executionEventsFetched,
setPendingExecution
} }
} }

View File

@@ -1,6 +1,6 @@
import Web3 from 'web3' import Web3 from 'web3'
import differenceInMilliseconds from 'date-fns/differenceInMilliseconds' import differenceInMilliseconds from 'date-fns/differenceInMilliseconds'
import { FOREIGN_RPC_POLLING_INTERVAL, HOME_RPC_POLLING_INTERVAL } from '../config/constants' import { HOME_RPC_POLLING_INTERVAL } from '../config/constants'
export class BlockNumberProvider { export class BlockNumberProvider {
private running: number private running: number
@@ -61,4 +61,4 @@ export class BlockNumberProvider {
} }
export const homeBlockNumberProvider = new BlockNumberProvider(HOME_RPC_POLLING_INTERVAL) export const homeBlockNumberProvider = new BlockNumberProvider(HOME_RPC_POLLING_INTERVAL)
export const foreignBlockNumberProvider = new BlockNumberProvider(FOREIGN_RPC_POLLING_INTERVAL) export const foreignBlockNumberProvider = new BlockNumberProvider(HOME_RPC_POLLING_INTERVAL)

View File

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

View File

@@ -17,33 +17,19 @@ const otherAddress = '0xD4075FB57fCf038bFc702c915Ef9592534bED5c1'
describe('getFailedTransactions', () => { describe('getFailedTransactions', () => {
test('should only return failed transactions', async () => { test('should only return failed transactions', async () => {
const to = otherAddress const transactions = [{ isError: '0' }, { isError: '1' }, { isError: '0' }, { isError: '1' }, { isError: '1' }]
const transactions = [
{ isError: '0', to },
{ isError: '1', to },
{ isError: '0', to },
{ isError: '1', to },
{ isError: '1', to }
]
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions) const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
const result = await getFailedTransactions('', to, 0, 1, '', fetchAccountTransactions) const result = await getFailedTransactions('', '', 0, 1, '', fetchAccountTransactions)
expect(result.length).toEqual(3) expect(result.length).toEqual(3)
}) })
}) })
describe('getSuccessTransactions', () => { describe('getSuccessTransactions', () => {
test('should only return success transactions', async () => { test('should only return success transactions', async () => {
const to = otherAddress const transactions = [{ isError: '0' }, { isError: '1' }, { isError: '0' }, { isError: '1' }, { isError: '1' }]
const transactions = [
{ isError: '0', to },
{ isError: '1', to },
{ isError: '0', to },
{ isError: '1', to },
{ isError: '1', to }
]
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions) const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
const result = await getSuccessTransactions('', to, 0, 1, '', fetchAccountTransactions) const result = await getSuccessTransactions('', '', 0, 1, '', fetchAccountTransactions)
expect(result.length).toEqual(2) expect(result.length).toEqual(2)
}) })
}) })
@@ -88,8 +74,8 @@ describe('getExecutionFailedTransactionForMessage', () => {
account: '', account: '',
to: '', to: '',
messageData, messageData,
startBlock: 0, startTimestamp: 0,
endBlock: 1 endTimestamp: 1
}, },
fetchAccountTransactions fetchAccountTransactions
) )

View File

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

View File

@@ -61,10 +61,8 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn() const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn() const getPendingExecution = jest.fn()
const setPendingExecution = jest.fn() const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent( await getFinalizationEvent(
true,
contract, contract,
eventName, eventName,
web3, web3,
@@ -78,8 +76,7 @@ describe('getFinalizationEvent', () => {
getFailedExecution, getFailedExecution,
setFailedExecution, setFailedExecution,
getPendingExecution, getPendingExecution,
setPendingExecution, setPendingExecution
setExecutionEventsFetched
) )
unsubscribe() unsubscribe()
@@ -113,10 +110,8 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn() const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn() const getPendingExecution = jest.fn()
const setPendingExecution = jest.fn() const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent( await getFinalizationEvent(
true,
contract, contract,
eventName, eventName,
web3, web3,
@@ -130,8 +125,7 @@ describe('getFinalizationEvent', () => {
getFailedExecution, getFailedExecution,
setFailedExecution, setFailedExecution,
getPendingExecution, getPendingExecution,
setPendingExecution, setPendingExecution
setExecutionEventsFetched
) )
unsubscribe() unsubscribe()
@@ -165,10 +159,8 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn() const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn().mockResolvedValue([]) const getPendingExecution = jest.fn().mockResolvedValue([])
const setPendingExecution = jest.fn() const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent( await getFinalizationEvent(
true,
contract, contract,
eventName, eventName,
web3, web3,
@@ -182,8 +174,7 @@ describe('getFinalizationEvent', () => {
getFailedExecution, getFailedExecution,
setFailedExecution, setFailedExecution,
getPendingExecution, getPendingExecution,
setPendingExecution, setPendingExecution
setExecutionEventsFetched
) )
unsubscribe() unsubscribe()
@@ -217,10 +208,8 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn() const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn().mockResolvedValue([{ hash: txHash }]) const getPendingExecution = jest.fn().mockResolvedValue([{ hash: txHash }])
const setPendingExecution = jest.fn() const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent( await getFinalizationEvent(
true,
contract, contract,
eventName, eventName,
web3, web3,
@@ -234,8 +223,7 @@ describe('getFinalizationEvent', () => {
getFailedExecution, getFailedExecution,
setFailedExecution, setFailedExecution,
getPendingExecution, getPendingExecution,
setPendingExecution, setPendingExecution
setExecutionEventsFetched
) )
unsubscribe() unsubscribe()
@@ -276,10 +264,8 @@ describe('getFinalizationEvent', () => {
const setFailedExecution = jest.fn() const setFailedExecution = jest.fn()
const getPendingExecution = jest.fn().mockResolvedValue([]) const getPendingExecution = jest.fn().mockResolvedValue([])
const setPendingExecution = jest.fn() const setPendingExecution = jest.fn()
const setExecutionEventsFetched = jest.fn()
await getFinalizationEvent( await getFinalizationEvent(
true,
contract, contract,
eventName, eventName,
web3, web3,
@@ -293,8 +279,7 @@ describe('getFinalizationEvent', () => {
getFailedExecution, getFailedExecution,
setFailedExecution, setFailedExecution,
getPendingExecution, getPendingExecution,
setPendingExecution, setPendingExecution
setExecutionEventsFetched
) )
unsubscribe() unsubscribe()

View File

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

View File

@@ -1,10 +1,8 @@
import { import {
BLOCK_RANGE,
EXECUTE_AFFIRMATION_HASH, EXECUTE_AFFIRMATION_HASH,
EXECUTE_SIGNATURES_HASH, EXECUTE_SIGNATURES_HASH,
FOREIGN_EXPLORER_API, FOREIGN_EXPLORER_API,
HOME_EXPLORER_API, HOME_EXPLORER_API,
MAX_TX_SEARCH_BLOCK_RANGE,
SUBMIT_SIGNATURE_HASH SUBMIT_SIGNATURE_HASH
} from '../config/constants' } from '../config/constants'
@@ -14,7 +12,6 @@ export interface APITransaction {
input: string input: string
to: string to: string
hash: string hash: string
blockNumber: string
} }
export interface APIPendingTransaction { export interface APIPendingTransaction {
@@ -30,42 +27,106 @@ export interface PendingTransactionsParams {
export interface AccountTransactionsParams { export interface AccountTransactionsParams {
account: string account: string
startBlock: number to: string
endBlock: number startTimestamp: number
endTimestamp: number
api: string api: string
} }
export interface GetFailedTransactionParams {
account: string
to: string
messageData: string
startTimestamp: number
endTimestamp: number
}
export interface GetPendingTransactionParams { export interface GetPendingTransactionParams {
account: string account: string
to: string to: string
messageData: string messageData: string
} }
export interface GetTransactionParams extends GetPendingTransactionParams { export const fetchAccountTransactionsFromBlockscout = async ({
startBlock: number account,
endBlock: number to,
} startTimestamp,
endTimestamp,
export const fetchAccountTransactions = async ({ account, startBlock, endBlock, api }: AccountTransactionsParams) => { api
const params = `module=account&action=txlist&address=${account}&filterby=from&startblock=${startBlock}&endblock=${endBlock}` }: AccountTransactionsParams): Promise<APITransaction[]> => {
const url = api.includes('blockscout') ? `${api}?${params}` : `${api}&${params}` const url = `${api}?module=account&action=txlist&address=${account}&filterby=from=${account}&to=${to}&starttimestamp=${startTimestamp}&endtimestamp=${endTimestamp}`
try {
const result = await fetch(url).then(res => res.json()) const result = await fetch(url).then(res => res.json())
if (result.status === '0') {
if (result.message === 'No transactions found') {
return [] return []
} }
return result.result return result.result
} catch (e) {
console.log(e)
return []
}
}
export const getBlockByTimestampUrl = (api: string, timestamp: number) =>
`${api}&module=block&action=getblocknobytime&timestamp=${timestamp}&closest=before`
export const fetchAccountTransactionsFromEtherscan = async ({
account,
to,
startTimestamp,
endTimestamp,
api
}: AccountTransactionsParams): Promise<APITransaction[]> => {
const startBlockUrl = getBlockByTimestampUrl(api, startTimestamp)
const endBlockUrl = getBlockByTimestampUrl(api, endTimestamp)
let fromBlock = 0
let toBlock = 9999999999999
try {
const [fromBlockResult, toBlockResult] = await Promise.all([
fetch(startBlockUrl).then(res => res.json()),
fetch(endBlockUrl).then(res => res.json())
])
if (fromBlockResult.status !== '0') {
fromBlock = parseInt(fromBlockResult.result)
}
if (toBlockResult.status !== '0') {
toBlock = parseInt(toBlockResult.result)
}
} catch (e) {
console.log(e)
return []
}
const url = `${api}&module=account&action=txlist&address=${account}&startblock=${fromBlock}&endblock=${toBlock}`
try {
const result = await fetch(url).then(res => res.json())
if (result.status === '0') {
return []
}
const toAddressLowerCase = to.toLowerCase()
const transactions: APITransaction[] = result.result
return transactions.filter(t => t.to.toLowerCase() === toAddressLowerCase)
} catch (e) {
console.log(e)
return []
}
}
export const fetchAccountTransactions = (api: string) => {
return api.includes('blockscout') ? fetchAccountTransactionsFromBlockscout : fetchAccountTransactionsFromEtherscan
} }
export const fetchPendingTransactions = async ({ export const fetchPendingTransactions = async ({
account, account,
api api
}: PendingTransactionsParams): Promise<APIPendingTransaction[]> => { }: PendingTransactionsParams): Promise<APIPendingTransaction[]> => {
if (!api.includes('blockscout')) {
return []
}
const url = `${api}?module=account&action=pendingtxlist&address=${account}` const url = `${api}?module=account&action=pendingtxlist&address=${account}`
try { try {
@@ -80,96 +141,30 @@ export const fetchPendingTransactions = async ({
} }
} }
export const getClosestBlockByTimestamp = async (api: string, timestamp: number): Promise<number> => {
if (api.includes('blockscout')) {
throw new Error('Blockscout does not support getblocknobytime')
}
const url = `${api}&module=block&action=getblocknobytime&timestamp=${timestamp}&closest=before`
const blockNumber = await fetch(url).then(res => res.json())
return parseInt(blockNumber.result)
}
// fast version of fetchAccountTransactions
// sequentially fetches transactions in small batches
// caches the result
const transactionsCache: { [key: string]: { lastBlock: number; transactions: APITransaction[] } } = {}
export const getAccountTransactions = async ({
account,
startBlock,
endBlock,
api
}: AccountTransactionsParams): Promise<APITransaction[]> => {
const key = `${account}-${startBlock}-${api}`
// initialize empty cache if it doesn't exist yet
if (!transactionsCache[key]) {
transactionsCache[key] = { lastBlock: startBlock - 1, transactions: [] }
}
// if cache contains events up to block X,
// new batch is fetched for range [X + 1, X + 1 + BLOCK_RANGE]
const newStartBlock = transactionsCache[key].lastBlock + 1
const newEndBlock = newStartBlock + BLOCK_RANGE
// search for new transactions only if max allowed block range is not yet exceeded
if (newEndBlock <= startBlock + MAX_TX_SEARCH_BLOCK_RANGE) {
const newTransactions = await fetchAccountTransactions({
account,
startBlock: newStartBlock,
endBlock: newEndBlock,
api
})
const transactions = transactionsCache[key].transactions.concat(...newTransactions)
// cache updated transactions list
transactionsCache[key].transactions = transactions
// enbBlock is assumed to be the current block number of the chain
// if the whole range is finalized, last block can be safely updated to the end of the range
// this works even if there are no transactions in the list
if (newEndBlock < endBlock) {
transactionsCache[key].lastBlock = newEndBlock
} else if (transactions.length > 0) {
transactionsCache[key].lastBlock = parseInt(transactions[transactions.length - 1].blockNumber, 10)
}
return transactions
}
console.warn(`Reached max transaction searching range, returning previously cached transactions for ${account}`)
return transactionsCache[key].transactions
}
const filterReceiver = (to: string) => (tx: APITransaction) => tx.to.toLowerCase() === to.toLowerCase()
export const getFailedTransactions = async ( export const getFailedTransactions = async (
account: string, account: string,
to: string, to: string,
startBlock: number, startTimestamp: number,
endBlock: number, endTimestamp: number,
api: string, api: string,
getAccountTransactionsMethod = getAccountTransactions fetchAccountTransactions: (args: AccountTransactionsParams) => Promise<APITransaction[]>
): Promise<APITransaction[]> => { ): Promise<APITransaction[]> => {
const transactions = await getAccountTransactionsMethod({ account, startBlock, endBlock, api }) const transactions = await fetchAccountTransactions({ account, to, startTimestamp, endTimestamp, api })
return transactions.filter(t => t.isError !== '0').filter(filterReceiver(to)) return transactions.filter(t => t.isError !== '0')
} }
export const getSuccessTransactions = async ( export const getSuccessTransactions = async (
account: string, account: string,
to: string, to: string,
startBlock: number, startTimestamp: number,
endBlock: number, endTimestamp: number,
api: string, api: string,
getAccountTransactionsMethod = getAccountTransactions fetchAccountTransactions: (args: AccountTransactionsParams) => Promise<APITransaction[]>
): Promise<APITransaction[]> => { ): Promise<APITransaction[]> => {
const transactions = await getAccountTransactionsMethod({ account, startBlock, endBlock, api }) const transactions = await fetchAccountTransactions({ account, to, startTimestamp, endTimestamp, api })
return transactions.filter(t => t.isError === '0').filter(filterReceiver(to)) return transactions.filter(t => t.isError === '0')
} }
export const filterValidatorSignatureTransaction = ( export const filterValidatorSignatureTransaction = (
@@ -188,10 +183,17 @@ export const getValidatorFailedTransactionsForMessage = async ({
account, account,
to, to,
messageData, messageData,
startBlock, startTimestamp,
endBlock endTimestamp
}: GetTransactionParams): Promise<APITransaction[]> => { }: GetFailedTransactionParams): Promise<APITransaction[]> => {
const failedTransactions = await getFailedTransactions(account, to, startBlock, endBlock, HOME_EXPLORER_API) const failedTransactions = await getFailedTransactions(
account,
to,
startTimestamp,
endTimestamp,
HOME_EXPLORER_API,
fetchAccountTransactionsFromBlockscout
)
return filterValidatorSignatureTransaction(failedTransactions, messageData) return filterValidatorSignatureTransaction(failedTransactions, messageData)
} }
@@ -200,19 +202,33 @@ export const getValidatorSuccessTransactionsForMessage = async ({
account, account,
to, to,
messageData, messageData,
startBlock, startTimestamp,
endBlock endTimestamp
}: GetTransactionParams): Promise<APITransaction[]> => { }: GetFailedTransactionParams): Promise<APITransaction[]> => {
const transactions = await getSuccessTransactions(account, to, startBlock, endBlock, HOME_EXPLORER_API) const transactions = await getSuccessTransactions(
account,
to,
startTimestamp,
endTimestamp,
HOME_EXPLORER_API,
fetchAccountTransactionsFromBlockscout
)
return filterValidatorSignatureTransaction(transactions, messageData) return filterValidatorSignatureTransaction(transactions, messageData)
} }
export const getExecutionFailedTransactionForMessage = async ( export const getExecutionFailedTransactionForMessage = async (
{ account, to, messageData, startBlock, endBlock }: GetTransactionParams, { account, to, messageData, startTimestamp, endTimestamp }: GetFailedTransactionParams,
getFailedTransactionsMethod = getFailedTransactions getFailedTransactionsMethod = getFailedTransactions
): Promise<APITransaction[]> => { ): Promise<APITransaction[]> => {
const failedTransactions = await getFailedTransactionsMethod(account, to, startBlock, endBlock, FOREIGN_EXPLORER_API) const failedTransactions = await getFailedTransactionsMethod(
account,
to,
startTimestamp,
endTimestamp,
FOREIGN_EXPLORER_API,
fetchAccountTransactions(FOREIGN_EXPLORER_API)
)
const messageDataValue = messageData.replace('0x', '') const messageDataValue = messageData.replace('0x', '')
return failedTransactions.filter(t => t.input.includes(EXECUTE_SIGNATURES_HASH) && t.input.includes(messageDataValue)) return failedTransactions.filter(t => t.input.includes(EXECUTE_SIGNATURES_HASH) && t.input.includes(messageDataValue))

View File

@@ -31,6 +31,7 @@ export const getCollectedSignaturesEvent = async (
if (filteredEvents.length) { if (filteredEvents.length) {
const event = filteredEvents[0] const event = filteredEvents[0]
setCollectedSignaturesEvent(event) setCollectedSignaturesEvent(event)
homeBlockNumberProvider.stop()
} else { } else {
const newFromBlock = currentBlock ? securedToBlock : fromBlock const newFromBlock = currentBlock ? securedToBlock : fromBlock
const newToBlock = currentBlock ? toBlock + BLOCK_RANGE : toBlock const newToBlock = currentBlock ? toBlock + BLOCK_RANGE : toBlock

View File

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

View File

@@ -1,20 +1,40 @@
import { Contract, EventData } from 'web3-eth-contract' import { Contract, EventData } from 'web3-eth-contract'
import Web3 from 'web3' import Web3 from 'web3'
import { CACHE_KEY_EXECUTION_FAILED, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants' import { CACHE_KEY_EXECUTION_FAILED, THREE_DAYS_TIMESTAMP, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { ExecutionData } from '../hooks/useMessageConfirmations' import { ExecutionData } from '../hooks/useMessageConfirmations'
import { APIPendingTransaction, APITransaction, GetTransactionParams, GetPendingTransactionParams } from './explorer' import {
APIPendingTransaction,
APITransaction,
GetFailedTransactionParams,
GetPendingTransactionParams
} from './explorer'
import { getBlock, MessageObject } from './web3' import { getBlock, MessageObject } from './web3'
import validatorsCache from '../services/ValidatorsCache' import validatorsCache from '../services/ValidatorsCache'
import { foreignBlockNumberProvider, homeBlockNumberProvider } from '../services/BlockNumberProvider'
export const getSuccessExecutionData = async (contract: Contract, eventName: string, web3: Web3, messageId: string) => { export const getFinalizationEvent = async (
contract: Maybe<Contract>,
eventName: string,
web3: Maybe<Web3>,
setResult: React.Dispatch<React.SetStateAction<ExecutionData>>,
waitingBlocksResolved: boolean,
message: MessageObject,
interval: number,
subscriptions: number[],
timestamp: number,
collectedSignaturesEvent: Maybe<EventData>,
getFailedExecution: (args: GetFailedTransactionParams) => Promise<APITransaction[]>,
setFailedExecution: Function,
getPendingExecution: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>,
setPendingExecution: Function
) => {
if (!contract || !web3 || !waitingBlocksResolved) return
// Since it filters by the message id, only one event will be fetched // Since it filters by the message id, only one event will be fetched
// so there is no need to limit the range of the block to reduce the network traffic // so there is no need to limit the range of the block to reduce the network traffic
const events: EventData[] = await contract.getPastEvents(eventName, { const events: EventData[] = await contract.getPastEvents(eventName, {
fromBlock: 0, fromBlock: 0,
toBlock: 'latest', toBlock: 'latest',
filter: { filter: {
messageId messageId: message.id
} }
}) })
if (events.length > 0) { if (events.length > 0) {
@@ -27,41 +47,14 @@ export const getSuccessExecutionData = async (contract: Contract, eventName: str
const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from) const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from)
return { setResult({
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
validator: validatorAddress, validator: validatorAddress,
txHash: event.transactionHash, txHash: event.transactionHash,
timestamp: blockTimestamp, timestamp: blockTimestamp,
executionResult: event.returnValues.status executionResult: event.returnValues.status
} })
}
return null
}
export const getFinalizationEvent = async (
fromHome: boolean,
contract: Maybe<Contract>,
eventName: string,
web3: Maybe<Web3>,
setResult: React.Dispatch<React.SetStateAction<ExecutionData>>,
waitingBlocksResolved: boolean,
message: MessageObject,
interval: number,
subscriptions: number[],
startBlock: number,
collectedSignaturesEvent: Maybe<EventData>,
getFailedExecution: (args: GetTransactionParams) => Promise<APITransaction[]>,
setFailedExecution: Function,
getPendingExecution: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>,
setPendingExecution: Function,
setExecutionEventsFetched: Function
) => {
if (!contract || !web3 || !waitingBlocksResolved) return
const successExecutionData = await getSuccessExecutionData(contract, eventName, web3, message.id)
if (successExecutionData) {
setResult(successExecutionData)
} else { } else {
setExecutionEventsFetched(true)
// If event is defined, it means it is a message from Home to Foreign // If event is defined, it means it is a message from Home to Foreign
if (collectedSignaturesEvent) { if (collectedSignaturesEvent) {
const validator = collectedSignaturesEvent.returnValues.authorityResponsibleForRelay const validator = collectedSignaturesEvent.returnValues.authorityResponsibleForRelay
@@ -89,15 +82,14 @@ export const getFinalizationEvent = async (
} else { } else {
const validatorExecutionCacheKey = `${CACHE_KEY_EXECUTION_FAILED}${validator}-${message.id}` const validatorExecutionCacheKey = `${CACHE_KEY_EXECUTION_FAILED}${validator}-${message.id}`
const failedFromCache = validatorsCache.get(validatorExecutionCacheKey) const failedFromCache = validatorsCache.get(validatorExecutionCacheKey)
const blockProvider = fromHome ? foreignBlockNumberProvider : homeBlockNumberProvider
if (!failedFromCache) { if (!failedFromCache) {
const failedTransactions = await getFailedExecution({ const failedTransactions = await getFailedExecution({
account: validator, account: validator,
to: contract.options.address, to: contract.options.address,
messageData: message.data, messageData: message.data,
startBlock, startTimestamp: timestamp,
endBlock: blockProvider.get() || 0 endTimestamp: timestamp + THREE_DAYS_TIMESTAMP
}) })
if (failedTransactions.length > 0) { if (failedTransactions.length > 0) {
@@ -123,7 +115,6 @@ export const getFinalizationEvent = async (
const timeoutId = setTimeout( const timeoutId = setTimeout(
() => () =>
getFinalizationEvent( getFinalizationEvent(
fromHome,
contract, contract,
eventName, eventName,
web3, web3,
@@ -132,13 +123,12 @@ export const getFinalizationEvent = async (
message, message,
interval, interval,
subscriptions, subscriptions,
startBlock, timestamp,
collectedSignaturesEvent, collectedSignaturesEvent,
getFailedExecution, getFailedExecution,
setFailedExecution, setFailedExecution,
getPendingExecution, getPendingExecution,
setPendingExecution, setPendingExecution
setExecutionEventsFetched
), ),
interval interval
) )

View File

@@ -16,6 +16,7 @@ export const checkSignaturesWaitingForBLocks = async (
if (currentBlock && currentBlock >= targetBlock) { if (currentBlock && currentBlock >= targetBlock) {
setWaitingBlocksResolved(true) setWaitingBlocksResolved(true)
setWaitingStatus(false) setWaitingStatus(false)
blockProvider.stop()
} else { } else {
let nextInterval = interval let nextInterval = interval
if (!currentBlock) { if (!currentBlock) {

View File

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

View File

@@ -2,9 +2,18 @@ import Web3 from 'web3'
import { Contract } from 'web3-eth-contract' import { Contract } from 'web3-eth-contract'
import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations' import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
import validatorsCache from '../services/ValidatorsCache' import validatorsCache from '../services/ValidatorsCache'
import { CACHE_KEY_FAILED, CACHE_KEY_SUCCESS, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants' import {
import { APIPendingTransaction, APITransaction, GetTransactionParams, GetPendingTransactionParams } from './explorer' CACHE_KEY_FAILED,
import { homeBlockNumberProvider } from '../services/BlockNumberProvider' CACHE_KEY_SUCCESS,
ONE_DAY_TIMESTAMP,
VALIDATOR_CONFIRMATION_STATUS
} from '../config/constants'
import {
APIPendingTransaction,
APITransaction,
GetFailedTransactionParams,
GetPendingTransactionParams
} from './explorer'
export const getValidatorConfirmation = ( export const getValidatorConfirmation = (
web3: Web3, web3: Web3,
@@ -36,13 +45,11 @@ export const getValidatorConfirmation = (
} }
} }
export const getSuccessExecutionTransaction = ( export const getValidatorSuccessTransaction = (
web3: Web3,
bridgeContract: Contract, bridgeContract: Contract,
fromHome: boolean,
messageData: string, messageData: string,
startBlock: number, timestamp: number,
getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]> getSuccessTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => { ) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
const { validator } = validatorData const { validator } = validatorData
const validatorCacheKey = `${CACHE_KEY_SUCCESS}${validatorData.validator}-${messageData}` const validatorCacheKey = `${CACHE_KEY_SUCCESS}${validatorData.validator}-${messageData}`
@@ -56,8 +63,8 @@ export const getSuccessExecutionTransaction = (
account: validatorData.validator, account: validatorData.validator,
to: bridgeContract.options.address, to: bridgeContract.options.address,
messageData, messageData,
startBlock, startTimestamp: timestamp,
endBlock: homeBlockNumberProvider.get() || 0 endTimestamp: timestamp + ONE_DAY_TIMESTAMP
}) })
let txHashTimestamp = 0 let txHashTimestamp = 0
@@ -89,8 +96,8 @@ export const getSuccessExecutionTransaction = (
export const getValidatorFailedTransaction = ( export const getValidatorFailedTransaction = (
bridgeContract: Contract, bridgeContract: Contract,
messageData: string, messageData: string,
startBlock: number, timestamp: number,
getFailedTransactions: (args: GetTransactionParams) => Promise<APITransaction[]> getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => { ) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
const validatorCacheKey = `${CACHE_KEY_FAILED}${validatorData.validator}-${messageData}` const validatorCacheKey = `${CACHE_KEY_FAILED}${validatorData.validator}-${messageData}`
const failedFromCache = validatorsCache.getData(validatorCacheKey) const failedFromCache = validatorsCache.getData(validatorCacheKey)
@@ -103,8 +110,8 @@ export const getValidatorFailedTransaction = (
account: validatorData.validator, account: validatorData.validator,
to: bridgeContract.options.address, to: bridgeContract.options.address,
messageData, messageData,
startBlock, startTimestamp: timestamp,
endBlock: homeBlockNumberProvider.get() || 0 endTimestamp: timestamp + ONE_DAY_TIMESTAMP
}) })
const newStatus = const newStatus =
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.FAILED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.FAILED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED

View File

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

View File

@@ -2600,11 +2600,6 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== 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": "@types/node@*", "@types/node@>= 8":
version "13.11.0" version "13.11.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b"
@@ -3220,11 +3215,6 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0" delegates "^1.0.0"
readable-stream "^2.0.6" 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: argparse@^1.0.7:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -3350,11 +3340,6 @@ assert@^1.1.1:
object-assign "^4.1.1" object-assign "^4.1.1"
util "0.10.3" 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: assign-symbols@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
@@ -3450,13 +3435,6 @@ axios@^0.18.0:
follow-redirects "1.5.10" follow-redirects "1.5.10"
is-buffer "^2.0.2" 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: axobject-query@^2.0.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799"
@@ -3821,11 +3799,6 @@ browser-resolve@^1.11.3:
dependencies: dependencies:
resolve "1.1.7" 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: browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
@@ -4215,18 +4188,6 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= 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: 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" version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@@ -4252,11 +4213,6 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== 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: check-types@^8.0.3:
version "8.0.3" version "8.0.3"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
@@ -4490,11 +4446,6 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies: dependencies:
delayed-stream "~1.0.0" 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: commander@2.17.x:
version "2.17.1" version "2.17.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
@@ -5290,13 +5241,6 @@ dedent@^0.7.0:
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= 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: deep-equal@^1.0.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
@@ -5469,16 +5413,6 @@ diff-sequences@^24.9.0:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== 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: diffie-hellman@^5.0.0:
version "5.0.3" version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@@ -7159,11 +7093,6 @@ get-caller-file@^2.0.1:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 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: get-own-enumerable-property-symbols@^3.0.0:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
@@ -7298,18 +7227,6 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= 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: glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
version "7.1.6" version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
@@ -7443,11 +7360,6 @@ 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" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
growl@1.10.5:
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: growly@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -7606,11 +7518,6 @@ hdkey@^1.1.1:
safe-buffer "^5.1.1" safe-buffer "^5.1.1"
secp256k1 "^3.0.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: he@1.2.x:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
@@ -9685,11 +9592,6 @@ make-dir@^2.0.0, make-dir@^2.1.0:
pify "^4.0.1" pify "^4.0.1"
semver "^5.6.0" 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: make-fetch-happen@^5.0.0:
version "5.0.2" version "5.0.2"
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd"
@@ -10041,11 +9943,6 @@ minimist-options@^3.0.1:
arrify "^1.0.1" arrify "^1.0.1"
is-plain-obj "^1.1.0" 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: minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5" version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
@@ -10110,13 +10007,6 @@ mkdirp@*:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== 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: mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.5" version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
@@ -10131,23 +10021,6 @@ mkdirp@~0.5.0, mkdirp@~0.5.1:
dependencies: dependencies:
minimist "^1.2.5" 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: mock-fs@^4.1.0:
version "4.11.0" version "4.11.0"
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.11.0.tgz#0828107e4b843a6ba855ecebfe3c6e073b69db92" resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.11.0.tgz#0828107e4b843a6ba855ecebfe3c6e073b69db92"
@@ -11094,11 +10967,6 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 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: pbkdf2@^3.0.3:
version "3.0.17" version "3.0.17"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
@@ -13403,14 +13271,6 @@ source-map-support@0.5.6:
buffer-from "^1.0.0" buffer-from "^1.0.0"
source-map "^0.6.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: source-map-support@^0.5.6, source-map-support@~0.5.10, source-map-support@~0.5.12:
version "0.5.16" version "0.5.16"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
@@ -13852,13 +13712,6 @@ stylis@^3.5.0:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== 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: supports-color@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -14223,17 +14076,6 @@ tryer@^1.0.1:
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== 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: ts-pnp@1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552"
@@ -14280,11 +14122,6 @@ type-check@~0.3.2:
dependencies: dependencies:
prelude-ls "~1.1.2" 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: type-fest@^0.3.0:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
@@ -14330,11 +14167,6 @@ typescript@3.5.3:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== 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: uglify-js@3.4.x:
version "3.4.10" version "3.4.10"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
@@ -15649,8 +15481,3 @@ yauzl@^2.4.2:
dependencies: dependencies:
buffer-crc32 "~0.2.3" buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0" 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,8 +5,7 @@
], ],
"rules": { "rules": {
"no-unused-expressions": "off", "no-unused-expressions": "off",
"import/no-extraneous-dependencies": "off", "import/no-extraneous-dependencies": "off"
"no-bitwise": "off"
}, },
"env": { "env": {
"mocha": true "mocha": true

View File

@@ -2,15 +2,9 @@ function strip0x(input) {
return input.replace(/^0x/, '') return input.replace(/^0x/, '')
} }
/** function addTxHashToData({ encodedData, transactionHash }) {
* Decodes the datatype byte from the AMB message. return encodedData.slice(0, 2) + strip0x(transactionHash) + encodedData.slice(2)
* 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) { function parseAMBMessage(message) {
message = strip0x(message) message = strip0x(message)
@@ -18,29 +12,16 @@ function parseAMBMessage(message) {
const messageId = `0x${message.slice(0, 64)}` const messageId = `0x${message.slice(0, 64)}`
const sender = `0x${message.slice(64, 104)}` const sender = `0x${message.slice(64, 104)}`
const executor = `0x${message.slice(104, 144)}` const executor = `0x${message.slice(104, 144)}`
const dataType = parseInt(message.slice(156, 158), 16)
return { return {
sender, sender,
executor, executor,
messageId, messageId
dataType,
decodedDataType: decodeAMBDataType(dataType)
} }
} }
const normalizeAMBMessageEvent = e => {
let msgData = e.returnValues.encodedData
if (!e.returnValues.messageId) {
// append tx hash to an old message, where message id was not used
// for old messages, e.messageId is a corresponding transactionHash
msgData = e.transactionHash + msgData.slice(2)
}
return parseAMBMessage(msgData)
}
module.exports = { module.exports = {
strip0x, addTxHashToData,
parseAMBMessage, parseAMBMessage,
normalizeAMBMessageEvent strip0x
} }

View File

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

View File

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

View File

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

View File

@@ -32,6 +32,8 @@ provisioner:
inventory: inventory:
host_vars: host_vars:
oracle-amb-host: oracle-amb-host:
COMMON_HOME_RPC_URL: "http://parity1:8545"
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ui-amb-stake-erc-to-erc-host: ui-amb-stake-erc-to-erc-host:

View File

@@ -21,6 +21,8 @@ provisioner:
inventory: inventory:
host_vars: host_vars:
oracle-amb-host: oracle-amb-host:
COMMON_HOME_RPC_URL: "http://parity1:8545"
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
verifier: verifier:

View File

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

View File

@@ -0,0 +1,26 @@
---
- name: Slurp docker compose file
slurp:
src: "/home/poadocker/bridge/oracle/{{ file }}.yml"
register: docker_compose_slurp
- name: Parse docker compose file
set_fact:
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
- name: Add the external network used to connect to Parity nodes
set_fact:
docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
- name: Add all Oracle containers to the network
set_fact:
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': docker_compose_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}"
with_items: "{{ docker_compose_parsed.services }}"
- name: Expose Redis port to allow connecting from redis-cli
set_fact:
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}"
- name: Write updated docker file
copy:
content: "{{ docker_compose_parsed | to_yaml }}"
dest: "/home/poadocker/bridge/oracle/{{ file }}.yml"

View File

@@ -1,22 +1,33 @@
--- ---
- name: Prepare Oracle for ultimate tests - name: Overwrite Oracle the docker-compose
hosts: oracle hosts: oracle
become: true become: true
tasks: tasks:
- name: Connect parity to oracle networks - name: stop the service
shell: "docker network create {{ item }} && docker network connect {{ item }} parity1 && docker network connect {{ item }} parity2" shell: service poabridge stop
with_items:
- oracle_net_db_bridge_request - name: ReTag current oracle image
- oracle_net_db_bridge_collected shell: docker tag $(docker images --format '{{ '{{' }}.Repository{{ '}}' }}:{{ '{{' }}.Tag{{ '}}' }}' | grep -m 1 tokenbridge-e2e-oracle) oracle:ultimate-testing
- oracle_net_db_bridge_affirmation
- oracle_net_db_bridge_transfer
- oracle_net_db_bridge_senderhome
- oracle_net_db_bridge_senderforeign
- oracle_net_rabbit_bridge_request
- oracle_net_rabbit_bridge_collected
- oracle_net_rabbit_bridge_affirmation
- oracle_net_rabbit_bridge_transfer
- oracle_net_rabbit_bridge_senderhome
- oracle_net_rabbit_bridge_senderforeign
delegate_to: 127.0.0.1 delegate_to: 127.0.0.1
become: false become: false
- name: Replace oracle image
replace:
path: "/home/poadocker/bridge/oracle/{{ item }}.yml"
regexp: 'poanetwork/tokenbridge-oracle:latest'
replace: "oracle:ultimate-testing"
with_items:
- docker-compose
- docker-compose-transfer
- docker-compose-erc-native
- include_tasks: oracle-add-docker-external-network.yml
with_items:
- docker-compose
- docker-compose-transfer
- docker-compose-erc-native
loop_control:
loop_var: file
- name: start the service
shell: service poabridge start

View File

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

View File

@@ -32,6 +32,8 @@ provisioner:
inventory: inventory:
host_vars: host_vars:
oracle-erc-to-erc-host: oracle-erc-to-erc-host:
COMMON_HOME_RPC_URL: "http://parity1:8545"
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ui-erc-to-erc-host: ui-erc-to-erc-host:

View File

@@ -32,6 +32,8 @@ provisioner:
inventory: inventory:
host_vars: host_vars:
oracle-erc-to-native-host: oracle-erc-to-native-host:
COMMON_HOME_RPC_URL: "http://parity1:8545"
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ORACLE_HOME_START_BLOCK: 1 ORACLE_HOME_START_BLOCK: 1

View File

@@ -32,6 +32,8 @@ provisioner:
inventory: inventory:
host_vars: host_vars:
oracle-native-to-erc-host: oracle-native-to-erc-host:
COMMON_HOME_RPC_URL: "http://parity1:8545"
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ui-native-to-erc-host: ui-native-to-erc-host:

View File

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

View File

@@ -33,7 +33,7 @@
mode: "0755" mode: "0755"
- name: Upgrade pip version - name: Upgrade pip version
shell: pip3 install --upgrade pip==19.3.1 shell: pip3 install --upgrade pip
- name: Install python docker library - name: Install python docker library
shell: pip3 install docker docker-compose setuptools shell: pip3 install docker docker-compose setuptools

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -63,7 +63,6 @@
"foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0", "foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
"homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1", "homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1", "foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"blockedHomeBox": "0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE",
"monitor": "http://monitor-amb:3013/bridge" "monitor": "http://monitor-amb:3013/bridge"
}, },
"ambStakeErcToErc": { "ambStakeErcToErc": {

View File

@@ -4,14 +4,12 @@ networks:
external: true external: true
services: services:
parity1: parity1:
container_name: parity1
build: ../parity build: ../parity
ports: ports:
- "8541:8545" - "8541:8545"
networks: networks:
- ultimate - ultimate
parity2: parity2:
container_name: parity2
build: build:
context: ../parity context: ../parity
dockerfile: Dockerfile-foreign dockerfile: Dockerfile-foreign
@@ -63,9 +61,6 @@ services:
environment: environment:
- NODE_ENV=production - NODE_ENV=production
command: "true" command: "true"
volumes:
- '../e2e-commons/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks: networks:
- ultimate - ultimate
ui: ui:

View File

@@ -53,8 +53,3 @@ node deploy.js
cd - > /dev/null cd - > /dev/null
node setupStakeTokens.js node setupStakeTokens.js
cd - > /dev/null 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,29 +17,21 @@ docker-compose up -d parity1 parity2 e2e
startValidator () { startValidator () {
docker-compose $1 run -d --name $4 redis docker-compose $1 run -d --name $4 redis
docker-compose $1 run -d --name $5 rabbit docker-compose $1 run -d --name $5 rabbit
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:signature-request
docker-compose $1 run $2 $3 -d oracle yarn watcher:collected-signatures 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 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: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: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:affirmation-request
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:transfer 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: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:collected-signatures
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:convert-to-chai 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:signature-request
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request 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:home
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:foreign docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:foreign
} }
@@ -56,7 +48,25 @@ startAMBValidator () {
while [ "$1" != "" ]; do while [ "$1" != "" ]; do
if [ "$1" == "oracle" ]; then if [ "$1" == "oracle" ]; then
startValidator "" "" "" "redis" "rabbit" 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
fi fi
if [ "$1" == "oracle-validator-2" ]; then if [ "$1" == "oracle-validator-2" ]; then
@@ -80,9 +90,9 @@ while [ "$1" != "" ]; do
docker-compose up -d ui ui-erc20 ui-erc20-native ui-amb-stake-erc20-erc20 docker-compose up -d ui ui-erc20 ui-erc20-native ui-amb-stake-erc20-erc20
docker-compose run -d -p 3000:3000 ui yarn start docker-compose run -d -p 3000:3000 ui yarn start
docker-compose run -d -p 3001:3001 ui-erc20 yarn start docker-compose run -d -p 3001:3000 ui-erc20 yarn start
docker-compose run -d -p 3002:3002 ui-erc20-native yarn start docker-compose run -d -p 3002:3000 ui-erc20-native yarn start
docker-compose run -d -p 3003:3003 ui-amb-stake-erc20-erc20 yarn start docker-compose run -d -p 3003:3000 ui-amb-stake-erc20-erc20 yarn start
fi fi
if [ "$1" == "alm" ]; then if [ "$1" == "alm" ]; then
@@ -100,23 +110,7 @@ while [ "$1" != "" ]; do
fi fi
if [ "$1" == "monitor" ]; then if [ "$1" == "monitor" ]; then
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 docker-compose up -d monitor monitor-erc20 monitor-erc20-native monitor-amb
;;
esac
fi fi
if [ "$1" == "alm-e2e" ]; then if [ "$1" == "alm-e2e" ]; then

View File

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

View File

@@ -1,28 +1,12 @@
cd $(dirname $0) cd $(dirname $0)
mode="$1" ../e2e-commons/up.sh deploy blocks monitor
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
MODE="$mode" ../e2e-commons/up.sh deploy blocks monitor ./wait-for-monitor.sh
MODE="$mode" ./wait-for-monitor.sh
nohup ./periodically-check-all.sh < /dev/null > /dev/null 2>&1 & nohup ./periodically-check-all.sh < /dev/null > /dev/null 2>&1 &
checkPID=$! checkPID=$!
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace monitor-e2e run start $script docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace monitor-e2e run start
rc=$? rc=$?
../e2e-commons/down.sh ../e2e-commons/down.sh

View File

@@ -25,7 +25,6 @@ describe('AMB', () => {
describe('general', async () => { describe('general', async () => {
it('should contain fromHomeToForeignDiff', () => assert(data.fromHomeToForeignDiff === 0)) 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 fromForeignToHomeDiff', () => assert(data.fromForeignToHomeDiff === 0))
it('should contain lastChecked', () => assert(data.lastChecked >= 0)) it('should contain lastChecked', () => assert(data.lastChecked >= 0))
it('should contain timeDiff', () => assert(data.timeDiff >= 0)) it('should contain timeDiff', () => assert(data.timeDiff >= 0))
@@ -115,16 +114,7 @@ describe('AMB', () => {
await waitUntil(async () => { await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`)) ;({ data } = await axios.get(`${baseUrl}`))
return data.fromHomeToForeignDiff === 1 && data.fromHomeToForeignPBUDiff === 0 return data.fromHomeToForeignDiff !== 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 () => { 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() { it('should consider chai token balance', async function() {
this.timeout(120000) this.timeout(60000)
await initializeChaiToken(foreignRPC.URL, ercToNativeBridge.foreign) await initializeChaiToken(foreignRPC.URL, ercToNativeBridge.foreign)
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign) await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign)
await waitUntil(async () => { await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`)) ;({ data } = await axios.get(`${baseUrl}`))
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
if (!data.foreign) { if (!data.foreign) {
return false return false
} }
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
return ( return (
data.balanceDiff === 0.02 && data.balanceDiff === 0.02 &&
erc20Balance === '0.02' && erc20Balance === '0.02' &&
@@ -77,10 +77,10 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => { await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`)) ;({ data } = await axios.get(`${baseUrl}`))
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
if (!data.foreign) { if (!data.foreign) {
return false return false
} }
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
return ( return (
data.balanceDiff === 0.02 && data.balanceDiff === 0.02 &&
erc20Balance === '0.01' && erc20Balance === '0.01' &&
@@ -94,10 +94,10 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
await waitUntil(async () => { await waitUntil(async () => {
;({ data } = await axios.get(`${baseUrl}`)) ;({ data } = await axios.get(`${baseUrl}`))
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
if (!data.foreign) { if (!data.foreign) {
return false return false
} }
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
return ( return (
data.balanceDiff === 0.02 && data.balanceDiff === 0.02 &&
erc20Balance === '0.005' && erc20Balance === '0.005' &&

View File

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

View File

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

View File

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

View File

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

View File

View File

@@ -1,16 +1,20 @@
require('dotenv').config() require('dotenv').config()
const BN = require('bignumber.js') const Web3 = require('web3')
const logger = require('./logger')('checkWorker') const logger = require('./logger')('checkWorker')
const { getBridgeMode } = require('../commons') const { getBridgeMode } = require('../commons')
const getBalances = require('./getBalances') const getBalances = require('./getBalances')
const getShortEventStats = require('./getShortEventStats') const getShortEventStats = require('./getShortEventStats')
const validators = require('./validators') const validators = require('./validators')
const getEventsInfo = require('./utils/events')
const { writeFile, createDir } = require('./utils/file') const { writeFile, createDir } = require('./utils/file')
const { saveCache } = require('./utils/web3Cache')
const { web3Home } = require('./utils/web3')
const { COMMON_HOME_BRIDGE_ADDRESS, MONITOR_BRIDGE_NAME } = process.env const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL, MONITOR_BRIDGE_NAME } = process.env
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
const web3Home = new Web3(homeProvider)
const { HOME_ERC_TO_ERC_ABI } = require('../commons') const { HOME_ERC_TO_ERC_ABI } = require('../commons')
@@ -20,27 +24,42 @@ async function checkWorker() {
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS) const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
const bridgeMode = await getBridgeMode(homeBridge) const bridgeMode = await getBridgeMode(homeBridge)
logger.debug('Bridge mode:', bridgeMode) logger.debug('Bridge mode:', bridgeMode)
logger.debug('calling getEventsInfo()')
const eventsInfo = await getEventsInfo(bridgeMode)
logger.debug('calling getBalances()') logger.debug('calling getBalances()')
const balances = await getBalances(bridgeMode, eventsInfo) const balances = await getBalances(bridgeMode)
logger.debug('calling getShortEventStats()') logger.debug('calling getShortEventStats()')
const events = await getShortEventStats(bridgeMode, eventsInfo) const events = await getShortEventStats(bridgeMode)
const home = Object.assign({}, balances.home, events.home) const home = Object.assign({}, balances.home, events.home)
const foreign = Object.assign({}, balances.foreign, events.foreign) const foreign = Object.assign({}, balances.foreign, events.foreign)
const status = Object.assign({}, balances, events, { home }, { 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)) if (!status) throw new Error('status is empty: ' + JSON.stringify(status))
status.health = true status.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/getBalances.json`, status) writeFile(`/responses/${MONITOR_BRIDGE_NAME}/getBalances.json`, status)
saveCache()
logger.debug('calling validators()') logger.debug('calling validators()')
const vBalances = await validators(bridgeMode) const vBalances = await validators(bridgeMode)
if (!vBalances) throw new Error('vBalances is empty: ' + JSON.stringify(vBalances)) if (!vBalances) throw new Error('vBalances is empty: ' + JSON.stringify(vBalances))
vBalances.homeOk = true
vBalances.foreignOk = true
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
for (const hv in vBalances.home.validators) {
if (vBalances.home.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
vBalances.homeOk = false
break
}
}
}
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
for (const hv in vBalances.foreign.validators) {
if (vBalances.foreign.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
vBalances.foreignOk = false
break
}
}
}
vBalances.ok = vBalances.homeOk && vBalances.foreignOk vBalances.ok = vBalances.homeOk && vBalances.foreignOk
vBalances.health = true vBalances.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/validators.json`, vBalances) writeFile(`/responses/${MONITOR_BRIDGE_NAME}/validators.json`, vBalances)

View File

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

View File

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

View File

@@ -1,86 +0,0 @@
require('dotenv').config()
const logger = require('./logger')('alerts')
const eventsInfo = require('./utils/events')
const { normalizeAMBMessageEvent } = require('../commons')
const { getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3')
function normalize(events) {
const requests = {}
events.forEach(event => {
const request = normalizeAMBMessageEvent(event)
request.requestTx = event.transactionHash
requests[request.messageId] = request
})
return confirmation => {
const request = requests[confirmation.returnValues.messageId] || {}
return {
...request,
status: false,
executionTx: confirmation.transactionHash,
executionBlockNumber: confirmation.blockNumber
}
}
}
async function main(mode) {
const {
homeToForeignRequests,
homeToForeignConfirmations,
foreignToHomeConfirmations,
foreignToHomeRequests
} = await eventsInfo(mode)
const hasFailed = event => !event.returnValues.status
const cmp = (a, b) => b.executionBlockNumber - a.executionBlockNumber
const failedForeignToHomeMessages = foreignToHomeConfirmations
.filter(hasFailed)
.map(normalize(foreignToHomeRequests))
.sort(cmp)
const failedHomeToForeignMessages = homeToForeignConfirmations
.filter(hasFailed)
.map(normalize(homeToForeignRequests))
.sort(cmp)
const homeBlockNumber = await getHomeBlockNumber()
const foreignBlockNumber = await getForeignBlockNumber()
const blockRanges = [1000, 10000, 100000, 1000000]
const rangeNames = [
`last${blockRanges[0]}blocks`,
...blockRanges.slice(0, blockRanges.length - 1).map((n, i) => `last${n}to${blockRanges[i + 1]}blocks`),
`before${blockRanges[blockRanges.length - 1]}blocks`
]
const countFailures = (failedMessages, lastBlockNumber) => {
const result = {}
rangeNames.forEach(name => {
result[name] = 0
})
failedMessages.forEach(message => {
const blockAge = lastBlockNumber - message.executionBlockNumber
let rangeIndex = blockRanges.findIndex(n => n > blockAge)
if (rangeIndex === -1) {
rangeIndex = blockRanges.length
}
result[rangeNames[rangeIndex]] += 1
})
return result
}
logger.debug('Done')
return {
homeToForeign: {
total: failedHomeToForeignMessages.length,
stats: countFailures(failedHomeToForeignMessages, foreignBlockNumber),
lastFailures: failedHomeToForeignMessages.slice(0, 5)
},
foreignToHome: {
total: failedForeignToHomeMessages.length,
stats: countFailures(failedForeignToHomeMessages, homeBlockNumber),
lastFailures: failedForeignToHomeMessages.slice(0, 5)
},
lastChecked: Math.floor(Date.now() / 1000)
}
}
module.exports = main

View File

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

View File

@@ -10,6 +10,5 @@ services:
- NODE_ENV=production - NODE_ENV=production
volumes: volumes:
- ./responses:/mono/monitor/responses - ./responses:/mono/monitor/responses
- ./cache:/mono/monitor/cache
restart: unless-stopped restart: unless-stopped
entrypoint: "yarn start" entrypoint: "yarn start"

View File

@@ -1,20 +1,8 @@
require('dotenv').config() require('dotenv').config()
const eventsInfo = require('./utils/events') const eventsInfo = require('./utils/events')
const { const { processedMsgNotDelivered, deliveredMsgNotProcessed, eventWithoutReference } = require('./utils/message')
processedMsgNotDelivered,
deliveredMsgNotProcessed,
eventWithoutReference,
unclaimedHomeToForeignRequests
} = require('./utils/message')
const { getHomeTxSender } = require('./utils/web3Cache')
const { BRIDGE_MODES } = require('../commons') 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() { async function main() {
const { const {
homeToForeignRequests, homeToForeignRequests,
@@ -45,30 +33,17 @@ async function main() {
lastChecked: Math.floor(Date.now() / 1000) lastChecked: Math.floor(Date.now() / 1000)
} }
} else { } else {
let onlyInHomeDeposits = homeToForeignRequests.filter(eventWithoutReference(homeToForeignConfirmations)) const onlyInHomeDeposits = homeToForeignRequests.filter(eventWithoutReference(homeToForeignConfirmations))
const onlyInForeignDeposits = homeToForeignConfirmations.filter(eventWithoutReference(homeToForeignRequests)) const onlyInForeignDeposits = homeToForeignConfirmations.filter(eventWithoutReference(homeToForeignRequests))
const onlyInHomeWithdrawals = foreignToHomeConfirmations.filter(eventWithoutReference(foreignToHomeRequests)) const onlyInHomeWithdrawals = foreignToHomeConfirmations.filter(eventWithoutReference(foreignToHomeRequests))
const onlyInForeignWithdrawals = foreignToHomeRequests.filter(eventWithoutReference(foreignToHomeConfirmations)) 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 { return {
onlyInHomeDeposits, onlyInHomeDeposits,
onlyInForeignDeposits, onlyInForeignDeposits,
onlyInHomeWithdrawals, onlyInHomeWithdrawals,
onlyInForeignWithdrawals, onlyInForeignWithdrawals,
...unclaimedStats,
lastChecked: Math.floor(Date.now() / 1000) lastChecked: Math.floor(Date.now() / 1000)
} }
} }

View File

@@ -1,11 +1,23 @@
require('dotenv').config() require('dotenv').config()
const BN = require('bignumber.js') const BN = require('bignumber.js')
const Web3Utils = require('web3').utils const Web3 = require('web3')
const logger = require('./logger')('getBalances') const logger = require('./logger')('getBalances')
const { BRIDGE_MODES } = require('../commons') const { BRIDGE_MODES } = require('../commons')
const { web3Home, web3Foreign, getHomeBlockNumber } = require('./utils/web3')
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env 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 { const {
ERC20_ABI, ERC20_ABI,
@@ -18,50 +30,21 @@ const {
FOREIGN_NATIVE_TO_ERC_ABI FOREIGN_NATIVE_TO_ERC_ABI
} = require('../commons') } = require('../commons')
async function main(bridgeMode, eventsInfo) { async function main(bridgeMode) {
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) { if (bridgeMode === BRIDGE_MODES.ERC_TO_ERC) {
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const erc20Address = await foreignBridge.methods.erc20token().call() const erc20Address = await foreignBridge.methods.erc20token().call()
const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address) const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
logger.debug('calling erc20Contract.methods.balanceOf') logger.debug('calling erc20Contract.methods.balanceOf')
const foreignErc20Balance = await erc20Contract.methods const foreignErc20Balance = await erc20Contract.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
.call({}, foreignDelayedBlockNumber)
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS) const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
logger.debug('calling homeBridge.methods.erc677token') logger.debug('calling homeBridge.methods.erc677token')
const tokenAddress = await homeBridge.methods.erc677token().call() const tokenAddress = await homeBridge.methods.erc677token().call()
const tokenContract = new web3Home.eth.Contract(ERC677_ABI, tokenAddress) const tokenContract = new web3Home.eth.Contract(ERC677_ABI, tokenAddress)
logger.debug('calling tokenContract.methods.totalSupply()') logger.debug('calling tokenContract.methods.totalSupply()')
const totalSupply = await tokenContract.methods.totalSupply().call({}, homeDelayedBlockNumber) const totalSupply = await tokenContract.methods.totalSupply().call()
const foreignBalanceBN = new BN(foreignErc20Balance).plus(lateForeignConfirmationsTotalValue) const foreignBalanceBN = new BN(foreignErc20Balance)
const foreignTotalSupplyBN = new BN(totalSupply).plus(lateHomeConfirmationsTotalValue) const foreignTotalSupplyBN = new BN(totalSupply)
const diff = foreignBalanceBN.minus(foreignTotalSupplyBN).toString(10) const diff = foreignBalanceBN.minus(foreignTotalSupplyBN).toString(10)
logger.debug('Done') logger.debug('Done')
return { return {
@@ -78,12 +61,12 @@ async function main(bridgeMode, eventsInfo) {
logger.debug('calling web3Home.eth.getBalance') logger.debug('calling web3Home.eth.getBalance')
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_NATIVE_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_NATIVE_TO_ERC_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const erc20Address = await foreignBridge.methods.erc677token().call() const erc20Address = await foreignBridge.methods.erc677token().call()
const homeBalance = await web3Home.eth.getBalance(COMMON_HOME_BRIDGE_ADDRESS, homeDelayedBlockNumber) const homeBalance = await web3Home.eth.getBalance(COMMON_HOME_BRIDGE_ADDRESS)
const tokenContract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address) const tokenContract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
logger.debug('calling tokenContract.methods.totalSupply()') logger.debug('calling tokenContract.methods.totalSupply()')
const totalSupply = await tokenContract.methods.totalSupply().call({}, foreignDelayedBlockNumber) const totalSupply = await tokenContract.methods.totalSupply().call()
const homeBalanceBN = new BN(homeBalance).plus(lateHomeConfirmationsTotalValue) const homeBalanceBN = new BN(homeBalance)
const foreignTotalSupplyBN = new BN(totalSupply).plus(lateForeignConfirmationsTotalValue) const foreignTotalSupplyBN = new BN(totalSupply)
const diff = homeBalanceBN.minus(foreignTotalSupplyBN).toString(10) const diff = homeBalanceBN.minus(foreignTotalSupplyBN).toString(10)
logger.debug('Done') logger.debug('Done')
return { return {
@@ -120,25 +103,21 @@ async function main(bridgeMode, eventsInfo) {
} }
logger.debug('calling erc20Contract.methods.balanceOf') logger.debug('calling erc20Contract.methods.balanceOf')
const foreignErc20Balance = await erc20Contract.methods const foreignErc20Balance = await erc20Contract.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
.call({}, foreignDelayedBlockNumber)
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS) const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS)
logger.debug('calling homeBridge.methods.blockRewardContract') logger.debug('calling homeBridge.methods.blockRewardContract')
const blockRewardAddress = await homeBridge.methods.blockRewardContract().call() const blockRewardAddress = await homeBridge.methods.blockRewardContract().call()
const blockRewardContract = new web3Home.eth.Contract(BLOCK_REWARD_ABI, blockRewardAddress) const blockRewardContract = new web3Home.eth.Contract(BLOCK_REWARD_ABI, blockRewardAddress)
const homeBlockNumber = await getHomeBlockNumber()
logger.debug('calling blockReward.methods.mintedTotally') logger.debug('calling blockReward.methods.mintedTotally')
const mintedCoins = await blockRewardContract.methods const mintedCoins = await blockRewardContract.methods.mintedTotallyByBridge(COMMON_HOME_BRIDGE_ADDRESS).call()
.mintedTotallyByBridge(COMMON_HOME_BRIDGE_ADDRESS)
.call({}, homeBlockNumber)
logger.debug('calling homeBridge.methods.totalBurntCoins') logger.debug('calling homeBridge.methods.totalBurntCoins')
const burntCoins = await homeBridge.methods.totalBurntCoins().call({}, homeDelayedBlockNumber) const burntCoins = await homeBridge.methods.totalBurntCoins().call()
const mintedCoinsBN = new BN(mintedCoins) const mintedCoinsBN = new BN(mintedCoins)
const burntCoinsBN = new BN(burntCoins) const burntCoinsBN = new BN(burntCoins)
const totalSupplyBN = mintedCoinsBN.minus(burntCoinsBN) const totalSupplyBN = mintedCoinsBN.minus(burntCoinsBN)
const foreignErc20BalanceBN = new BN(foreignErc20Balance).plus(lateForeignConfirmationsTotalValue) const foreignErc20BalanceBN = new BN(foreignErc20Balance)
const investedAmountInDaiBN = new BN(investedAmountInDai) const investedAmountInDaiBN = new BN(investedAmountInDai)
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance) const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)

View File

@@ -1,36 +1,18 @@
require('dotenv').config() require('dotenv').config()
const BN = require('bignumber.js') const eventsInfo = require('./utils/events')
const Web3Utils = require('web3').utils
const {
eventWithoutReference,
deliveredMsgNotProcessed,
unclaimedHomeToForeignRequests,
manuallyProcessedAMBHomeToForeignRequests
} = require('./utils/message')
const { BRIDGE_MODES } = require('../commons') const { BRIDGE_MODES } = require('../commons')
const { getHomeTxSender } = require('./utils/web3Cache')
const { async function main(bridgeMode) {
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 { const {
homeToForeignConfirmations, homeToForeignConfirmations,
homeToForeignRequests, homeToForeignRequests,
foreignToHomeConfirmations, foreignToHomeConfirmations,
foreignToHomeRequests foreignToHomeRequests
} = eventsInfo } = await eventsInfo(bridgeMode)
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) { if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
const onlyInHomeRequests = homeToForeignRequests.filter(deliveredMsgNotProcessed(homeToForeignConfirmations))
const manuallyProcessedRequests = onlyInHomeRequests.filter(manuallyProcessedAMBHomeToForeignRequests())
return { return {
fromHomeToForeignDiff: fromHomeToForeignDiff: homeToForeignRequests.length - homeToForeignConfirmations.length,
homeToForeignRequests.length - homeToForeignConfirmations.length - manuallyProcessedRequests.length,
fromHomeToForeignPBUDiff: manuallyProcessedRequests.length,
fromForeignToHomeDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length, fromForeignToHomeDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length,
home: { home: {
toForeign: homeToForeignRequests.length, toForeign: homeToForeignRequests.length,
@@ -42,26 +24,9 @@ async function main(bridgeMode, eventsInfo) {
} }
} }
} else { } else {
const stats = {
depositsDiff: homeToForeignRequests.length - homeToForeignConfirmations.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 { return {
...stats, depositsDiff: homeToForeignRequests.length - homeToForeignConfirmations.length,
withdrawalDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length,
home: { home: {
deposits: homeToForeignRequests.length, deposits: homeToForeignRequests.length,
withdrawals: foreignToHomeConfirmations.length withdrawals: foreignToHomeConfirmations.length

View File

@@ -1,21 +1,16 @@
require('dotenv').config() require('dotenv').config()
const express = require('express') const express = require('express')
const cors = require('cors')
const { readFile } = require('./utils/file') const { readFile } = require('./utils/file')
const { getPrometheusMetrics } = require('./prometheusMetrics')
const app = express() const app = express()
const bridgeRouter = express.Router({ mergeParams: true }) const bridgeRouter = express.Router({ mergeParams: true })
app.use(cors())
app.get('/favicon.ico', (req, res) => res.sendStatus(204)) app.get('/favicon.ico', (req, res) => res.sendStatus(204))
app.use('/:bridgeName', bridgeRouter) app.use('/:bridgeName', bridgeRouter)
bridgeRouter.get('/:file(validators|eventsStats|alerts|mediators|stuckTransfers|failures)?', (req, res, next) => { bridgeRouter.get('/', async (req, res, next) => {
try { try {
const { bridgeName, file } = req.params const results = await readFile(`./responses/${req.params.bridgeName}/getBalances.json`)
const results = readFile(`./responses/${bridgeName}/${file || 'getBalances'}.json`)
res.json(results) res.json(results)
} catch (e) { } catch (e) {
// this will eventually be handled by your error handling middleware // this will eventually be handled by your error handling middleware
@@ -23,10 +18,39 @@ bridgeRouter.get('/:file(validators|eventsStats|alerts|mediators|stuckTransfers|
} }
}) })
bridgeRouter.get('/metrics', (req, res, next) => { bridgeRouter.get('/validators', async (req, res, next) => {
try { try {
const metrics = getPrometheusMetrics(req.params.bridgeName) const results = await readFile(`./responses/${req.params.bridgeName}/validators.json`)
res.type('text').send(metrics) res.json(results)
} catch (e) {
// this will eventually be handled by your error handling middleware
next(e)
}
})
bridgeRouter.get('/eventsStats', async (req, res, next) => {
try {
const results = await readFile(`./responses/${req.params.bridgeName}/eventsStats.json`)
res.json(results)
} catch (e) {
// this will eventually be handled by your error handling middleware
next(e)
}
})
bridgeRouter.get('/alerts', async (req, res, next) => {
try {
const results = await readFile(`./responses/${req.params.bridgeName}/alerts.json`)
res.json(results)
} catch (e) {
next(e)
}
})
bridgeRouter.get('/stuckTransfers', async (req, res, next) => {
try {
const results = await readFile(`./responses/${req.params.bridgeName}/stuckTransfers.json`)
res.json(results)
} catch (e) { } catch (e) {
next(e) next(e)
} }

View File

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

View File

@@ -1,104 +0,0 @@
const { readFile } = require('./utils/file')
const {
MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST,
MONITOR_HOME_TO_FOREIGN_BLOCK_LIST,
MONITOR_HOME_VALIDATORS_BALANCE_ENABLE,
MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE
} = process.env
function BridgeConf(type, validatorsBalanceEnable, alertTargetFunc, failureDirection) {
this.type = type
this.validatorsBalanceEnable = validatorsBalanceEnable
this.alertTargetFunc = alertTargetFunc
this.failureDirection = failureDirection
}
const BRIDGE_CONFS = [
new BridgeConf('home', MONITOR_HOME_VALIDATORS_BALANCE_ENABLE, 'executeAffirmations', 'homeToForeign'),
new BridgeConf('foreign', MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE, 'executeSignatures', 'foreignToHome')
]
function hasError(obj) {
return 'error' in obj
}
function getPrometheusMetrics(bridgeName) {
const responsePath = jsonName => `./responses/${bridgeName}/${jsonName}.json`
const metrics = {}
// Balance metrics
const balancesFile = readFile(responsePath('getBalances'))
if (!hasError(balancesFile)) {
const { home: homeBalances, foreign: foreignBalances, ...commonBalances } = balancesFile
metrics.balances_home_value = homeBalances.totalSupply
metrics.balances_home_txs_deposit = homeBalances.deposits
metrics.balances_home_txs_withdrawal = homeBalances.withdrawals
metrics.balances_foreign_value = foreignBalances.erc20Balance
metrics.balances_foreign_txs_deposit = foreignBalances.deposits
metrics.balances_foreign_txs_withdrawal = foreignBalances.withdrawals
metrics.balances_diff_value = commonBalances.balanceDiff
metrics.balances_diff_deposit = commonBalances.depositsDiff
metrics.balances_diff_withdrawal = commonBalances.withdrawalDiff
if (MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST || MONITOR_HOME_TO_FOREIGN_BLOCK_LIST) {
metrics.balances_unclaimed_txs = commonBalances.unclaimedDiff
metrics.balances_unclaimed_value = commonBalances.unclaimedBalance
}
}
// Validator metrics
const validatorsFile = readFile(responsePath('validators'))
if (!hasError(validatorsFile)) {
for (const bridge of BRIDGE_CONFS) {
const allValidators = validatorsFile[bridge.type].validators
const validatorAddressesWithBalanceCheck =
typeof bridge.validatorsBalanceEnable === 'string'
? bridge.validatorsBalanceEnable.split(' ')
: Object.keys(allValidators)
validatorAddressesWithBalanceCheck.forEach((addr, ind) => {
metrics[`validators_balances_${bridge.type}${ind}{address="${addr}"}`] = allValidators[addr].balance
})
}
}
// Alert metrics
const alertsFile = readFile(responsePath('alerts'))
if (!hasError(alertsFile)) {
for (const bridge of BRIDGE_CONFS) {
Object.entries(alertsFile[bridge.alertTargetFunc].misbehavior).forEach(([period, val]) => {
metrics[`misbehavior_${bridge.type}_${period}`] = val
})
}
}
// Failure metrics
const failureFile = readFile(responsePath('failures'))
if (!hasError(failureFile)) {
for (const bridge of BRIDGE_CONFS) {
const dir = bridge.failureDirection
const failures = failureFile[dir]
metrics[`failures_${dir}_total`] = failures.total
Object.entries(failures.stats).forEach(([period, count]) => {
metrics[`failures_${dir}_${period}`] = count
})
}
}
// Pack metrcis into a plain text
return Object.entries(metrics).reduceRight(
// Prometheus supports `Nan` and possibly signed `Infinity`
// in case cast to `Number` fails
(acc, [key, val]) => `${key} ${val ? Number(val) : 0}\n${acc}`,
''
)
}
module.exports = { getPrometheusMetrics }

View File

@@ -2,10 +2,6 @@
CONFIGDIR="configs" CONFIGDIR="configs"
RESPONSESDIR="responses" RESPONSESDIR="responses"
ACLDIR="access-lists"
ALLOWANCEFILE="allowance_list.txt"
BLOCKFILE="block_list.txt"
CACHEDIR="cache"
IMAGETAG="latest" IMAGETAG="latest"
cd $(dirname $0)/.. cd $(dirname $0)/..
@@ -30,18 +26,10 @@ if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
fi fi
done 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" containername=${bridgename}"-checker"
docker container stats --no-stream ${containername} 2>/dev/null 1>&2 docker container stats --no-stream ${containername} 2>/dev/null 1>&2
if [ ! "$?" == "0" ]; then if [ ! "$?" == "0" ]; then
mkdir -p "$(pwd)/$CACHEDIR/$bridgename"
docker run --rm --env-file $file -v $(pwd)/${RESPONSESDIR}:/mono/monitor/responses \ 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} \ --name ${containername} poanetwork/tokenbridge-monitor:${IMAGETAG} \
/bin/bash -c 'yarn check-all' /bin/bash -c 'yarn check-all'
shasum -a 256 -s -c ${checksumfile} shasum -a 256 -s -c ${checksumfile}

View File

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

11
monitor/utils/contract.js Normal file
View File

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

View File

@@ -1,9 +1,9 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
function readFile(filePath) { async function readFile(filePath) {
try { try {
const content = fs.readFileSync(filePath) const content = await fs.readFileSync(filePath)
const json = JSON.parse(content) const json = JSON.parse(content)
const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked
return Object.assign({}, json, { timeDiff }) return Object.assign({}, json, { timeDiff })
@@ -38,25 +38,9 @@ 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 = { module.exports = {
readFile, readFile,
writeFile, writeFile,
createDir, createDir,
readCacheFile, readCacheFile
writeCacheFile,
readAccessListFile
} }

View File

@@ -1,93 +0,0 @@
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,31 +1,44 @@
const { normalizeAMBMessageEvent } = require('../../commons') const web3Utils = require('web3').utils
const { readAccessListFile } = require('./file') const { parseAMBMessage } = require('../../commons')
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()
function deliveredMsgNotProcessed(processedList) { function deliveredMsgNotProcessed(processedList) {
const keys = new Set() return deliveredMsg => {
processedList.forEach(processedMsg => keys.add(keyAMB(processedMsg.returnValues))) let msgData = deliveredMsg.returnValues.encodedData
return deliveredMsg => !keys.has(keyAMB(normalizeAMBMessageEvent(deliveredMsg))) 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
)
}
} }
function processedMsgNotDelivered(deliveredList) { function processedMsgNotDelivered(deliveredList) {
const keys = new Set() return processedMsg => {
deliveredList.forEach(deliveredMsg => keys.add(keyAMB(normalizeAMBMessageEvent(deliveredMsg)))) return (
return processedMsg => !keys.has(keyAMB(processedMsg.returnValues)) 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 addExecutionStatus(processedList) { function messageEqualsEvent(parsedMsg, event) {
const statuses = {} return (
processedList.forEach(processedMsg => { web3Utils.toChecksumAddress(parsedMsg.sender) === event.sender &&
statuses[keyAMB(processedMsg.returnValues)] = processedMsg.returnValues.status web3Utils.toChecksumAddress(parsedMsg.executor) === event.executor &&
}) parsedMsg.messageId === event.messageId // for an old messages, event.messageId is actually a transactionHash
return deliveredMsg => { )
deliveredMsg.status = statuses[keyAMB(deliveredMsg)]
return deliveredMsg
}
} }
/** /**
@@ -47,50 +60,13 @@ const normalizeEventInformation = event => ({
value: event.returnValues.value value: event.returnValues.value
}) })
const key = e => [e.referenceTx, e.recipient, e.value].join(',').toLowerCase() const eventWithoutReference = otherSideEvents => e =>
otherSideEvents.filter(a => a.referenceTx === e.referenceTx && a.recipient === e.recipient && a.value === e.value)
const eventWithoutReference = otherSideEvents => { .length === 0
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 } = normalizeAMBMessageEvent(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 } = normalizeAMBMessageEvent(e)
return blockList.includes(sender) || blockList.includes(executor) || decodedDataType.manualLane
}
} else {
return e => normalizeAMBMessageEvent(e).decodedDataType.manualLane
}
}
module.exports = { module.exports = {
deliveredMsgNotProcessed, deliveredMsgNotProcessed,
processedMsgNotDelivered, processedMsgNotDelivered,
addExecutionStatus,
normalizeEventInformation, normalizeEventInformation,
eventWithoutReference, eventWithoutReference
unclaimedHomeToForeignRequests,
manuallyProcessedAMBHomeToForeignRequests
} }

View File

@@ -0,0 +1,16 @@
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
}

View File

@@ -1,27 +0,0 @@
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)
}

View File

@@ -1,171 +0,0 @@
const logger = require('../logger')('web3Cache')
const { readCacheFile, writeCacheFile } = require('./file')
const { web3Home, web3Foreign } = require('./web3')
const { getPastEvents: commonGetPastEvents } = require('../../commons')
const { MONITOR_BRIDGE_NAME, MONITOR_CACHE_EVENTS } = process.env
let isDirty = false
const homeTxSendersCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/home/txSenders.json`
const cachedHomeTxSenders = readCacheFile(homeTxSendersCacheFile) || {}
const foreignTxSendersCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/foreign/txSenders.json`
const cachedForeignTxSenders = readCacheFile(foreignTxSendersCacheFile) || {}
const homeIsContractCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/home/isContract.json`
const cachedHomeIsContract = readCacheFile(homeIsContractCacheFile) || {}
const foreignIsContractCacheFile = `./cache/${MONITOR_BRIDGE_NAME}/foreign/isContract.json`
const cachedForeignIsContract = readCacheFile(foreignIsContractCacheFile) || {}
async function getHomeTxSender(txHash) {
if (!cachedHomeTxSenders[txHash]) {
logger.debug(`Fetching sender for home tx ${txHash}`)
cachedHomeTxSenders[txHash] = (await web3Home.eth.getTransaction(txHash)).from.toLowerCase()
isDirty = true
}
return cachedHomeTxSenders[txHash]
}
async function getForeignTxSender(txHash) {
if (!cachedForeignTxSenders[txHash]) {
logger.debug(`Fetching sender for foreign tx ${txHash}`)
cachedForeignTxSenders[txHash] = (await web3Foreign.eth.getTransaction(txHash)).from.toLowerCase()
isDirty = true
}
return cachedForeignTxSenders[txHash]
}
async function isHomeContract(address) {
if (typeof cachedHomeIsContract[address] !== 'boolean') {
logger.debug(`Fetching home contract code size for tx ${address}`)
cachedHomeIsContract[address] = (await web3Home.eth.getCode(address)).length > 2
isDirty = true
}
return cachedHomeIsContract[address]
}
async function isForeignContract(address) {
if (typeof cachedForeignIsContract[address] !== 'boolean') {
logger.debug(`Fetching foreign contract code size for tx ${address}`)
cachedForeignIsContract[address] = (await web3Foreign.eth.getCode(address)).length > 2
isDirty = true
}
return cachedForeignIsContract[address]
}
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)
writeCacheFile(homeIsContractCacheFile, cachedHomeIsContract)
writeCacheFile(foreignIsContractCacheFile, cachedForeignIsContract)
}
}
module.exports = {
getHomeTxSender,
getForeignTxSender,
isHomeContract,
isForeignContract,
getPastEvents,
saveCache
}

View File

@@ -1,12 +1,13 @@
require('dotenv').config() require('dotenv').config()
const Web3Utils = require('web3').utils const Web3 = require('web3')
const fetch = require('node-fetch') const fetch = require('node-fetch')
const logger = require('./logger')('validators') const logger = require('./logger')('validators')
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, gasPriceFromSupplier } = require('../commons') const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, getValidatorList, gasPriceFromSupplier } = require('../commons')
const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3') const { getBlockNumber } = require('./utils/contract')
const { getValidatorList } = require('./utils/getValidatorsList')
const { const {
COMMON_HOME_RPC_URL,
COMMON_FOREIGN_RPC_URL,
COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_BRIDGE_ADDRESS,
COMMON_FOREIGN_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS,
COMMON_HOME_GAS_PRICE_SUPPLIER_URL, COMMON_HOME_GAS_PRICE_SUPPLIER_URL,
@@ -16,13 +17,20 @@ const {
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL, COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL,
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE, COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE,
COMMON_FOREIGN_GAS_PRICE_FALLBACK, COMMON_FOREIGN_GAS_PRICE_FALLBACK,
COMMON_FOREIGN_GAS_PRICE_FACTOR, COMMON_FOREIGN_GAS_PRICE_FACTOR
MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE,
MONITOR_HOME_VALIDATORS_BALANCE_ENABLE
} = process.env } = 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_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_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 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 = { const homeGasPriceSupplierOpts = {
speedType: COMMON_HOME_GAS_PRICE_SPEED_TYPE, speedType: COMMON_HOME_GAS_PRICE_SPEED_TYPE,
@@ -36,6 +44,12 @@ const foreignGasPriceSupplierOpts = {
logger logger
} }
const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
async function main(bridgeMode) { async function main(bridgeMode) {
const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(bridgeMode) const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(bridgeMode)
const homeBridge = new web3Home.eth.Contract(HOME_ABI, COMMON_HOME_BRIDGE_ADDRESS) const homeBridge = new web3Home.eth.Contract(HOME_ABI, COMMON_HOME_BRIDGE_ADDRESS)
@@ -44,12 +58,7 @@ async function main(bridgeMode) {
const homeBridgeValidators = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, homeValidatorsAddress) const homeBridgeValidators = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, homeValidatorsAddress)
logger.debug('getting last block numbers') logger.debug('getting last block numbers')
const homeBlockNumber = await getHomeBlockNumber() const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
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()') logger.debug('calling foreignBridge.methods.validatorContract().call()')
const foreignValidatorsAddress = await foreignBridge.methods.validatorContract().call() const foreignValidatorsAddress = await foreignBridge.methods.validatorContract().call()
@@ -57,18 +66,16 @@ async function main(bridgeMode) {
logger.debug('calling foreignBridgeValidators getValidatorList()') logger.debug('calling foreignBridgeValidators getValidatorList()')
const foreignValidators = (await getValidatorList(foreignValidatorsAddress, web3Foreign.eth, { const foreignValidators = (await getValidatorList(foreignValidatorsAddress, web3Foreign.eth, {
toBlock: foreignBlockNumber, from: MONITOR_FOREIGN_START_BLOCK,
logger, to: foreignBlockNumber,
chain: 'foreign', logger
safeToBlock: foreignDelayedBlockNumber
})).map(web3Foreign.utils.toChecksumAddress) })).map(web3Foreign.utils.toChecksumAddress)
logger.debug('calling homeBridgeValidators getValidatorList()') logger.debug('calling homeBridgeValidators getValidatorList()')
const homeValidators = (await getValidatorList(homeValidatorsAddress, web3Home.eth, { const homeValidators = (await getValidatorList(homeValidatorsAddress, web3Home.eth, {
toBlock: homeBlockNumber, from: MONITOR_HOME_START_BLOCK,
logger, to: homeBlockNumber,
chain: 'home', logger
safeToBlock: homeDelayedBlockNumber
})).map(web3Home.utils.toChecksumAddress) })).map(web3Home.utils.toChecksumAddress)
const foreignVBalances = {} const foreignVBalances = {}
@@ -106,24 +113,21 @@ async function main(bridgeMode) {
} }
let validatorsMatch = true let validatorsMatch = true
const foreignValidatorsWithBalanceCheck = logger.debug('calling asyncForEach foreignValidators foreignVBalances')
typeof MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE === 'string' await asyncForEach(foreignValidators, async v => {
? MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE.split(' ')
: foreignValidators
logger.debug('getting foreignValidators balances')
await Promise.all(
foreignValidators.map(async v => {
foreignVBalances[v] = {}
if (foreignValidatorsWithBalanceCheck.includes(v)) {
const balance = await web3Foreign.eth.getBalance(v) const balance = await web3Foreign.eth.getBalance(v)
foreignVBalances[v].balance = Web3Utils.fromWei(balance)
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) { if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
foreignVBalances[v].leftTx = Number( const leftTx = Web3Utils.toBN(balance)
Web3Utils.toBN(balance)
.div(foreignTxCost) .div(foreignTxCost)
.toString(10) .toString(10)
) foreignVBalances[v] = {
foreignVBalances[v].gasPrice = parseFloat(foreignGasPriceGwei) balance: Web3Utils.fromWei(balance),
leftTx: Number(leftTx),
gasPrice: Number(foreignGasPriceGwei)
}
} else {
foreignVBalances[v] = {
balance: Web3Utils.fromWei(balance)
} }
} }
@@ -132,26 +136,22 @@ async function main(bridgeMode) {
foreignVBalances[v].onlyOnForeign = true foreignVBalances[v].onlyOnForeign = true
} }
}) })
)
const homeValidatorsWithBalanceCheck = logger.debug('calling asyncForEach homeValidators homeVBalances')
typeof MONITOR_HOME_VALIDATORS_BALANCE_ENABLE === 'string' await asyncForEach(homeValidators, async v => {
? MONITOR_HOME_VALIDATORS_BALANCE_ENABLE.split(' ')
: homeValidators
logger.debug('calling homeValidators balances')
await Promise.all(
homeValidators.map(async v => {
homeVBalances[v] = {}
if (homeValidatorsWithBalanceCheck.includes(v)) {
const balance = await web3Home.eth.getBalance(v) const balance = await web3Home.eth.getBalance(v)
homeVBalances[v].balance = Web3Utils.fromWei(balance)
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) { if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
homeVBalances[v].leftTx = Number( const leftTx = Web3Utils.toBN(balance)
Web3Utils.toBN(balance)
.div(homeTxCost) .div(homeTxCost)
.toString(10) .toString(10)
) homeVBalances[v] = {
homeVBalances[v].gasPrice = parseFloat(homeGasPriceGwei) balance: Web3Utils.fromWei(balance),
leftTx: Number(leftTx),
gasPrice: Number(homeGasPriceGwei)
}
} else {
homeVBalances[v] = {
balance: Web3Utils.fromWei(balance)
} }
} }
@@ -160,7 +160,6 @@ async function main(bridgeMode) {
homeVBalances[v].onlyOnHome = true homeVBalances[v].onlyOnHome = true
} }
}) })
)
logger.debug('calling homeBridgeValidators.methods.requiredSignatures().call()') logger.debug('calling homeBridgeValidators.methods.requiredSignatures().call()')
const reqSigHome = await homeBridgeValidators.methods.requiredSignatures().call() const reqSigHome = await homeBridgeValidators.methods.requiredSignatures().call()
@@ -169,22 +168,20 @@ async function main(bridgeMode) {
logger.debug('Done') logger.debug('Done')
return { return {
home: { home: {
validators: homeVBalances, validators: {
...homeVBalances
},
requiredSignatures: Number(reqSigHome) requiredSignatures: Number(reqSigHome)
}, },
foreign: { foreign: {
validators: foreignVBalances, validators: {
...foreignVBalances
},
requiredSignatures: Number(reqSigForeign) requiredSignatures: Number(reqSigForeign)
}, },
requiredSignaturesMatch: reqSigHome === reqSigForeign, requiredSignaturesMatch: reqSigHome === reqSigForeign,
validatorsMatch, validatorsMatch,
lastChecked: Math.floor(Date.now() / 1000), lastChecked: Math.floor(Date.now() / 1000)
homeOk: Object.values(homeVBalances)
.filter(vb => typeof vb.leftTx === 'number')
.every(vb => vb.leftTx >= MONITOR_TX_NUMBER_THRESHOLD),
foreignOk: Object.values(foreignVBalances)
.filter(vb => typeof vb.leftTx === 'number')
.every(vb => vb.leftTx >= MONITOR_TX_NUMBER_THRESHOLD)
} }
} }

View File

@@ -1,24 +1,8 @@
cd $(dirname $0) cd $(dirname $0)
mode="$1" ../e2e-commons/up.sh deploy blocks oracle oracle-validator-2 oracle-validator-3
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
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
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start $script
rc=$? rc=$?
../e2e-commons/down.sh ../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 { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json')
const { uniformRetry } = require('../../e2e-commons/utils') const { uniformRetry } = require('../../e2e-commons/utils')
const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI } = require('../../commons') const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI } = require('../../commons')
const { delay, setRequiredSignatures } = require('./utils') const { setRequiredSignatures } = require('./utils')
const { toBN } = Web3.utils const { toBN } = Web3.utils
@@ -19,17 +19,14 @@ foreignWeb3.eth.accounts.wallet.add(user.privateKey)
foreignWeb3.eth.accounts.wallet.add(validator.privateKey) foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox) 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 foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox)
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, COMMON_HOME_BRIDGE_ADDRESS) 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) const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
describe('arbitrary message bridging', () => { describe('arbitrary message bridging', () => {
let requiredSignatures = 1
before(async () => { before(async () => {
// Only 1 validator is used in ultimate tests // Only 1 validator is used in ultimate tests
if (process.env.ULTIMATE !== 'true') { if (process.env.ULTIMATE !== 'true') {
requiredSignatures = 2
// Set 2 required signatures for home bridge // Set 2 required signatures for home bridge
await setRequiredSignatures({ await setRequiredSignatures({
bridgeContract: homeBridge, bridgeContract: homeBridge,
@@ -79,85 +76,6 @@ 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(10000)
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', () => { describe('Foreign to Home', () => {

View File

@@ -1,9 +1,5 @@
const { BRIDGE_VALIDATORS_ABI } = require('../../commons') 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 setRequiredSignatures = async ({ bridgeContract, web3, requiredSignatures, options }) => {
const validatorAddress = await bridgeContract.methods.validatorContract().call() const validatorAddress = await bridgeContract.methods.validatorContract().call()
const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorAddress) const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorAddress)
@@ -12,6 +8,5 @@ const setRequiredSignatures = async ({ bridgeContract, web3, requiredSignatures,
} }
module.exports = { module.exports = {
delay,
setRequiredSignatures setRequiredSignatures
} }

View File

@@ -29,7 +29,6 @@ ORACLE_FOREIGN_START_BLOCK=
ORACLE_LOG_LEVEL=debug ORACLE_LOG_LEVEL=debug
ORACLE_MAX_PROCESSING_TIME=20000 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_ALLOWANCE_LIST=access-lists/allowance_list.txt
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=access-lists/block_list.txt ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=access-lists/block_list.txt

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