Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b4b01a79b | ||
|
|
881fafe9a8 | ||
|
|
c1ed6f21e6 | ||
|
|
8ae0fa82d5 | ||
|
|
dbf3d3d90d | ||
|
|
8977ed6d3b | ||
|
|
df0dc1c313 | ||
|
|
7b2bebbcf0 | ||
|
|
85104d67c0 | ||
|
|
2a3d7c8e08 | ||
|
|
e6052f162a | ||
|
|
52ed4e85e2 | ||
|
|
bc6dd13193 | ||
|
|
bce1e6509e | ||
|
|
52358d477b | ||
|
|
232f807e9d | ||
|
|
fe4a569e34 | ||
|
|
c408d57716 | ||
|
|
7fcb118c8c | ||
|
|
1eaf774e33 | ||
|
|
8650ba4d21 | ||
|
|
edc51c78e2 | ||
|
|
612f130544 | ||
|
|
73d5002105 | ||
|
|
65dd131107 | ||
|
|
4de24efc01 | ||
|
|
727371f251 | ||
|
|
9cb1a2041d | ||
|
|
b6d96d7f62 | ||
|
|
dc06ee8ceb | ||
|
|
8c2f58b06f | ||
|
|
3ad62d6a7f | ||
|
|
9f9638970a | ||
|
|
7054ff26a0 | ||
|
|
afb601b7f5 | ||
|
|
1736fd615d | ||
|
|
ef0a734650 | ||
|
|
6e2238fc9b | ||
|
|
0f3bea5a41 | ||
|
|
0829c95561 | ||
|
|
5bb99a7e95 | ||
|
|
3cd53f7bda | ||
|
|
0eeae74ffa | ||
|
|
8fa715089b | ||
|
|
ab2c0ea120 | ||
|
|
5583ea8b6b | ||
|
|
a4eb446f7b | ||
|
|
2d526a1454 | ||
|
|
9811c13a04 | ||
|
|
406ede9352 | ||
|
|
1360c79e69 | ||
|
|
12229e5e0b | ||
|
|
588b289bb9 | ||
|
|
b3419ccca6 | ||
|
|
ecd20890c8 | ||
|
|
b6588ff3c5 | ||
|
|
ed2de112a2 | ||
|
|
c19f48ef3f | ||
|
|
eb8de323ee | ||
|
|
c42b2f03b7 | ||
|
|
303b02f3ca | ||
|
|
98e0f8e998 | ||
|
|
ecf613954a | ||
|
|
f2a6a64637 | ||
|
|
8d4eb86a19 | ||
|
|
cc6afb3736 | ||
|
|
84ecfc30d9 | ||
|
|
5d770e8607 | ||
|
|
db89d1c12e | ||
|
|
4fd4ac3d73 | ||
|
|
cbd9d607ce | ||
|
|
346fa1e732 | ||
|
|
f6fa83d7ea | ||
|
|
1564ccc580 | ||
|
|
7a54e584d5 | ||
|
|
1d79cf82f3 | ||
|
|
9884b4b424 | ||
|
|
d577a71096 |
@@ -11,10 +11,10 @@ orbs:
|
||||
sudo apt-get clean
|
||||
sudo apt-get update
|
||||
sudo apt-get install dpkg
|
||||
- run:
|
||||
- run:
|
||||
name: Install Chrome
|
||||
command: |
|
||||
wget -O chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
wget -O chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_77.0.3865.120-1_amd64.deb
|
||||
sudo dpkg -i chrome.deb
|
||||
install-node:
|
||||
steps:
|
||||
@@ -89,29 +89,29 @@ jobs:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- restore_cache:
|
||||
name: Restore Yarn Package Cache
|
||||
keys:
|
||||
- yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
- run: git submodule status > submodule.status
|
||||
name: Restore Yarn Package Cache
|
||||
keys:
|
||||
- yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
- run: git submodule status > submodule.status
|
||||
- restore_cache:
|
||||
name: Restore contracts submodule with compiled contracts
|
||||
keys:
|
||||
- contracts-{{ checksum "submodule.status" }}
|
||||
name: Restore contracts submodule with compiled contracts
|
||||
keys:
|
||||
- contracts-{{ checksum "submodule.status" }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
key: yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
name: Save Yarn Package Cache
|
||||
key: yarn-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
- run: touch install_deploy.log; test -d contracts/build/contracts || yarn install:deploy &> install_deploy.log
|
||||
- store_artifacts:
|
||||
path: install_deploy.log
|
||||
- run: test -d contracts/build/contracts || yarn compile:contracts
|
||||
- save_cache:
|
||||
name: Save contracts submodule with compiled contracts
|
||||
key: contracts-{{ checksum "submodule.status" }}
|
||||
paths:
|
||||
- contracts
|
||||
name: Save contracts submodule with compiled contracts
|
||||
key: contracts-{{ checksum "submodule.status" }}
|
||||
paths:
|
||||
- contracts
|
||||
- save_cache:
|
||||
name: Save initialized project for subsequent jobs
|
||||
key: initialize-{{ .Environment.CIRCLE_SHA1 }}
|
||||
@@ -143,11 +143,11 @@ jobs:
|
||||
oracle-e2e:
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
- run: yarn run oracle-e2e
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
- run: yarn run oracle-e2e
|
||||
ui-e2e:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
@@ -161,9 +161,9 @@ jobs:
|
||||
monitor-e2e:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: ./monitor-e2e/run-tests.sh
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: ./monitor-e2e/run-tests.sh
|
||||
cover:
|
||||
executor: tokenbridge-orb/docker-node
|
||||
steps:
|
||||
@@ -194,7 +194,7 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run:
|
||||
- run:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh monitor
|
||||
no_output_timeout: 40m
|
||||
@@ -210,7 +210,15 @@ jobs:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh repo
|
||||
no_output_timeout: 40m
|
||||
|
||||
deployment-multiple:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run:
|
||||
name: Run the scenario
|
||||
command: deployment-e2e/molecule.sh multiple
|
||||
no_output_timeout: 40m
|
||||
ultimate:
|
||||
executor: tokenbridge-orb/machine-with-docker-caching
|
||||
parameters:
|
||||
@@ -222,6 +230,11 @@ jobs:
|
||||
type: string
|
||||
ui-e2e-grep:
|
||||
description: "Mocha grep string used to run ui-e2e tests specific to given type of bridge"
|
||||
default: ''
|
||||
type: string
|
||||
oracle-e2e-script:
|
||||
description: "Yarn script string used to run oracle-e2e tests specific to given type of bridge"
|
||||
default: ''
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
@@ -232,16 +245,24 @@ jobs:
|
||||
- tokenbridge-orb/yarn-install-cached-on-machine
|
||||
- run:
|
||||
name: Prepare the infrastructure
|
||||
command: e2e-commons/up.sh deploy << parameters.scenario-name >>
|
||||
command: e2e-commons/up.sh deploy << parameters.scenario-name >> blocks
|
||||
no_output_timeout: 50m
|
||||
- tokenbridge-orb/wait-for-oracle:
|
||||
redis-key: << parameters.redis-key >>
|
||||
- run:
|
||||
name: Run the ui-e2e tests
|
||||
command: |
|
||||
nvm use default;
|
||||
node ./e2e-commons/scripts/blocks.js &
|
||||
cd ui-e2e; yarn mocha -g "<< parameters.ui-e2e-grep >>" -b ./test.js
|
||||
- when:
|
||||
condition: << parameters.ui-e2e-grep >>
|
||||
steps:
|
||||
- run:
|
||||
name: Run the ui-e2e tests
|
||||
command: |
|
||||
nvm use default;
|
||||
cd ui-e2e; yarn mocha -g "<< parameters.ui-e2e-grep >>" -b ./test.js
|
||||
- when:
|
||||
condition: << parameters.oracle-e2e-script >>
|
||||
steps:
|
||||
- run:
|
||||
name: Run the oracle-e2e tests
|
||||
command: cd e2e-commons && docker-compose run e2e yarn workspace oracle-e2e run << parameters.oracle-e2e-script >>
|
||||
workflows:
|
||||
tokenbridge:
|
||||
jobs:
|
||||
@@ -272,18 +293,24 @@ workflows:
|
||||
- deployment-ui
|
||||
- deployment-monitor
|
||||
- deployment-repo
|
||||
- deployment-multiple
|
||||
- ultimate:
|
||||
name: "ultimate: native to erc"
|
||||
name: "ultimate: native to erc"
|
||||
scenario-name: native-to-erc
|
||||
redis-key: native-erc-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "NATIVE TO ERC"
|
||||
- ultimate:
|
||||
name: "ultimate: erc to native"
|
||||
name: "ultimate: erc to native"
|
||||
scenario-name: erc-to-native
|
||||
redis-key: erc-native-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "ERC TO NATIVE"
|
||||
- ultimate:
|
||||
name: "ultimate: erc to erc"
|
||||
name: "ultimate: erc to erc"
|
||||
scenario-name: erc-to-erc
|
||||
redis-key: erc-erc-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "ERC TO ERC"
|
||||
- ultimate:
|
||||
name: "ultimate: amb"
|
||||
scenario-name: amb
|
||||
redis-key: amb-collected-signatures:lastProcessedBlock
|
||||
oracle-e2e-script: "amb"
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "contracts"]
|
||||
path = contracts
|
||||
url = https://github.com/poanetwork/poa-bridge-contracts.git
|
||||
url = https://github.com/poanetwork/tokenbridge-contracts.git
|
||||
|
||||
@@ -69,3 +69,4 @@ MONITOR_VALIDATOR_HOME_TX_LIMIT | Average gas usage of a transaction sent by a v
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT | Average gas usage of a transaction sent by a validator, it is used to estimate the number of transaction that can be paid by the validator. | integer
|
||||
MONITOR_TX_NUMBER_THRESHOLD | If estimated number of transaction is equal to or below this value, the monitor will report that the validator has less funds than it is required. | integer
|
||||
MONITOR_PORT | The port for the Monitor. | integer
|
||||
MONITOR_BRIDGE_NAME | The name to be used in the url path for the bridge | string
|
||||
|
||||
12
README.md
12
README.md
@@ -30,7 +30,7 @@ Sub-repositories maintained within this monorepo are listed below.
|
||||
| [Commons](commons/README.md) | Interfaces, constants and utilities shared between the sub-repositories |
|
||||
| [E2E-Commons](e2e-commons/README.md) | Common utilities and configuration used in end to end tests |
|
||||
|
||||
Additionally there are [Smart Contracts](https://github.com/poanetwork/poa-bridge-contracts) used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
|
||||
Additionally there are [Smart Contracts](https://github.com/poanetwork/tokenbridge-contracts) used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
|
||||
|
||||
## Available deployments
|
||||
|
||||
@@ -52,11 +52,12 @@ Additionally there are [Smart Contracts](https://github.com/poanetwork/poa-bridg
|
||||
|
||||
## Operational Modes
|
||||
|
||||
The POA TokenBridge provides three operational modes:
|
||||
The POA TokenBridge provides four operational modes:
|
||||
|
||||
- [x] `Native-to-ERC20` **Coins** on a Home network can be converted to ERC20-compatible **tokens** on a Foreign network. Coins are locked on the Home side and the corresponding amount of ERC20 tokens are minted on the Foreign side. When the operation is reversed, tokens are burnt on the Foreign side and unlocked in the Home network. **More Information: [POA-to-POA20 Bridge](https://medium.com/poa-network/introducing-poa-bridge-and-poa20-55d8b78058ac)**
|
||||
- [x] `ERC20-to-ERC20` ERC20-compatible tokens on the Foreign network are locked and minted as ERC20-compatible tokens (ERC677 tokens) on the Home network. When transferred from Home to Foreign, they are burnt on the Home side and unlocked in the Foreign network. This can be considered a form of atomic swap when a user swaps the token "X" in network "A" to the token "Y" in network "B". **More Information: [ERC20-to-ERC20](https://medium.com/poa-network/introducing-the-erc20-to-erc20-tokenbridge-ce266cc1a2d0)**
|
||||
- [x] `ERC20-to-Native`: Pre-existing **tokens** in the Foreign network are locked and **coins** are minted in the `Home` network. In this mode, the Home network consensus engine invokes [Parity's Block Reward contract](https://wiki.parity.io/Block-Reward-Contract.html) to mint coins per the bridge contract request. **More Information: [xDai Chain](https://medium.com/poa-network/poa-network-partners-with-makerdao-on-xdai-chain-the-first-ever-usd-stable-blockchain-65a078c41e6a)**
|
||||
- [x] `Arbitrary-Message`: Transfer arbitrary data between two networks as so the data could be interpreted as an arbitrary contract method invocation.
|
||||
|
||||
## Initializing the monorepository
|
||||
|
||||
@@ -65,11 +66,13 @@ Clone the repository:
|
||||
git clone https://github.com/poanetwork/tokenbridge
|
||||
```
|
||||
|
||||
Initialize submodules, install dependencies, compile the Smart Contracts:
|
||||
If there is no need to build docker images for the TokenBridge components (oracle, monitor, UI), initialize submodules, install dependencies, compile the Smart Contracts:
|
||||
```
|
||||
yarn initialize
|
||||
```
|
||||
|
||||
Then refer to the corresponding README files to get information about particular TokenBridge component.
|
||||
|
||||
## Linting
|
||||
|
||||
Running linter for all JS projects:
|
||||
@@ -105,5 +108,4 @@ This project is licensed under the GNU Lesser General Public License v3.0. See t
|
||||
|
||||
## References
|
||||
|
||||
* [Additional Documentation](https://forum.poa.network/c/tokenbridge)
|
||||
* [POA20 Bridge FAQ](https://forum.poa.network/c/tokenbridge/poa20-bridge)
|
||||
* [TokenBridge Documentation](http://www.tokenbridge.net/)
|
||||
|
||||
Binary file not shown.
@@ -10,6 +10,10 @@ const ERC677_BRIDGE_TOKEN_ABI = require('../contracts/build/contracts/ERC677Brid
|
||||
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/IBlockReward').abi
|
||||
const BRIDGE_VALIDATORS_ABI = require('../contracts/build/contracts/BridgeValidators').abi
|
||||
const REWARDABLE_VALIDATORS_ABI = require('../contracts/build/contracts/RewardableValidators').abi
|
||||
const HOME_AMB_ABI = require('../contracts/build/contracts/HomeAMB').abi
|
||||
const FOREIGN_AMB_ABI = require('../contracts/build/contracts/ForeignAMB').abi
|
||||
const BOX_ABI = require('../contracts/build/contracts/Box').abi
|
||||
const SAI_TOP = require('../contracts/build/contracts/SaiTopMock').abi
|
||||
|
||||
const { HOME_V1_ABI, FOREIGN_V1_ABI } = require('./v1Abis')
|
||||
const { BRIDGE_MODES } = require('./constants')
|
||||
@@ -60,6 +64,9 @@ function getBridgeABIs(bridgeMode) {
|
||||
} else if (bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC_V1) {
|
||||
HOME_ABI = HOME_V1_ABI
|
||||
FOREIGN_ABI = FOREIGN_V1_ABI
|
||||
} else if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
HOME_ABI = HOME_AMB_ABI
|
||||
FOREIGN_ABI = FOREIGN_AMB_ABI
|
||||
} else {
|
||||
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
|
||||
}
|
||||
@@ -83,5 +90,9 @@ module.exports = {
|
||||
REWARDABLE_VALIDATORS_ABI,
|
||||
HOME_V1_ABI,
|
||||
FOREIGN_V1_ABI,
|
||||
ERC20_BYTES32_ABI
|
||||
ERC20_BYTES32_ABI,
|
||||
HOME_AMB_ABI,
|
||||
FOREIGN_AMB_ABI,
|
||||
BOX_ABI,
|
||||
SAI_TOP
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ const BRIDGE_MODES = {
|
||||
NATIVE_TO_ERC: 'NATIVE_TO_ERC',
|
||||
ERC_TO_ERC: 'ERC_TO_ERC',
|
||||
ERC_TO_NATIVE: 'ERC_TO_NATIVE',
|
||||
NATIVE_TO_ERC_V1: 'NATIVE_TO_ERC_V1'
|
||||
NATIVE_TO_ERC_V1: 'NATIVE_TO_ERC_V1',
|
||||
ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE'
|
||||
}
|
||||
|
||||
const ERC_TYPES = {
|
||||
@@ -16,4 +17,11 @@ const FEE_MANAGER_MODE = {
|
||||
UNDEFINED: 'UNDEFINED'
|
||||
}
|
||||
|
||||
module.exports = { BRIDGE_MODES, ERC_TYPES, FEE_MANAGER_MODE }
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
||||
|
||||
module.exports = {
|
||||
BRIDGE_MODES,
|
||||
ERC_TYPES,
|
||||
FEE_MANAGER_MODE,
|
||||
ZERO_ADDRESS
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const constants = require('./constants')
|
||||
const abis = require('./abis')
|
||||
const utils = require('./utils')
|
||||
const message = require('./message')
|
||||
|
||||
module.exports = {
|
||||
...constants,
|
||||
...abis,
|
||||
...utils
|
||||
...utils,
|
||||
...message
|
||||
}
|
||||
|
||||
27
commons/message.js
Normal file
27
commons/message.js
Normal file
@@ -0,0 +1,27 @@
|
||||
function strip0x(input) {
|
||||
return input.replace(/^0x/, '')
|
||||
}
|
||||
|
||||
function addTxHashToData({ encodedData, transactionHash }) {
|
||||
return encodedData.slice(0, 2) + strip0x(transactionHash) + encodedData.slice(2)
|
||||
}
|
||||
|
||||
function parseAMBMessage(message) {
|
||||
message = strip0x(message)
|
||||
|
||||
const txHash = `0x${message.slice(0, 64)}`
|
||||
const sender = `0x${message.slice(64, 104)}`
|
||||
const executor = `0x${message.slice(104, 144)}`
|
||||
|
||||
return {
|
||||
sender,
|
||||
executor,
|
||||
txHash
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addTxHashToData,
|
||||
parseAMBMessage,
|
||||
strip0x
|
||||
}
|
||||
@@ -8,6 +8,10 @@
|
||||
"test": "NODE_ENV=test mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"web3-utils": "1.0.0-beta.30"
|
||||
"web3-utils": "1.0.0-beta.34"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bn-chai": "^1.0.1",
|
||||
"chai": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ const { BRIDGE_MODES, ERC_TYPES } = require('../constants')
|
||||
|
||||
describe('constants', () => {
|
||||
it('should contain correct number of bridge types', () => {
|
||||
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(4)
|
||||
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(5)
|
||||
})
|
||||
|
||||
it('should contain correct number of erc types', () => {
|
||||
69
commons/test/message.test.js
Normal file
69
commons/test/message.test.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const { BN } = require('web3-utils')
|
||||
const { expect } = require('chai').use(require('bn-chai')(BN))
|
||||
const { parseAMBMessage, strip0x, addTxHashToData } = require('../message')
|
||||
|
||||
describe('strip0x', () => {
|
||||
it('should remove 0x from input', () => {
|
||||
// Given
|
||||
const input = '0x12345'
|
||||
|
||||
// When
|
||||
const result = strip0x(input)
|
||||
|
||||
// Then
|
||||
expect(result).to.be.equal('12345')
|
||||
})
|
||||
it('should not modify input if 0x is not present', () => {
|
||||
// Given
|
||||
const input = '12345'
|
||||
|
||||
// When
|
||||
const result = strip0x(input)
|
||||
|
||||
// Then
|
||||
expect(result).to.be.equal(input)
|
||||
})
|
||||
})
|
||||
describe('addTxHashToData', () => {
|
||||
it('should add txHash to encoded data at position 2', () => {
|
||||
// Given
|
||||
const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e'
|
||||
const msgExecutor = '0xf4bef13f9f4f2b203faf0c3cbbaabe1afe056955'
|
||||
const msgGasLimit = '000000000000000000000000000000000000000000000000000000005b877705'
|
||||
const msgDataType = '00'
|
||||
const msgData = '0xb1591967aed668a4b27645ff40c444892d91bf5951b382995d4d4f6ee3a2ce03'
|
||||
const encodedData = `0x${strip0x(msgSender)}${strip0x(msgExecutor)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
|
||||
|
||||
const transactionHash = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
|
||||
const message = `0x${strip0x(transactionHash)}${strip0x(msgSender)}${strip0x(
|
||||
msgExecutor
|
||||
)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
|
||||
|
||||
// When
|
||||
const result = addTxHashToData({ encodedData, transactionHash })
|
||||
|
||||
// Then
|
||||
expect(result).to.be.equal(message)
|
||||
})
|
||||
})
|
||||
describe('parseAMBMessage', () => {
|
||||
it('should parse data type 00', () => {
|
||||
const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e'
|
||||
const msgExecutor = '0xf4bef13f9f4f2b203faf0c3cbbaabe1afe056955'
|
||||
const msgTxHash = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
|
||||
const msgGasLimit = '000000000000000000000000000000000000000000000000000000005b877705'
|
||||
const msgDataType = '00'
|
||||
const msgData = '0xb1591967aed668a4b27645ff40c444892d91bf5951b382995d4d4f6ee3a2ce03'
|
||||
const message = `0x${strip0x(msgTxHash)}${strip0x(msgSender)}${strip0x(
|
||||
msgExecutor
|
||||
)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
|
||||
|
||||
// when
|
||||
const { sender, executor, txHash } = parseAMBMessage(message)
|
||||
|
||||
// then
|
||||
expect(sender).to.be.equal(msgSender)
|
||||
expect(executor).to.be.equal(msgExecutor)
|
||||
expect(txHash).to.be.equal(msgTxHash)
|
||||
})
|
||||
})
|
||||
@@ -10,6 +10,8 @@ function decodeBridgeMode(bridgeModeHash) {
|
||||
return BRIDGE_MODES.ERC_TO_ERC
|
||||
case '0x18762d46':
|
||||
return BRIDGE_MODES.ERC_TO_NATIVE
|
||||
case '0x2544fbb9':
|
||||
return BRIDGE_MODES.ARBITRARY_MESSAGE
|
||||
default:
|
||||
throw new Error(`Unrecognized bridge mode hash: '${bridgeModeHash}'`)
|
||||
}
|
||||
|
||||
Submodule contracts updated: 86b35f8382...a5946e7024
3
deployment-e2e/molecule/monitor/converge.yml
Normal file
3
deployment-e2e/molecule/monitor/converge.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
- import_playbook: ../../../deployment/site.yml
|
||||
- import_playbook: ./run-checks.yml
|
||||
@@ -29,10 +29,11 @@ provisioner:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../../../deployment/site.yml
|
||||
converge: ./converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
monitor-host:
|
||||
MONITOR_PORT: 3003
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
|
||||
7
deployment-e2e/molecule/monitor/run-checks.yml
Normal file
7
deployment-e2e/molecule/monitor/run-checks.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Generate initial data for monitor
|
||||
hosts: monitor
|
||||
become: true
|
||||
tasks:
|
||||
- name: Run monitor checks
|
||||
shell: /bin/bash -c 'cd /home/poadocker/bridge/monitor/scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err'
|
||||
@@ -34,14 +34,14 @@ def test_logging(host, filename):
|
||||
|
||||
def test_home_exists(host):
|
||||
assert host.run_test(
|
||||
'curl -s http://localhost:3003 | '
|
||||
'curl -s http://localhost:3003/bridge | '
|
||||
'grep -q -i "home"'
|
||||
)
|
||||
|
||||
|
||||
def test_foreign_exists(host):
|
||||
assert host.run_test(
|
||||
'curl -s http://localhost:3003 | '
|
||||
'curl -s http://localhost:3003/bridge | '
|
||||
'grep -q -i "foreign"'
|
||||
)
|
||||
|
||||
@@ -49,6 +49,6 @@ def test_foreign_exists(host):
|
||||
def test_no_error(host):
|
||||
assert host.run_expect(
|
||||
[1],
|
||||
'curl -s http://localhost:3003 | '
|
||||
'curl -s http://localhost:3003/bridge | '
|
||||
'grep -i -q "error"'
|
||||
)
|
||||
|
||||
14
deployment-e2e/molecule/multiple/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/multiple/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
59
deployment-e2e/molecule/multiple/molecule.yml
Normal file
59
deployment-e2e/molecule/multiple/molecule.yml
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: multiple-host
|
||||
groups:
|
||||
- example
|
||||
children:
|
||||
- oracle
|
||||
- monitor
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
lint:
|
||||
name: ansible-lint
|
||||
enabled: True
|
||||
options:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../monitor/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
multiple-host:
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
MONITOR_PORT: 3003
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: multiple
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
- destroy
|
||||
32
deployment-e2e/molecule/multiple/tests/test_multiple.py
Normal file
32
deployment-e2e/molecule/multiple/tests/test_multiple.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import os
|
||||
import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("service", [
|
||||
("poabridge"),
|
||||
("tokenbridge-ui"),
|
||||
("tokenbridge-monitor")
|
||||
])
|
||||
def test_services(host, service):
|
||||
assert host.service(service).is_enabled
|
||||
assert host.service(service).is_running
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", [
|
||||
("oracle_rabbit_1"),
|
||||
("oracle_redis_1"),
|
||||
("oracle_bridge_request_1"),
|
||||
("oracle_bridge_collected_1"),
|
||||
("oracle_bridge_affirmation_1"),
|
||||
("oracle_bridge_senderhome_1"),
|
||||
("oracle_bridge_senderforeign_1"),
|
||||
("ui_ui_1"),
|
||||
("monitor_monitor_1")
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
container = host.docker(name)
|
||||
assert container.is_running
|
||||
@@ -9,7 +9,6 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
def test_repo(host):
|
||||
assert host.file('/home/poadocker/bridge').exists
|
||||
assert host.file('/home/poadocker/bridge').is_directory
|
||||
assert host.file('/home/poadocker/bridge/package.json').exists
|
||||
|
||||
|
||||
def test_docker_group(host):
|
||||
|
||||
14
deployment-e2e/molecule/ultimate-amb/Dockerfile.j2
Normal file
14
deployment-e2e/molecule/ultimate-amb/Dockerfile.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
40
deployment-e2e/molecule/ultimate-amb/molecule.yml
Normal file
40
deployment-e2e/molecule/ultimate-amb/molecule.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: oracle-amb-host
|
||||
groups:
|
||||
- ultimate
|
||||
- amb
|
||||
children:
|
||||
- oracle
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../ultimate-commons/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-amb-host:
|
||||
COMMON_HOME_RPC_URL: "http://parity1:8545"
|
||||
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-amb
|
||||
test_sequence:
|
||||
- cleanup
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "/home/poadocker/bridge/oracle/{{ file }}.yml"
|
||||
register: docker_compose_slurp
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Add the external network used to connect to Parity nodes
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
|
||||
|
||||
- name: Add all Oracle containers to the network
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': docker_compose_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Expose Redis port to allow connecting from redis-cli
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}"
|
||||
|
||||
- name: Write updated docker file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "/home/poadocker/bridge/oracle/{{ file }}.yml"
|
||||
@@ -5,32 +5,31 @@
|
||||
tasks:
|
||||
- name: stop the service
|
||||
shell: service poabridge stop
|
||||
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "/home/poadocker/bridge/oracle/docker-compose.yml"
|
||||
register: docker_compose_slurp
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Add the external network used to connect to Parity nodes
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}"
|
||||
- name: Build current oracle image
|
||||
shell: docker build -t oracle:ultimate-testing --file oracle/Dockerfile .
|
||||
delegate_to: 127.0.0.1
|
||||
become: false
|
||||
args:
|
||||
chdir: "{{ lookup('env', 'PWD') }}/.."
|
||||
|
||||
- name: Add all Oracle containers to the network
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': docker_compose_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Expose Redis port to allow connecting from redis-cli
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}"
|
||||
- name: Replace oracle image
|
||||
replace:
|
||||
path: "/home/poadocker/bridge/oracle/{{ item }}.yml"
|
||||
regexp: 'poanetwork/tokenbridge-oracle:latest'
|
||||
replace: "oracle:ultimate-testing"
|
||||
with_items:
|
||||
- docker-compose
|
||||
- docker-compose-transfer
|
||||
- docker-compose-erc-native
|
||||
|
||||
- name: Write new docker-compose file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "/home/poadocker/bridge/oracle/docker-compose.yml"
|
||||
- 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
|
||||
|
||||
@@ -36,6 +36,8 @@ provisioner:
|
||||
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ORACLE_HOME_START_BLOCK: 1
|
||||
ORACLE_FOREIGN_START_BLOCK: 1
|
||||
ui-erc-to-native-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
|
||||
@@ -78,6 +78,10 @@ Example config for installing only UI:
|
||||
1. Go to the group_vars folder.
|
||||
`cd group_vars`
|
||||
2. Note the <bridge_name> and add it to the hosts.yml configuration. For example, if a bridge file is named sokol-kovan.yml, you would change the <bridge_name> value in hosts.yml to sokol-kovan.
|
||||
|
||||
## Examples
|
||||
|
||||
[Deploy a monitor for multiple bridges](./MONITOR.md)
|
||||
|
||||
## Administrator Configurations
|
||||
|
||||
|
||||
104
deployment/MONITOR.md
Normal file
104
deployment/MONITOR.md
Normal file
@@ -0,0 +1,104 @@
|
||||
## Deploy multiple bridge monitor on the same host
|
||||
|
||||
If you want to deploy a monitor for different bridges, the [monitor variables](../monitor/.env.example) should be configured in `group_vars/<bridge_name>.yml` for each bridge.
|
||||
|
||||
For example, let's say we are going to deploy a monitor for xDai bridge and for WETC bridge.
|
||||
|
||||
#### Setup ansible configuration for xDai Bridge
|
||||
|
||||
First we create `hosts.yml` file to deploy the monitor for xdai bridge
|
||||
```yaml
|
||||
---
|
||||
xdai:
|
||||
children:
|
||||
monitor:
|
||||
hosts:
|
||||
<host_ip_A>:
|
||||
ansible_user: ubuntu
|
||||
```
|
||||
In `group_vars/xdai.yml`
|
||||
```
|
||||
---
|
||||
MONITOR_BRIDGE_NAME: "xdai"
|
||||
MONITOR_PORT: 3003
|
||||
|
||||
COMMON_HOME_RPC_URL: "https://dai.poa.network"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
|
||||
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io/v3/INFURA_KEY"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016"
|
||||
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK: 0
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
MONITOR_HOME_START_BLOCK: 759
|
||||
MONITOR_FOREIGN_START_BLOCK: 6478417
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 0
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
```
|
||||
|
||||
Run the playbook to deploy the monitor for xdai bridge
|
||||
```
|
||||
ansible-playbook -i hosts.yml site.yml
|
||||
```
|
||||
|
||||
This command will deploy the monitor component and enable statistics for xdai bridge.
|
||||
|
||||
#### Setup ansible configuration for WETC Bridge
|
||||
|
||||
Update `hosts.yml` file to deploy the monitor for WETC Bridge
|
||||
```yaml
|
||||
---
|
||||
wetc:
|
||||
children:
|
||||
monitor:
|
||||
hosts:
|
||||
<host_ip_A>:
|
||||
ansible_user: ubuntu
|
||||
```
|
||||
|
||||
In `group_vars/wetc.yml`
|
||||
```
|
||||
---
|
||||
MONITOR_BRIDGE_NAME: "wetc"
|
||||
|
||||
COMMON_HOME_RPC_URL: "https://ethereumclassic.network"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
|
||||
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io/v3/32e8e252699a4ac1b5dd5c1ef53cc301"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x0cB781EE62F815bdD9CD4c2210aE8600d43e7040"
|
||||
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice-etc.poa.network/"
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK: 15000000000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR: 1
|
||||
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard"
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
MONITOR_HOME_START_BLOCK: 7703292
|
||||
MONITOR_FOREIGN_START_BLOCK: 7412459
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
```
|
||||
Given that there is a monitor component deployed in the system, the `MONITOR_PORT` variable is not needed.
|
||||
|
||||
Run the playbook to deploy the monitor for WETC Bridge
|
||||
```
|
||||
ansible-playbook -i hosts.yml site.yml
|
||||
```
|
||||
|
||||
They playbook will detect that the monitor component is already deployed in the system, so it will only generate the configuration needed to enable the WETC Bridge statistics.
|
||||
|
||||
##### Get Monitor results
|
||||
The monitor output will be available at `http://host_ip_A:MONITOR_PORT/MONITOR_BRIDGE_NAME`.
|
||||
|
||||
Given that in `xdai.env` the variable `MONITOR_BRIDGE_NAME` is set to `xdai`, the results are in the url `http://host_ip_A:3003/xdai/`.
|
||||
|
||||
Similar to the xdai case, in `wetc.env` the variable `MONITOR_BRIDGE_NAME` is set to `wetc`, so the results are in the url `http://host_ip_A:3003/wetc/`.
|
||||
5
deployment/group_vars/amb.yml
Normal file
5
deployment/group_vars/amb.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
ORACLE_BRIDGE_MODE: "ARBITRARY_MESSAGE"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0"
|
||||
MONITOR_PORT: 3013
|
||||
@@ -42,6 +42,7 @@ UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "xdai"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 759
|
||||
MONITOR_FOREIGN_START_BLOCK: 6478417
|
||||
|
||||
@@ -44,9 +44,10 @@ UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_LEFT_TX_THRESHOLD: 100
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
|
||||
@@ -41,9 +41,10 @@ UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
#montior
|
||||
#monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_LEFT_TX_THRESHOLD: 100
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
|
||||
@@ -43,6 +43,7 @@ UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "wetc"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_HOME_START_BLOCK: 7703292
|
||||
MONITOR_FOREIGN_START_BLOCK: 7412459
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
group: "root"
|
||||
mode: "0755"
|
||||
|
||||
- name: Upgrade pip version
|
||||
shell: pip3 install --upgrade pip
|
||||
|
||||
- name: Install python docker library
|
||||
shell: pip3 install docker docker-compose setuptools
|
||||
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
---
|
||||
- include_tasks: dependencies.yml
|
||||
- include_tasks: repo.yml
|
||||
- include_tasks: logging.yml
|
||||
- name: Check if component is already deployed
|
||||
shell: "test -f {{ bridge_path }}/{{ component }}/.env && echo 'true'"
|
||||
ignore_errors: True
|
||||
register: already_deployed
|
||||
when: check_deployed is defined
|
||||
|
||||
- name: Set if tasks should be skipped
|
||||
set_fact: skip_task="{{ already_deployed.stdout | default('false') }}"
|
||||
|
||||
- name: Include dependencies tasks
|
||||
include_tasks: dependencies.yml
|
||||
when: skip_task != true
|
||||
|
||||
- name: Include repo tasks
|
||||
include_tasks: repo.yml
|
||||
when: skip_task != true and skip_repo is undefined
|
||||
|
||||
- name: Include logging tasks
|
||||
include_tasks: logging.yml
|
||||
when: skip_task != true
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- role: common
|
||||
- { role: common, skip_repo: true, check_deployed: true, component: 'monitor' }
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
day: "{{ monitor_cron_schedule.split(' ')[2] }}"
|
||||
month: "{{ monitor_cron_schedule.split(' ')[3] }}"
|
||||
weekday: "{{ monitor_cron_schedule.split(' ')[4] }}"
|
||||
job: "/bin/bash -c 'cd {{ bridge_path }}/monitor/scripts; ./checkDocker.sh >cronWorker.out 2>cronWorker.err'"
|
||||
job: "/bin/bash -c 'cd {{ bridge_path }}/monitor/scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err'"
|
||||
- name: Add cron entry
|
||||
cron:
|
||||
name: "RUN_MONITOR_CHECKS"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
- name: Build the containers
|
||||
shell: docker-compose build
|
||||
- name: Pull the containers images
|
||||
shell: docker-compose pull
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/monitor"
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
---
|
||||
- include_tasks: pre_config.yml
|
||||
- include_tasks: logging.yml
|
||||
- include_tasks: jumpbox.yml
|
||||
- include_tasks: servinstall.yml
|
||||
- include_tasks: cron.yml
|
||||
|
||||
- name: Include logging tasks
|
||||
include_tasks: logging.yml
|
||||
when: skip_task != true
|
||||
|
||||
- name: Include jumpbox tasks
|
||||
include_tasks: jumpbox.yml
|
||||
when: skip_task != true
|
||||
|
||||
- name: Include servinstall tasks
|
||||
include_tasks: servinstall.yml
|
||||
when: skip_task != true
|
||||
|
||||
- name: Include cron tasks
|
||||
include_tasks: cron.yml
|
||||
when: skip_task != true
|
||||
|
||||
@@ -1,5 +1,47 @@
|
||||
---
|
||||
- name: Create configs directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/monitor/configs"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Create responses directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/monitor/responses"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Create scripts directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/monitor/scripts"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Install .env config
|
||||
template:
|
||||
src: .env.j2
|
||||
dest: "{{ bridge_path }}/monitor/.env"
|
||||
when: skip_task != true
|
||||
|
||||
- name: Copy docker-compose file
|
||||
copy:
|
||||
src: ../../../../monitor/docker-compose.yml
|
||||
dest: "{{ bridge_path }}/monitor/docker-compose.yml"
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Copy script file
|
||||
copy:
|
||||
src: ../../../../monitor/scripts/getBridgeStats.sh
|
||||
dest: "{{ bridge_path }}/monitor/scripts/getBridgeStats.sh"
|
||||
owner: "{{ compose_service_user }}"
|
||||
mode: '0755'
|
||||
when: skip_task != true
|
||||
|
||||
- name: Install bridge config env
|
||||
template:
|
||||
src: config.env.j2
|
||||
dest: "{{ bridge_path }}/monitor/configs/{{ MONITOR_BRIDGE_NAME }}.env"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# This role creates a tokenbridge-monitor service which is designed to manage docker-compose monitor deployment.
|
||||
# /etc/init.d/tokenbridge-monitor start, status, stop, restart - does what the services usually do in such cases.
|
||||
# /etc/init.d/tokenbridge-monitor rebuild - builds a new monitor deployment from scratch.
|
||||
---
|
||||
- name: "Set the service"
|
||||
template:
|
||||
|
||||
@@ -1,24 +1 @@
|
||||
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
|
||||
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
|
||||
COMMON_HOME_BRIDGE_ADDRESS={{ COMMON_HOME_BRIDGE_ADDRESS }}
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
|
||||
MONITOR_HOME_START_BLOCK={{ MONITOR_HOME_START_BLOCK }}
|
||||
MONITOR_FOREIGN_START_BLOCK={{ MONITOR_FOREIGN_START_BLOCK }}
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT={{ MONITOR_VALIDATOR_HOME_TX_LIMIT }}
|
||||
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
|
||||
{% endif %}
|
||||
{% if COMMON_HOME_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE={{ COMMON_HOME_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK={{ COMMON_HOME_GAS_PRICE_FALLBACK }}
|
||||
{% if COMMON_HOME_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_FACTOR={{ COMMON_HOME_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT={{ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT }}
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL={{ COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL }}
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE={{ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE }}
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK={{ COMMON_FOREIGN_GAS_PRICE_FALLBACK }}
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
|
||||
MONITOR_LEFT_TX_THRESHOLD={{ MONITOR_LEFT_TX_THRESHOLD }}
|
||||
MONITOR_PORT={{ MONITOR_PORT }}
|
||||
|
||||
34
deployment/roles/monitor/templates/config.env.j2
Normal file
34
deployment/roles/monitor/templates/config.env.j2
Normal file
@@ -0,0 +1,34 @@
|
||||
MONITOR_BRIDGE_NAME={{ MONITOR_BRIDGE_NAME }}
|
||||
|
||||
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
|
||||
COMMON_HOME_BRIDGE_ADDRESS={{ COMMON_HOME_BRIDGE_ADDRESS }}
|
||||
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
|
||||
|
||||
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
|
||||
{% endif %}
|
||||
{% if COMMON_HOME_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE={{ COMMON_HOME_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK={{ COMMON_HOME_GAS_PRICE_FALLBACK }}
|
||||
{% if COMMON_HOME_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_FACTOR={{ COMMON_HOME_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL={{ COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL }}
|
||||
{% endif %}
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE={{ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK={{ COMMON_FOREIGN_GAS_PRICE_FALLBACK }}
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
|
||||
MONITOR_HOME_START_BLOCK={{ MONITOR_HOME_START_BLOCK }}
|
||||
MONITOR_FOREIGN_START_BLOCK={{ MONITOR_FOREIGN_START_BLOCK }}
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT={{ MONITOR_VALIDATOR_HOME_TX_LIMIT }}
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT={{ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT }}
|
||||
MONITOR_TX_NUMBER_THRESHOLD={{ MONITOR_TX_NUMBER_THRESHOLD }}
|
||||
@@ -18,6 +18,7 @@ start(){
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose up --detach
|
||||
sudo -u "{{ compose_service_user }}" /bin/bash -c 'cd scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err'
|
||||
}
|
||||
|
||||
stop(){
|
||||
@@ -33,14 +34,6 @@ status(){
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose ps
|
||||
}
|
||||
|
||||
rebuild(){
|
||||
echo "Rebuild TokenBridge Monitor.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose up --detach --force-recreate --no-deps --build
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
|
||||
@@ -62,12 +55,8 @@ case "$1" in
|
||||
start
|
||||
;;
|
||||
|
||||
rebuild)
|
||||
rebuild
|
||||
;;
|
||||
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|rebuild|status}"
|
||||
echo $"Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- role: common
|
||||
- { role: common, skip_repo: true }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
- name: Build the containers
|
||||
shell: docker-compose build
|
||||
- name: Pull the containers images
|
||||
shell: docker-compose pull
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
---
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "{{ bridge_path }}/oracle/docker-compose.yml"
|
||||
register: docker_compose_slurp
|
||||
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Set logger to remote server
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Write new docker-compose file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "{{ bridge_path }}/oracle/docker-compose.yml"
|
||||
- include_tasks: logging_by_syslog.yml
|
||||
with_items:
|
||||
- docker-compose
|
||||
- docker-compose-transfer
|
||||
- docker-compose-erc-native
|
||||
loop_control:
|
||||
loop_var: file
|
||||
|
||||
- name: Set the local container logs configuration file
|
||||
template:
|
||||
|
||||
19
deployment/roles/oracle/tasks/logging_by_syslog.yml
Normal file
19
deployment/roles/oracle/tasks/logging_by_syslog.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Slurp docker compose file
|
||||
slurp:
|
||||
src: "{{ bridge_path }}/oracle/{{ file }}.yml"
|
||||
register: docker_compose_slurp
|
||||
|
||||
- name: Parse docker compose file
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- name: Set logger to remote server
|
||||
set_fact:
|
||||
docker_compose_parsed: "{{ docker_compose_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}"
|
||||
with_items: "{{ docker_compose_parsed.services }}"
|
||||
|
||||
- name: Write updated docker file
|
||||
copy:
|
||||
content: "{{ docker_compose_parsed | to_yaml }}"
|
||||
dest: "{{ bridge_path }}/oracle/{{ file }}.yml"
|
||||
@@ -1,22 +1,24 @@
|
||||
---
|
||||
- name: Get blocks
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run --entrypoint "node scripts/getValidatorStartBlocks.js" bridge_affirmation
|
||||
shell: docker-compose run --rm --entrypoint "node scripts/getValidatorStartBlocks.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: BLOCKS
|
||||
when: (ORACLE_HOME_START_BLOCK is not defined) or (ORACLE_FOREIGN_START_BLOCK is not defined)
|
||||
|
||||
- name: Write blocks
|
||||
blockinfile:
|
||||
path: "{{ bridge_path }}/oracle/.env"
|
||||
marker: "## {mark} Calculated by scripts/getValidatorStartBlocks.js"
|
||||
block: |
|
||||
HOME_START_BLOCK={{ (BLOCKS.stdout | from_json).homeStartBlock }}
|
||||
FOREIGN_START_BLOCK={{ (BLOCKS.stdout | from_json).foreignStartBlock }}
|
||||
ORACLE_HOME_START_BLOCK={{ (BLOCKS.stdout | from_json).homeStartBlock }}
|
||||
ORACLE_FOREIGN_START_BLOCK={{ (BLOCKS.stdout | from_json).foreignStartBlock }}
|
||||
when: (ORACLE_HOME_START_BLOCK is not defined) or (ORACLE_FOREIGN_START_BLOCK is not defined)
|
||||
|
||||
- name: Get validator address
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="{{ ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }}" --entrypoint "node scripts/privateKeyToAddress.js" bridge_affirmation
|
||||
shell: docker-compose run --rm -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="{{ ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }}" --entrypoint "node scripts/privateKeyToAddress.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: VADDRESS
|
||||
@@ -25,6 +27,25 @@
|
||||
set_fact:
|
||||
ORACLE_VALIDATOR_ADDRESS: "{{ VADDRESS.stdout }}"
|
||||
|
||||
- name: Get foreign erc type
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run --rm --entrypoint "node scripts/initialChecks.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: ERCTYPE
|
||||
|
||||
- name: Set FOREIGN_ERC_TYPE variable
|
||||
set_fact:
|
||||
FOREIGN_ERC_TYPE: "{{ (ERCTYPE.stdout).foreignERC | default('') }}"
|
||||
|
||||
- name: Extend docker compose file
|
||||
set_fact: composefileoverride="-f docker-compose-transfer.yml"
|
||||
when: ORACLE_BRIDGE_MODE == "ERC_TO_ERC" and FOREIGN_ERC_TYPE != "ERC677"
|
||||
|
||||
- name: Extend docker compose file for erc to native
|
||||
set_fact: composefileoverride="-f docker-compose-erc-native.yml"
|
||||
when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE"
|
||||
|
||||
- name: Install .key config
|
||||
template:
|
||||
src: key.j2
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
---
|
||||
- name: Create oracle directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/oracle"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Install .env config
|
||||
template:
|
||||
src: .env.j2
|
||||
dest: "{{ bridge_path }}/oracle/.env"
|
||||
|
||||
- name: Copy docker-compose files
|
||||
copy:
|
||||
src: ../../../../oracle/{{ item }}
|
||||
dest: "{{ bridge_path }}/oracle/"
|
||||
mode: '0755'
|
||||
with_items:
|
||||
- docker-compose.yml
|
||||
- docker-compose-transfer.yml
|
||||
- docker-compose-erc-native.yml
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# This role creates a poabridge service which is designed to manage docker-compose bridge deployment.
|
||||
# /etc/init.d/poabridge start, status, stop, restart - does what the services usually do in such cases.
|
||||
# /etc/init.d/poabridge rebuild - builds a new bridge deployment from scratch.
|
||||
---
|
||||
- name: "Set poabridge service"
|
||||
template:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
## General settings
|
||||
ORACLE_BRIDGE_MODE={{ ORACLE_BRIDGE_MODE }}
|
||||
{% if ORACLE_LOG_LEVEL | default('') != '' %}
|
||||
ORACLE_LOG_LEVEL={{ ORACLE_LOG_LEVEL }}
|
||||
{% endif %}
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
|
||||
@@ -45,3 +47,10 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC={{ "yes" if ORACLE_ALLOW_HTTP_FOR_RPC else "no" }}
|
||||
ORACLE_QUEUE_URL={{ ORACLE_QUEUE_URL }}
|
||||
ORACLE_REDIS_URL={{ ORACLE_REDIS_URL }}
|
||||
|
||||
{% if ORACLE_HOME_START_BLOCK | default('') != '' %}
|
||||
ORACLE_HOME_START_BLOCK={{ ORACLE_HOME_START_BLOCK }}
|
||||
{% endif %}
|
||||
{% if ORACLE_FOREIGN_START_BLOCK | default('') != '' %}
|
||||
ORACLE_FOREIGN_START_BLOCK={{ ORACLE_FOREIGN_START_BLOCK }}
|
||||
{% endif %}
|
||||
|
||||
@@ -16,6 +16,7 @@ WORKDIR="{{ '/home/' + compose_service_user | default('poadocker') + '/' + bridg
|
||||
keyfile="{{ keyfile_path }}"
|
||||
vaddr="ORACLE_VALIDATOR_ADDRESS="
|
||||
vkey="ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="
|
||||
composefileoverride="{{ composefileoverride | default('') }}"
|
||||
|
||||
#Parsing file content and add key to variable
|
||||
while read -r line
|
||||
@@ -33,30 +34,22 @@ done < $keyfile
|
||||
start(){
|
||||
echo "Starting bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
|
||||
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose up --detach
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride rm -fv
|
||||
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose $composefileoverride up --detach
|
||||
}
|
||||
|
||||
stop(){
|
||||
echo "Stopping bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride down -v
|
||||
sleep 2
|
||||
}
|
||||
|
||||
status(){
|
||||
echo "Bridge status:"
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose ps
|
||||
}
|
||||
|
||||
rebuild(){
|
||||
echo "Rebuild bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
|
||||
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose up --detach --force-recreate --no-deps --build
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride ps
|
||||
}
|
||||
|
||||
|
||||
@@ -80,12 +73,8 @@ case "$1" in
|
||||
start
|
||||
;;
|
||||
|
||||
rebuild)
|
||||
rebuild
|
||||
;;
|
||||
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|rebuild|status}"
|
||||
echo $"Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
|
||||
19
e2e-commons/components-envs/monitor-amb.env
Normal file
19
e2e-commons/components-envs/monitor-amb.env
Normal file
@@ -0,0 +1,19 @@
|
||||
COMMON_HOME_RPC_URL=http://parity1:8545
|
||||
COMMON_FOREIGN_RPC_URL=http://parity2:8545
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
|
||||
MONITOR_HOME_START_BLOCK=0
|
||||
MONITOR_FOREIGN_START_BLOCK=0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT=300000
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR=1
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT=300000
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3013
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
@@ -16,3 +16,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3012
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
@@ -16,3 +16,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3011
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
@@ -16,3 +16,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3010
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
23
e2e-commons/components-envs/oracle-amb.env
Normal file
23
e2e-commons/components-envs/oracle-amb.env
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE
|
||||
ORACLE_QUEUE_URL=amqp://rabbit
|
||||
ORACLE_REDIS_URL=redis://redis
|
||||
COMMON_HOME_RPC_URL=http://parity1:8545
|
||||
COMMON_FOREIGN_RPC_URL=http://parity2:8545
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
|
||||
ORACLE_VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=1000000000
|
||||
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
COMMON_HOME_GAS_PRICE_FACTOR=1
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK=10000000000
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL=500
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC=yes
|
||||
@@ -21,3 +21,5 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=0.1
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL=500
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC=yes
|
||||
ORACLE_HOME_START_BLOCK=1
|
||||
ORACLE_FOREIGN_START_BLOCK=1
|
||||
|
||||
@@ -7,10 +7,26 @@
|
||||
"address": "0xcca2fb44C8C36E51f743269d6F484Fd027B9F9Aa",
|
||||
"privateKey": "0xcf954e07e6a439faf392eb474e95ddb444c2ca444847f2ad6ecc79e1a585e2b8"
|
||||
},
|
||||
"thirdUser": {
|
||||
"address": "0x441cc8537aB6cE63d060b63F3A44eE021d62e6cF",
|
||||
"privateKey": "0xd3915199f27691d7784cb01ab0c7220308053b229f95d592e97493326314a8d0"
|
||||
},
|
||||
"fourthUser": {
|
||||
"address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B",
|
||||
"privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1"
|
||||
},
|
||||
"validator": {
|
||||
"address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b",
|
||||
"privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
},
|
||||
"secondValidator": {
|
||||
"address": "0xdCC784657C78054aa61FbcFFd2605F32374816A4",
|
||||
"privateKey": "0x5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||
},
|
||||
"thirdValidator": {
|
||||
"address": "0xDcef88209a20D52165230104B245803C3269454d",
|
||||
"privateKey": "0xf877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||
},
|
||||
"blockGenerator": {
|
||||
"address": "0xB4579fd5AfEaB60B03Db3F408AAdD315035943f7",
|
||||
"privateKey": "0xd6143d390d8b28c33601bb0fe29392fb1c35c24ccfe8722c09c2bdd6ada2699f"
|
||||
@@ -20,7 +36,7 @@
|
||||
"foreign": "0x2B6871b9B02F73fa24F4864322CdC78604207769",
|
||||
"foreignToken": "0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B",
|
||||
"ui": "http://localhost:3000",
|
||||
"monitor": "http://monitor:3010"
|
||||
"monitor": "http://monitor:3010/bridge"
|
||||
},
|
||||
"ercToErcBridge": {
|
||||
"home": "0x1feB40aD9420b186F019A717c37f5546165d411E",
|
||||
@@ -28,14 +44,24 @@
|
||||
"homeToken": "0x792455a6bCb62Ed4C4362D323E0590654CA4765c",
|
||||
"foreignToken": "0x3C665A31199694Bf723fD08844AD290207B5797f",
|
||||
"ui": "http://localhost:3001",
|
||||
"monitor": "http://monitor-erc20:3011"
|
||||
"monitor": "http://monitor-erc20:3011/bridge"
|
||||
},
|
||||
"ercToNativeBridge": {
|
||||
"home": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
|
||||
"foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
|
||||
"foreignToken": "0x3C665A31199694Bf723fD08844AD290207B5797f",
|
||||
"foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9",
|
||||
"halfDuplexToken": "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359",
|
||||
"saiTop": "0x9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3",
|
||||
"chaiToken": "0x06af07097c9eeb7fd685c692751d5c66db49c215",
|
||||
"ui": "http://localhost:3002",
|
||||
"monitor": "http://monitor-erc20-native:3012"
|
||||
"monitor": "http://monitor-erc20-native:3012/bridge"
|
||||
},
|
||||
"amb": {
|
||||
"home": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
|
||||
"foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
|
||||
"homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
|
||||
"foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
|
||||
"monitor": "http://monitor-amb:3013/bridge"
|
||||
},
|
||||
"homeRPC": {
|
||||
"URL": "http://parity1:8545",
|
||||
|
||||
25
e2e-commons/contracts-envs/amb.env
Normal file
25
e2e-commons/contracts-envs/amb.env
Normal file
@@ -0,0 +1,25 @@
|
||||
BRIDGE_MODE=ARBITRARY_MESSAGE
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
HOME_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50
|
||||
DEPLOYMENT_GAS_LIMIT_EXTRA=0.2
|
||||
|
||||
HOME_RPC_URL=http://parity1:8545
|
||||
HOME_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_MAX_AMOUNT_PER_TX=8000000
|
||||
HOME_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
HOME_GAS_PRICE=1000000000
|
||||
|
||||
FOREIGN_RPC_URL=http://parity2:8545
|
||||
FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_MAX_AMOUNT_PER_TX=8000000
|
||||
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
FOREIGN_GAS_PRICE=10000000000
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
@@ -34,8 +34,7 @@ FOREIGN_REWARDABLE=false
|
||||
ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
VALIDATORS_REWARD_ACCOUNTS=0x0000000000000000000000000000000000000000
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
BLOCK_REWARD_ADDRESS=0x0000000000000000000000000000000000000000
|
||||
DPOS_STAKING_ADDRESS=0x0000000000000000000000000000000000000000
|
||||
ERC20_EXTENDED_BY_ERC677=false
|
||||
|
||||
@@ -32,8 +32,7 @@ FOREIGN_GAS_PRICE=10000000000
|
||||
FOREIGN_REWARDABLE=false
|
||||
|
||||
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
|
||||
ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f
|
||||
ERC20_TOKEN_ADDRESS=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
VALIDATORS_REWARD_ACCOUNTS=0x0000000000000000000000000000000000000000
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
|
||||
@@ -34,6 +34,5 @@ FOREIGN_GAS_PRICE=10000000000
|
||||
FOREIGN_REWARDABLE=false
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
VALIDATORS_REWARD_ACCOUNTS=0x0000000000000000000000000000000000000000
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
BLOCK_REWARD_ADDRESS=0x0000000000000000000000000000000000000000
|
||||
|
||||
@@ -24,16 +24,13 @@ services:
|
||||
- ultimate
|
||||
rabbit:
|
||||
image: "rabbitmq:3-management"
|
||||
ports:
|
||||
- "15672:15672"
|
||||
networks:
|
||||
- ultimate
|
||||
oracle:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/oracle.env
|
||||
env_file: ../e2e-commons/components-envs/oracle.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
command: "true"
|
||||
@@ -43,8 +40,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/oracle-erc20.env
|
||||
env_file: ../e2e-commons/components-envs/oracle-erc20.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
command: "true"
|
||||
@@ -54,8 +50,17 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/oracle-erc20-native.env
|
||||
env_file: ../e2e-commons/components-envs/oracle-erc20-native.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
command: "true"
|
||||
networks:
|
||||
- ultimate
|
||||
oracle-amb:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: oracle/Dockerfile
|
||||
env_file: ../e2e-commons/components-envs/oracle-amb.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
command: "true"
|
||||
@@ -92,8 +97,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/monitor.env
|
||||
env_file: ../e2e-commons/components-envs/monitor.env
|
||||
entrypoint: yarn check-and-start
|
||||
ports:
|
||||
- "3010:3010"
|
||||
@@ -103,8 +107,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/monitor-erc20.env
|
||||
env_file: ../e2e-commons/components-envs/monitor-erc20.env
|
||||
entrypoint: yarn check-and-start
|
||||
ports:
|
||||
- "3011:3011"
|
||||
@@ -114,13 +117,22 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
args:
|
||||
DOT_ENV_PATH: e2e-commons/components-envs/monitor-erc20-native.env
|
||||
env_file: ../e2e-commons/components-envs/monitor-erc20-native.env
|
||||
entrypoint: yarn check-and-start
|
||||
ports:
|
||||
- "3012:3012"
|
||||
networks:
|
||||
- ultimate
|
||||
monitor-amb:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
env_file: ../e2e-commons/components-envs/monitor-amb.env
|
||||
entrypoint: yarn check-and-start
|
||||
ports:
|
||||
- "3013:3013"
|
||||
networks:
|
||||
- ultimate
|
||||
e2e:
|
||||
build:
|
||||
context: ..
|
||||
@@ -128,3 +140,10 @@ services:
|
||||
command: "true"
|
||||
networks:
|
||||
- ultimate
|
||||
blocks:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile.e2e
|
||||
entrypoint: node e2e-commons/scripts/blocks.js
|
||||
networks:
|
||||
- ultimate
|
||||
|
||||
@@ -5,4 +5,6 @@ if [ $CI ]; then exit $rc; fi
|
||||
|
||||
ps | grep node | grep -v grep | awk '{print "kill " $1}' | /bin/bash
|
||||
docker-compose down
|
||||
docker-compose -p validator2 down
|
||||
docker-compose -p validator3 down
|
||||
docker network rm ultimate || true
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
const Web3 = require('web3')
|
||||
const { generateNewBlock } = require('../utils')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8541'))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8542'))
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity1:8545'))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity2:8545'))
|
||||
const {user, blockGenerator} = require('../constants.json');
|
||||
|
||||
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
@@ -10,10 +9,24 @@ foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
homeWeb3.eth.accounts.wallet.add(blockGenerator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(blockGenerator.privateKey)
|
||||
|
||||
function generateNewBlock(web3, address) {
|
||||
return web3.eth.sendTransaction({
|
||||
from: address,
|
||||
to: '0x0000000000000000000000000000000000000000',
|
||||
gasPrice: '1',
|
||||
gas: '21000',
|
||||
value: '1'
|
||||
})
|
||||
}
|
||||
|
||||
function main() {
|
||||
setTimeout(async () => {
|
||||
generateNewBlock(homeWeb3, blockGenerator.address)
|
||||
generateNewBlock(foreignWeb3, blockGenerator.address)
|
||||
try {
|
||||
generateNewBlock(homeWeb3, blockGenerator.address)
|
||||
} catch {} // in case of Transaction with the same hash was already imported.
|
||||
try {
|
||||
generateNewBlock(foreignWeb3, blockGenerator.address)
|
||||
} catch {} // in case of Transaction with the same hash was already imported.
|
||||
main()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ CONTRACTS_PATH="../../contracts"
|
||||
DEPLOY_PATH="$CONTRACTS_PATH/deploy"
|
||||
ENVS_PATH="../contracts-envs"
|
||||
|
||||
# mock bridge validators contract with the one with deterministic isValidatorDuty
|
||||
mv "$CONTRACTS_PATH/build/contracts/BridgeValidatorsDeterministic.json" "$CONTRACTS_PATH/build/contracts/BridgeValidators.json"
|
||||
|
||||
echo -e "\n\n############ Deploying native-to-erc ############\n"
|
||||
cp "$ENVS_PATH/native-to-erc.env" "$DEPLOY_PATH/.env"
|
||||
cd "$DEPLOY_PATH"
|
||||
@@ -29,3 +32,14 @@ cp "$ENVS_PATH/erc-to-native.env" "$DEPLOY_PATH/.env"
|
||||
cd "$DEPLOY_PATH"
|
||||
node deploy.js
|
||||
cd - > /dev/null
|
||||
|
||||
echo -e "\n\n############ Deploying amb ############\n"
|
||||
cp "$ENVS_PATH/amb.env" "$DEPLOY_PATH/.env"
|
||||
cd "$DEPLOY_PATH"
|
||||
node deploy.js
|
||||
cd - > /dev/null
|
||||
|
||||
echo -e "\n\n############ Deploying test contract for amb ############\n"
|
||||
cd "$DEPLOY_PATH"
|
||||
node src/utils/deployTestBox.js
|
||||
cd - > /dev/null
|
||||
|
||||
@@ -7,9 +7,33 @@ docker-compose build
|
||||
docker network create --driver bridge ultimate || true
|
||||
docker-compose up -d parity1 parity2 e2e
|
||||
|
||||
startValidator () {
|
||||
docker-compose $1 run -d --name $4 redis
|
||||
docker-compose $1 run -d --name $5 rabbit
|
||||
docker-compose $1 run $2 $3 -d oracle yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:transfer
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:half-duplex-transfer
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:swap-tokens
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:convert-to-chai
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:home
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:foreign
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
if [ "$1" == "oracle" ]; then
|
||||
docker-compose up -d redis rabbit oracle oracle-erc20 oracle-erc20-native
|
||||
docker-compose up -d redis rabbit oracle oracle-erc20 oracle-erc20-native oracle-amb
|
||||
|
||||
docker-compose run -d oracle yarn watcher:signature-request
|
||||
docker-compose run -d oracle yarn watcher:collected-signatures
|
||||
@@ -17,13 +41,35 @@ while [ "$1" != "" ]; do
|
||||
docker-compose run -d oracle-erc20 yarn watcher:signature-request
|
||||
docker-compose run -d oracle-erc20 yarn watcher:collected-signatures
|
||||
docker-compose run -d oracle-erc20 yarn watcher:affirmation-request
|
||||
docker-compose run -d oracle-erc20 yarn watcher:transfer
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:signature-request
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:collected-signatures
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:affirmation-request
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:transfer
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:half-duplex-transfer
|
||||
docker-compose run -d oracle-erc20-native yarn worker:swap-tokens
|
||||
docker-compose run -d oracle-erc20-native yarn worker:convert-to-chai
|
||||
docker-compose run -d oracle-amb yarn watcher:signature-request
|
||||
docker-compose run -d oracle-amb yarn watcher:collected-signatures
|
||||
docker-compose run -d oracle-amb yarn watcher:affirmation-request
|
||||
docker-compose run -d oracle yarn sender:home
|
||||
docker-compose run -d oracle yarn sender:foreign
|
||||
fi
|
||||
|
||||
if [ "$1" == "oracle-validator-2" ]; then
|
||||
oracle2name="-p validator2"
|
||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||
oracle2comp="-e ORACLE_QUEUE_URL=amqp://rabbit2 -e ORACLE_REDIS_URL=redis://redis2"
|
||||
startValidator "$oracle2name" "$oracle2Values" "$oracle2comp" "redis2" "rabbit2"
|
||||
fi
|
||||
|
||||
if [ "$1" == "oracle-validator-3" ]; then
|
||||
oracle3name="-p validator3"
|
||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||
oracle3comp="-e ORACLE_QUEUE_URL=amqp://rabbit3 -e ORACLE_REDIS_URL=redis://redis3"
|
||||
startValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3"
|
||||
fi
|
||||
|
||||
if [ "$1" == "ui" ]; then
|
||||
docker-compose up -d ui ui-erc20 ui-erc20-native
|
||||
|
||||
@@ -37,11 +83,11 @@ while [ "$1" != "" ]; do
|
||||
fi
|
||||
|
||||
if [ "$1" == "blocks" ]; then
|
||||
node ./scripts/blocks.js &
|
||||
docker-compose up -d blocks
|
||||
fi
|
||||
|
||||
if [ "$1" == "monitor" ]; then
|
||||
docker-compose up -d monitor monitor-erc20 monitor-erc20-native
|
||||
docker-compose up -d monitor monitor-erc20 monitor-erc20-native monitor-amb
|
||||
fi
|
||||
|
||||
if [ "$1" == "native-to-erc" ]; then
|
||||
@@ -56,5 +102,9 @@ while [ "$1" != "" ]; do
|
||||
../deployment-e2e/molecule.sh ultimate-erc-to-erc
|
||||
fi
|
||||
|
||||
if [ "$1" == "amb" ]; then
|
||||
../deployment-e2e/molecule.sh ultimate-amb
|
||||
fi
|
||||
|
||||
shift # Shift all the parameters down by one
|
||||
done
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
function generateNewBlock(web3, address) {
|
||||
return web3.eth.sendTransaction({
|
||||
from: address,
|
||||
to: '0x0000000000000000000000000000000000000000',
|
||||
gasPrice: '1',
|
||||
gas: '21000',
|
||||
value: '1'
|
||||
const promiseRetry = require('promise-retry')
|
||||
|
||||
async function uniformRetry(f) {
|
||||
return promiseRetry(f, {
|
||||
forever: true,
|
||||
factor: 1,
|
||||
minTimeout: 500
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateNewBlock
|
||||
async function sleep(timeout) {
|
||||
return new Promise(res => setTimeout(res, timeout))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
uniformRetry,
|
||||
sleep
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ while true; do
|
||||
docker-compose -f ../e2e-commons/docker-compose.yml exec monitor yarn check-all
|
||||
docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20 yarn check-all
|
||||
docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20-native yarn check-all
|
||||
docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-amb yarn check-all
|
||||
done
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cd $(dirname $0)
|
||||
|
||||
../e2e-commons/up.sh deploy monitor
|
||||
../e2e-commons/up.sh deploy blocks monitor
|
||||
|
||||
./wait-for-monitor.sh
|
||||
nohup ./periodically-check-all.sh < /dev/null > /dev/null 2>&1 &
|
||||
|
||||
128
monitor-e2e/test/amb.js
Normal file
128
monitor-e2e/test/amb.js
Normal file
@@ -0,0 +1,128 @@
|
||||
const assert = require('assert')
|
||||
const axios = require('axios')
|
||||
const { amb, user, foreignRPC, homeRPC, validator } = require('../../e2e-commons/constants.json')
|
||||
const { waitUntil, sendAMBMessage, addValidator } = require('../utils')
|
||||
|
||||
const baseUrl = amb.monitor
|
||||
|
||||
describe('AMB', () => {
|
||||
describe('balances', async () => {
|
||||
let data
|
||||
|
||||
before(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
})
|
||||
|
||||
describe('home', async () => {
|
||||
it('should contain toForeign:', () => assert(data.home.toForeign === 0))
|
||||
it('should contain fromForeign', () => assert(data.home.fromForeign === 0))
|
||||
})
|
||||
|
||||
describe('foreign', async () => {
|
||||
it('should contain fromHome:', () => assert(data.foreign.fromHome === 0))
|
||||
it('should contain toHome', () => assert(data.foreign.toHome === 0))
|
||||
})
|
||||
|
||||
describe('general', async () => {
|
||||
it('should contain fromHomeToForeignDiff', () => assert(data.fromHomeToForeignDiff === 0))
|
||||
it('should contain fromForeignToHomeDiff', () => assert(data.fromForeignToHomeDiff === 0))
|
||||
it('should contain lastChecked', () => assert(data.lastChecked >= 0))
|
||||
it('should contain timeDiff', () => assert(data.timeDiff >= 0))
|
||||
it('should contain lastChecked', () => assert(data.lastChecked >= 0))
|
||||
})
|
||||
})
|
||||
describe('validators', async () => {
|
||||
let data
|
||||
|
||||
before(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}/validators`))
|
||||
})
|
||||
|
||||
it('home', () => {
|
||||
assert(typeof data.home.validators === 'object')
|
||||
assert(data.home.validators[validator.address].balance > 0)
|
||||
assert(data.home.validators[validator.address].leftTx > 0)
|
||||
assert(data.home.validators[validator.address].gasPrice > 0)
|
||||
})
|
||||
|
||||
it('foreign', () => {
|
||||
assert(typeof data.foreign.validators === 'object')
|
||||
assert(data.foreign.validators[validator.address].balance > 0)
|
||||
assert(data.foreign.validators[validator.address].leftTx > 0)
|
||||
assert(data.foreign.validators[validator.address].gasPrice > 0)
|
||||
})
|
||||
|
||||
it('requiredSignaturesMatch', () => assert(data.requiredSignaturesMatch, 1))
|
||||
it('validatorsMatch', () => assert(data.validatorsMatch))
|
||||
it('lastChecked', () => assert(data.lastChecked >= 0))
|
||||
it('timeDiff', () => assert(data.timeDiff >= 0))
|
||||
it('homeOk', () => assert(data.homeOk))
|
||||
it('foreignOk', () => assert(data.foreignOk))
|
||||
it('ok', () => assert(data.ok))
|
||||
})
|
||||
describe('eventsStats', async () => {
|
||||
let data
|
||||
|
||||
before(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}/eventsStats`))
|
||||
})
|
||||
|
||||
it('ok', () => assert(data.ok))
|
||||
it('lastChecked', () => assert(data.lastChecked >= 0))
|
||||
it('timeDiff', () => assert(data.timeDiff >= 0))
|
||||
it('home-deliveredMsgNotProcessedInForeign', () =>
|
||||
assert(typeof data.home.deliveredMsgNotProcessedInForeign === 'object'))
|
||||
it('home-processedMsgNotDeliveredInForeign', () =>
|
||||
assert(typeof data.home.processedMsgNotDeliveredInForeign === 'object'))
|
||||
it('foreign-deliveredMsgNotProcessedInHome', () =>
|
||||
assert(typeof data.foreign.deliveredMsgNotProcessedInHome === 'object'))
|
||||
it('foreign-processedMsgNotDeliveredInHome', () =>
|
||||
assert(typeof data.foreign.processedMsgNotDeliveredInHome === 'object'))
|
||||
})
|
||||
describe('alerts', async () => {
|
||||
let data
|
||||
|
||||
before(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}/alerts`))
|
||||
})
|
||||
|
||||
it('ok', () => assert(data.ok))
|
||||
it('lastChecked', () => assert(data.lastChecked >= 0))
|
||||
it('timeDiff', () => assert(data.timeDiff >= 0))
|
||||
it('executeSignatures', () => assert(typeof data.executeSignatures === 'object'))
|
||||
it('executeAffirmations', () => assert(typeof data.executeAffirmations === 'object'))
|
||||
})
|
||||
describe('changing state of contracts', () => {
|
||||
let data
|
||||
|
||||
before(async () => {
|
||||
assert((await axios.get(`${baseUrl}/validators`)).data.validatorsMatch === true)
|
||||
})
|
||||
|
||||
it('should change fromForeignToHomeDiff', async () => {
|
||||
// send message
|
||||
await sendAMBMessage(foreignRPC.URL, user, amb.foreignBox, amb.foreign, amb.homeBox)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
return data.fromForeignToHomeDiff !== 0
|
||||
})
|
||||
})
|
||||
it('should change fromHomeToForeignDiff', async () => {
|
||||
// send message
|
||||
await sendAMBMessage(homeRPC.URL, user, amb.homeBox, amb.home, amb.foreignBox)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
return data.fromHomeToForeignDiff !== 0
|
||||
})
|
||||
})
|
||||
it('should change validatorsMatch', async () => {
|
||||
await addValidator(foreignRPC.URL, validator, amb.foreign)
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}/validators`))
|
||||
return data.validatorsMatch === false
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,15 @@
|
||||
const assert = require('assert')
|
||||
const axios = require('axios')
|
||||
const { ercToNativeBridge, user, foreignRPC, validator } = require('../../e2e-commons/constants.json')
|
||||
const { waitUntil, sendTokens, addValidator } = require('../utils')
|
||||
const {
|
||||
waitUntil,
|
||||
sendTokens,
|
||||
addValidator,
|
||||
initializeChaiToken,
|
||||
convertDaiToChai,
|
||||
setMinDaiTokenBalance,
|
||||
migrateToMCD
|
||||
} = require('../utils')
|
||||
|
||||
const baseUrl = ercToNativeBridge.monitor
|
||||
|
||||
@@ -24,12 +32,45 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
||||
assert((await axios.get(`${baseUrl}/validators`)).data.validatorsMatch === true)
|
||||
})
|
||||
|
||||
it('should change balanceDiff', async () => {
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign)
|
||||
it('should change balanceDiff', async function() {
|
||||
this.timeout(60000)
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.halfDuplexToken, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
return data.balanceDiff !== 0
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.01 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === undefined &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
})
|
||||
|
||||
await migrateToMCD(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.01 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0' &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
})
|
||||
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.halfDuplexToken, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.02 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,4 +81,52 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
||||
return data.validatorsMatch === false
|
||||
})
|
||||
})
|
||||
|
||||
it('should consider chai token balance', async function() {
|
||||
this.timeout(60000)
|
||||
await initializeChaiToken(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
erc20Balance === '0.02' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0' &&
|
||||
accumulatedInterest === '0.001' // value of dsrBalance() is initially defined in genesis block as 0.001
|
||||
)
|
||||
})
|
||||
|
||||
await setMinDaiTokenBalance(foreignRPC.URL, ercToNativeBridge.foreign, '0.01')
|
||||
await convertDaiToChai(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0.01' &&
|
||||
accumulatedInterest === '0.001'
|
||||
)
|
||||
})
|
||||
|
||||
await setMinDaiTokenBalance(foreignRPC.URL, ercToNativeBridge.foreign, '0.005')
|
||||
await convertDaiToChai(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
erc20Balance === '0.005' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0.015' &&
|
||||
accumulatedInterest === '0.001'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
const Web3 = require('web3')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, BRIDGE_VALIDATORS_ABI, FOREIGN_NATIVE_TO_ERC_ABI } = require('../commons')
|
||||
const {
|
||||
ERC677_BRIDGE_TOKEN_ABI,
|
||||
BRIDGE_VALIDATORS_ABI,
|
||||
FOREIGN_NATIVE_TO_ERC_ABI,
|
||||
FOREIGN_ERC_TO_NATIVE_ABI,
|
||||
BOX_ABI
|
||||
} = require('../commons')
|
||||
const { validator } = require('../e2e-commons/constants')
|
||||
|
||||
const waitUntil = async (predicate, step = 100, timeout = 10000) => {
|
||||
const waitUntil = async (predicate, step = 100, timeout = 20000) => {
|
||||
const stopTime = Date.now() + timeout
|
||||
while (Date.now() <= stopTime) {
|
||||
const result = await predicate()
|
||||
@@ -37,6 +44,17 @@ const sendTokens = async (rpcUrl, account, tokenAddress, recipientAddress) => {
|
||||
})
|
||||
}
|
||||
|
||||
const sendAMBMessage = async (rpcUrl, account, boxAddress, bridgeAddress, boxOtherSideAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(account.privateKey)
|
||||
const homeBox = new web3.eth.Contract(BOX_ABI, boxAddress)
|
||||
|
||||
await homeBox.methods.setValueOnOtherNetwork(3, bridgeAddress, boxOtherSideAddress).send({
|
||||
from: account.address,
|
||||
gas: '400000'
|
||||
})
|
||||
}
|
||||
|
||||
const addValidator = async (rpcUrl, account, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(account.privateKey)
|
||||
@@ -49,9 +67,57 @@ const addValidator = async (rpcUrl, account, bridgeAddress) => {
|
||||
})
|
||||
}
|
||||
|
||||
const migrateToMCD = async (rpcUrl, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
await bridgeContract.methods.migrateToMCD().send({
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
})
|
||||
}
|
||||
|
||||
const initializeChaiToken = async (rpcUrl, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
|
||||
await bridgeContract.methods.initializeChaiToken().send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
}
|
||||
|
||||
const setMinDaiTokenBalance = async (rpcUrl, bridgeAddress, limit) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
|
||||
await bridgeContract.methods.setMinDaiTokenBalance(web3.utils.toWei(limit)).send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
}
|
||||
|
||||
const convertDaiToChai = async (rpcUrl, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
|
||||
await bridgeContract.methods.convertDaiToChai().send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
waitUntil,
|
||||
sendEther,
|
||||
sendTokens,
|
||||
addValidator
|
||||
addValidator,
|
||||
sendAMBMessage,
|
||||
migrateToMCD,
|
||||
initializeChaiToken,
|
||||
setMinDaiTokenBalance,
|
||||
convertDaiToChai
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ FILES=(getBalances.json validators.json eventsStats.json alerts.json)
|
||||
check_files_exist() {
|
||||
rc=0
|
||||
for f in "${FILES[@]}"; do
|
||||
command="test -f responses/$f"
|
||||
command="test -f responses/bridge/$f"
|
||||
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor /bin/bash -c "$command") || rc=1
|
||||
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20 /bin/bash -c "$command") || rc=1
|
||||
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-erc20-native /bin/bash -c "$command") || rc=1
|
||||
(docker-compose -f ../e2e-commons/docker-compose.yml exec monitor-amb /bin/bash -c "$command") || rc=1
|
||||
done
|
||||
return $rc
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
|
||||
COMMON_HOME_RPC_URL=https://sokol.poa.network
|
||||
COMMON_FOREIGN_RPC_URL=https://kovan.infura.io/mew
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0xABb4C1399DcC28FBa3Beb76CAE2b50Be3e087353
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
"rules": {
|
||||
"no-use-before-define": "off",
|
||||
"node/no-unpublished-require": "off"
|
||||
},
|
||||
"env": {
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ RUN mv ./contracts/build ./ && rm -rf ./contracts/* ./contracts/.[!.]* && mv ./b
|
||||
COPY ./commons ./commons
|
||||
|
||||
COPY ./monitor ./monitor
|
||||
ARG DOT_ENV_PATH=./monitor/.env
|
||||
COPY ${DOT_ENV_PATH} ./monitor/.env
|
||||
|
||||
WORKDIR /mono/monitor
|
||||
CMD echo "To start the monitor run:" \
|
||||
"yarn check-and-start"
|
||||
CMD echo "To start the monitor web service run:" \
|
||||
"yarn start" \
|
||||
"To run monitor scripts run:" \
|
||||
"yarn check-all"
|
||||
|
||||
@@ -4,7 +4,12 @@ Tool for checking balances and unprocessed events in bridged networks.
|
||||
## Overview
|
||||
Please refer to the [POA TokenBridge](../README.md) overview first of all.
|
||||
|
||||
- Deployed version: https://bridge-monitoring.poa.net/
|
||||
- Deployed version serves several monitor configurations:
|
||||
* https://bridge-monitoring.poa.net/poa
|
||||
* https://bridge-monitoring.poa.net/xdai
|
||||
* https://bridge-monitoring.poa.net/wetc
|
||||
* https://bridge-monitoring.poa.net/amb-dai
|
||||
* https://bridge-monitoring.poa.net/amb-poa
|
||||
|
||||
This tool allows you to spin up a NODE.JS server to monitor for health of the TokenBridge contracts: check for the balance difference, discover inconsistency in the validators list, catch unhandled transactions.
|
||||
|
||||
@@ -122,14 +127,19 @@ yarn start
|
||||
You can run web interface via [pm2](https://www.npmjs.com/package/pm2) or similar supervisor program.
|
||||
|
||||
Using Docker:
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
* to run it very first time (or after changes related to the monitor code):
|
||||
```
|
||||
docker-compose -f docker-compose-build.yml -f docker-compose.yml up -d --build
|
||||
```
|
||||
* next time (or in case of usage of an official docker image)
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
- The application will run on `http://localhost:PORT`, where `PORT` is specified in your `.env` file.
|
||||
- The application will run on `http://localhost:MONITOR_PORT/MONITOR_BRIDGE_NAME`, where `MONITOR_PORT` and `MONITOR_BRIDGE_NAME` are specified in your `.env` file.
|
||||
- To enabled debug logging, set `DEBUG=1` variable in `.env`.
|
||||
|
||||
## Check balances of contracts and validators, get unprocessed events
|
||||
## Preparing statistic about balances of bridge contracts and validators, get unprocessed events
|
||||
|
||||
Using Yarn:
|
||||
```
|
||||
@@ -138,13 +148,32 @@ yarn check-all
|
||||
|
||||
Using Docker:
|
||||
```
|
||||
docker-compose exec monitor yarn check-all
|
||||
docker run --rm --env-file .env -v $(pwd)/responses:/mono/monitor/responses \
|
||||
poanetwork/tokenbridge-monitor:latest /bin/bash -c 'yarn check-all'
|
||||
```
|
||||
|
||||
As soon as the process finishes, use the URL described above to get the statistic.
|
||||
|
||||
### Cron
|
||||
|
||||
You can create cron job to run workers (see `crontab.example` for reference):
|
||||
|
||||
## Ad-hoc monitoring
|
||||
|
||||
There is a possibility to get bridge statistics without running the web interface use the commands provided above. In this case the results will be located in the `responses` directory.
|
||||
|
||||
## Build the image without running the monitor
|
||||
|
||||
To build the image change the directory:
|
||||
```
|
||||
cd monitor
|
||||
```
|
||||
|
||||
And run the docker composer:
|
||||
```
|
||||
docker-compose -f docker-compose-build.yml build
|
||||
```
|
||||
|
||||
## Linting
|
||||
|
||||
Running linter:
|
||||
|
||||
@@ -3,6 +3,8 @@ const Web3 = require('web3')
|
||||
const logger = require('./logger')('alerts')
|
||||
const eventsInfo = require('./utils/events')
|
||||
const { getBlockNumber } = require('./utils/contract')
|
||||
const { processedMsgNotDelivered, eventWithoutReference } = require('./utils/message')
|
||||
const { BRIDGE_MODES } = require('../commons')
|
||||
|
||||
const { COMMON_HOME_RPC_URL, COMMON_FOREIGN_RPC_URL } = process.env
|
||||
|
||||
@@ -13,11 +15,23 @@ const foreignProvider = new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)
|
||||
const web3Foreign = new Web3(foreignProvider)
|
||||
|
||||
async function main() {
|
||||
const { foreignDeposits, homeDeposits, homeWithdrawals, foreignWithdrawals } = await eventsInfo()
|
||||
|
||||
const xSignatures = foreignDeposits.filter(findDifferences(homeDeposits))
|
||||
const xAffirmations = homeWithdrawals.filter(findDifferences(foreignWithdrawals))
|
||||
const {
|
||||
homeToForeignRequests,
|
||||
homeToForeignConfirmations,
|
||||
foreignToHomeConfirmations,
|
||||
foreignToHomeRequests,
|
||||
bridgeMode
|
||||
} = await eventsInfo()
|
||||
|
||||
let xSignatures
|
||||
let xAffirmations
|
||||
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
xSignatures = homeToForeignConfirmations.filter(processedMsgNotDelivered(homeToForeignRequests))
|
||||
xAffirmations = foreignToHomeConfirmations.filter(processedMsgNotDelivered(foreignToHomeRequests))
|
||||
} else {
|
||||
xSignatures = homeToForeignConfirmations.filter(eventWithoutReference(homeToForeignRequests))
|
||||
xAffirmations = foreignToHomeConfirmations.filter(eventWithoutReference(foreignToHomeRequests))
|
||||
}
|
||||
logger.debug('building misbehavior blocks')
|
||||
const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
|
||||
|
||||
@@ -37,8 +51,8 @@ async function main() {
|
||||
const foreignValidators = await Promise.all(xSignatures.map(event => findTxSender(web3Foreign)(event)))
|
||||
const homeValidators = await Promise.all(xAffirmations.map(event => findTxSender(web3Home)(event)))
|
||||
|
||||
const xSignaturesTxs = xSignatures.map(normalizeEventInformation).reduce(buildTxList(foreignValidators), {})
|
||||
const xAffirmationsTxs = xAffirmations.map(normalizeEventInformation).reduce(buildTxList(homeValidators), {})
|
||||
const xSignaturesTxs = xSignatures.reduce(buildTxList(foreignValidators), {})
|
||||
const xAffirmationsTxs = xAffirmations.reduce(buildTxList(homeValidators), {})
|
||||
|
||||
logger.debug('Done')
|
||||
|
||||
@@ -135,7 +149,7 @@ const findTxSender = web3 => async ({ transactionHash }) => {
|
||||
* }}}
|
||||
*/
|
||||
const buildTxList = validatorsList => (acc, event, index) => {
|
||||
acc[event.txHash] = {
|
||||
acc[event.transactionHash] = {
|
||||
value: event.value,
|
||||
block: event.blockNumber,
|
||||
referenceTx: event.referenceTx,
|
||||
@@ -145,38 +159,4 @@ const buildTxList = validatorsList => (acc, event, index) => {
|
||||
return acc
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a missing destDeposit in src list if there's any
|
||||
* @param {Array} src
|
||||
* @returns {function(*=): boolean}
|
||||
*/
|
||||
const findDifferences = src => dest => {
|
||||
const b = normalizeEventInformation(dest)
|
||||
|
||||
return (
|
||||
src
|
||||
.map(normalizeEventInformation)
|
||||
.filter(a => a.referenceTx === b.referenceTx && a.recipient === b.recipient && a.value === b.value).length === 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the different event objects to facilitate data processing
|
||||
* @param {Object} event
|
||||
* @returns {{
|
||||
* txHash: string,
|
||||
* blockNumber: number,
|
||||
* referenceTx: string,
|
||||
* recipient: string | *,
|
||||
* value: *
|
||||
* }}
|
||||
*/
|
||||
const normalizeEventInformation = event => ({
|
||||
txHash: event.transactionHash,
|
||||
blockNumber: event.blockNumber,
|
||||
referenceTx: event.returnValues.transactionHash || event.transactionHash,
|
||||
recipient: event.returnValues.recipient || event.returnValues.from,
|
||||
value: event.returnValues.value
|
||||
})
|
||||
|
||||
module.exports = main
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Web3 = require('web3')
|
||||
const logger = require('./logger')('checkWorker')
|
||||
const { getBridgeMode } = require('../commons')
|
||||
const getBalances = require('./getBalances')
|
||||
const getShortEventStats = require('./getShortEventStats')
|
||||
const validators = require('./validators')
|
||||
const { writeFile, createDir } = require('./utils/file')
|
||||
|
||||
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL, MONITOR_BRIDGE_NAME } = process.env
|
||||
|
||||
const MONITOR_VALIDATOR_HOME_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_HOME_TX_LIMIT) || 0
|
||||
const MONITOR_VALIDATOR_FOREIGN_TX_LIMIT = Number(process.env.MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) || 0
|
||||
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
|
||||
|
||||
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL } = process.env
|
||||
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
|
||||
const web3Home = new Web3(homeProvider)
|
||||
|
||||
@@ -15,6 +19,7 @@ const { HOME_ERC_TO_ERC_ABI } = require('../commons')
|
||||
|
||||
async function checkWorker() {
|
||||
try {
|
||||
createDir(`/responses/${MONITOR_BRIDGE_NAME}`)
|
||||
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const bridgeMode = await getBridgeMode(homeBridge)
|
||||
logger.debug('Bridge mode:', bridgeMode)
|
||||
@@ -26,12 +31,35 @@ async function checkWorker() {
|
||||
const foreign = Object.assign({}, balances.foreign, events.foreign)
|
||||
const status = Object.assign({}, balances, events, { home }, { foreign })
|
||||
if (!status) throw new Error('status is empty: ' + JSON.stringify(status))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/getBalances.json'), JSON.stringify(status, null, 4))
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/getBalances.json`, status)
|
||||
|
||||
logger.debug('calling validators()')
|
||||
const vBalances = await validators(bridgeMode)
|
||||
if (!vBalances) throw new Error('vBalances is empty: ' + JSON.stringify(vBalances))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/validators.json'), JSON.stringify(vBalances, null, 4))
|
||||
|
||||
vBalances.homeOk = true
|
||||
vBalances.foreignOk = true
|
||||
|
||||
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
|
||||
for (const hv in vBalances.home.validators) {
|
||||
if (vBalances.home.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
|
||||
vBalances.homeOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
|
||||
for (const hv in vBalances.foreign.validators) {
|
||||
if (vBalances.foreign.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
|
||||
vBalances.foreignOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vBalances.ok = vBalances.homeOk && vBalances.foreignOk
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/validators.json`, vBalances)
|
||||
logger.debug('Done')
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const logger = require('./logger')('checkWorker2')
|
||||
const eventsStats = require('./eventsStats')
|
||||
const alerts = require('./alerts')
|
||||
const { writeFile, createDir } = require('./utils/file')
|
||||
|
||||
const { MONITOR_BRIDGE_NAME } = process.env
|
||||
|
||||
async function checkWorker2() {
|
||||
try {
|
||||
createDir(`/responses/${MONITOR_BRIDGE_NAME}`)
|
||||
logger.debug('calling eventsStats()')
|
||||
const evStats = await eventsStats()
|
||||
if (!evStats) throw new Error('evStats is empty: ' + JSON.stringify(evStats))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/eventsStats.json'), JSON.stringify(evStats, null, 4))
|
||||
evStats.ok =
|
||||
(evStats.onlyInHomeDeposits || evStats.home.deliveredMsgNotProcessedInForeign).length === 0 &&
|
||||
(evStats.onlyInForeignDeposits || evStats.home.processedMsgNotDeliveredInForeign).length === 0 &&
|
||||
(evStats.onlyInHomeWithdrawals || evStats.foreign.deliveredMsgNotProcessedInHome).length === 0 &&
|
||||
(evStats.onlyInForeignWithdrawals || evStats.foreign.processedMsgNotDeliveredInHome).length === 0
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/eventsStats.json`, evStats)
|
||||
|
||||
logger.debug('calling alerts()')
|
||||
const _alerts = await alerts()
|
||||
if (!_alerts) throw new Error('alerts is empty: ' + JSON.stringify(_alerts))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/alerts.json'), JSON.stringify(_alerts, null, 4))
|
||||
_alerts.ok = !_alerts.executeAffirmations.mostRecentTxHash && !_alerts.executeSignatures.mostRecentTxHash
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/alerts.json`, _alerts)
|
||||
logger.debug('Done x2')
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Web3 = require('web3')
|
||||
const logger = require('./logger')('checkWorker3')
|
||||
const stuckTransfers = require('./stuckTransfers')
|
||||
const { writeFile, createDir } = require('./utils/file')
|
||||
|
||||
const { MONITOR_BRIDGE_NAME, COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_RPC_URL } = process.env
|
||||
const { getBridgeMode, HOME_NATIVE_TO_ERC_ABI, BRIDGE_MODES } = require('../commons')
|
||||
|
||||
const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL)
|
||||
const web3Home = new Web3(homeProvider)
|
||||
|
||||
async function checkWorker3() {
|
||||
try {
|
||||
logger.debug('calling stuckTransfers()')
|
||||
const transfers = await stuckTransfers()
|
||||
// console.log(transfers)
|
||||
if (!transfers) throw new Error('transfers is empty: ' + JSON.stringify(transfers))
|
||||
fs.writeFileSync(path.join(__dirname, '/responses/stuckTransfers.json'), JSON.stringify(transfers, null, 4))
|
||||
logger.debug('Done')
|
||||
const homeBridge = new web3Home.eth.Contract(HOME_NATIVE_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const bridgeMode = await getBridgeMode(homeBridge)
|
||||
if (bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC_V1) {
|
||||
createDir(`/responses/${MONITOR_BRIDGE_NAME}`)
|
||||
logger.debug('calling stuckTransfers()')
|
||||
const transfers = await stuckTransfers()
|
||||
if (!transfers) throw new Error('transfers is empty: ' + JSON.stringify(transfers))
|
||||
transfers.ok = transfers.total.length === 0
|
||||
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/stuckTransfers.json`, transfers)
|
||||
logger.debug('Done')
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('checkWorker3.js', e)
|
||||
}
|
||||
|
||||
0
monitor/configs/.gitkeep
Normal file
0
monitor/configs/.gitkeep
Normal file
@@ -1,5 +1,5 @@
|
||||
# Yarn:
|
||||
*/4 * * * * cd $HOME/bridge-monitor; yarn check-all >>cronWorker.out 2>>cronWorker.err
|
||||
*/4 * * * * cd $HOME/tokenbridge/monitor; yarn check-all >>cronWorker.out 2>>cronWorker.err
|
||||
|
||||
# Docker:
|
||||
*/4 * * * * cd $HOME/bridge-monitor; docker-compose exec monitor yarn check-all >>cronWorker.out 2>>cronWorker.err
|
||||
*/4 * * * * cd $HOME/tokenbridge/monitor; docker run --rm --env-file .env -v $(pwd)/responses:/mono/monitor/responses poanetwork/tokenbridge-monitor:latest /bin/bash -c 'yarn check-all' >>cronWorker.out 2>>cronWorker.err
|
||||
|
||||
6
monitor/docker-compose-build.yml
Normal file
6
monitor/docker-compose-build.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
version: '2.4'
|
||||
services:
|
||||
monitor:
|
||||
image: poanetwork/tokenbridge-monitor
|
||||
build: .
|
||||
@@ -2,13 +2,13 @@
|
||||
version: '2.4'
|
||||
services:
|
||||
monitor:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: monitor/Dockerfile
|
||||
image: poanetwork/tokenbridge-monitor:latest
|
||||
ports:
|
||||
- "${MONITOR_PORT}:${MONITOR_PORT}"
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
volumes:
|
||||
- ./responses:/mono/monitor/responses
|
||||
restart: unless-stopped
|
||||
entrypoint: "yarn check-and-start"
|
||||
entrypoint: "yarn start"
|
||||
|
||||
@@ -1,81 +1,51 @@
|
||||
require('dotenv').config()
|
||||
const logger = require('./logger')('eventsStats')
|
||||
const eventsInfo = require('./utils/events')
|
||||
|
||||
function compareDepositsHome(foreign) {
|
||||
return homeDeposit => {
|
||||
return (
|
||||
foreign.filter(foreignDeposit => {
|
||||
return (
|
||||
foreignDeposit.returnValues.transactionHash === homeDeposit.transactionHash &&
|
||||
foreignDeposit.returnValues.recipient === homeDeposit.returnValues.recipient &&
|
||||
foreignDeposit.returnValues.value === homeDeposit.returnValues.value
|
||||
)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
function compareDepositsForeign(home) {
|
||||
return foreignDeposit => {
|
||||
return (
|
||||
home.filter(homeDeposit => {
|
||||
return (
|
||||
homeDeposit.transactionHash === foreignDeposit.returnValues.transactionHash &&
|
||||
homeDeposit.returnValues.recipient === foreignDeposit.returnValues.recipient &&
|
||||
homeDeposit.returnValues.value === foreignDeposit.returnValues.value
|
||||
)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function compareTransferHome(foreign) {
|
||||
return homeDeposit => {
|
||||
return (
|
||||
foreign.filter(foreignDeposit => {
|
||||
return (
|
||||
homeDeposit.returnValues.transactionHash === foreignDeposit.transactionHash &&
|
||||
homeDeposit.returnValues.recipient === foreignDeposit.returnValues.from &&
|
||||
homeDeposit.returnValues.value === foreignDeposit.returnValues.value
|
||||
)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
function compareTransferForeign(home) {
|
||||
return foreignDeposit => {
|
||||
return (
|
||||
home.filter(homeDeposit => {
|
||||
return (
|
||||
foreignDeposit.transactionHash === homeDeposit.returnValues.transactionHash &&
|
||||
foreignDeposit.returnValues.from === homeDeposit.returnValues.recipient &&
|
||||
foreignDeposit.returnValues.value === homeDeposit.returnValues.value
|
||||
)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
const { processedMsgNotDelivered, deliveredMsgNotProcessed, eventWithoutReference } = require('./utils/message')
|
||||
const { BRIDGE_MODES } = require('../commons')
|
||||
|
||||
async function main() {
|
||||
const { foreignDeposits, homeDeposits, homeWithdrawals, foreignWithdrawals, isExternalErc20 } = await eventsInfo()
|
||||
const {
|
||||
homeToForeignRequests,
|
||||
homeToForeignConfirmations,
|
||||
foreignToHomeConfirmations,
|
||||
foreignToHomeRequests,
|
||||
bridgeMode
|
||||
} = await eventsInfo()
|
||||
|
||||
const onlyInHomeDeposits = homeDeposits.filter(compareDepositsHome(foreignDeposits))
|
||||
const onlyInForeignDeposits = foreignDeposits.concat([]).filter(compareDepositsForeign(homeDeposits))
|
||||
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
return {
|
||||
home: {
|
||||
deliveredMsgNotProcessedInForeign: homeToForeignRequests.filter(
|
||||
deliveredMsgNotProcessed(homeToForeignConfirmations)
|
||||
),
|
||||
processedMsgNotDeliveredInForeign: foreignToHomeConfirmations.filter(
|
||||
processedMsgNotDelivered(foreignToHomeRequests)
|
||||
)
|
||||
},
|
||||
foreign: {
|
||||
deliveredMsgNotProcessedInHome: foreignToHomeRequests.filter(
|
||||
deliveredMsgNotProcessed(foreignToHomeConfirmations)
|
||||
),
|
||||
processedMsgNotDeliveredInHome: homeToForeignConfirmations.filter(
|
||||
processedMsgNotDelivered(homeToForeignRequests)
|
||||
)
|
||||
},
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
} else {
|
||||
const onlyInHomeDeposits = homeToForeignRequests.filter(eventWithoutReference(homeToForeignConfirmations))
|
||||
const onlyInForeignDeposits = homeToForeignConfirmations.filter(eventWithoutReference(homeToForeignRequests))
|
||||
|
||||
const onlyInHomeWithdrawals = isExternalErc20
|
||||
? homeWithdrawals.filter(compareTransferHome(foreignWithdrawals))
|
||||
: homeWithdrawals.filter(compareDepositsForeign(foreignWithdrawals))
|
||||
const onlyInForeignWithdrawals = isExternalErc20
|
||||
? foreignWithdrawals.filter(compareTransferForeign(homeWithdrawals))
|
||||
: foreignWithdrawals.filter(compareDepositsHome(homeWithdrawals))
|
||||
const onlyInHomeWithdrawals = foreignToHomeConfirmations.filter(eventWithoutReference(foreignToHomeRequests))
|
||||
const onlyInForeignWithdrawals = foreignToHomeRequests.filter(eventWithoutReference(foreignToHomeConfirmations))
|
||||
|
||||
logger.debug('Done')
|
||||
return {
|
||||
onlyInHomeDeposits,
|
||||
onlyInForeignDeposits,
|
||||
onlyInHomeWithdrawals,
|
||||
onlyInForeignWithdrawals,
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
return {
|
||||
onlyInHomeDeposits,
|
||||
onlyInForeignDeposits,
|
||||
onlyInHomeWithdrawals,
|
||||
onlyInForeignWithdrawals,
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,44 @@ async function main(bridgeMode) {
|
||||
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const erc20Address = await foreignBridge.methods.erc20token().call()
|
||||
const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
|
||||
let investedAmountInDai = 0
|
||||
let bridgeDsrBalance = 0
|
||||
let foreignHalfDuplexErc20Balance = 0
|
||||
let displayChaiToken = false
|
||||
let displayHalfDuplexToken = false
|
||||
let tokenSwapAllowed = false
|
||||
try {
|
||||
const halfDuplexTokenAddress = await foreignBridge.methods.halfDuplexErc20token().call()
|
||||
if (halfDuplexTokenAddress !== erc20Address) {
|
||||
const halfDuplexToken = new web3Foreign.eth.Contract(ERC20_ABI, halfDuplexTokenAddress)
|
||||
logger.debug('calling halfDuplexToken.methods.balanceOf')
|
||||
foreignHalfDuplexErc20Balance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
logger.debug('getting last block numbers')
|
||||
const block = await web3Foreign.eth.getBlock('latest')
|
||||
|
||||
logger.debug(`Checking if SCD Emergency Shutdown has happened`)
|
||||
tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call()
|
||||
displayHalfDuplexToken = true
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug('Methods for half duplex token are not present')
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug('calling foreignBridge.methods.isChaiTokenEnabled')
|
||||
if (await foreignBridge.methods.isChaiTokenEnabled().call()) {
|
||||
displayChaiToken = true
|
||||
logger.debug('calling foreignBridge.methods.investedAmountInDai')
|
||||
investedAmountInDai = await foreignBridge.methods.investedAmountInDai().call()
|
||||
logger.debug('calling foreignBridge.methods.dsrBalance')
|
||||
bridgeDsrBalance = await foreignBridge.methods.dsrBalance().call()
|
||||
} else {
|
||||
logger.debug('Chai token is currently disabled')
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug('Methods for chai token are not present')
|
||||
}
|
||||
|
||||
logger.debug('calling erc20Contract.methods.balanceOf')
|
||||
const foreignErc20Balance = await erc20Contract.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
@@ -91,7 +129,7 @@ async function main(bridgeMode) {
|
||||
const blockRewardAddress = await homeBridge.methods.blockRewardContract().call()
|
||||
const blockRewardContract = new web3Home.eth.Contract(BLOCK_REWARD_ABI, blockRewardAddress)
|
||||
logger.debug('calling blockReward.methods.mintedTotally')
|
||||
const mintedCoins = await blockRewardContract.methods.mintedTotally().call()
|
||||
const mintedCoins = await blockRewardContract.methods.mintedTotallyByBridge(COMMON_HOME_BRIDGE_ADDRESS).call()
|
||||
logger.debug('calling homeBridge.methods.totalBurntCoins')
|
||||
const burntCoins = await homeBridge.methods.totalBurntCoins().call()
|
||||
|
||||
@@ -99,19 +137,53 @@ async function main(bridgeMode) {
|
||||
const burntCoinsBN = new BN(burntCoins)
|
||||
const totalSupplyBN = mintedCoinsBN.minus(burntCoinsBN)
|
||||
const foreignErc20BalanceBN = new BN(foreignErc20Balance)
|
||||
const investedAmountInDaiBN = new BN(investedAmountInDai)
|
||||
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)
|
||||
const halfDuplexErc20BalanceBN =
|
||||
displayHalfDuplexToken && tokenSwapAllowed ? new BN(foreignHalfDuplexErc20Balance) : new BN(0)
|
||||
|
||||
const diff = foreignErc20BalanceBN
|
||||
.plus(halfDuplexErc20BalanceBN)
|
||||
.plus(investedAmountInDaiBN)
|
||||
.minus(totalSupplyBN)
|
||||
.toFixed()
|
||||
|
||||
let foreign = {
|
||||
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
|
||||
}
|
||||
|
||||
if (displayHalfDuplexToken && tokenSwapAllowed) {
|
||||
foreign = {
|
||||
...foreign,
|
||||
halfDuplexErc20Balance: Web3Utils.fromWei(foreignHalfDuplexErc20Balance)
|
||||
}
|
||||
} else if (displayHalfDuplexToken && !tokenSwapAllowed) {
|
||||
foreign = {
|
||||
...foreign,
|
||||
halfDuplexErc20BalanceAfterES: Web3Utils.fromWei(foreignHalfDuplexErc20Balance)
|
||||
}
|
||||
}
|
||||
|
||||
if (displayChaiToken) {
|
||||
foreign.investedErc20Balance = Web3Utils.fromWei(investedAmountInDai)
|
||||
foreign.accumulatedInterest = Web3Utils.fromWei(bridgeDsrBalanceBN.minus(investedAmountInDaiBN).toString(10))
|
||||
}
|
||||
|
||||
const diff = foreignErc20BalanceBN.minus(totalSupplyBN).toFixed()
|
||||
logger.debug('Done')
|
||||
return {
|
||||
home: {
|
||||
totalSupply: Web3Utils.fromWei(totalSupplyBN.toFixed())
|
||||
},
|
||||
foreign: {
|
||||
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
|
||||
},
|
||||
foreign,
|
||||
balanceDiff: Number(Web3Utils.fromWei(diff)),
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
} else if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
return {
|
||||
home: {},
|
||||
foreign: {},
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unrecognized bridge mode: '${bridgeMode}'`)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,40 @@
|
||||
require('dotenv').config()
|
||||
const eventsInfo = require('./utils/events')
|
||||
const { BRIDGE_MODES } = require('../commons')
|
||||
|
||||
async function main(bridgeMode) {
|
||||
const { foreignDeposits, homeDeposits, homeWithdrawals, foreignWithdrawals } = await eventsInfo(bridgeMode)
|
||||
const {
|
||||
homeToForeignConfirmations,
|
||||
homeToForeignRequests,
|
||||
foreignToHomeConfirmations,
|
||||
foreignToHomeRequests
|
||||
} = await eventsInfo(bridgeMode)
|
||||
|
||||
return {
|
||||
depositsDiff: homeDeposits.length - foreignDeposits.length,
|
||||
withdrawalDiff: homeWithdrawals.length - foreignWithdrawals.length,
|
||||
home: {
|
||||
deposits: homeDeposits.length,
|
||||
withdrawals: homeWithdrawals.length
|
||||
},
|
||||
foreign: {
|
||||
deposits: foreignDeposits.length,
|
||||
withdrawals: foreignWithdrawals.length
|
||||
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
return {
|
||||
fromHomeToForeignDiff: homeToForeignRequests.length - homeToForeignConfirmations.length,
|
||||
fromForeignToHomeDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length,
|
||||
home: {
|
||||
toForeign: homeToForeignRequests.length,
|
||||
fromForeign: foreignToHomeConfirmations.length
|
||||
},
|
||||
foreign: {
|
||||
fromHome: homeToForeignConfirmations.length,
|
||||
toHome: foreignToHomeRequests.length
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
depositsDiff: homeToForeignRequests.length - homeToForeignConfirmations.length,
|
||||
withdrawalDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length,
|
||||
home: {
|
||||
deposits: homeToForeignRequests.length,
|
||||
withdrawals: foreignToHomeConfirmations.length
|
||||
},
|
||||
foreign: {
|
||||
deposits: homeToForeignConfirmations.length,
|
||||
withdrawals: foreignToHomeRequests.length
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,16 @@
|
||||
require('dotenv').config()
|
||||
const express = require('express')
|
||||
const fs = require('fs')
|
||||
const { isV1Bridge } = require('./utils/serverUtils')
|
||||
const { readFile } = require('./utils/file')
|
||||
|
||||
const app = express()
|
||||
const bridgeRouter = express.Router({ mergeParams: true })
|
||||
|
||||
const MONITOR_TX_NUMBER_THRESHOLD = Number(process.env.MONITOR_TX_NUMBER_THRESHOLD) || 100
|
||||
console.log('MONITOR_TX_NUMBER_THRESHOLD = ' + MONITOR_TX_NUMBER_THRESHOLD)
|
||||
app.get('/favicon.ico', (req, res) => res.sendStatus(204))
|
||||
app.use('/:bridgeName', bridgeRouter)
|
||||
|
||||
async function readFile(path) {
|
||||
bridgeRouter.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const content = await fs.readFileSync(path)
|
||||
const json = JSON.parse(content)
|
||||
const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked
|
||||
return Object.assign({}, json, { timeDiff })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return {
|
||||
error: 'please check your worker'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initV1routes(app) {
|
||||
const exposeV1Routes = await isV1Bridge()
|
||||
if (exposeV1Routes) {
|
||||
app.get('/stuckTransfers', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/stuckTransfers.json')
|
||||
results.ok = results.total.length === 0
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/getBalances.json')
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/getBalances.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
// this will eventually be handled by your error handling middleware
|
||||
@@ -47,24 +18,9 @@ app.get('/', async (req, res, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/validators', async (req, res, next) => {
|
||||
bridgeRouter.get('/validators', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/validators.json')
|
||||
results.homeOk = true
|
||||
results.foreignOk = true
|
||||
for (const hv in results.home.validators) {
|
||||
if (results.home.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
|
||||
results.homeOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
for (const hv in results.foreign.validators) {
|
||||
if (results.foreign.validators[hv].leftTx < MONITOR_TX_NUMBER_THRESHOLD) {
|
||||
results.foreignOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
results.ok = results.homeOk && results.foreignOk
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/validators.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
// this will eventually be handled by your error handling middleware
|
||||
@@ -72,15 +28,9 @@ app.get('/validators', async (req, res, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
// responses/eventsStats.json
|
||||
app.get('/eventsStats', async (req, res, next) => {
|
||||
bridgeRouter.get('/eventsStats', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/eventsStats.json')
|
||||
results.ok =
|
||||
results.onlyInHomeDeposits.length === 0 &&
|
||||
results.onlyInForeignDeposits.length === 0 &&
|
||||
results.onlyInHomeWithdrawals.length === 0 &&
|
||||
results.onlyInForeignWithdrawals.length === 0
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/eventsStats.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
// this will eventually be handled by your error handling middleware
|
||||
@@ -88,17 +38,23 @@ app.get('/eventsStats', async (req, res, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/alerts', async (req, res, next) => {
|
||||
bridgeRouter.get('/alerts', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile('./responses/alerts.json')
|
||||
results.ok = !results.executeAffirmations.mostRecentTxHash && !results.executeSignatures.mostRecentTxHash
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/alerts.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
|
||||
initV1routes(app)
|
||||
bridgeRouter.get('/stuckTransfers', async (req, res, next) => {
|
||||
try {
|
||||
const results = await readFile(`./responses/${req.params.bridgeName}/stuckTransfers.json`)
|
||||
res.json(results)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
|
||||
const port = process.env.MONITOR_PORT || 3003
|
||||
app.set('port', port)
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"check-all": "timeout -s 9 5m node checkWorker.js && timeout -s 9 5m node checkWorker2.js",
|
||||
"check-all": "timeout -s 9 5m node checkWorker.js && timeout -s 9 5m node checkWorker2.js && timeout -s 9 5m node checkWorker3.js",
|
||||
"start": "node index.js",
|
||||
"check-and-start": "yarn check-all && yarn start",
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
"lint:fix": "eslint . --fix"
|
||||
"lint:fix": "eslint . --fix",
|
||||
"test": "NODE_ENV=test mocha"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
@@ -23,5 +24,6 @@
|
||||
"node": ">=8.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
cd $(dirname $0)/..
|
||||
|
||||
if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
|
||||
# https://github.com/docker/compose/issues/3352
|
||||
COMPOSE_INTERACTIVE_NO_CLI=1 /usr/local/bin/docker-compose exec -T monitor /bin/bash -c 'yarn check-all'
|
||||
else
|
||||
echo "Monitor is not running, skipping checks."
|
||||
fi
|
||||
11
monitor/scripts/getBridgeStats.sh
Executable file
11
monitor/scripts/getBridgeStats.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
cd $(dirname $0)/..
|
||||
|
||||
if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
|
||||
for file in configs/*.env
|
||||
do
|
||||
docker run --rm --env-file $file -v $(pwd)/responses:/mono/monitor/responses poanetwork/tokenbridge-monitor:latest /bin/bash -c 'yarn check-all'
|
||||
done
|
||||
else
|
||||
echo "Monitor is not running, skipping checks."
|
||||
fi
|
||||
206
monitor/test/message.test.js
Normal file
206
monitor/test/message.test.js
Normal file
@@ -0,0 +1,206 @@
|
||||
const { expect } = require('chai')
|
||||
const { normalizeEventInformation, eventWithoutReference } = require('../utils/message')
|
||||
|
||||
describe('normalizeEventInformation', () => {
|
||||
it('should return normalized object for UserRequestForSignature event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6',
|
||||
blockNumber: 324231,
|
||||
transactionHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
returnValues: {
|
||||
recipient: '0xA84944735B66e957Fe385567dcc85975022Fe68A',
|
||||
value: '100000000000000000000'
|
||||
},
|
||||
event: 'UserRequestForSignature'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.recipient)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
it('should return normalized object for UserRequestForAffirmation event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6',
|
||||
blockNumber: 324231,
|
||||
transactionHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
returnValues: {
|
||||
recipient: '0xA84944735B66e957Fe385567dcc85975022Fe68A',
|
||||
value: '100000000000000000000'
|
||||
},
|
||||
event: 'UserRequestForAffirmation'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.recipient)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
it('should return normalized object for transfer event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
|
||||
blockNumber: 6593953,
|
||||
transactionHash: '0x05afb402e27946d3600b100020dc23419ffd10cb61d3b241cee7b4a84909b48a',
|
||||
returnValues: {
|
||||
from: '0x13C0a8009A578837fB7A80Aa252F6A3ba4aD6B79',
|
||||
to: '0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016',
|
||||
value: '4000000000000000000'
|
||||
},
|
||||
event: 'Transfer'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.from)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
it('should return normalized object for RelayedMessage event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016',
|
||||
blockNumber: 7025826,
|
||||
transactionHash: '0x6ee5969973da763d6d9f162d2dd1b1ec34c2dd977dc39e6b25030b4f04471567',
|
||||
returnValues: {
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '4900000000000000000',
|
||||
transactionHash: '0x5c5c2ab5e333bda4acd035a6a30ea29c7370351891d85373b2d06c7cc6cbb210'
|
||||
},
|
||||
event: 'RelayedMessage'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.returnValues.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.recipient)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
it('should return normalized object for AffirmationCompleted event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6',
|
||||
blockNumber: 474439,
|
||||
transactionHash: '0x654004b372ba32754cef34f403153bbdf43f0fbb3191d5e4683bba7f32e0dc4a',
|
||||
|
||||
returnValues: {
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '5000000000000000000',
|
||||
transactionHash: '0xe96da94bbda2cfc865acd3f98040f5c79a627ee9de839d86885d34acd8ecd10d'
|
||||
},
|
||||
event: 'AffirmationCompleted'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.returnValues.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.recipient)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
})
|
||||
describe('eventWithoutReference', () => {
|
||||
it('should return false if event is present', () => {
|
||||
// Given
|
||||
const event = {
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '5000000000000000000'
|
||||
}
|
||||
|
||||
const otherSideEvents = [
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '5000000000000000000'
|
||||
},
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '6000000000000000000'
|
||||
},
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '8000000000000000000'
|
||||
}
|
||||
]
|
||||
|
||||
// When
|
||||
const result = eventWithoutReference(otherSideEvents)(event)
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(false)
|
||||
})
|
||||
it('should return true if event is not present', () => {
|
||||
// Given
|
||||
const event = {
|
||||
txHash: '0xe96da94bbda2cfc865acd3f98040f5c79a627ee9de839d86885d34acd8ecd10d',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0xe96da94bbda2cfc865acd3f98040f5c79a627ee9de839d86885d34acd8ecd10d',
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '2000000000000000000'
|
||||
}
|
||||
|
||||
const otherSideEvents = [
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '5000000000000000000'
|
||||
},
|
||||
{
|
||||
txHash: '0x05afb402e27946d3600b100020dc23419ffd10cb61d3b241cee7b4a84909b48a',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x05afb402e27946d3600b100020dc23419ffd10cb61d3b241cee7b4a84909b48a',
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '6000000000000000000'
|
||||
},
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '8000000000000000000'
|
||||
}
|
||||
]
|
||||
|
||||
// When
|
||||
const result = eventWithoutReference(otherSideEvents)(event)
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(true)
|
||||
})
|
||||
})
|
||||
@@ -11,8 +11,11 @@ const {
|
||||
ERC20_ABI,
|
||||
ERC677_BRIDGE_TOKEN_ABI,
|
||||
getTokenType,
|
||||
getPastEvents
|
||||
getPastEvents,
|
||||
ZERO_ADDRESS
|
||||
} = require('../../commons')
|
||||
const { normalizeEventInformation } = require('./message')
|
||||
const { filterTransferBeforeES } = require('./tokenUtils')
|
||||
|
||||
const {
|
||||
COMMON_HOME_RPC_URL,
|
||||
@@ -38,61 +41,146 @@ async function main(mode) {
|
||||
const homeBridge = new web3Home.eth.Contract(HOME_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const v1Bridge = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC_V1
|
||||
const erc20MethodName = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC || v1Bridge ? 'erc677token' : 'erc20token'
|
||||
const erc20Address = await foreignBridge.methods[erc20MethodName]().call()
|
||||
const tokenType = await getTokenType(
|
||||
new web3Foreign.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, erc20Address),
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS
|
||||
)
|
||||
const isExternalErc20 = tokenType === ERC_TYPES.ERC20
|
||||
const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
|
||||
let isExternalErc20
|
||||
let erc20Contract
|
||||
let erc20Address
|
||||
let normalizeEvent = normalizeEventInformation
|
||||
if (bridgeMode !== BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
const erc20MethodName = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC || v1Bridge ? 'erc677token' : 'erc20token'
|
||||
erc20Address = await foreignBridge.methods[erc20MethodName]().call()
|
||||
const tokenType = await getTokenType(
|
||||
new web3Foreign.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, erc20Address),
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS
|
||||
)
|
||||
isExternalErc20 = tokenType === ERC_TYPES.ERC20
|
||||
erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
|
||||
} else {
|
||||
normalizeEvent = e => e
|
||||
}
|
||||
|
||||
logger.debug('getting last block numbers')
|
||||
const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
|
||||
|
||||
logger.debug("calling homeBridge.getPastEvents('UserRequestForSignature')")
|
||||
const homeDeposits = await getPastEvents(homeBridge, {
|
||||
const homeToForeignRequests = (await getPastEvents(homeBridge, {
|
||||
event: v1Bridge ? 'Deposit' : 'UserRequestForSignature',
|
||||
fromBlock: MONITOR_HOME_START_BLOCK,
|
||||
toBlock: homeBlockNumber
|
||||
})
|
||||
})).map(normalizeEvent)
|
||||
|
||||
logger.debug("calling foreignBridge.getPastEvents('RelayedMessage')")
|
||||
const foreignDeposits = await getPastEvents(foreignBridge, {
|
||||
const homeToForeignConfirmations = (await getPastEvents(foreignBridge, {
|
||||
event: v1Bridge ? 'Deposit' : 'RelayedMessage',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber
|
||||
})
|
||||
})).map(normalizeEvent)
|
||||
|
||||
logger.debug("calling homeBridge.getPastEvents('AffirmationCompleted')")
|
||||
const homeWithdrawals = await getPastEvents(homeBridge, {
|
||||
const foreignToHomeConfirmations = (await getPastEvents(homeBridge, {
|
||||
event: v1Bridge ? 'Withdraw' : 'AffirmationCompleted',
|
||||
fromBlock: MONITOR_HOME_START_BLOCK,
|
||||
toBlock: homeBlockNumber
|
||||
})
|
||||
})).map(normalizeEvent)
|
||||
|
||||
logger.debug("calling foreignBridge.getPastEvents('UserRequestForAffirmation')")
|
||||
const foreignWithdrawals = isExternalErc20
|
||||
? await getPastEvents(erc20Contract, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber,
|
||||
options: {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
}
|
||||
})
|
||||
: await getPastEvents(foreignBridge, {
|
||||
event: v1Bridge ? 'Withdraw' : 'UserRequestForAffirmation',
|
||||
let foreignToHomeRequests = (await getPastEvents(foreignBridge, {
|
||||
event: v1Bridge ? 'Withdraw' : 'UserRequestForAffirmation',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber
|
||||
})).map(normalizeEvent)
|
||||
if (isExternalErc20) {
|
||||
logger.debug("calling erc20Contract.getPastEvents('Transfer')")
|
||||
let transferEvents = (await getPastEvents(erc20Contract, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber,
|
||||
options: {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
}
|
||||
})).map(normalizeEvent)
|
||||
|
||||
let directTransfers = transferEvents
|
||||
const tokensSwappedAbiExists = FOREIGN_ABI.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0]
|
||||
if (tokensSwappedAbiExists) {
|
||||
const tokensSwappedEvents = await getPastEvents(foreignBridge, {
|
||||
event: 'TokensSwapped',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber
|
||||
})
|
||||
|
||||
// Get token swap events emitted by foreign bridge
|
||||
const bridgeTokensSwappedEvents = tokensSwappedEvents.filter(e => e.address === COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
|
||||
// Get transfer events for each previous erc20
|
||||
const uniqueTokenAddressesSet = new Set(bridgeTokensSwappedEvents.map(e => e.returnValues.from))
|
||||
|
||||
// Exclude chai token from previous erc20
|
||||
try {
|
||||
logger.debug('calling foreignBridge.chaiToken()')
|
||||
const chaiToken = await foreignBridge.methods.chaiToken().call()
|
||||
uniqueTokenAddressesSet.delete(chaiToken)
|
||||
} catch (e) {
|
||||
logger.debug('call to foreignBridge.chaiToken() failed')
|
||||
}
|
||||
// Exclude dai token from previous erc20
|
||||
try {
|
||||
logger.debug('calling foreignBridge.erc20token()')
|
||||
const daiToken = await foreignBridge.methods.erc20token().call()
|
||||
uniqueTokenAddressesSet.delete(daiToken)
|
||||
} catch (e) {
|
||||
logger.debug('call to foreignBridge.erc20token() failed')
|
||||
}
|
||||
|
||||
const uniqueTokenAddresses = [...uniqueTokenAddressesSet]
|
||||
await Promise.all(
|
||||
uniqueTokenAddresses.map(async tokenAddress => {
|
||||
const halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, tokenAddress)
|
||||
|
||||
const halfDuplexTransferEvents = (await getPastEvents(halfDuplexTokenContract, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber,
|
||||
options: {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
}
|
||||
})).map(normalizeEvent)
|
||||
|
||||
// Remove events after the ES
|
||||
const validHalfDuplexTransfers = await filterTransferBeforeES(
|
||||
halfDuplexTransferEvents,
|
||||
web3Foreign,
|
||||
foreignBridge
|
||||
)
|
||||
|
||||
transferEvents = [...validHalfDuplexTransfers, ...transferEvents]
|
||||
})
|
||||
)
|
||||
|
||||
// filter transfer that is part of a token swap
|
||||
directTransfers = transferEvents.filter(
|
||||
e =>
|
||||
bridgeTokensSwappedEvents.findIndex(
|
||||
t => t.transactionHash === e.referenceTx && e.recipient === ZERO_ADDRESS
|
||||
) === -1
|
||||
)
|
||||
}
|
||||
|
||||
// Get transfer events that didn't have a UserRequestForAffirmation event in the same transaction
|
||||
directTransfers = directTransfers.filter(
|
||||
e => foreignToHomeRequests.findIndex(t => t.referenceTx === e.referenceTx) === -1
|
||||
)
|
||||
|
||||
foreignToHomeRequests = [...foreignToHomeRequests, ...directTransfers]
|
||||
}
|
||||
|
||||
logger.debug('Done')
|
||||
return {
|
||||
homeDeposits,
|
||||
foreignDeposits,
|
||||
homeWithdrawals,
|
||||
foreignWithdrawals,
|
||||
isExternalErc20
|
||||
homeToForeignRequests,
|
||||
homeToForeignConfirmations,
|
||||
foreignToHomeConfirmations,
|
||||
foreignToHomeRequests,
|
||||
isExternalErc20,
|
||||
bridgeMode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
36
monitor/utils/file.js
Normal file
36
monitor/utils/file.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
async function readFile(filePath) {
|
||||
try {
|
||||
const content = await fs.readFileSync(filePath)
|
||||
const json = JSON.parse(content)
|
||||
const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked
|
||||
return Object.assign({}, json, { timeDiff })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return {
|
||||
error: 'the bridge statistics are not available'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(filePath, object) {
|
||||
fs.writeFileSync(path.join(process.cwd(), filePath), JSON.stringify(object, null, 4))
|
||||
}
|
||||
|
||||
function createDir(dirPath) {
|
||||
try {
|
||||
fs.mkdirSync(path.join(process.cwd(), dirPath), { recursive: true })
|
||||
} catch (e) {
|
||||
if (!e.message.includes('exists')) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readFile,
|
||||
writeFile,
|
||||
createDir
|
||||
}
|
||||
72
monitor/utils/message.js
Normal file
72
monitor/utils/message.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const web3Utils = require('web3').utils
|
||||
const { addTxHashToData, parseAMBMessage } = require('../../commons')
|
||||
|
||||
function deliveredMsgNotProcessed(processedList) {
|
||||
return deliveredMsg => {
|
||||
const msg = parseAMBMessage(
|
||||
addTxHashToData({
|
||||
encodedData: deliveredMsg.returnValues.encodedData,
|
||||
transactionHash: deliveredMsg.transactionHash
|
||||
})
|
||||
)
|
||||
return (
|
||||
processedList.filter(processedMsg => {
|
||||
return messageEqualsEvent(msg, processedMsg.returnValues)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function processedMsgNotDelivered(deliveredList) {
|
||||
return processedMsg => {
|
||||
return (
|
||||
deliveredList.filter(deliveredMsg => {
|
||||
const msg = parseAMBMessage(
|
||||
addTxHashToData({
|
||||
encodedData: deliveredMsg.returnValues.encodedData,
|
||||
transactionHash: deliveredMsg.transactionHash
|
||||
})
|
||||
)
|
||||
return messageEqualsEvent(msg, processedMsg.returnValues)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function messageEqualsEvent(parsedMsg, event) {
|
||||
return (
|
||||
web3Utils.toChecksumAddress(parsedMsg.sender) === event.sender &&
|
||||
web3Utils.toChecksumAddress(parsedMsg.executor) === event.executor &&
|
||||
parsedMsg.txHash === event.transactionHash
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the different event objects to facilitate data processing
|
||||
* @param {Object} event
|
||||
* @returns {{
|
||||
* transactionHash: string,
|
||||
* blockNumber: number,
|
||||
* referenceTx: string,
|
||||
* recipient: string | *,
|
||||
* value: *
|
||||
* }}
|
||||
*/
|
||||
const normalizeEventInformation = event => ({
|
||||
transactionHash: event.transactionHash,
|
||||
blockNumber: event.blockNumber,
|
||||
referenceTx: event.returnValues.transactionHash || event.transactionHash,
|
||||
recipient: event.returnValues.recipient || event.returnValues.from,
|
||||
value: event.returnValues.value
|
||||
})
|
||||
|
||||
const eventWithoutReference = otherSideEvents => e =>
|
||||
otherSideEvents.filter(a => a.referenceTx === e.referenceTx && a.recipient === e.recipient && a.value === e.value)
|
||||
.length === 0
|
||||
|
||||
module.exports = {
|
||||
deliveredMsgNotProcessed,
|
||||
processedMsgNotDelivered,
|
||||
normalizeEventInformation,
|
||||
eventWithoutReference
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user