From 0228fc7d5f5f8d3ca6d60c5d23d5f76cacc3cd61 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Sat, 31 Oct 2020 21:02:56 +0300 Subject: [PATCH] Support of manual lane in the AMB oracle (#483) --- commons/.eslintrc | 3 +- commons/message.js | 15 +++- contracts | 2 +- e2e-commons/access-lists/block_list.txt | 3 +- e2e-commons/components-envs/oracle-amb.env | 1 + e2e-commons/constants.json | 1 + e2e-commons/docker-compose.yml | 3 + e2e-commons/scripts/deploy.sh | 5 ++ e2e-commons/up.sh | 58 ++++++------- oracle-e2e/run-tests.sh | 20 ++++- oracle-e2e/test/amb.js | 81 ++++++++++++++++++- oracle-e2e/test/utils.js | 5 ++ .../processAMBCollectedSignatures/index.js | 42 +++++++++- package.json | 4 +- 14 files changed, 199 insertions(+), 44 deletions(-) diff --git a/commons/.eslintrc b/commons/.eslintrc index 0194f8e8..78c69fae 100644 --- a/commons/.eslintrc +++ b/commons/.eslintrc @@ -5,7 +5,8 @@ ], "rules": { "no-unused-expressions": "off", - "import/no-extraneous-dependencies": "off" + "import/no-extraneous-dependencies": "off", + "no-bitwise": "off" }, "env": { "mocha": true diff --git a/commons/message.js b/commons/message.js index efa5de30..1afb4117 100644 --- a/commons/message.js +++ b/commons/message.js @@ -6,17 +6,30 @@ function addTxHashToData({ encodedData, transactionHash }) { return encodedData.slice(0, 2) + strip0x(transactionHash) + encodedData.slice(2) } +/** + * Decodes the datatype byte from the AMB message. + * First (the most significant bit) denotes if the message should be forwarded to the manual lane. + * @param dataType: number datatype of the received AMB message. + * @return {{manualLane: boolean}} + */ +const decodeAMBDataType = dataType => ({ + manualLane: (dataType & 128) === 128 +}) + function parseAMBMessage(message) { message = strip0x(message) const messageId = `0x${message.slice(0, 64)}` const sender = `0x${message.slice(64, 104)}` const executor = `0x${message.slice(104, 144)}` + const dataType = parseInt(message.slice(156, 158), 16) return { sender, executor, - messageId + messageId, + dataType, + decodedDataType: decodeAMBDataType(dataType) } } diff --git a/contracts b/contracts index dd461352..1748f947 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit dd46135248dbb4684752735aab3cf64db170a405 +Subproject commit 1748f94757c07ce99d13d99a0a5d1d738b292354 diff --git a/e2e-commons/access-lists/block_list.txt b/e2e-commons/access-lists/block_list.txt index f130606c..8edcbf93 100644 --- a/e2e-commons/access-lists/block_list.txt +++ b/e2e-commons/access-lists/block_list.txt @@ -1 +1,2 @@ -0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04 \ No newline at end of file +0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04 +0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE diff --git a/e2e-commons/components-envs/oracle-amb.env b/e2e-commons/components-envs/oracle-amb.env index 4dba7b41..5c529691 100644 --- a/e2e-commons/components-envs/oracle-amb.env +++ b/e2e-commons/components-envs/oracle-amb.env @@ -23,3 +23,4 @@ ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500 ORACLE_ALLOW_HTTP_FOR_RPC=yes ORACLE_HOME_START_BLOCK=1 ORACLE_FOREIGN_START_BLOCK=1 +ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt diff --git a/e2e-commons/constants.json b/e2e-commons/constants.json index 9aa0609b..b19d81fd 100644 --- a/e2e-commons/constants.json +++ b/e2e-commons/constants.json @@ -63,6 +63,7 @@ "foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0", "homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1", "foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1", + "blockedHomeBox": "0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE", "monitor": "http://monitor-amb:3013/bridge" }, "ambStakeErcToErc": { diff --git a/e2e-commons/docker-compose.yml b/e2e-commons/docker-compose.yml index 4fbd84c9..d6863e2a 100644 --- a/e2e-commons/docker-compose.yml +++ b/e2e-commons/docker-compose.yml @@ -61,6 +61,9 @@ services: environment: - NODE_ENV=production command: "true" + volumes: + - '../e2e-commons/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt' + - '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt' networks: - ultimate ui: diff --git a/e2e-commons/scripts/deploy.sh b/e2e-commons/scripts/deploy.sh index 12466562..109861d6 100755 --- a/e2e-commons/scripts/deploy.sh +++ b/e2e-commons/scripts/deploy.sh @@ -53,3 +53,8 @@ node deploy.js cd - > /dev/null node setupStakeTokens.js cd - > /dev/null + +echo -e "\n\n############ Deploying one more test contract for amb ############\n" +cd "$DEPLOY_PATH" +node src/utils/deployTestBox.js +cd - > /dev/null diff --git a/e2e-commons/up.sh b/e2e-commons/up.sh index f0a367c0..7b3c7afe 100755 --- a/e2e-commons/up.sh +++ b/e2e-commons/up.sh @@ -17,21 +17,29 @@ docker-compose up -d parity1 parity2 e2e startValidator () { docker-compose $1 run -d --name $4 redis docker-compose $1 run -d --name $5 rabbit - docker-compose $1 run $2 $3 -d oracle yarn watcher:signature-request - docker-compose $1 run $2 $3 -d oracle yarn watcher:collected-signatures - docker-compose $1 run $2 $3 -d oracle yarn watcher:affirmation-request - docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:signature-request - docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:collected-signatures - docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:affirmation-request - docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:transfer - docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:signature-request - docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:collected-signatures - docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request - docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer - docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:convert-to-chai - docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:signature-request - docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures - docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request + if [[ -z "$MODE" || "$MODE" == native-to-erc ]]; then + docker-compose $1 run $2 $3 -d oracle yarn watcher:signature-request + docker-compose $1 run $2 $3 -d oracle yarn watcher:collected-signatures + docker-compose $1 run $2 $3 -d oracle yarn watcher:affirmation-request + fi + if [[ -z "$MODE" || "$MODE" == erc-to-erc ]]; then + docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:signature-request + docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:collected-signatures + docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:affirmation-request + docker-compose $1 run $2 $3 -d oracle-erc20 yarn watcher:transfer + fi + if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then + docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:signature-request + docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:collected-signatures + docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request + docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer + docker-compose $1 run $2 $3 -d oracle-erc20-native yarn worker:convert-to-chai + fi + if [[ -z "$MODE" || "$MODE" == amb ]]; then + docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:signature-request + docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures + docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request + fi docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:home docker-compose $1 run $2 $3 -d oracle-erc20-native yarn sender:foreign } @@ -48,25 +56,7 @@ startAMBValidator () { while [ "$1" != "" ]; do if [ "$1" == "oracle" ]; then - docker-compose up -d redis rabbit - - docker-compose run -d oracle yarn watcher:signature-request - docker-compose run -d oracle yarn watcher:collected-signatures - docker-compose run -d oracle yarn watcher:affirmation-request - docker-compose run -d oracle-erc20 yarn watcher:signature-request - docker-compose run -d oracle-erc20 yarn watcher:collected-signatures - docker-compose run -d oracle-erc20 yarn watcher:affirmation-request - docker-compose run -d oracle-erc20 yarn watcher:transfer - docker-compose run -d oracle-erc20-native yarn watcher:signature-request - docker-compose run -d oracle-erc20-native yarn watcher:collected-signatures - docker-compose run -d oracle-erc20-native yarn watcher:affirmation-request - docker-compose run -d oracle-erc20-native yarn watcher:transfer - docker-compose run -d oracle-erc20-native yarn worker:convert-to-chai - docker-compose run -d oracle-amb yarn watcher:signature-request - docker-compose run -d oracle-amb yarn watcher:collected-signatures - docker-compose run -d oracle-amb yarn watcher:affirmation-request - docker-compose run -d oracle yarn sender:home - docker-compose run -d oracle yarn sender:foreign + startValidator "" "" "" "redis" "rabbit" fi if [ "$1" == "oracle-validator-2" ]; then diff --git a/oracle-e2e/run-tests.sh b/oracle-e2e/run-tests.sh index 455f3fd2..2e05bcc0 100755 --- a/oracle-e2e/run-tests.sh +++ b/oracle-e2e/run-tests.sh @@ -1,8 +1,24 @@ cd $(dirname $0) -../e2e-commons/up.sh deploy blocks oracle oracle-validator-2 oracle-validator-3 +mode="$1" +case "$mode" in + amb) + script=./test/amb.js + ;; + native-to-erc) + script=./test/nativeToErc.js + ;; + erc-to-erc) + script=./test/ercToErc.js + ;; + erc-to-native) + script=./test/ercToNative.js + ;; +esac -docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start +MODE="$mode" ../e2e-commons/up.sh deploy blocks oracle oracle-validator-2 oracle-validator-3 + +docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start $script rc=$? ../e2e-commons/down.sh diff --git a/oracle-e2e/test/amb.js b/oracle-e2e/test/amb.js index 57c6c972..3e194a75 100644 --- a/oracle-e2e/test/amb.js +++ b/oracle-e2e/test/amb.js @@ -3,7 +3,7 @@ const assert = require('assert') const { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json') const { uniformRetry } = require('../../e2e-commons/utils') const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI } = require('../../commons') -const { setRequiredSignatures } = require('./utils') +const { delay, setRequiredSignatures } = require('./utils') const { toBN } = Web3.utils @@ -19,14 +19,17 @@ foreignWeb3.eth.accounts.wallet.add(user.privateKey) foreignWeb3.eth.accounts.wallet.add(validator.privateKey) const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox) +const blockHomeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.blockedHomeBox) const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox) const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, COMMON_HOME_BRIDGE_ADDRESS) const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) describe('arbitrary message bridging', () => { + let requiredSignatures = 1 before(async () => { // Only 1 validator is used in ultimate tests if (process.env.ULTIMATE !== 'true') { + requiredSignatures = 2 // Set 2 required signatures for home bridge await setRequiredSignatures({ bridgeContract: homeBridge, @@ -76,6 +79,82 @@ describe('arbitrary message bridging', () => { } }) }) + + it('should confirm but not relay message from blocked contract', async () => { + const newValue = 4 + + const initialValue = await foreignBox.methods.value().call() + assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value') + + const signatures = await homeBridge.getPastEvents('SignedForUserRequest', { + fromBlock: 0, + toBlock: 'latest' + }) + + await blockHomeBox.methods + .setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox) + .send({ + from: user.address, + gas: '400000' + }) + .catch(e => { + console.error(e) + }) + + await delay(5000) + + const newSignatures = await homeBridge.getPastEvents('SignedForUserRequest', { + fromBlock: 0, + toBlock: 'latest' + }) + + assert( + newSignatures.length === signatures.length + requiredSignatures, + `Incorrect amount of signatures submitted, got ${newSignatures.length}, expected ${signatures.length + + requiredSignatures}` + ) + + const value = await foreignBox.methods.value().call() + assert(!toBN(value).eq(toBN(newValue)), 'Message should not be relayed by oracle automatically') + }) + + it('should confirm but not relay message from manual lane', async () => { + const newValue = 5 + + const initialValue = await foreignBox.methods.value().call() + assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value') + + const signatures = await homeBridge.getPastEvents('SignedForUserRequest', { + fromBlock: 0, + toBlock: 'latest' + }) + + await homeBox.methods + .setValueOnOtherNetworkUsingManualLane(newValue, amb.home, amb.foreignBox) + .send({ + from: user.address, + gas: '400000' + }) + .catch(e => { + console.error(e) + }) + + await delay(5000) + + const newSignatures = await homeBridge.getPastEvents('SignedForUserRequest', { + fromBlock: 0, + toBlock: 'latest' + }) + + assert( + newSignatures.length === signatures.length + requiredSignatures, + `Incorrect amount of signatures submitted, got ${newSignatures.length}, expected ${signatures.length + + requiredSignatures}` + ) + + const value = await foreignBox.methods.value().call() + assert(!toBN(value).eq(toBN(newValue)), 'Message should not be relayed by oracle automatically') + }) }) }) describe('Foreign to Home', () => { diff --git a/oracle-e2e/test/utils.js b/oracle-e2e/test/utils.js index f1b85de0..48ab703e 100644 --- a/oracle-e2e/test/utils.js +++ b/oracle-e2e/test/utils.js @@ -1,5 +1,9 @@ const { BRIDGE_VALIDATORS_ABI } = require('../../commons') +async function delay(ms) { + return new Promise(res => setTimeout(res, ms)) +} + const setRequiredSignatures = async ({ bridgeContract, web3, requiredSignatures, options }) => { const validatorAddress = await bridgeContract.methods.validatorContract().call() const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorAddress) @@ -8,5 +12,6 @@ const setRequiredSignatures = async ({ bridgeContract, web3, requiredSignatures, } module.exports = { + delay, setRequiredSignatures } diff --git a/oracle/src/events/processAMBCollectedSignatures/index.js b/oracle/src/events/processAMBCollectedSignatures/index.js index fe983728..bc719f25 100644 --- a/oracle/src/events/processAMBCollectedSignatures/index.js +++ b/oracle/src/events/processAMBCollectedSignatures/index.js @@ -5,6 +5,7 @@ const bridgeValidatorsABI = require('../../../../contracts/build/contracts/Bridg const rootLogger = require('../../services/logger') const { web3Home, web3Foreign } = require('../../services/web3') const { signatureToVRS, packSignatures } = require('../../utils/message') +const { readAccessListFile } = require('../../utils/utils') const { parseAMBMessage } = require('../../../../commons') const estimateGas = require('./estimateGas') const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors') @@ -12,7 +13,11 @@ const { MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/const const limit = promiseLimit(MAX_CONCURRENT_EVENTS) -const { ORACLE_ALWAYS_RELAY_SIGNATURES } = process.env +const { + ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST, + ORACLE_HOME_TO_FOREIGN_BLOCK_LIST, + ORACLE_ALWAYS_RELAY_SIGNATURES +} = process.env let validatorContract = null @@ -50,6 +55,41 @@ function processCollectedSignaturesBuilder(config) { logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`) const message = await homeBridge.methods.message(messageHash).call() + const parsedMessage = parseAMBMessage(message) + + if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) { + const sender = parsedMessage.sender.toLowerCase() + const executor = parsedMessage.executor.toLowerCase() + + if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST) { + const allowanceList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST, logger) + if (!allowanceList.includes(executor) && !allowanceList.includes(sender)) { + logger.info( + { sender, executor }, + 'Validator skips a message. Neither sender nor executor addresses are in the allowance list.' + ) + return + } + } else if (ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) { + const blockList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_BLOCK_LIST, logger) + if (blockList.includes(executor)) { + logger.info({ executor }, 'Validator skips a message. Executor address is in the block list.') + return + } + if (blockList.includes(sender)) { + logger.info({ sender }, 'Validator skips a message. Sender address is in the block list.') + return + } + } + } + + if (parsedMessage.decodedDataType.manualLane) { + logger.info( + { dataType: parsedMessage.dataType }, + 'Validator skips a message. Message was forwarded to the manual lane by the extension' + ) + return + } logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get') diff --git a/package.json b/package.json index af29d73d..b77f2e88 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "build:ui": "yarn workspace ui run build", "build:alm": "yarn workspace alm run build", "build:plugin": "yarn workspace burner-wallet-plugin run build", - "lint": "yarn wsrun --exclude token-bridge-contracts lint", + "lint": "yarn wsrun --exclude tokenbridge-contracts lint", "test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e --exclude alm-e2e test", "oracle-e2e": "./oracle-e2e/run-tests.sh", "ui-e2e": "./ui-e2e/run-tests.sh", @@ -47,7 +47,7 @@ "monitor-e2e": "./monitor-e2e/run-tests.sh", "alm-e2e": "./alm-e2e/run-tests.sh", "clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build ./**/**/dist", - "compile:contracts": "yarn workspace token-bridge-contracts run compile", + "compile:contracts": "yarn workspace tokenbridge-contracts run compile", "install:deploy": "cd contracts/deploy && npm install --unsafe-perm --silent", "postinstall": "test -n \"$NOYARNPOSTINSTALL\" || ln -sf $(pwd)/node_modules/openzeppelin-solidity/ contracts/node_modules/openzeppelin-solidity" }