Oracle watcher for AMB async calls (#509)
This commit is contained in:
parent
38f1bae8f5
commit
ffbca8b941
@ -47,6 +47,7 @@ ORACLE_FOREIGN_TX_RESEND_INTERVAL | Interval in milliseconds for automatic resen
|
|||||||
ORACLE_SHUTDOWN_SERVICE_URL | Optional external URL to some other service/monitor/configuration manager that controls the remote shutdown process. GET request should return `application/json` message with the following schema: `{ shutdown: true/false }`. | URL
|
ORACLE_SHUTDOWN_SERVICE_URL | Optional external URL to some other service/monitor/configuration manager that controls the remote shutdown process. GET request should return `application/json` message with the following schema: `{ shutdown: true/false }`. | URL
|
||||||
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | Optional interval in milliseconds used to request the side RPC node or external shutdown service. Default is 120000. | integer
|
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | Optional interval in milliseconds used to request the side RPC node or external shutdown service. Default is 120000. | integer
|
||||||
ORACLE_SIDE_RPC_URL | Optional HTTPS URL(s) for communication with the external shutdown service or side RPC nodes, used for shutdown manager activities. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. | URL(s)
|
ORACLE_SIDE_RPC_URL | Optional HTTPS URL(s) for communication with the external shutdown service or side RPC nodes, used for shutdown manager activities. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. | URL(s)
|
||||||
|
ORACLE_FOREIGN_ARCHIVE_RPC_URL | Optional HTTPS URL(s) for communication with the archive nodes on the foreign network. Only used in AMB bridge mode for async information request processing. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. | URL(s)
|
||||||
ORACLE_SHUTDOWN_CONTRACT_ADDRESS | Optional contract address in the side chain accessible through `ORACLE_SIDE_RPC_URL`, where the method passed in `ORACLE_SHUTDOWN_CONTRACT_METHOD` is implemented. | `address`
|
ORACLE_SHUTDOWN_CONTRACT_ADDRESS | Optional contract address in the side chain accessible through `ORACLE_SIDE_RPC_URL`, where the method passed in `ORACLE_SHUTDOWN_CONTRACT_METHOD` is implemented. | `address`
|
||||||
ORACLE_SHUTDOWN_CONTRACT_METHOD | Method signature to be used in the side chain to identify the current shutdown status. Method should return boolean. Default value is `isShutdown()`. | `function signature`
|
ORACLE_SHUTDOWN_CONTRACT_METHOD | Method signature to be used in the side chain to identify the current shutdown status. Method should return boolean. Default value is `isShutdown()`. | `function signature`
|
||||||
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Foreign chain. Infinite, if not provided. | `integer`
|
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Foreign chain. Infinite, if not provided. | `integer`
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
- oracle_net_db_bridge_request
|
- oracle_net_db_bridge_request
|
||||||
- oracle_net_db_bridge_collected
|
- oracle_net_db_bridge_collected
|
||||||
- oracle_net_db_bridge_affirmation
|
- oracle_net_db_bridge_affirmation
|
||||||
|
- oracle_net_db_bridge_information
|
||||||
- oracle_net_db_bridge_transfer
|
- oracle_net_db_bridge_transfer
|
||||||
- oracle_net_db_bridge_senderhome
|
- oracle_net_db_bridge_senderhome
|
||||||
- oracle_net_db_bridge_senderforeign
|
- oracle_net_db_bridge_senderforeign
|
||||||
@ -16,6 +17,7 @@
|
|||||||
- oracle_net_rabbit_bridge_request
|
- oracle_net_rabbit_bridge_request
|
||||||
- oracle_net_rabbit_bridge_collected
|
- oracle_net_rabbit_bridge_collected
|
||||||
- oracle_net_rabbit_bridge_affirmation
|
- oracle_net_rabbit_bridge_affirmation
|
||||||
|
- oracle_net_rabbit_bridge_information
|
||||||
- oracle_net_rabbit_bridge_transfer
|
- oracle_net_rabbit_bridge_transfer
|
||||||
- oracle_net_rabbit_bridge_senderhome
|
- oracle_net_rabbit_bridge_senderhome
|
||||||
- oracle_net_rabbit_bridge_senderforeign
|
- oracle_net_rabbit_bridge_senderforeign
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
with_items:
|
with_items:
|
||||||
- docker-compose
|
- docker-compose
|
||||||
- docker-compose-transfer
|
- docker-compose-transfer
|
||||||
|
- docker-compose-amb
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: file
|
loop_var: file
|
||||||
|
|
||||||
|
@ -42,6 +42,10 @@
|
|||||||
set_fact: composefileoverride="-f docker-compose-transfer.yml"
|
set_fact: composefileoverride="-f docker-compose-transfer.yml"
|
||||||
when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE"
|
when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE"
|
||||||
|
|
||||||
|
- name: Extend docker compose file for amb
|
||||||
|
set_fact: composefileoverride="-f docker-compose-amb.yml"
|
||||||
|
when: ORACLE_BRIDGE_MODE == "ARBITRARY_MESSAGE"
|
||||||
|
|
||||||
- name: Install .key config
|
- name: Install .key config
|
||||||
template:
|
template:
|
||||||
src: key.j2
|
src: key.j2
|
||||||
|
@ -20,3 +20,4 @@
|
|||||||
with_items:
|
with_items:
|
||||||
- docker-compose.yml
|
- docker-compose.yml
|
||||||
- docker-compose-transfer.yml
|
- docker-compose-transfer.yml
|
||||||
|
- docker-compose-amb.yml
|
||||||
|
@ -24,3 +24,4 @@ ORACLE_ALLOW_HTTP_FOR_RPC=yes
|
|||||||
ORACLE_HOME_START_BLOCK=1
|
ORACLE_HOME_START_BLOCK=1
|
||||||
ORACLE_FOREIGN_START_BLOCK=1
|
ORACLE_FOREIGN_START_BLOCK=1
|
||||||
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt
|
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt
|
||||||
|
ORACLE_FOREIGN_ARCHIVE_RPC_URL=http://parity2:8545
|
||||||
|
@ -9,7 +9,7 @@ HOME_RPC_URL=http://parity1:8545
|
|||||||
HOME_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
HOME_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||||
HOME_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
HOME_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||||
HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||||
HOME_MAX_AMOUNT_PER_TX=8000000
|
HOME_MAX_AMOUNT_PER_TX=2000000
|
||||||
HOME_REQUIRED_BLOCK_CONFIRMATIONS=1
|
HOME_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||||
HOME_GAS_PRICE=1000000000
|
HOME_GAS_PRICE=1000000000
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ FOREIGN_RPC_URL=http://parity2:8545
|
|||||||
FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||||
FOREIGN_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
FOREIGN_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||||
FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||||
FOREIGN_MAX_AMOUNT_PER_TX=8000000
|
FOREIGN_MAX_AMOUNT_PER_TX=2000000
|
||||||
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1
|
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||||
FOREIGN_GAS_PRICE=10000000000
|
FOREIGN_GAS_PRICE=10000000000
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ if [ $CI ]; then exit $rc; fi
|
|||||||
|
|
||||||
ps | grep node | grep -v grep | grep -v yarn | awk '{print "kill " $1}' | /bin/bash
|
ps | grep node | grep -v grep | grep -v yarn | awk '{print "kill " $1}' | /bin/bash
|
||||||
docker-compose down
|
docker-compose down
|
||||||
|
docker-compose -p validator1 down
|
||||||
docker-compose -p validator2 down
|
docker-compose -p validator2 down
|
||||||
docker-compose -p validator3 down
|
docker-compose -p validator3 down
|
||||||
docker network rm ultimate || true
|
docker network rm ultimate || true
|
||||||
|
@ -15,52 +15,42 @@ docker network create --driver bridge ultimate || true
|
|||||||
docker-compose up -d parity1 parity2 e2e
|
docker-compose up -d parity1 parity2 e2e
|
||||||
|
|
||||||
startValidator () {
|
startValidator () {
|
||||||
docker-compose $1 run -d --name $4 redis
|
db_env="-e ORACLE_QUEUE_URL=amqp://$4 -e ORACLE_REDIS_URL=redis://$3"
|
||||||
docker-compose $1 run -d --name $5 rabbit
|
|
||||||
|
docker-compose $1 run -d --name $3 redis
|
||||||
|
docker-compose $1 run -d --name $4 rabbit
|
||||||
|
|
||||||
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
|
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 $db_env -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 $db_env -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 $db_env -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 $db_env -d oracle-erc20-native yarn watcher:transfer
|
||||||
fi
|
fi
|
||||||
if [[ -z "$MODE" || "$MODE" == amb ]]; then
|
if [[ -z "$MODE" || "$MODE" == amb ]]; then
|
||||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:signature-request
|
docker-compose $1 run $2 $db_env -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 $db_env -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 $db_env -d oracle-amb yarn watcher:affirmation-request
|
||||||
|
docker-compose $1 run $2 $db_env -d oracle-amb yarn watcher:information-request
|
||||||
fi
|
fi
|
||||||
docker-compose $1 run $2 $3 -d oracle-amb yarn sender:home
|
|
||||||
docker-compose $1 run $2 $3 -d oracle-amb yarn sender:foreign
|
|
||||||
docker-compose $1 run $2 $3 -d oracle-amb yarn manager:shutdown
|
|
||||||
}
|
|
||||||
|
|
||||||
startAMBValidator () {
|
docker-compose $1 run $2 $db_env -d oracle-amb yarn sender:home
|
||||||
docker-compose $1 run -d --name $4 redis
|
docker-compose $1 run $2 $db_env -d oracle-amb yarn sender:foreign
|
||||||
docker-compose $1 run -d --name $5 rabbit
|
docker-compose $1 run $2 $db_env -d oracle-amb yarn manager:shutdown
|
||||||
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-amb yarn sender:home
|
|
||||||
docker-compose $1 run $2 $3 -d oracle-amb yarn sender:foreign
|
|
||||||
docker-compose $1 run $2 $3 -d oracle-amb yarn manager:shutdown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while [ "$1" != "" ]; do
|
while [ "$1" != "" ]; do
|
||||||
if [ "$1" == "oracle" ]; then
|
if [ "$1" == "oracle" ]; then
|
||||||
startValidator "" "" "" "redis" "rabbit"
|
startValidator "-p validator1" "" redis rabbit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$1" == "oracle-validator-2" ]; then
|
if [ "$1" == "oracle-validator-2" ]; then
|
||||||
oracle2name="-p validator2"
|
|
||||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
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 "-p validator2" "$oracle2Values" redis2 rabbit2
|
||||||
startValidator "$oracle2name" "$oracle2Values" "$oracle2comp" "redis2" "rabbit2"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$1" == "oracle-validator-3" ]; then
|
if [ "$1" == "oracle-validator-3" ]; then
|
||||||
oracle3name="-p validator3"
|
|
||||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
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 "-p validator3" "$oracle3Values" redis3 rabbit3
|
||||||
startValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$1" == "alm" ]; then
|
if [ "$1" == "alm" ]; then
|
||||||
@ -92,17 +82,15 @@ while [ "$1" != "" ]; do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$1" == "alm-e2e" ]; then
|
if [ "$1" == "alm-e2e" ]; then
|
||||||
startAMBValidator "" "" "" "redis" "rabbit"
|
MODE=amb
|
||||||
|
|
||||||
|
startValidator "-p validator1" "" redis rabbit
|
||||||
|
|
||||||
oracle2name="-p validator2"
|
|
||||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
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 "-p validator2" "$oracle2Values" redis2 rabbit2
|
||||||
startAMBValidator "$oracle2name" "$oracle2Values" "$oracle2comp" "redis2" "rabbit2"
|
|
||||||
|
|
||||||
oracle3name="-p validator3"
|
|
||||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
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 "-p validator3" "$oracle3Values" redis3 rabbit3
|
||||||
startAMBValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
shift # Shift all the parameters down by one
|
shift # Shift all the parameters down by one
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "mocha",
|
"start": "mocha --exit",
|
||||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||||
"amb": "mocha test/amb.js",
|
"amb": "mocha test/amb.js",
|
||||||
"erc-to-native": "mocha test/ercToNative.js",
|
"erc-to-native": "mocha test/ercToNative.js",
|
||||||
|
@ -10,23 +10,47 @@ const { toBN } = Web3.utils
|
|||||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(foreignRPC.URL))
|
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(foreignRPC.URL))
|
||||||
|
|
||||||
const COMMON_HOME_BRIDGE_ADDRESS = amb.home
|
|
||||||
const COMMON_FOREIGN_BRIDGE_ADDRESS = amb.foreign
|
|
||||||
|
|
||||||
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||||
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
|
homeWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||||
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||||
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||||
|
|
||||||
const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox)
|
const opts = {
|
||||||
const blockHomeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.blockedHomeBox)
|
from: user.address,
|
||||||
const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox)
|
gas: 400000,
|
||||||
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
gasPrice: '1'
|
||||||
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
}
|
||||||
|
const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox, opts)
|
||||||
|
const blockHomeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.blockedHomeBox, opts)
|
||||||
|
const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox, opts)
|
||||||
|
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, amb.home, opts)
|
||||||
|
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, amb.foreign, opts)
|
||||||
|
|
||||||
describe('arbitrary message bridging', () => {
|
describe('arbitrary message bridging', () => {
|
||||||
let requiredSignatures = 1
|
let requiredSignatures = 1
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
const allowedMethods = [
|
||||||
|
'eth_call(address,bytes)',
|
||||||
|
'eth_call(address,bytes,uint256)',
|
||||||
|
'eth_call(address,address,uint256,bytes)',
|
||||||
|
'eth_blockNumber()',
|
||||||
|
'eth_getBlockByNumber()',
|
||||||
|
'eth_getBlockByNumber(uint256)',
|
||||||
|
'eth_getBlockByHash(bytes32)',
|
||||||
|
'eth_getBalance(address)',
|
||||||
|
'eth_getBalance(address,uint256)',
|
||||||
|
'eth_getTransactionCount(address)',
|
||||||
|
'eth_getTransactionCount(address,uint256)',
|
||||||
|
'eth_getTransactionByHash(bytes32)',
|
||||||
|
'eth_getTransactionReceipt(bytes32)',
|
||||||
|
'eth_getStorageAt(address,bytes32)',
|
||||||
|
'eth_getStorageAt(address,bytes32,uint256)'
|
||||||
|
]
|
||||||
|
for (const method of allowedMethods) {
|
||||||
|
const selector = homeWeb3.utils.soliditySha3(method)
|
||||||
|
await homeBridge.methods.enableAsyncRequestSelector(selector, true).send({ from: validator.address })
|
||||||
|
}
|
||||||
|
|
||||||
// Only 1 validator is used in ultimate tests
|
// Only 1 validator is used in ultimate tests
|
||||||
if (process.env.ULTIMATE === 'true') {
|
if (process.env.ULTIMATE === 'true') {
|
||||||
return
|
return
|
||||||
@ -66,10 +90,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
await homeBox.methods
|
await homeBox.methods
|
||||||
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
|
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
|
||||||
.send({
|
.send()
|
||||||
from: user.address,
|
|
||||||
gas: '400000'
|
|
||||||
})
|
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
})
|
})
|
||||||
@ -98,10 +119,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
await blockHomeBox.methods
|
await blockHomeBox.methods
|
||||||
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
|
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
|
||||||
.send({
|
.send()
|
||||||
from: user.address,
|
|
||||||
gas: '400000'
|
|
||||||
})
|
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
})
|
})
|
||||||
@ -137,10 +155,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
await homeBox.methods
|
await homeBox.methods
|
||||||
.setValueOnOtherNetworkUsingManualLane(newValue, amb.home, amb.foreignBox)
|
.setValueOnOtherNetworkUsingManualLane(newValue, amb.home, amb.foreignBox)
|
||||||
.send({
|
.send()
|
||||||
from: user.address,
|
|
||||||
gas: '400000'
|
|
||||||
})
|
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
})
|
})
|
||||||
@ -173,10 +188,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
await foreignBox.methods
|
await foreignBox.methods
|
||||||
.setValueOnOtherNetwork(newValue, amb.foreign, amb.homeBox)
|
.setValueOnOtherNetwork(newValue, amb.foreign, amb.homeBox)
|
||||||
.send({
|
.send()
|
||||||
from: user.address,
|
|
||||||
gas: '400000'
|
|
||||||
})
|
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
})
|
})
|
||||||
@ -191,4 +203,363 @@ describe('arbitrary message bridging', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('Home to Foreign Async Call', () => {
|
||||||
|
async function makeAsyncCall(selector, data) {
|
||||||
|
const prevMessageId = await homeBox.methods.messageId().call()
|
||||||
|
|
||||||
|
await homeBox.methods
|
||||||
|
.makeAsyncCall(amb.home, selector, data)
|
||||||
|
.send()
|
||||||
|
.catch(e => {
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
// check that value changed and balance decreased
|
||||||
|
await uniformRetry(async retry => {
|
||||||
|
const messageId = await homeBox.methods.messageId().call()
|
||||||
|
if (messageId === prevMessageId) {
|
||||||
|
retry()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should make async eth_call', async () => {
|
||||||
|
const foreignValue = await foreignBox.methods.value().call()
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_call(address,bytes)')
|
||||||
|
const data = homeWeb3.eth.abi.encodeParameters(
|
||||||
|
['address', 'bytes'],
|
||||||
|
[amb.foreignBox, foreignBox.methods.value().encodeABI()]
|
||||||
|
)
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
assert.strictEqual(
|
||||||
|
await homeBox.methods.data().call(),
|
||||||
|
homeWeb3.eth.abi.encodeParameters(['bytes'], [homeWeb3.eth.abi.encodeParameter('uint256', foreignValue)]),
|
||||||
|
'returned data is incorrect'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_call with 4 arguments', async () => {
|
||||||
|
const foreignValue = await foreignBox.methods.value().call()
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_call(address,address,uint256,bytes)')
|
||||||
|
const data1 = homeWeb3.eth.abi.encodeParameters(
|
||||||
|
['address', 'address', 'uint256', 'bytes'],
|
||||||
|
[amb.foreignBox, user.address, '100000', foreignBox.methods.value().encodeABI()]
|
||||||
|
)
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data1)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
assert.strictEqual(
|
||||||
|
await homeBox.methods.data().call(),
|
||||||
|
homeWeb3.eth.abi.encodeParameters(['bytes'], [homeWeb3.eth.abi.encodeParameter('uint256', foreignValue)]),
|
||||||
|
'returned data is incorrect'
|
||||||
|
)
|
||||||
|
|
||||||
|
const data2 = homeWeb3.eth.abi.encodeParameters(
|
||||||
|
['address', 'address', 'uint256', 'bytes'],
|
||||||
|
[amb.foreignBox, user.address, '1000', foreignBox.methods.value().encodeABI()]
|
||||||
|
)
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data2)
|
||||||
|
|
||||||
|
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||||
|
assert.strictEqual(await homeBox.methods.data().call(), null, 'returned data is incorrect')
|
||||||
|
|
||||||
|
const data3 = homeWeb3.eth.abi.encodeParameters(
|
||||||
|
['address', 'address', 'uint256', 'bytes'],
|
||||||
|
[amb.foreignBox, user.address, '21300', foreignBox.methods.value().encodeABI()]
|
||||||
|
)
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data3)
|
||||||
|
|
||||||
|
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||||
|
assert.strictEqual(await homeBox.methods.data().call(), null, 'returned data is incorrect')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_call for specific block', async () => {
|
||||||
|
const foreignValue = await foreignBox.methods.value().call()
|
||||||
|
const blockNumber = await foreignWeb3.eth.getBlockNumber()
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_call(address,bytes,uint256)')
|
||||||
|
const data1 = homeWeb3.eth.abi.encodeParameters(
|
||||||
|
['address', 'bytes', 'uint256'],
|
||||||
|
[amb.foreignBox, foreignBox.methods.value().encodeABI(), 60]
|
||||||
|
)
|
||||||
|
const data2 = homeWeb3.eth.abi.encodeParameters(
|
||||||
|
['address', 'bytes', 'uint256'],
|
||||||
|
[amb.foreignBox, foreignBox.methods.value().encodeABI(), blockNumber - 2]
|
||||||
|
)
|
||||||
|
const data3 = homeWeb3.eth.abi.encodeParameters(
|
||||||
|
['address', 'bytes', 'uint256'],
|
||||||
|
[amb.foreignBox, foreignBox.methods.value().encodeABI(), blockNumber + 20]
|
||||||
|
)
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data1)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
assert.strictEqual(
|
||||||
|
await homeBox.methods.data().call(),
|
||||||
|
homeWeb3.eth.abi.encodeParameters(['bytes'], [homeWeb3.eth.abi.encodeParameter('uint256', 0)]),
|
||||||
|
'returned data is incorrect'
|
||||||
|
)
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data2)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
assert.strictEqual(
|
||||||
|
await homeBox.methods.data().call(),
|
||||||
|
homeWeb3.eth.abi.encodeParameters(['bytes'], [homeWeb3.eth.abi.encodeParameter('uint256', foreignValue)]),
|
||||||
|
'returned data is incorrect'
|
||||||
|
)
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data3)
|
||||||
|
|
||||||
|
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_blockNumber', async () => {
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_blockNumber()')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, '0x')
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
assert.strictEqual((await homeBox.methods.data().call()).length, 66, 'invalid block number')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getBlockByNumber', async () => {
|
||||||
|
const blockNumber = ((await foreignWeb3.eth.getBlockNumber()) - 5).toString()
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getBlockByNumber(uint256)')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, homeWeb3.eth.abi.encodeParameter('uint256', blockNumber))
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
const data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64 * 3)
|
||||||
|
const { 0: number, 1: hash, 2: miner } = homeWeb3.eth.abi.decodeParameters(
|
||||||
|
['uint256', 'bytes32', 'address'],
|
||||||
|
data
|
||||||
|
)
|
||||||
|
const block = await foreignWeb3.eth.getBlock(blockNumber)
|
||||||
|
assert.strictEqual(number, blockNumber, 'wrong block number returned')
|
||||||
|
assert.strictEqual(hash, block.hash, 'wrong block hash returned')
|
||||||
|
assert.strictEqual(miner, block.miner, 'wrong block miner returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getBlockByNumber and return latest block', async () => {
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getBlockByNumber()')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, '0x')
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
const data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64 * 3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getBlockByHash', async () => {
|
||||||
|
const blockNumber = ((await foreignWeb3.eth.getBlockNumber()) - 5).toString()
|
||||||
|
const block = await foreignWeb3.eth.getBlock(blockNumber)
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getBlockByHash(bytes32)')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, block.hash)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
const data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64 * 3)
|
||||||
|
|
||||||
|
const { 0: number, 1: hash, 2: miner } = homeWeb3.eth.abi.decodeParameters(
|
||||||
|
['uint256', 'bytes32', 'address'],
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.strictEqual(number, blockNumber, 'wrong block number returned')
|
||||||
|
assert.strictEqual(hash, block.hash, 'wrong block hash returned')
|
||||||
|
assert.strictEqual(miner, block.miner, 'wrong block miner returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getBalance', async () => {
|
||||||
|
const balance = await foreignWeb3.eth.getBalance(user.address)
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getBalance(address)')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, homeWeb3.eth.abi.encodeParameter('address', user.address))
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
const data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64)
|
||||||
|
|
||||||
|
assert.strictEqual(homeWeb3.eth.abi.decodeParameter('uint256', data), balance, 'wrong user balance returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getBalance for specific block', async () => {
|
||||||
|
const balance = await foreignWeb3.eth.getBalance(user.address)
|
||||||
|
const { blockNumber } = await foreignWeb3.eth.sendTransaction({
|
||||||
|
to: user.address,
|
||||||
|
value: 1,
|
||||||
|
from: user.address,
|
||||||
|
gas: 21000
|
||||||
|
})
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getBalance(address,uint256)')
|
||||||
|
|
||||||
|
const data1 = homeWeb3.eth.abi.encodeParameters(['address', 'uint256'], [user.address, blockNumber - 1])
|
||||||
|
const data2 = homeWeb3.eth.abi.encodeParameters(['address', 'uint256'], [user.address, blockNumber])
|
||||||
|
await makeAsyncCall(selector, data1)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
let data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64)
|
||||||
|
|
||||||
|
assert.strictEqual(homeWeb3.eth.abi.decodeParameter('uint256', data), balance, 'wrong user balance returned')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data2)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64)
|
||||||
|
|
||||||
|
assert.notStrictEqual(homeWeb3.eth.abi.decodeParameter('uint256', data), balance, 'wrong user balance returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getTransactionCount', async () => {
|
||||||
|
const nonce = (await foreignWeb3.eth.getTransactionCount(user.address)).toString()
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getTransactionCount(address)')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, homeWeb3.eth.abi.encodeParameter('address', user.address))
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
const data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64)
|
||||||
|
|
||||||
|
assert.strictEqual(homeWeb3.eth.abi.decodeParameter('uint256', data), nonce, 'wrong user nonce returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getTransactionCount for specific block', async () => {
|
||||||
|
let nonce = (await foreignWeb3.eth.getTransactionCount(user.address)).toString()
|
||||||
|
const { blockNumber } = await foreignWeb3.eth.sendTransaction({
|
||||||
|
to: user.address,
|
||||||
|
value: 1,
|
||||||
|
from: user.address,
|
||||||
|
gas: 21000
|
||||||
|
})
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getTransactionCount(address,uint256)')
|
||||||
|
|
||||||
|
const data1 = homeWeb3.eth.abi.encodeParameters(['address', 'uint256'], [user.address, blockNumber - 1])
|
||||||
|
const data2 = homeWeb3.eth.abi.encodeParameters(['address', 'uint256'], [user.address, blockNumber])
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data1)
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
let data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64)
|
||||||
|
|
||||||
|
assert.strictEqual(homeWeb3.eth.abi.decodeParameter('uint256', data), nonce, 'wrong user nonce returned')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data2)
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
data = await homeBox.methods.data().call()
|
||||||
|
assert.strictEqual(data.length, 2 + 64)
|
||||||
|
|
||||||
|
nonce = (parseInt(nonce, 10) + 1).toString()
|
||||||
|
assert.strictEqual(homeWeb3.eth.abi.decodeParameter('uint256', data), nonce, 'wrong user nonce returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getTransactionByHash', async () => {
|
||||||
|
const txHash = '0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
|
||||||
|
const tx = await foreignWeb3.eth.getTransaction(txHash)
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getTransactionByHash(bytes32)')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, txHash)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
const data = await homeBox.methods.data().call()
|
||||||
|
const dataTypes = [
|
||||||
|
'bytes32',
|
||||||
|
'uint256',
|
||||||
|
'address',
|
||||||
|
'address',
|
||||||
|
'uint256',
|
||||||
|
'uint256',
|
||||||
|
'uint256',
|
||||||
|
'uint256',
|
||||||
|
'bytes'
|
||||||
|
]
|
||||||
|
const values = homeWeb3.eth.abi.decodeParameters(dataTypes, data)
|
||||||
|
|
||||||
|
assert.strictEqual(values[0], txHash, 'wrong txHash returned')
|
||||||
|
assert.strictEqual(values[1], tx.blockNumber.toString(), 'wrong tx blockNumber returned')
|
||||||
|
assert.strictEqual(values[2], tx.from, 'wrong tx from returned')
|
||||||
|
assert.strictEqual(values[3], tx.to, 'wrong tx to returned')
|
||||||
|
assert.strictEqual(values[4], tx.value, 'wrong tx value returned')
|
||||||
|
assert.strictEqual(values[5], tx.nonce.toString(), 'wrong tx nonce returned')
|
||||||
|
assert.strictEqual(values[6], tx.gas.toString(), 'wrong tx gas returned')
|
||||||
|
assert.strictEqual(values[7], tx.gasPrice, 'wrong tx gasPrice returned')
|
||||||
|
assert.strictEqual(values[8], tx.input, 'wrong tx data returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getTransactionReceipt', async () => {
|
||||||
|
const txHash = '0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
|
||||||
|
const receipt = await foreignWeb3.eth.getTransactionReceipt(txHash)
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getTransactionReceipt(bytes32)')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, txHash)
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
const data = await homeBox.methods.data().call()
|
||||||
|
const dataTypes = ['bytes32', 'uint256', 'bool', '(address,bytes32[],bytes)[]']
|
||||||
|
const values = homeWeb3.eth.abi.decodeParameters(dataTypes, data)
|
||||||
|
|
||||||
|
assert.strictEqual(values[0], txHash, 'wrong txHash returned')
|
||||||
|
assert.strictEqual(values[1], receipt.blockNumber.toString(), 'wrong tx blockNumber returned')
|
||||||
|
assert.strictEqual(values[2], receipt.status, 'wrong tx status returned')
|
||||||
|
assert.strictEqual(values[3].length, 1, 'wrong logs length returned')
|
||||||
|
assert.strictEqual(values[3][0][0], receipt.logs[0].address, 'wrong log address returned')
|
||||||
|
assert.strictEqual(values[3][0][1].length, 2, 'wrong log topics length returned')
|
||||||
|
assert.strictEqual(values[3][0][1][0], receipt.logs[0].topics[0], 'wrong event signature returned')
|
||||||
|
assert.strictEqual(values[3][0][1][1], receipt.logs[0].topics[1], 'wrong message id returned')
|
||||||
|
assert.strictEqual(values[3][0][2], receipt.logs[0].data, 'wrong log data returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getStorageAt', async () => {
|
||||||
|
// slot for uintStorage[MAX_GAS_PER_TX]
|
||||||
|
const slot = '0x3d7fe2ee9790702383ef0118b516833ef2542132d3ca4ac6c77f62f1230fa610'
|
||||||
|
const value = await foreignWeb3.eth.getStorageAt(amb.foreign, slot)
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getStorageAt(address,bytes32)')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, homeWeb3.eth.abi.encodeParameters(['address', 'bytes32'], [amb.foreign, slot]))
|
||||||
|
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
const data = await homeBox.methods.data().call()
|
||||||
|
|
||||||
|
assert.strictEqual(data, value, 'wrong storage value returned')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should make async eth_getStorageAt for specific block', async () => {
|
||||||
|
// slot for uintStorage[MAX_GAS_PER_TX]
|
||||||
|
const slot = '0x3d7fe2ee9790702383ef0118b516833ef2542132d3ca4ac6c77f62f1230fa610'
|
||||||
|
const value = await foreignWeb3.eth.getStorageAt(amb.foreign, slot)
|
||||||
|
const blockNumber = await foreignWeb3.eth.getBlockNumber()
|
||||||
|
const selector = homeWeb3.utils.soliditySha3('eth_getStorageAt(address,bytes32,uint256)')
|
||||||
|
|
||||||
|
const data1 = homeWeb3.eth.abi.encodeParameters(
|
||||||
|
['address', 'bytes32', 'uint256'],
|
||||||
|
[amb.foreign, slot, blockNumber]
|
||||||
|
)
|
||||||
|
const data2 = homeWeb3.eth.abi.encodeParameters(['address', 'bytes32', 'uint256'], [amb.foreign, slot, 1])
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data1)
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
let data = await homeBox.methods.data().call()
|
||||||
|
|
||||||
|
assert.strictEqual(data, value, 'wrong storage value returned')
|
||||||
|
|
||||||
|
await makeAsyncCall(selector, data2)
|
||||||
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
|
data = await homeBox.methods.data().call()
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
data,
|
||||||
|
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
'wrong storage value returned'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
],
|
],
|
||||||
"plugins": ["node"],
|
"plugins": ["node"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"node/no-unpublished-require": "off"
|
"node/no-unpublished-require": "off",
|
||||||
|
"global-require": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ const baseConfig = require('./base.config')
|
|||||||
const id = `${baseConfig.id}-affirmation-request`
|
const id = `${baseConfig.id}-affirmation-request`
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...baseConfig.bridgeConfig,
|
...baseConfig,
|
||||||
...baseConfig.foreignConfig,
|
main: baseConfig.foreign,
|
||||||
event: 'UserRequestForAffirmation',
|
event: 'UserRequestForAffirmation',
|
||||||
queue: 'home-prioritized',
|
queue: 'home-prioritized',
|
||||||
name: `watcher-${id}`,
|
name: `watcher-${id}`,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
require('../env')
|
require('../env')
|
||||||
|
|
||||||
const { toBN } = require('web3').utils
|
|
||||||
const {
|
const {
|
||||||
BRIDGE_MODES,
|
BRIDGE_MODES,
|
||||||
HOME_ERC_TO_NATIVE_ABI,
|
HOME_ERC_TO_NATIVE_ABI,
|
||||||
@ -11,13 +10,26 @@ const {
|
|||||||
const { web3Home, web3Foreign } = require('../src/services/web3')
|
const { web3Home, web3Foreign } = require('../src/services/web3')
|
||||||
const { privateKeyToAddress } = require('../src/utils/utils')
|
const { privateKeyToAddress } = require('../src/utils/utils')
|
||||||
|
|
||||||
const { ORACLE_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
|
const {
|
||||||
|
ORACLE_BRIDGE_MODE,
|
||||||
|
ORACLE_VALIDATOR_ADDRESS,
|
||||||
|
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
|
||||||
|
ORACLE_MAX_PROCESSING_TIME,
|
||||||
|
COMMON_HOME_BRIDGE_ADDRESS,
|
||||||
|
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||||
|
ORACLE_HOME_RPC_POLLING_INTERVAL,
|
||||||
|
ORACLE_FOREIGN_RPC_POLLING_INTERVAL,
|
||||||
|
ORACLE_HOME_START_BLOCK,
|
||||||
|
ORACLE_FOREIGN_START_BLOCK,
|
||||||
|
ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT,
|
||||||
|
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT
|
||||||
|
} = process.env
|
||||||
|
|
||||||
let homeAbi
|
let homeAbi
|
||||||
let foreignAbi
|
let foreignAbi
|
||||||
let id
|
let id
|
||||||
|
|
||||||
switch (process.env.ORACLE_BRIDGE_MODE) {
|
switch (ORACLE_BRIDGE_MODE) {
|
||||||
case BRIDGE_MODES.ERC_TO_NATIVE:
|
case BRIDGE_MODES.ERC_TO_NATIVE:
|
||||||
homeAbi = HOME_ERC_TO_NATIVE_ABI
|
homeAbi = HOME_ERC_TO_NATIVE_ABI
|
||||||
foreignAbi = FOREIGN_ERC_TO_NATIVE_ABI
|
foreignAbi = FOREIGN_ERC_TO_NATIVE_ABI
|
||||||
@ -30,7 +42,7 @@ switch (process.env.ORACLE_BRIDGE_MODE) {
|
|||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
if (process.env.NODE_ENV !== 'test') {
|
if (process.env.NODE_ENV !== 'test') {
|
||||||
throw new Error(`Bridge Mode: ${process.env.ORACLE_BRIDGE_MODE} not supported.`)
|
throw new Error(`Bridge Mode: ${ORACLE_BRIDGE_MODE} not supported.`)
|
||||||
} else {
|
} else {
|
||||||
homeAbi = HOME_ERC_TO_NATIVE_ABI
|
homeAbi = HOME_ERC_TO_NATIVE_ABI
|
||||||
foreignAbi = FOREIGN_ERC_TO_NATIVE_ABI
|
foreignAbi = FOREIGN_ERC_TO_NATIVE_ABI
|
||||||
@ -38,56 +50,41 @@ switch (process.env.ORACLE_BRIDGE_MODE) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let maxProcessingTime = null
|
const homeContract = new web3Home.eth.Contract(homeAbi, COMMON_HOME_BRIDGE_ADDRESS)
|
||||||
if (String(process.env.ORACLE_MAX_PROCESSING_TIME) === '0') {
|
const homeConfig = {
|
||||||
maxProcessingTime = 0
|
chain: 'home',
|
||||||
} else if (!process.env.ORACLE_MAX_PROCESSING_TIME) {
|
bridgeAddress: COMMON_HOME_BRIDGE_ADDRESS,
|
||||||
maxProcessingTime =
|
bridgeABI: homeAbi,
|
||||||
4 * Math.max(process.env.ORACLE_HOME_RPC_POLLING_INTERVAL, process.env.ORACLE_FOREIGN_RPC_POLLING_INTERVAL)
|
pollingInterval: parseInt(ORACLE_HOME_RPC_POLLING_INTERVAL, 10),
|
||||||
} else {
|
startBlock: parseInt(ORACLE_HOME_START_BLOCK, 10) || 0,
|
||||||
maxProcessingTime = Number(process.env.ORACLE_MAX_PROCESSING_TIME)
|
blockPollingLimit: parseInt(ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT, 10),
|
||||||
|
web3: web3Home,
|
||||||
|
bridgeContract: homeContract,
|
||||||
|
eventContract: homeContract
|
||||||
}
|
}
|
||||||
|
|
||||||
const bridgeConfig = {
|
const foreignContract = new web3Foreign.eth.Contract(foreignAbi, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||||
homeBridgeAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS,
|
const foreignConfig = {
|
||||||
homeBridgeAbi: homeAbi,
|
chain: 'foreign',
|
||||||
foreignBridgeAddress: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS,
|
bridgeAddress: COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||||
foreignBridgeAbi: foreignAbi,
|
bridgeABI: foreignAbi,
|
||||||
|
pollingInterval: parseInt(ORACLE_FOREIGN_RPC_POLLING_INTERVAL, 10),
|
||||||
|
startBlock: parseInt(ORACLE_FOREIGN_START_BLOCK, 10) || 0,
|
||||||
|
blockPollingLimit: parseInt(ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT, 10),
|
||||||
|
web3: web3Foreign,
|
||||||
|
bridgeContract: foreignContract,
|
||||||
|
eventContract: foreignContract
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxProcessingTime =
|
||||||
|
parseInt(ORACLE_MAX_PROCESSING_TIME, 10) || 4 * Math.max(homeConfig.pollingInterval, foreignConfig.pollingInterval)
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
eventFilter: {},
|
eventFilter: {},
|
||||||
validatorAddress: ORACLE_VALIDATOR_ADDRESS || privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY),
|
validatorAddress: ORACLE_VALIDATOR_ADDRESS || privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY),
|
||||||
maxProcessingTime,
|
maxProcessingTime,
|
||||||
shutdownKey: 'oracle-shutdown'
|
shutdownKey: 'oracle-shutdown',
|
||||||
}
|
home: homeConfig,
|
||||||
|
foreign: foreignConfig,
|
||||||
const toBNOrNull = x => (x ? toBN(x) : null)
|
|
||||||
|
|
||||||
const homeConfig = {
|
|
||||||
chain: 'home',
|
|
||||||
eventContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS,
|
|
||||||
eventAbi: homeAbi,
|
|
||||||
bridgeContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS,
|
|
||||||
bridgeAbi: homeAbi,
|
|
||||||
pollingInterval: process.env.ORACLE_HOME_RPC_POLLING_INTERVAL,
|
|
||||||
startBlock: toBN(process.env.ORACLE_HOME_START_BLOCK || 0),
|
|
||||||
blockPollingLimit: toBNOrNull(process.env.ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT),
|
|
||||||
web3: web3Home
|
|
||||||
}
|
|
||||||
|
|
||||||
const foreignConfig = {
|
|
||||||
chain: 'foreign',
|
|
||||||
eventContractAddress: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS,
|
|
||||||
eventAbi: foreignAbi,
|
|
||||||
bridgeContractAddress: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS,
|
|
||||||
bridgeAbi: foreignAbi,
|
|
||||||
pollingInterval: process.env.ORACLE_FOREIGN_RPC_POLLING_INTERVAL,
|
|
||||||
startBlock: toBN(process.env.ORACLE_FOREIGN_START_BLOCK || 0),
|
|
||||||
blockPollingLimit: toBNOrNull(process.env.ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT),
|
|
||||||
web3: web3Foreign
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
bridgeConfig,
|
|
||||||
homeConfig,
|
|
||||||
foreignConfig,
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ const baseConfig = require('./base.config')
|
|||||||
const id = `${baseConfig.id}-collected-signatures`
|
const id = `${baseConfig.id}-collected-signatures`
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...baseConfig.bridgeConfig,
|
...baseConfig,
|
||||||
...baseConfig.homeConfig,
|
main: baseConfig.home,
|
||||||
event: 'CollectedSignatures',
|
event: 'CollectedSignatures',
|
||||||
queue: 'foreign-prioritized',
|
queue: 'foreign-prioritized',
|
||||||
name: `watcher-${id}`,
|
name: `watcher-${id}`,
|
||||||
|
@ -6,7 +6,7 @@ const { web3Foreign, web3ForeignRedundant, web3ForeignFallback } = require('../s
|
|||||||
const { ORACLE_FOREIGN_TX_RESEND_INTERVAL } = process.env
|
const { ORACLE_FOREIGN_TX_RESEND_INTERVAL } = process.env
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...baseConfig.bridgeConfig,
|
...baseConfig,
|
||||||
queue: 'foreign-prioritized',
|
queue: 'foreign-prioritized',
|
||||||
oldQueue: 'foreign',
|
oldQueue: 'foreign',
|
||||||
id: 'foreign',
|
id: 'foreign',
|
||||||
|
@ -6,7 +6,7 @@ const { web3Home, web3HomeRedundant, web3HomeFallback } = require('../src/servic
|
|||||||
const { ORACLE_HOME_TX_RESEND_INTERVAL } = process.env
|
const { ORACLE_HOME_TX_RESEND_INTERVAL } = process.env
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...baseConfig.bridgeConfig,
|
...baseConfig,
|
||||||
queue: 'home-prioritized',
|
queue: 'home-prioritized',
|
||||||
oldQueue: 'home',
|
oldQueue: 'home',
|
||||||
id: 'home',
|
id: 'home',
|
||||||
|
14
oracle/config/information-request-watcher.config.js
Normal file
14
oracle/config/information-request-watcher.config.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const baseConfig = require('./base.config')
|
||||||
|
const { web3ForeignArchive } = require('../src/services/web3')
|
||||||
|
|
||||||
|
const id = `${baseConfig.id}-information-request`
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...baseConfig,
|
||||||
|
web3ForeignArchive: web3ForeignArchive || baseConfig.foreign.web3,
|
||||||
|
main: baseConfig.home,
|
||||||
|
event: 'UserRequestForInformation',
|
||||||
|
queue: 'home-prioritized',
|
||||||
|
name: `watcher-${id}`,
|
||||||
|
id
|
||||||
|
}
|
@ -8,7 +8,7 @@ const {
|
|||||||
} = process.env
|
} = process.env
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...baseConfig.bridgeConfig,
|
...baseConfig,
|
||||||
id: 'shutdown-manager',
|
id: 'shutdown-manager',
|
||||||
name: 'shutdown-manager',
|
name: 'shutdown-manager',
|
||||||
pollingInterval: ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL || 120000,
|
pollingInterval: ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL || 120000,
|
||||||
@ -16,5 +16,6 @@ module.exports = {
|
|||||||
checksBeforeStop: 1,
|
checksBeforeStop: 1,
|
||||||
shutdownServiceURL: ORACLE_SHUTDOWN_SERVICE_URL,
|
shutdownServiceURL: ORACLE_SHUTDOWN_SERVICE_URL,
|
||||||
shutdownContractAddress: ORACLE_SHUTDOWN_CONTRACT_ADDRESS,
|
shutdownContractAddress: ORACLE_SHUTDOWN_CONTRACT_ADDRESS,
|
||||||
shutdownMethod: (ORACLE_SHUTDOWN_CONTRACT_METHOD || 'isShutdown()').trim()
|
shutdownMethod: (ORACLE_SHUTDOWN_CONTRACT_METHOD || 'isShutdown()').trim(),
|
||||||
|
requestTimeout: 2000
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ const baseConfig = require('./base.config')
|
|||||||
const id = `${baseConfig.id}-signature-request`
|
const id = `${baseConfig.id}-signature-request`
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...baseConfig.bridgeConfig,
|
...baseConfig,
|
||||||
...baseConfig.homeConfig,
|
main: baseConfig.home,
|
||||||
event: 'UserRequestForSignature',
|
event: 'UserRequestForSignature',
|
||||||
queue: 'home-prioritized',
|
queue: 'home-prioritized',
|
||||||
name: `watcher-${id}`,
|
name: `watcher-${id}`,
|
||||||
|
@ -23,11 +23,12 @@ if (baseConfig.id !== 'erc-native') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...baseConfig.bridgeConfig,
|
...baseConfig,
|
||||||
...baseConfig.foreignConfig,
|
main: {
|
||||||
|
...baseConfig.foreign,
|
||||||
|
eventContract: new baseConfig.foreign.web3.eth.Contract(ERC20_ABI, initialChecks.bridgeableTokenAddress)
|
||||||
|
},
|
||||||
event: 'Transfer',
|
event: 'Transfer',
|
||||||
eventContractAddress: initialChecks.bridgeableTokenAddress,
|
|
||||||
eventAbi: ERC20_ABI,
|
|
||||||
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
||||||
queue: 'home-prioritized',
|
queue: 'home-prioritized',
|
||||||
name: `watcher-${id}`,
|
name: `watcher-${id}`,
|
||||||
|
97
oracle/docker-compose-amb.yml
Normal file
97
oracle/docker-compose-amb.yml
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
version: '2.4'
|
||||||
|
services:
|
||||||
|
rabbit:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.yml
|
||||||
|
service: rabbit
|
||||||
|
networks:
|
||||||
|
- net_rabbit_bridge_information
|
||||||
|
redis:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.yml
|
||||||
|
service: redis
|
||||||
|
networks:
|
||||||
|
- net_db_bridge_information
|
||||||
|
bridge_request:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.yml
|
||||||
|
service: bridge_request
|
||||||
|
networks:
|
||||||
|
- net_db_bridge_request
|
||||||
|
- net_rabbit_bridge_request
|
||||||
|
bridge_collected:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.yml
|
||||||
|
service: bridge_collected
|
||||||
|
networks:
|
||||||
|
- net_db_bridge_request
|
||||||
|
- net_rabbit_bridge_request
|
||||||
|
bridge_affirmation:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.yml
|
||||||
|
service: bridge_affirmation
|
||||||
|
networks:
|
||||||
|
- net_db_bridge_request
|
||||||
|
- net_rabbit_bridge_request
|
||||||
|
bridge_information:
|
||||||
|
cpus: 0.1
|
||||||
|
mem_limit: 500m
|
||||||
|
image: poanetwork/tokenbridge-oracle:latest
|
||||||
|
env_file: ./.env
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
|
||||||
|
restart: unless-stopped
|
||||||
|
entrypoint: yarn watcher:information-request
|
||||||
|
networks:
|
||||||
|
- net_db_bridge_information
|
||||||
|
- net_rabbit_bridge_information
|
||||||
|
bridge_senderhome:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.yml
|
||||||
|
service: bridge_senderhome
|
||||||
|
networks:
|
||||||
|
- net_db_bridge_request
|
||||||
|
- net_rabbit_bridge_request
|
||||||
|
bridge_senderforeign:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.yml
|
||||||
|
service: bridge_senderforeign
|
||||||
|
networks:
|
||||||
|
- net_db_bridge_request
|
||||||
|
- net_rabbit_bridge_request
|
||||||
|
bridge_shutdown:
|
||||||
|
extends:
|
||||||
|
file: docker-compose.yml
|
||||||
|
service: bridge_shutdown
|
||||||
|
networks:
|
||||||
|
- net_db_bridge_shutdown
|
||||||
|
|
||||||
|
networks:
|
||||||
|
net_db_bridge_request:
|
||||||
|
driver: bridge
|
||||||
|
net_db_bridge_collected:
|
||||||
|
driver: bridge
|
||||||
|
net_db_bridge_affirmation:
|
||||||
|
driver: bridge
|
||||||
|
net_db_bridge_information:
|
||||||
|
driver: bridge
|
||||||
|
net_db_bridge_senderhome:
|
||||||
|
driver: bridge
|
||||||
|
net_db_bridge_senderforeign:
|
||||||
|
driver: bridge
|
||||||
|
net_rabbit_bridge_request:
|
||||||
|
driver: bridge
|
||||||
|
net_db_bridge_shutdown:
|
||||||
|
driver: bridge
|
||||||
|
net_rabbit_bridge_collected:
|
||||||
|
driver: bridge
|
||||||
|
net_rabbit_bridge_affirmation:
|
||||||
|
driver: bridge
|
||||||
|
net_rabbit_bridge_information:
|
||||||
|
driver: bridge
|
||||||
|
net_rabbit_bridge_senderhome:
|
||||||
|
driver: bridge
|
||||||
|
net_rabbit_bridge_senderforeign:
|
||||||
|
driver: bridge
|
@ -8,6 +8,7 @@
|
|||||||
"watcher:signature-request": "./scripts/start-worker.sh watcher signature-request-watcher",
|
"watcher:signature-request": "./scripts/start-worker.sh watcher signature-request-watcher",
|
||||||
"watcher:collected-signatures": "./scripts/start-worker.sh watcher collected-signatures-watcher",
|
"watcher:collected-signatures": "./scripts/start-worker.sh watcher collected-signatures-watcher",
|
||||||
"watcher:affirmation-request": "./scripts/start-worker.sh watcher affirmation-request-watcher",
|
"watcher:affirmation-request": "./scripts/start-worker.sh watcher affirmation-request-watcher",
|
||||||
|
"watcher:information-request": "./scripts/start-worker.sh watcher information-request-watcher",
|
||||||
"watcher:transfer": "./scripts/start-worker.sh watcher transfer-watcher",
|
"watcher:transfer": "./scripts/start-worker.sh watcher transfer-watcher",
|
||||||
"sender:home": "./scripts/start-worker.sh sender home-sender",
|
"sender:home": "./scripts/start-worker.sh sender home-sender",
|
||||||
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
|
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
require('../env')
|
require('../env')
|
||||||
|
|
||||||
const { BRIDGE_VALIDATORS_ABI } = require('../../commons')
|
const { BRIDGE_VALIDATORS_ABI } = require('../../commons')
|
||||||
const { web3Home, web3Foreign } = require('../src/services/web3')
|
const { home, foreign } = require('../config/base.config')
|
||||||
const { bridgeConfig } = require('../config/base.config')
|
|
||||||
|
|
||||||
const homeABI = bridgeConfig.homeBridgeAbi
|
async function getStartBlock(bridgeContract, web3) {
|
||||||
const foreignABI = bridgeConfig.foreignBridgeAbi
|
|
||||||
|
|
||||||
async function getStartBlock(web3, bridgeAddress, bridgeAbi) {
|
|
||||||
try {
|
try {
|
||||||
const bridgeContract = new web3.eth.Contract(bridgeAbi, bridgeAddress)
|
|
||||||
|
|
||||||
const deployedAtBlock = await bridgeContract.methods.deployedAtBlock().call()
|
const deployedAtBlock = await bridgeContract.methods.deployedAtBlock().call()
|
||||||
|
|
||||||
const validatorContractAddress = await bridgeContract.methods.validatorContract().call()
|
const validatorContractAddress = await bridgeContract.methods.validatorContract().call()
|
||||||
@ -30,10 +24,8 @@ async function getStartBlock(web3, bridgeAddress, bridgeAbi) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
|
const homeStartBlock = await getStartBlock(home.bridgeContract, home.web3)
|
||||||
|
const foreignStartBlock = await getStartBlock(foreign.bridgeContract, foreign.web3)
|
||||||
const homeStartBlock = await getStartBlock(web3Home, COMMON_HOME_BRIDGE_ADDRESS, homeABI)
|
|
||||||
const foreignStartBlock = await getStartBlock(web3Foreign, COMMON_FOREIGN_BRIDGE_ADDRESS, foreignABI)
|
|
||||||
const result = {
|
const result = {
|
||||||
homeStartBlock,
|
homeStartBlock,
|
||||||
foreignStartBlock
|
foreignStartBlock
|
||||||
|
@ -25,10 +25,9 @@ const processTransfers = require('./events/processTransfers')(config)
|
|||||||
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
|
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
|
||||||
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
|
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
|
||||||
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
|
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
|
||||||
|
const processAMBInformationRequests = require('./events/processAMBInformationRequests')(config)
|
||||||
|
|
||||||
const web3Instance = config.web3
|
const { web3, eventContract } = config.main
|
||||||
const { eventContractAddress } = config
|
|
||||||
const eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress)
|
|
||||||
|
|
||||||
let attached
|
let attached
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ async function initialize() {
|
|||||||
try {
|
try {
|
||||||
const checkHttps = checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger)
|
const checkHttps = checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger)
|
||||||
|
|
||||||
web3Instance.currentProvider.urls.forEach(checkHttps(config.chain))
|
web3.currentProvider.urls.forEach(checkHttps(config.chain))
|
||||||
|
|
||||||
attached = await isAttached()
|
attached = await isAttached()
|
||||||
if (attached) {
|
if (attached) {
|
||||||
@ -93,6 +92,8 @@ function processEvents(events) {
|
|||||||
return processAMBCollectedSignatures(events)
|
return processAMBCollectedSignatures(events)
|
||||||
case 'amb-affirmation-request':
|
case 'amb-affirmation-request':
|
||||||
return processAMBAffirmationRequests(events)
|
return processAMBAffirmationRequests(events)
|
||||||
|
case 'amb-information-request':
|
||||||
|
return processAMBInformationRequests(events)
|
||||||
default:
|
default:
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -101,7 +102,7 @@ function processEvents(events) {
|
|||||||
async function main({ sendJob, txHash }) {
|
async function main({ sendJob, txHash }) {
|
||||||
try {
|
try {
|
||||||
const events = await getEventsFromTx({
|
const events = await getEventsFromTx({
|
||||||
web3: web3Instance,
|
web3,
|
||||||
contract: eventContract,
|
contract: eventContract,
|
||||||
event: config.event,
|
event: config.event,
|
||||||
txHash,
|
txHash,
|
||||||
@ -128,8 +129,8 @@ async function main({ sendJob, txHash }) {
|
|||||||
|
|
||||||
async function sendJobTx(jobs) {
|
async function sendJobTx(jobs) {
|
||||||
const gasPrice = await GasPrice.start(config.chain, true)
|
const gasPrice = await GasPrice.start(config.chain, true)
|
||||||
const chainId = await getChainId(web3Instance)
|
const chainId = await getChainId(web3)
|
||||||
let nonce = await getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
|
let nonce = await getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
|
||||||
|
|
||||||
await syncForEach(jobs, async job => {
|
await syncForEach(jobs, async job => {
|
||||||
let gasLimit
|
let gasLimit
|
||||||
@ -150,7 +151,7 @@ async function sendJobTx(jobs) {
|
|||||||
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
|
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
|
||||||
to: job.to,
|
to: job.to,
|
||||||
chainId,
|
chainId,
|
||||||
web3: web3Instance
|
web3
|
||||||
})
|
})
|
||||||
|
|
||||||
nonce++
|
nonce++
|
||||||
@ -166,7 +167,7 @@ async function sendJobTx(jobs) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (e.message.toLowerCase().includes('insufficient funds')) {
|
if (e.message.toLowerCase().includes('insufficient funds')) {
|
||||||
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
|
const currentBalance = await web3.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
|
||||||
const minimumBalance = gasLimit.multipliedBy(gasPrice)
|
const minimumBalance = gasLimit.multipliedBy(gasPrice)
|
||||||
logger.error(
|
logger.error(
|
||||||
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`
|
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`
|
||||||
|
@ -2,8 +2,7 @@ require('dotenv').config()
|
|||||||
const promiseLimit = require('promise-limit')
|
const promiseLimit = require('promise-limit')
|
||||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const { web3Home } = require('../../services/web3')
|
const { getValidatorContract } = require('../../tx/web3')
|
||||||
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
|
|
||||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
|
const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
|
||||||
const estimateGas = require('./estimateGas')
|
const estimateGas = require('./estimateGas')
|
||||||
const { parseAMBMessage } = require('../../../../commons')
|
const { parseAMBMessage } = require('../../../../commons')
|
||||||
@ -11,20 +10,16 @@ const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = req
|
|||||||
|
|
||||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||||
|
|
||||||
let validatorContract = null
|
|
||||||
|
|
||||||
function processAffirmationRequestsBuilder(config) {
|
function processAffirmationRequestsBuilder(config) {
|
||||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
const { bridgeContract, web3 } = config.home
|
||||||
|
|
||||||
|
let validatorContract = null
|
||||||
|
|
||||||
return async function processAffirmationRequests(affirmationRequests) {
|
return async function processAffirmationRequests(affirmationRequests) {
|
||||||
const txToSend = []
|
const txToSend = []
|
||||||
|
|
||||||
if (validatorContract === null) {
|
if (validatorContract === null) {
|
||||||
rootLogger.debug('Getting validator contract address')
|
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
|
||||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
|
||||||
|
|
||||||
validatorContract = new web3Home.eth.Contract(bridgeValidatorsABI, validatorContractAddress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootLogger.debug(`Processing ${affirmationRequests.length} AffirmationRequest events`)
|
rootLogger.debug(`Processing ${affirmationRequests.length} AffirmationRequest events`)
|
||||||
@ -45,8 +40,8 @@ function processAffirmationRequestsBuilder(config) {
|
|||||||
try {
|
try {
|
||||||
logger.debug('Estimate gas')
|
logger.debug('Estimate gas')
|
||||||
gasEstimate = await estimateGas({
|
gasEstimate = await estimateGas({
|
||||||
web3: web3Home,
|
web3,
|
||||||
homeBridge,
|
homeBridge: bridgeContract,
|
||||||
validatorContract,
|
validatorContract,
|
||||||
message,
|
message,
|
||||||
address: config.validatorAddress
|
address: config.validatorAddress
|
||||||
@ -70,14 +65,13 @@ function processAffirmationRequestsBuilder(config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await homeBridge.methods.executeAffirmation(message).encodeABI()
|
const data = bridgeContract.methods.executeAffirmation(message).encodeABI()
|
||||||
|
|
||||||
txToSend.push({
|
txToSend.push({
|
||||||
data,
|
data,
|
||||||
gasEstimate,
|
gasEstimate,
|
||||||
extraGas: EXTRA_GAS_ABSOLUTE,
|
extraGas: EXTRA_GAS_ABSOLUTE,
|
||||||
transactionReference: affirmationRequest.transactionHash,
|
transactionReference: affirmationRequest.transactionHash,
|
||||||
to: config.homeBridgeAddress
|
to: config.home.bridgeAddress
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(promise => limit(promise))
|
.map(promise => limit(promise))
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
require('dotenv').config()
|
require('dotenv').config()
|
||||||
const promiseLimit = require('promise-limit')
|
const promiseLimit = require('promise-limit')
|
||||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
|
const { getValidatorContract } = require('../../tx/web3')
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const { web3Home, web3Foreign } = require('../../services/web3')
|
|
||||||
const { signatureToVRS, packSignatures } = require('../../utils/message')
|
const { signatureToVRS, packSignatures } = require('../../utils/message')
|
||||||
const { readAccessListFile } = require('../../utils/utils')
|
const { readAccessListFile } = require('../../utils/utils')
|
||||||
const { parseAMBMessage } = require('../../../../commons')
|
const { parseAMBMessage } = require('../../../../commons')
|
||||||
@ -19,22 +18,16 @@ const {
|
|||||||
ORACLE_ALWAYS_RELAY_SIGNATURES
|
ORACLE_ALWAYS_RELAY_SIGNATURES
|
||||||
} = process.env
|
} = process.env
|
||||||
|
|
||||||
let validatorContract = null
|
|
||||||
|
|
||||||
function processCollectedSignaturesBuilder(config) {
|
function processCollectedSignaturesBuilder(config) {
|
||||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
const { home, foreign } = config
|
||||||
|
|
||||||
const foreignBridge = new web3Foreign.eth.Contract(config.foreignBridgeAbi, config.foreignBridgeAddress)
|
let validatorContract = null
|
||||||
|
|
||||||
return async function processCollectedSignatures(signatures) {
|
return async function processCollectedSignatures(signatures) {
|
||||||
const txToSend = []
|
const txToSend = []
|
||||||
|
|
||||||
if (validatorContract === null) {
|
if (validatorContract === null) {
|
||||||
rootLogger.debug('Getting validator contract address')
|
validatorContract = await getValidatorContract(foreign.bridgeContract, foreign.web3)
|
||||||
const validatorContractAddress = await foreignBridge.methods.validatorContract().call()
|
|
||||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
|
||||||
|
|
||||||
validatorContract = new web3Foreign.eth.Contract(bridgeValidatorsABI, validatorContractAddress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootLogger.debug(`Processing ${signatures.length} CollectedSignatures events`)
|
rootLogger.debug(`Processing ${signatures.length} CollectedSignatures events`)
|
||||||
@ -48,13 +41,13 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
|
|
||||||
if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
|
if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
|
||||||
logger.debug('Validator handles all CollectedSignature requests')
|
logger.debug('Validator handles all CollectedSignature requests')
|
||||||
} else if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) {
|
} else if (authorityResponsibleForRelay !== home.web3.utils.toChecksumAddress(config.validatorAddress)) {
|
||||||
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
|
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
|
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
|
||||||
const message = await homeBridge.methods.message(messageHash).call()
|
const message = await home.bridgeContract.methods.message(messageHash).call()
|
||||||
const parsedMessage = parseAMBMessage(message)
|
const parsedMessage = parseAMBMessage(message)
|
||||||
|
|
||||||
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
|
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
|
||||||
@ -102,7 +95,7 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
logger.debug('Getting message signatures')
|
logger.debug('Getting message signatures')
|
||||||
const signaturePromises = requiredSignatures.map(async (el, index) => {
|
const signaturePromises = requiredSignatures.map(async (el, index) => {
|
||||||
logger.debug({ index }, 'Getting message signature')
|
logger.debug({ index }, 'Getting message signature')
|
||||||
const signature = await homeBridge.methods.signature(messageHash, index).call()
|
const signature = await home.bridgeContract.methods.signature(messageHash, index).call()
|
||||||
const vrs = signatureToVRS(signature)
|
const vrs = signatureToVRS(signature)
|
||||||
v.push(vrs.v)
|
v.push(vrs.v)
|
||||||
r.push(vrs.r)
|
r.push(vrs.r)
|
||||||
@ -120,7 +113,7 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
try {
|
try {
|
||||||
logger.debug('Estimate gas')
|
logger.debug('Estimate gas')
|
||||||
gasEstimate = await estimateGas({
|
gasEstimate = await estimateGas({
|
||||||
foreignBridge,
|
foreignBridge: foreign.bridgeContract,
|
||||||
validatorContract,
|
validatorContract,
|
||||||
v,
|
v,
|
||||||
r,
|
r,
|
||||||
@ -146,14 +139,13 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const data = await foreignBridge.methods.executeSignatures(message, signatures).encodeABI()
|
const data = foreign.bridgeContract.methods.executeSignatures(message, signatures).encodeABI()
|
||||||
|
|
||||||
txToSend.push({
|
txToSend.push({
|
||||||
data,
|
data,
|
||||||
gasEstimate,
|
gasEstimate,
|
||||||
extraGas: EXTRA_GAS_ABSOLUTE,
|
extraGas: EXTRA_GAS_ABSOLUTE,
|
||||||
transactionReference: colSignature.transactionHash,
|
transactionReference: colSignature.transactionHash,
|
||||||
to: config.foreignBridgeAddress
|
to: config.foreign.bridgeAddress
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(promise => limit(promise))
|
.map(promise => limit(promise))
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
'eth_blockNumber()': async (web3, _, block) => [true, web3.eth.abi.encodeParameter('uint256', block.number)]
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
const { zipToObject } = require('../../../utils/utils')
|
||||||
|
|
||||||
|
const argTypes = {
|
||||||
|
to: 'address',
|
||||||
|
from: 'address',
|
||||||
|
gas: 'uint256',
|
||||||
|
data: 'bytes',
|
||||||
|
blockNumber: 'uint256'
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCall(argNames) {
|
||||||
|
return async function(web3, data, foreignBlock) {
|
||||||
|
const types = argNames.map(name => argTypes[name])
|
||||||
|
const args = web3.eth.abi.decodeParameters(types, data)
|
||||||
|
const { blockNumber, ...opts } = zipToObject(argNames, args)
|
||||||
|
|
||||||
|
if (blockNumber && toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
|
return [false, '0x']
|
||||||
|
}
|
||||||
|
|
||||||
|
const [status, result] = await web3.eth
|
||||||
|
.call(opts, blockNumber || foreignBlock.number)
|
||||||
|
.then(result => [true, result], err => [false, err.data])
|
||||||
|
|
||||||
|
return [status, web3.eth.abi.encodeParameter('bytes', result)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'eth_call(address,bytes)': makeCall(['to', 'data']),
|
||||||
|
'eth_call(address,bytes,uint256)': makeCall(['to', 'data', 'blockNumber']),
|
||||||
|
'eth_call(address,address,bytes)': makeCall(['to', 'from', 'data']),
|
||||||
|
'eth_call(address,address,bytes,uint256)': makeCall(['to', 'from', 'data', 'blockNumber']),
|
||||||
|
'eth_call(address,uint256,bytes)': makeCall(['to', 'gas', 'data']),
|
||||||
|
'eth_call(address,uint256,bytes,uint256)': makeCall(['to', 'gas', 'data', 'blockNumber']),
|
||||||
|
'eth_call(address,address,uint256,bytes)': makeCall(['to', 'from', 'gas', 'data']),
|
||||||
|
'eth_call(address,address,uint256,bytes,uint256)': makeCall(['to', 'from', 'gas', 'data', 'blockNumber'])
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
async function call(web3, data, foreignBlock) {
|
||||||
|
const address = web3.eth.abi.decodeParameter('address', data)
|
||||||
|
|
||||||
|
const balance = await web3.eth.getBalance(address, foreignBlock.number)
|
||||||
|
|
||||||
|
return [true, web3.eth.abi.encodeParameter('uint256', balance)]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callArchive(web3, data, foreignBlock) {
|
||||||
|
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
|
||||||
|
|
||||||
|
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
|
return [false, '0x']
|
||||||
|
}
|
||||||
|
|
||||||
|
const balance = await web3.eth.getBalance(address, blockNumber)
|
||||||
|
|
||||||
|
return [true, web3.eth.abi.encodeParameter('uint256', balance)]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'eth_getBalance(address)': call,
|
||||||
|
'eth_getBalance(address,uint256)': callArchive
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
const { serializeBlock } = require('./serializers')
|
||||||
|
|
||||||
|
async function call(web3, data, foreignBlock) {
|
||||||
|
const blockHash = web3.eth.abi.decodeParameter('bytes32', data)
|
||||||
|
|
||||||
|
const block = await web3.eth.getBlock(blockHash)
|
||||||
|
|
||||||
|
if (block === null || block.number > foreignBlock.number) {
|
||||||
|
return [false, '0x']
|
||||||
|
}
|
||||||
|
|
||||||
|
return [true, serializeBlock(web3, block)]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'eth_getBlockByHash(bytes32)': call
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
const { serializeBlock } = require('./serializers')
|
||||||
|
|
||||||
|
async function call(web3, data, foreignBlock) {
|
||||||
|
const blockNumber = web3.eth.abi.decodeParameter('uint256', data)
|
||||||
|
|
||||||
|
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
|
return [false, '0x']
|
||||||
|
}
|
||||||
|
|
||||||
|
const block = await web3.eth.getBlock(blockNumber)
|
||||||
|
|
||||||
|
return [true, serializeBlock(web3, block)]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'eth_getBlockByNumber()': async (web3, _, block) => [true, serializeBlock(web3, block)],
|
||||||
|
'eth_getBlockByNumber(uint256)': call
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
async function call(web3, data, foreignBlock) {
|
||||||
|
const { 0: address, 1: slot } = web3.eth.abi.decodeParameters(['address', 'bytes32'], data)
|
||||||
|
|
||||||
|
const value = await web3.eth.getStorageAt(address, slot, foreignBlock.number)
|
||||||
|
|
||||||
|
return [true, web3.eth.abi.encodeParameter('bytes32', value)]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callArchive(web3, data, foreignBlock) {
|
||||||
|
const { 0: address, 1: slot, 2: blockNumber } = web3.eth.abi.decodeParameters(['address', 'bytes32', 'uint256'], data)
|
||||||
|
|
||||||
|
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
|
return [false, '0x']
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = await web3.eth.getStorageAt(address, slot, blockNumber)
|
||||||
|
|
||||||
|
return [true, web3.eth.abi.encodeParameter('bytes32', value)]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'eth_getStorageAt(address,bytes32)': call,
|
||||||
|
'eth_getStorageAt(address,bytes32,uint256)': callArchive
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
const { serializeTx } = require('./serializers')
|
||||||
|
|
||||||
|
async function call(web3, data, foreignBlock) {
|
||||||
|
const hash = web3.eth.abi.decodeParameter('bytes32', data)
|
||||||
|
|
||||||
|
const tx = await web3.eth.getTransaction(hash)
|
||||||
|
|
||||||
|
if (tx === null || tx.blockNumber > foreignBlock.number) {
|
||||||
|
return [false, '0x']
|
||||||
|
}
|
||||||
|
|
||||||
|
return [true, serializeTx(web3, tx)]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'eth_getTransactionByHash(bytes32)': call
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
async function call(web3, data, foreignBlock) {
|
||||||
|
const address = web3.eth.abi.decodeParameter('address', data)
|
||||||
|
|
||||||
|
const nonce = await web3.eth.getTransactionCount(address, foreignBlock.number)
|
||||||
|
|
||||||
|
return [true, web3.eth.abi.encodeParameter('uint256', nonce)]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callArchive(web3, data, foreignBlock) {
|
||||||
|
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
|
||||||
|
|
||||||
|
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
|
return [false, '0x']
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonce = await web3.eth.getTransactionCount(address, blockNumber)
|
||||||
|
|
||||||
|
return [true, web3.eth.abi.encodeParameter('uint256', nonce)]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'eth_getTransactionCount(address)': call,
|
||||||
|
'eth_getTransactionCount(address,uint256)': callArchive
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
const { serializeReceipt } = require('./serializers')
|
||||||
|
|
||||||
|
async function call(web3, data, foreignBlock) {
|
||||||
|
const hash = web3.eth.abi.decodeParameter('bytes32', data)
|
||||||
|
|
||||||
|
const receipt = await web3.eth.getTransactionReceipt(hash)
|
||||||
|
|
||||||
|
if (receipt === null || receipt.blockNumber > foreignBlock.number) {
|
||||||
|
return [false, '0x']
|
||||||
|
}
|
||||||
|
|
||||||
|
return [true, serializeReceipt(web3, receipt)]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'eth_getTransactionReceipt(bytes32)': call
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
const { ZERO_ADDRESS } = require('../../../../../commons')
|
||||||
|
|
||||||
|
const serializeBlock = (web3, block) => {
|
||||||
|
const args = [block.number, block.hash, block.miner]
|
||||||
|
const types = ['uint256', 'bytes32', 'address']
|
||||||
|
return web3.eth.abi.encodeParameters(types, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializeTx = (web3, tx) => {
|
||||||
|
const args = [
|
||||||
|
tx.hash,
|
||||||
|
tx.blockNumber,
|
||||||
|
tx.from,
|
||||||
|
tx.to || ZERO_ADDRESS,
|
||||||
|
tx.value,
|
||||||
|
tx.nonce,
|
||||||
|
tx.gas,
|
||||||
|
tx.gasPrice,
|
||||||
|
tx.input
|
||||||
|
]
|
||||||
|
const types = ['bytes32', 'uint256', 'address', 'address', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes']
|
||||||
|
return web3.eth.abi.encodeParameters(types, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeLog = log => [log.address, log.topics, log.data]
|
||||||
|
|
||||||
|
const serializeReceipt = (web3, receipt) => {
|
||||||
|
const args = [receipt.transactionHash, receipt.blockNumber, receipt.status, receipt.logs.map(normalizeLog)]
|
||||||
|
const types = ['bytes32', 'uint256', 'bool', '(address,bytes32[],bytes)[]']
|
||||||
|
return web3.eth.abi.encodeParameters(types, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
serializeBlock,
|
||||||
|
serializeTx,
|
||||||
|
serializeReceipt
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
|
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||||
|
const logger = require('../../services/logger').child({
|
||||||
|
module: 'processInformationRequests:estimateGas'
|
||||||
|
})
|
||||||
|
const { strip0x } = require('../../../../commons')
|
||||||
|
const { AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: estimateExtraGas } = require('../../utils/constants')
|
||||||
|
|
||||||
|
async function estimateGas({ web3, homeBridge, validatorContract, messageId, status, result, address }) {
|
||||||
|
try {
|
||||||
|
const gasEstimate = await homeBridge.methods.confirmInformation(messageId, status, result).estimateGas({
|
||||||
|
from: address
|
||||||
|
})
|
||||||
|
|
||||||
|
// message length in bytes
|
||||||
|
const len = strip0x(result).length / 2
|
||||||
|
|
||||||
|
const callbackGasLimit = parseInt(await homeBridge.methods.maxGasPerTx().call(), 10)
|
||||||
|
|
||||||
|
return gasEstimate + callbackGasLimit + estimateExtraGas(len)
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof HttpListProviderError) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageHash = web3.utils.soliditySha3(messageId, status, result)
|
||||||
|
const senderHash = web3.utils.soliditySha3(address, messageHash)
|
||||||
|
|
||||||
|
// Check if minimum number of validations was already reached
|
||||||
|
logger.debug('Check if minimum number of validations was already reached')
|
||||||
|
const numAffirmationsSigned = await homeBridge.methods.numAffirmationsSigned(messageHash).call()
|
||||||
|
const alreadyProcessed = await homeBridge.methods.isAlreadyProcessed(numAffirmationsSigned).call()
|
||||||
|
|
||||||
|
if (alreadyProcessed) {
|
||||||
|
throw new AlreadyProcessedError(e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the message was already signed by this validator
|
||||||
|
logger.debug('Check if the message was already signed')
|
||||||
|
const alreadySigned = await homeBridge.methods.affirmationsSigned(senderHash).call()
|
||||||
|
|
||||||
|
if (alreadySigned) {
|
||||||
|
throw new AlreadySignedError(e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if address is validator
|
||||||
|
logger.debug('Check if address is a validator')
|
||||||
|
const isValidator = await validatorContract.methods.isValidator(address).call()
|
||||||
|
|
||||||
|
if (!isValidator) {
|
||||||
|
throw new InvalidValidatorError(`${address} is not a validator`)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unknown error while processing message')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = estimateGas
|
137
oracle/src/events/processAMBInformationRequests/index.js
Normal file
137
oracle/src/events/processAMBInformationRequests/index.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
require('dotenv').config()
|
||||||
|
const promiseLimit = require('promise-limit')
|
||||||
|
const { soliditySha3 } = require('web3').utils
|
||||||
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
|
const rootLogger = require('../../services/logger')
|
||||||
|
const makeBlockFinder = require('../../services/blockFinder')
|
||||||
|
const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
|
||||||
|
const estimateGas = require('./estimateGas')
|
||||||
|
const { getValidatorContract, getBlock, getBlockNumber, getRequiredBlockConfirmations } = require('../../tx/web3')
|
||||||
|
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||||
|
|
||||||
|
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||||
|
|
||||||
|
const asyncCalls = {
|
||||||
|
...require('./calls/ethCall'),
|
||||||
|
...require('./calls/ethBlockNumber'),
|
||||||
|
...require('./calls/ethGetBlockByNumber'),
|
||||||
|
...require('./calls/ethGetBlockByHash'),
|
||||||
|
...require('./calls/ethGetTransactionByHash'),
|
||||||
|
...require('./calls/ethGetTransactionReceipt'),
|
||||||
|
...require('./calls/ethGetBalance'),
|
||||||
|
...require('./calls/ethGetTransactionCount'),
|
||||||
|
...require('./calls/ethGetStorageAt')
|
||||||
|
}
|
||||||
|
|
||||||
|
const asyncCallsSelectorsMapping = {}
|
||||||
|
Object.keys(asyncCalls).forEach(method => {
|
||||||
|
asyncCallsSelectorsMapping[soliditySha3(method)] = method
|
||||||
|
})
|
||||||
|
|
||||||
|
function processInformationRequestsBuilder(config) {
|
||||||
|
const { home, foreign, web3ForeignArchive } = config
|
||||||
|
|
||||||
|
let validatorContract = null
|
||||||
|
let blockFinder = null
|
||||||
|
|
||||||
|
return async function processInformationRequests(informationRequests) {
|
||||||
|
const txToSend = []
|
||||||
|
|
||||||
|
if (validatorContract === null) {
|
||||||
|
validatorContract = await getValidatorContract(home.bridgeContract, home.web3)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockFinder === null) {
|
||||||
|
rootLogger.debug('Initializing block finder')
|
||||||
|
blockFinder = await makeBlockFinder('foreign', foreign.web3)
|
||||||
|
}
|
||||||
|
|
||||||
|
const foreignBlockNumber =
|
||||||
|
(await getBlockNumber(foreign.web3)) - (await getRequiredBlockConfirmations(foreign.bridgeContract))
|
||||||
|
const homeBlock = await getBlock(home.web3, informationRequests[0].blockNumber)
|
||||||
|
const lastForeignBlock = await getBlock(foreign.web3, foreignBlockNumber)
|
||||||
|
|
||||||
|
if (homeBlock.timestamp > lastForeignBlock.timestamp) {
|
||||||
|
rootLogger.debug(
|
||||||
|
{ homeTimestamp: homeBlock.timestamp, foreignTimestamp: lastForeignBlock.timestamp },
|
||||||
|
`Waiting for the closest foreign block to be confirmed`
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const foreignClosestBlock = await blockFinder(homeBlock.timestamp, lastForeignBlock)
|
||||||
|
|
||||||
|
rootLogger.debug(`Processing ${informationRequests.length} UserRequestForInformation events`)
|
||||||
|
const callbacks = informationRequests
|
||||||
|
.map(informationRequest => async () => {
|
||||||
|
const { messageId, requestSelector, data } = informationRequest.returnValues
|
||||||
|
|
||||||
|
const logger = rootLogger.child({
|
||||||
|
eventTransactionHash: informationRequest.transactionHash,
|
||||||
|
eventMessageId: messageId
|
||||||
|
})
|
||||||
|
|
||||||
|
const asyncCallMethod = asyncCallsSelectorsMapping[requestSelector]
|
||||||
|
|
||||||
|
if (!asyncCallMethod) {
|
||||||
|
logger.warn({ requestSelector }, 'Unknown async request selector received')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.info({ requestSelector, method: asyncCallMethod, data }, 'Processing async request')
|
||||||
|
|
||||||
|
const call = asyncCalls[asyncCallMethod]
|
||||||
|
const [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => {
|
||||||
|
if (e instanceof HttpListProviderError) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
return [false, '0x']
|
||||||
|
})
|
||||||
|
logger.info({ requestSelector, method: asyncCallMethod, status, result }, 'Request result obtained')
|
||||||
|
|
||||||
|
let gasEstimate
|
||||||
|
try {
|
||||||
|
logger.debug('Estimate gas')
|
||||||
|
gasEstimate = await estimateGas({
|
||||||
|
web3: home.web3,
|
||||||
|
homeBridge: home.bridgeContract,
|
||||||
|
validatorContract,
|
||||||
|
messageId,
|
||||||
|
status,
|
||||||
|
result,
|
||||||
|
address: config.validatorAddress
|
||||||
|
})
|
||||||
|
logger.debug({ gasEstimate }, 'Gas estimated')
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof HttpListProviderError) {
|
||||||
|
throw new Error('RPC Connection Error: confirmInformation Gas Estimate cannot be obtained.')
|
||||||
|
} else if (e instanceof InvalidValidatorError) {
|
||||||
|
logger.fatal({ address: config.validatorAddress }, 'Invalid validator')
|
||||||
|
process.exit(EXIT_CODES.INCOMPATIBILITY)
|
||||||
|
} else if (e instanceof AlreadySignedError) {
|
||||||
|
logger.info(`Already signed informationRequest ${messageId}`)
|
||||||
|
return
|
||||||
|
} else if (e instanceof AlreadyProcessedError) {
|
||||||
|
logger.info(`informationRequest ${messageId} was already processed by other validators`)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
logger.error(e, 'Unknown error while processing transaction')
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmationData = home.bridgeContract.methods.confirmInformation(messageId, status, result).encodeABI()
|
||||||
|
txToSend.push({
|
||||||
|
data: confirmationData,
|
||||||
|
gasEstimate,
|
||||||
|
extraGas: EXTRA_GAS_ABSOLUTE,
|
||||||
|
transactionReference: informationRequest.transactionHash,
|
||||||
|
to: config.home.bridgeAddress
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(promise => limit(promise))
|
||||||
|
|
||||||
|
await Promise.all(callbacks)
|
||||||
|
return txToSend
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processInformationRequestsBuilder
|
@ -1,9 +1,8 @@
|
|||||||
require('dotenv').config()
|
require('dotenv').config()
|
||||||
const promiseLimit = require('promise-limit')
|
const promiseLimit = require('promise-limit')
|
||||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
|
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const { web3Home } = require('../../services/web3')
|
const { getValidatorContract } = require('../../tx/web3')
|
||||||
const { parseAMBMessage } = require('../../../../commons')
|
const { parseAMBMessage } = require('../../../../commons')
|
||||||
const estimateGas = require('../processSignatureRequests/estimateGas')
|
const estimateGas = require('../processSignatureRequests/estimateGas')
|
||||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||||
@ -13,20 +12,16 @@ const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
|
|||||||
|
|
||||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||||
|
|
||||||
let validatorContract = null
|
|
||||||
|
|
||||||
function processSignatureRequestsBuilder(config) {
|
function processSignatureRequestsBuilder(config) {
|
||||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
const { bridgeContract, web3 } = config.home
|
||||||
|
|
||||||
|
let validatorContract = null
|
||||||
|
|
||||||
return async function processSignatureRequests(signatureRequests) {
|
return async function processSignatureRequests(signatureRequests) {
|
||||||
const txToSend = []
|
const txToSend = []
|
||||||
|
|
||||||
if (validatorContract === null) {
|
if (validatorContract === null) {
|
||||||
rootLogger.debug('Getting validator contract address')
|
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
|
||||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
|
||||||
|
|
||||||
validatorContract = new web3Home.eth.Contract(bridgeValidatorsABI, validatorContractAddress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootLogger.debug(`Processing ${signatureRequests.length} SignatureRequest events`)
|
rootLogger.debug(`Processing ${signatureRequests.length} SignatureRequest events`)
|
||||||
@ -42,14 +37,14 @@ function processSignatureRequestsBuilder(config) {
|
|||||||
const { sender, executor } = parseAMBMessage(message)
|
const { sender, executor } = parseAMBMessage(message)
|
||||||
logger.info({ sender, executor }, `Processing signatureRequest ${messageId}`)
|
logger.info({ sender, executor }, `Processing signatureRequest ${messageId}`)
|
||||||
|
|
||||||
const signature = web3Home.eth.accounts.sign(message, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
|
const signature = web3.eth.accounts.sign(message, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
|
||||||
|
|
||||||
let gasEstimate
|
let gasEstimate
|
||||||
try {
|
try {
|
||||||
logger.debug('Estimate gas')
|
logger.debug('Estimate gas')
|
||||||
gasEstimate = await estimateGas({
|
gasEstimate = await estimateGas({
|
||||||
web3: web3Home,
|
web3,
|
||||||
homeBridge,
|
homeBridge: bridgeContract,
|
||||||
validatorContract,
|
validatorContract,
|
||||||
signature: signature.signature,
|
signature: signature.signature,
|
||||||
message,
|
message,
|
||||||
@ -74,13 +69,12 @@ function processSignatureRequestsBuilder(config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await homeBridge.methods.submitSignature(signature.signature, message).encodeABI()
|
const data = bridgeContract.methods.submitSignature(signature.signature, message).encodeABI()
|
||||||
|
|
||||||
txToSend.push({
|
txToSend.push({
|
||||||
data,
|
data,
|
||||||
gasEstimate,
|
gasEstimate,
|
||||||
transactionReference: signatureRequest.transactionHash,
|
transactionReference: signatureRequest.transactionHash,
|
||||||
to: config.homeBridgeAddress
|
to: config.home.bridgeAddress
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(promise => limit(promise))
|
.map(promise => limit(promise))
|
||||||
|
@ -2,29 +2,23 @@ require('../../../env')
|
|||||||
const promiseLimit = require('promise-limit')
|
const promiseLimit = require('promise-limit')
|
||||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const { web3Home } = require('../../services/web3')
|
const { getValidatorContract } = require('../../tx/web3')
|
||||||
|
|
||||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
|
||||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
||||||
const estimateGas = require('./estimateGas')
|
const estimateGas = require('./estimateGas')
|
||||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||||
|
|
||||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||||
|
|
||||||
let validatorContract = null
|
|
||||||
|
|
||||||
function processAffirmationRequestsBuilder(config) {
|
function processAffirmationRequestsBuilder(config) {
|
||||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
const { bridgeContract, web3 } = config.home
|
||||||
|
|
||||||
|
let validatorContract = null
|
||||||
|
|
||||||
return async function processAffirmationRequests(affirmationRequests) {
|
return async function processAffirmationRequests(affirmationRequests) {
|
||||||
const txToSend = []
|
const txToSend = []
|
||||||
|
|
||||||
if (validatorContract === null) {
|
if (validatorContract === null) {
|
||||||
rootLogger.debug('Getting validator contract address')
|
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
|
||||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
|
||||||
|
|
||||||
validatorContract = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootLogger.debug(`Processing ${affirmationRequests.length} AffirmationRequest events`)
|
rootLogger.debug(`Processing ${affirmationRequests.length} AffirmationRequest events`)
|
||||||
@ -42,8 +36,8 @@ function processAffirmationRequestsBuilder(config) {
|
|||||||
try {
|
try {
|
||||||
logger.debug('Estimate gas')
|
logger.debug('Estimate gas')
|
||||||
gasEstimate = await estimateGas({
|
gasEstimate = await estimateGas({
|
||||||
web3: web3Home,
|
web3,
|
||||||
homeBridge,
|
homeBridge: bridgeContract,
|
||||||
validatorContract,
|
validatorContract,
|
||||||
recipient,
|
recipient,
|
||||||
value,
|
value,
|
||||||
@ -71,15 +65,14 @@ function processAffirmationRequestsBuilder(config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await homeBridge.methods
|
const data = bridgeContract.methods
|
||||||
.executeAffirmation(recipient, value, affirmationRequest.transactionHash)
|
.executeAffirmation(recipient, value, affirmationRequest.transactionHash)
|
||||||
.encodeABI({ from: config.validatorAddress })
|
.encodeABI()
|
||||||
|
|
||||||
txToSend.push({
|
txToSend.push({
|
||||||
data,
|
data,
|
||||||
gasEstimate,
|
gasEstimate,
|
||||||
transactionReference: affirmationRequest.transactionHash,
|
transactionReference: affirmationRequest.transactionHash,
|
||||||
to: config.homeBridgeAddress
|
to: config.home.bridgeAddress
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(promise => limit(promise))
|
.map(promise => limit(promise))
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
require('../../../env')
|
require('../../../env')
|
||||||
const promiseLimit = require('promise-limit')
|
const promiseLimit = require('promise-limit')
|
||||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const { web3Home, web3Foreign } = require('../../services/web3')
|
const { getValidatorContract } = require('../../tx/web3')
|
||||||
const { signatureToVRS, packSignatures, parseMessage } = require('../../utils/message')
|
const { signatureToVRS, packSignatures, parseMessage } = require('../../utils/message')
|
||||||
const { readAccessListFile } = require('../../utils/utils')
|
const { readAccessListFile } = require('../../utils/utils')
|
||||||
const estimateGas = require('./estimateGas')
|
const estimateGas = require('./estimateGas')
|
||||||
@ -19,22 +18,16 @@ const {
|
|||||||
|
|
||||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||||
|
|
||||||
let validatorContract = null
|
|
||||||
|
|
||||||
function processCollectedSignaturesBuilder(config) {
|
function processCollectedSignaturesBuilder(config) {
|
||||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
const { home, foreign } = config
|
||||||
|
|
||||||
const foreignBridge = new web3Foreign.eth.Contract(config.foreignBridgeAbi, config.foreignBridgeAddress)
|
let validatorContract = null
|
||||||
|
|
||||||
return async function processCollectedSignatures(signatures) {
|
return async function processCollectedSignatures(signatures) {
|
||||||
const txToSend = []
|
const txToSend = []
|
||||||
|
|
||||||
if (validatorContract === null) {
|
if (validatorContract === null) {
|
||||||
rootLogger.debug('Getting validator contract address')
|
validatorContract = await getValidatorContract(foreign.bridgeContract, foreign.web3)
|
||||||
const validatorContractAddress = await foreignBridge.methods.validatorContract().call()
|
|
||||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
|
||||||
|
|
||||||
validatorContract = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootLogger.debug(`Processing ${signatures.length} CollectedSignatures events`)
|
rootLogger.debug(`Processing ${signatures.length} CollectedSignatures events`)
|
||||||
@ -48,13 +41,13 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
|
|
||||||
if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
|
if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
|
||||||
logger.debug('Validator handles all CollectedSignature requests')
|
logger.debug('Validator handles all CollectedSignature requests')
|
||||||
} else if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) {
|
} else if (authorityResponsibleForRelay !== home.web3.utils.toChecksumAddress(config.validatorAddress)) {
|
||||||
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
|
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
|
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
|
||||||
const message = await homeBridge.methods.message(messageHash).call()
|
const message = await home.bridgeContract.methods.message(messageHash).call()
|
||||||
|
|
||||||
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
|
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
|
||||||
const parsedMessage = parseMessage(message)
|
const parsedMessage = parseMessage(message)
|
||||||
@ -66,7 +59,7 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
if (allowanceList.indexOf(recipient) === -1) {
|
if (allowanceList.indexOf(recipient) === -1) {
|
||||||
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
|
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
|
||||||
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
|
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
|
||||||
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
const sender = (await home.web3.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
||||||
if (allowanceList.indexOf(sender) === -1) {
|
if (allowanceList.indexOf(sender) === -1) {
|
||||||
logger.info(
|
logger.info(
|
||||||
{ sender, recipient },
|
{ sender, recipient },
|
||||||
@ -90,7 +83,7 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
}
|
}
|
||||||
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
|
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
|
||||||
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
|
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
|
||||||
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
const sender = (await home.bridgeContract.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
||||||
if (blockList.indexOf(sender) > -1) {
|
if (blockList.indexOf(sender) > -1) {
|
||||||
logger.info({ sender }, 'Validator skips a transaction. Sender address is in the block list.')
|
logger.info({ sender }, 'Validator skips a transaction. Sender address is in the block list.')
|
||||||
return
|
return
|
||||||
@ -110,7 +103,7 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
logger.debug('Getting message signatures')
|
logger.debug('Getting message signatures')
|
||||||
const signaturePromises = requiredSignatures.map(async (el, index) => {
|
const signaturePromises = requiredSignatures.map(async (el, index) => {
|
||||||
logger.debug({ index }, 'Getting message signature')
|
logger.debug({ index }, 'Getting message signature')
|
||||||
const signature = await homeBridge.methods.signature(messageHash, index).call()
|
const signature = await home.bridgeContract.methods.signature(messageHash, index).call()
|
||||||
const vrs = signatureToVRS(signature)
|
const vrs = signatureToVRS(signature)
|
||||||
v.push(vrs.v)
|
v.push(vrs.v)
|
||||||
r.push(vrs.r)
|
r.push(vrs.r)
|
||||||
@ -125,7 +118,7 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
try {
|
try {
|
||||||
logger.debug('Estimate gas')
|
logger.debug('Estimate gas')
|
||||||
gasEstimate = await estimateGas({
|
gasEstimate = await estimateGas({
|
||||||
foreignBridge,
|
foreignBridge: foreign.bridgeContract,
|
||||||
validatorContract,
|
validatorContract,
|
||||||
v,
|
v,
|
||||||
r,
|
r,
|
||||||
@ -149,12 +142,12 @@ function processCollectedSignaturesBuilder(config) {
|
|||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const data = await foreignBridge.methods.executeSignatures(message, signatures).encodeABI()
|
const data = foreign.bridgeContract.methods.executeSignatures(message, signatures).encodeABI()
|
||||||
txToSend.push({
|
txToSend.push({
|
||||||
data,
|
data,
|
||||||
gasEstimate,
|
gasEstimate,
|
||||||
transactionReference: colSignature.transactionHash,
|
transactionReference: colSignature.transactionHash,
|
||||||
to: config.foreignBridgeAddress
|
to: config.foreign.bridgeAddress
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(promise => limit(promise))
|
.map(promise => limit(promise))
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
require('../../../env')
|
require('../../../env')
|
||||||
const promiseLimit = require('promise-limit')
|
const promiseLimit = require('promise-limit')
|
||||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const { web3Home } = require('../../services/web3')
|
const { getValidatorContract } = require('../../tx/web3')
|
||||||
const { createMessage } = require('../../utils/message')
|
const { createMessage } = require('../../utils/message')
|
||||||
const estimateGas = require('./estimateGas')
|
const estimateGas = require('./estimateGas')
|
||||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||||
@ -13,25 +12,21 @@ const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
|
|||||||
|
|
||||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||||
|
|
||||||
let expectedMessageLength = null
|
|
||||||
let validatorContract = null
|
|
||||||
|
|
||||||
function processSignatureRequestsBuilder(config) {
|
function processSignatureRequestsBuilder(config) {
|
||||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
const { bridgeContract, web3 } = config.home
|
||||||
|
|
||||||
|
let expectedMessageLength = null
|
||||||
|
let validatorContract = null
|
||||||
|
|
||||||
return async function processSignatureRequests(signatureRequests) {
|
return async function processSignatureRequests(signatureRequests) {
|
||||||
const txToSend = []
|
const txToSend = []
|
||||||
|
|
||||||
if (expectedMessageLength === null) {
|
if (expectedMessageLength === null) {
|
||||||
expectedMessageLength = await homeBridge.methods.requiredMessageLength().call()
|
expectedMessageLength = await bridgeContract.methods.requiredMessageLength().call()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validatorContract === null) {
|
if (validatorContract === null) {
|
||||||
rootLogger.debug('Getting validator contract address')
|
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
|
||||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
|
||||||
|
|
||||||
validatorContract = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootLogger.debug(`Processing ${signatureRequests.length} SignatureRequest events`)
|
rootLogger.debug(`Processing ${signatureRequests.length} SignatureRequest events`)
|
||||||
@ -49,18 +44,18 @@ function processSignatureRequestsBuilder(config) {
|
|||||||
recipient,
|
recipient,
|
||||||
value,
|
value,
|
||||||
transactionHash: signatureRequest.transactionHash,
|
transactionHash: signatureRequest.transactionHash,
|
||||||
bridgeAddress: config.foreignBridgeAddress,
|
bridgeAddress: config.foreign.bridgeAddress,
|
||||||
expectedMessageLength
|
expectedMessageLength
|
||||||
})
|
})
|
||||||
|
|
||||||
const signature = web3Home.eth.accounts.sign(message, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
|
const signature = web3.eth.accounts.sign(message, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
|
||||||
|
|
||||||
let gasEstimate
|
let gasEstimate
|
||||||
try {
|
try {
|
||||||
logger.debug('Estimate gas')
|
logger.debug('Estimate gas')
|
||||||
gasEstimate = await estimateGas({
|
gasEstimate = await estimateGas({
|
||||||
web3: web3Home,
|
web3,
|
||||||
homeBridge,
|
homeBridge: bridgeContract,
|
||||||
validatorContract,
|
validatorContract,
|
||||||
signature: signature.signature,
|
signature: signature.signature,
|
||||||
message,
|
message,
|
||||||
@ -87,15 +82,12 @@ function processSignatureRequestsBuilder(config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await homeBridge.methods
|
const data = bridgeContract.methods.submitSignature(signature.signature, message).encodeABI()
|
||||||
.submitSignature(signature.signature, message)
|
|
||||||
.encodeABI({ from: config.validatorAddress })
|
|
||||||
|
|
||||||
txToSend.push({
|
txToSend.push({
|
||||||
data,
|
data,
|
||||||
gasEstimate,
|
gasEstimate,
|
||||||
transactionReference: signatureRequest.transactionHash,
|
transactionReference: signatureRequest.transactionHash,
|
||||||
to: config.homeBridgeAddress
|
to: config.home.bridgeAddress
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(promise => limit(promise))
|
.map(promise => limit(promise))
|
||||||
|
@ -1,35 +1,32 @@
|
|||||||
require('../../../env')
|
require('../../../env')
|
||||||
const promiseLimit = require('promise-limit')
|
const promiseLimit = require('promise-limit')
|
||||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
const { BRIDGE_VALIDATORS_ABI, ZERO_ADDRESS } = require('../../../../commons')
|
const { ZERO_ADDRESS } = require('../../../../commons')
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const { web3Home, web3Foreign } = require('../../services/web3')
|
const { getValidatorContract } = require('../../tx/web3')
|
||||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
||||||
const estimateGas = require('../processAffirmationRequests/estimateGas')
|
const estimateGas = require('../processAffirmationRequests/estimateGas')
|
||||||
|
|
||||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||||
|
|
||||||
let validatorContract = null
|
|
||||||
|
|
||||||
function processTransfersBuilder(config) {
|
function processTransfersBuilder(config) {
|
||||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
const { bridgeContract, web3 } = config.home
|
||||||
const userRequestForAffirmationAbi = config.foreignBridgeAbi.filter(
|
|
||||||
|
const userRequestForAffirmationAbi = config.foreign.bridgeABI.find(
|
||||||
e => e.type === 'event' && e.name === 'UserRequestForAffirmation'
|
e => e.type === 'event' && e.name === 'UserRequestForAffirmation'
|
||||||
)[0]
|
)
|
||||||
const tokensSwappedAbi = config.foreignBridgeAbi.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0]
|
const tokensSwappedAbi = config.foreign.bridgeABI.find(e => e.type === 'event' && e.name === 'TokensSwapped')
|
||||||
const userRequestForAffirmationHash = web3Home.eth.abi.encodeEventSignature(userRequestForAffirmationAbi)
|
const userRequestForAffirmationHash = web3.eth.abi.encodeEventSignature(userRequestForAffirmationAbi)
|
||||||
const tokensSwappedHash = tokensSwappedAbi ? web3Home.eth.abi.encodeEventSignature(tokensSwappedAbi) : '0x'
|
const tokensSwappedHash = tokensSwappedAbi ? web3.eth.abi.encodeEventSignature(tokensSwappedAbi) : '0x'
|
||||||
|
|
||||||
|
let validatorContract = null
|
||||||
|
|
||||||
return async function processTransfers(transfers) {
|
return async function processTransfers(transfers) {
|
||||||
const txToSend = []
|
const txToSend = []
|
||||||
|
|
||||||
if (validatorContract === null) {
|
if (validatorContract === null) {
|
||||||
rootLogger.debug('Getting validator contract address')
|
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
|
||||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
|
||||||
|
|
||||||
validatorContract = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootLogger.debug(`Processing ${transfers.length} Transfer events`)
|
rootLogger.debug(`Processing ${transfers.length} Transfer events`)
|
||||||
@ -43,10 +40,10 @@ function processTransfersBuilder(config) {
|
|||||||
|
|
||||||
logger.info({ from, value }, `Processing transfer ${transfer.transactionHash}`)
|
logger.info({ from, value }, `Processing transfer ${transfer.transactionHash}`)
|
||||||
|
|
||||||
const receipt = await web3Foreign.eth.getTransactionReceipt(transfer.transactionHash)
|
const receipt = await config.foreign.web3.eth.getTransactionReceipt(transfer.transactionHash)
|
||||||
|
|
||||||
const existsAffirmationEvent = receipt.logs.some(
|
const existsAffirmationEvent = receipt.logs.some(
|
||||||
e => e.address === config.foreignBridgeAddress && e.topics[0] === userRequestForAffirmationHash
|
e => e.address === config.foreign.bridgeAddress && e.topics[0] === userRequestForAffirmationHash
|
||||||
)
|
)
|
||||||
|
|
||||||
if (existsAffirmationEvent) {
|
if (existsAffirmationEvent) {
|
||||||
@ -59,7 +56,7 @@ function processTransfersBuilder(config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const existsTokensSwappedEvent = tokensSwappedAbi
|
const existsTokensSwappedEvent = tokensSwappedAbi
|
||||||
? receipt.logs.some(e => e.address === config.foreignBridgeAddress && e.topics[0] === tokensSwappedHash)
|
? receipt.logs.some(e => e.address === config.foreign.bridgeAddress && e.topics[0] === tokensSwappedHash)
|
||||||
: false
|
: false
|
||||||
|
|
||||||
if (from === ZERO_ADDRESS && existsTokensSwappedEvent) {
|
if (from === ZERO_ADDRESS && existsTokensSwappedEvent) {
|
||||||
@ -73,8 +70,8 @@ function processTransfersBuilder(config) {
|
|||||||
try {
|
try {
|
||||||
logger.debug('Estimate gas')
|
logger.debug('Estimate gas')
|
||||||
gasEstimate = await estimateGas({
|
gasEstimate = await estimateGas({
|
||||||
web3: web3Home,
|
web3,
|
||||||
homeBridge,
|
homeBridge: bridgeContract,
|
||||||
validatorContract,
|
validatorContract,
|
||||||
recipient: from,
|
recipient: from,
|
||||||
value,
|
value,
|
||||||
@ -100,15 +97,12 @@ function processTransfersBuilder(config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await homeBridge.methods
|
const data = bridgeContract.methods.executeAffirmation(from, value, transfer.transactionHash).encodeABI()
|
||||||
.executeAffirmation(from, value, transfer.transactionHash)
|
|
||||||
.encodeABI({ from: config.validatorAddress })
|
|
||||||
|
|
||||||
txToSend.push({
|
txToSend.push({
|
||||||
data,
|
data,
|
||||||
gasEstimate,
|
gasEstimate,
|
||||||
transactionReference: transfer.transactionHash,
|
transactionReference: transfer.transactionHash,
|
||||||
to: config.homeBridgeAddress
|
to: config.home.bridgeAddress
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(promise => limit(promise))
|
.map(promise => limit(promise))
|
||||||
|
@ -30,9 +30,8 @@ if (process.argv.length < 3) {
|
|||||||
|
|
||||||
const config = require(path.join('../config/', process.argv[2]))
|
const config = require(path.join('../config/', process.argv[2]))
|
||||||
|
|
||||||
const web3Instance = config.web3
|
const { web3, web3Fallback } = config
|
||||||
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.web3Redundant : config.web3
|
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.web3Redundant : web3
|
||||||
const { web3Fallback } = config
|
|
||||||
|
|
||||||
const nonceKey = `${config.id}:nonce`
|
const nonceKey = `${config.id}:nonce`
|
||||||
let chainId = 0
|
let chainId = 0
|
||||||
@ -41,12 +40,11 @@ async function initialize() {
|
|||||||
try {
|
try {
|
||||||
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
|
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
|
||||||
|
|
||||||
web3Instance.currentProvider.urls.forEach(checkHttps(config.chain))
|
web3.currentProvider.urls.forEach(checkHttps(config.chain))
|
||||||
|
|
||||||
GasPrice.start(config.id)
|
GasPrice.start(config.id)
|
||||||
|
|
||||||
chainId = await getChainId(web3Instance)
|
chainId = await getChainId(web3)
|
||||||
|
|
||||||
connectQueue()
|
connectQueue()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e.message)
|
logger.error(e.message)
|
||||||
@ -86,7 +84,7 @@ async function readNonce(forceUpdate) {
|
|||||||
logger.debug('Reading nonce')
|
logger.debug('Reading nonce')
|
||||||
if (forceUpdate) {
|
if (forceUpdate) {
|
||||||
logger.debug('Forcing update of nonce')
|
logger.debug('Forcing update of nonce')
|
||||||
return getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
|
return getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonce = await redis.get(nonceKey)
|
const nonce = await redis.get(nonceKey)
|
||||||
@ -95,7 +93,7 @@ async function readNonce(forceUpdate) {
|
|||||||
return Number(nonce)
|
return Number(nonce)
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Nonce wasn't found in the DB")
|
logger.warn("Nonce wasn't found in the DB")
|
||||||
return getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
|
return getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +208,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
|||||||
|
|
||||||
if (message.includes('insufficient funds')) {
|
if (message.includes('insufficient funds')) {
|
||||||
insufficientFunds = true
|
insufficientFunds = true
|
||||||
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
|
const currentBalance = await web3.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
|
||||||
minimumBalance = gasLimit.multipliedBy(gasPrice)
|
minimumBalance = gasLimit.multipliedBy(gasPrice)
|
||||||
logger.error(
|
logger.error(
|
||||||
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`
|
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`
|
||||||
@ -240,7 +238,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
|||||||
if (insufficientFunds) {
|
if (insufficientFunds) {
|
||||||
logger.warn('Insufficient funds. Stop sending transactions until the account has the minimum balance')
|
logger.warn('Insufficient funds. Stop sending transactions until the account has the minimum balance')
|
||||||
channel.close()
|
channel.close()
|
||||||
waitForFunds(web3Instance, ORACLE_VALIDATOR_ADDRESS, minimumBalance, resume, logger)
|
waitForFunds(web3, ORACLE_VALIDATOR_ADDRESS, minimumBalance, resume, logger)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
|
67
oracle/src/services/blockFinder.js
Normal file
67
oracle/src/services/blockFinder.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
require('../../env')
|
||||||
|
const rootLogger = require('./logger')
|
||||||
|
|
||||||
|
async function makeBlockFinder(name, web3) {
|
||||||
|
const logger = rootLogger.child({
|
||||||
|
module: `blockFinder-${name}`
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info('Estimating average block time')
|
||||||
|
const lastBlock = await web3.eth.getBlock('latest')
|
||||||
|
const oldBlock = await web3.eth.getBlock(Math.max(lastBlock.number - 10000, 1))
|
||||||
|
const blockDiff = lastBlock.number - oldBlock.number
|
||||||
|
const timeDiff = lastBlock.timestamp - oldBlock.timestamp
|
||||||
|
const averageBlockTime = timeDiff / blockDiff
|
||||||
|
logger.info(`Average block time among last ${blockDiff} blocks is ${averageBlockTime} seconds`)
|
||||||
|
|
||||||
|
return async (timestamp, startingBlock) => {
|
||||||
|
logger.info(`Searching for block with timestamp ${timestamp}, starting from block #${startingBlock.number}`)
|
||||||
|
let currentBlock = startingBlock
|
||||||
|
|
||||||
|
let requests = 0
|
||||||
|
const getBlock = number => {
|
||||||
|
requests++
|
||||||
|
return web3.eth.getBlock(number)
|
||||||
|
}
|
||||||
|
|
||||||
|
let prevBlockDiff = Infinity
|
||||||
|
while (true) {
|
||||||
|
const timeDiff = currentBlock.timestamp - timestamp
|
||||||
|
const blockDiff = Math.ceil(timeDiff / averageBlockTime)
|
||||||
|
if (Math.abs(blockDiff) < 3 || Math.abs(blockDiff) >= Math.abs(prevBlockDiff)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
prevBlockDiff = blockDiff
|
||||||
|
const blockNumber = currentBlock.number - blockDiff
|
||||||
|
logger.debug(`Moving ${-blockDiff} blocks from #${currentBlock.number} to #${blockNumber}`)
|
||||||
|
currentBlock = await getBlock(blockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentBlock.timestamp < timestamp) {
|
||||||
|
while (true) {
|
||||||
|
logger.debug(`Checking next block #${currentBlock.number + 1}`)
|
||||||
|
const nextBlock = await getBlock(currentBlock.number + 1)
|
||||||
|
if (nextBlock.timestamp <= timestamp) {
|
||||||
|
currentBlock = nextBlock
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (currentBlock.timestamp > timestamp) {
|
||||||
|
while (currentBlock.timestamp > timestamp) {
|
||||||
|
logger.debug(`Checking previous block #${currentBlock.number - 1}`)
|
||||||
|
currentBlock = await getBlock(currentBlock.number - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Found block #${currentBlock.number}, with timestamp ${
|
||||||
|
currentBlock.timestamp
|
||||||
|
}. Made a total of ${requests} eth_getBlockByNumber requests`
|
||||||
|
)
|
||||||
|
|
||||||
|
return currentBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = makeBlockFinder
|
@ -1,7 +1,6 @@
|
|||||||
require('../../env')
|
require('../../env')
|
||||||
const fetch = require('node-fetch')
|
const fetch = require('node-fetch')
|
||||||
const { web3Home, web3Foreign } = require('../services/web3')
|
const { home, foreign } = require('../../config/base.config')
|
||||||
const { bridgeConfig } = require('../../config/base.config')
|
|
||||||
const logger = require('../services/logger').child({
|
const logger = require('../services/logger').child({
|
||||||
module: 'gasPrice'
|
module: 'gasPrice'
|
||||||
})
|
})
|
||||||
@ -9,17 +8,12 @@ const { setIntervalAndRun } = require('../utils/utils')
|
|||||||
const { DEFAULT_UPDATE_INTERVAL, GAS_PRICE_BOUNDARIES, DEFAULT_GAS_PRICE_FACTOR } = require('../utils/constants')
|
const { DEFAULT_UPDATE_INTERVAL, GAS_PRICE_BOUNDARIES, DEFAULT_GAS_PRICE_FACTOR } = require('../utils/constants')
|
||||||
const { gasPriceFromSupplier, gasPriceFromContract } = require('../../../commons')
|
const { gasPriceFromSupplier, gasPriceFromContract } = require('../../../commons')
|
||||||
|
|
||||||
const HomeABI = bridgeConfig.homeBridgeAbi
|
|
||||||
const ForeignABI = bridgeConfig.foreignBridgeAbi
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
|
||||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK,
|
COMMON_FOREIGN_GAS_PRICE_FALLBACK,
|
||||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL,
|
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL,
|
||||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE,
|
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE,
|
||||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL,
|
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL,
|
||||||
COMMON_FOREIGN_GAS_PRICE_FACTOR,
|
COMMON_FOREIGN_GAS_PRICE_FACTOR,
|
||||||
COMMON_HOME_BRIDGE_ADDRESS,
|
|
||||||
COMMON_HOME_GAS_PRICE_FALLBACK,
|
COMMON_HOME_GAS_PRICE_FALLBACK,
|
||||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL,
|
COMMON_HOME_GAS_PRICE_SUPPLIER_URL,
|
||||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE,
|
COMMON_HOME_GAS_PRICE_SPEED_TYPE,
|
||||||
@ -27,10 +21,6 @@ const {
|
|||||||
COMMON_HOME_GAS_PRICE_FACTOR
|
COMMON_HOME_GAS_PRICE_FACTOR
|
||||||
} = process.env
|
} = process.env
|
||||||
|
|
||||||
const homeBridge = new web3Home.eth.Contract(HomeABI, COMMON_HOME_BRIDGE_ADDRESS)
|
|
||||||
|
|
||||||
const foreignBridge = new web3Foreign.eth.Contract(ForeignABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
|
||||||
|
|
||||||
let cachedGasPrice = null
|
let cachedGasPrice = null
|
||||||
|
|
||||||
let fetchGasPriceInterval = null
|
let fetchGasPriceInterval = null
|
||||||
@ -48,13 +38,13 @@ const fetchGasPrice = async (speedType, factor, bridgeContract, gasPriceSupplier
|
|||||||
async function start(chainId, fetchOnce) {
|
async function start(chainId, fetchOnce) {
|
||||||
clearInterval(fetchGasPriceInterval)
|
clearInterval(fetchGasPriceInterval)
|
||||||
|
|
||||||
let bridgeContract = null
|
let contract = null
|
||||||
let gasPriceSupplierUrl = null
|
let gasPriceSupplierUrl = null
|
||||||
let speedType = null
|
let speedType = null
|
||||||
let updateInterval = null
|
let updateInterval = null
|
||||||
let factor = null
|
let factor = null
|
||||||
if (chainId === 'home') {
|
if (chainId === 'home') {
|
||||||
bridgeContract = homeBridge
|
contract = home.bridgeContract
|
||||||
gasPriceSupplierUrl = COMMON_HOME_GAS_PRICE_SUPPLIER_URL
|
gasPriceSupplierUrl = COMMON_HOME_GAS_PRICE_SUPPLIER_URL
|
||||||
speedType = COMMON_HOME_GAS_PRICE_SPEED_TYPE
|
speedType = COMMON_HOME_GAS_PRICE_SPEED_TYPE
|
||||||
updateInterval = ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL || DEFAULT_UPDATE_INTERVAL
|
updateInterval = ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL || DEFAULT_UPDATE_INTERVAL
|
||||||
@ -62,7 +52,7 @@ async function start(chainId, fetchOnce) {
|
|||||||
|
|
||||||
cachedGasPrice = COMMON_HOME_GAS_PRICE_FALLBACK
|
cachedGasPrice = COMMON_HOME_GAS_PRICE_FALLBACK
|
||||||
} else if (chainId === 'foreign') {
|
} else if (chainId === 'foreign') {
|
||||||
bridgeContract = foreignBridge
|
contract = foreign.bridgeContract
|
||||||
gasPriceSupplierUrl = COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL
|
gasPriceSupplierUrl = COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL
|
||||||
speedType = COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE
|
speedType = COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE
|
||||||
updateInterval = ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL || DEFAULT_UPDATE_INTERVAL
|
updateInterval = ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL || DEFAULT_UPDATE_INTERVAL
|
||||||
@ -79,14 +69,13 @@ async function start(chainId, fetchOnce) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fetchOnce) {
|
if (fetchOnce) {
|
||||||
await fetchGasPrice(speedType, factor, bridgeContract, fetchFn)
|
await fetchGasPrice(speedType, factor, contract, fetchFn)
|
||||||
} else {
|
} else {
|
||||||
fetchGasPriceInterval = await setIntervalAndRun(
|
fetchGasPriceInterval = await setIntervalAndRun(
|
||||||
() => fetchGasPrice(speedType, factor, bridgeContract, fetchFn),
|
() => fetchGasPrice(speedType, factor, contract, fetchFn),
|
||||||
updateInterval
|
updateInterval
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return getPrice()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPrice() {
|
function getPrice() {
|
||||||
|
@ -3,6 +3,7 @@ const path = require('path')
|
|||||||
const {
|
const {
|
||||||
web3Home,
|
web3Home,
|
||||||
web3Foreign,
|
web3Foreign,
|
||||||
|
web3ForeignArchive,
|
||||||
web3Side,
|
web3Side,
|
||||||
web3HomeFallback,
|
web3HomeFallback,
|
||||||
web3ForeignFallback,
|
web3ForeignFallback,
|
||||||
@ -31,6 +32,10 @@ web3ForeignFallback.currentProvider.setLogger(logger)
|
|||||||
web3HomeRedundant.currentProvider.setLogger(logger)
|
web3HomeRedundant.currentProvider.setLogger(logger)
|
||||||
web3ForeignRedundant.currentProvider.setLogger(logger)
|
web3ForeignRedundant.currentProvider.setLogger(logger)
|
||||||
|
|
||||||
|
if (web3ForeignArchive) {
|
||||||
|
web3ForeignArchive.currentProvider.setLogger(logger)
|
||||||
|
}
|
||||||
|
|
||||||
if (web3Side) {
|
if (web3Side) {
|
||||||
web3Side.currentProvider.setLogger(logger)
|
web3Side.currentProvider.setLogger(logger)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ const {
|
|||||||
COMMON_HOME_RPC_URL,
|
COMMON_HOME_RPC_URL,
|
||||||
COMMON_FOREIGN_RPC_URL,
|
COMMON_FOREIGN_RPC_URL,
|
||||||
ORACLE_SIDE_RPC_URL,
|
ORACLE_SIDE_RPC_URL,
|
||||||
|
ORACLE_FOREIGN_ARCHIVE_RPC_URL,
|
||||||
ORACLE_RPC_REQUEST_TIMEOUT,
|
ORACLE_RPC_REQUEST_TIMEOUT,
|
||||||
ORACLE_HOME_RPC_POLLING_INTERVAL,
|
ORACLE_HOME_RPC_POLLING_INTERVAL,
|
||||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL
|
ORACLE_FOREIGN_RPC_POLLING_INTERVAL
|
||||||
@ -42,6 +43,18 @@ const web3Home = new Web3(homeProvider)
|
|||||||
const foreignProvider = new HttpListProvider(foreignUrls, foreignOptions)
|
const foreignProvider = new HttpListProvider(foreignUrls, foreignOptions)
|
||||||
const web3Foreign = new Web3(foreignProvider)
|
const web3Foreign = new Web3(foreignProvider)
|
||||||
|
|
||||||
|
let web3ForeignArchive = null
|
||||||
|
if (ORACLE_FOREIGN_ARCHIVE_RPC_URL) {
|
||||||
|
const archiveUrls = ORACLE_FOREIGN_ARCHIVE_RPC_URL.split(' ').filter(url => url.length > 0)
|
||||||
|
const options = {
|
||||||
|
requestTimeout: configuredTimeout || 2000,
|
||||||
|
retry: RETRY_CONFIG
|
||||||
|
}
|
||||||
|
|
||||||
|
const archiveProvider = new HttpListProvider(archiveUrls, options)
|
||||||
|
web3ForeignArchive = new Web3(archiveProvider)
|
||||||
|
}
|
||||||
|
|
||||||
let web3Side = null
|
let web3Side = null
|
||||||
if (ORACLE_SIDE_RPC_URL) {
|
if (ORACLE_SIDE_RPC_URL) {
|
||||||
const sideUrls = ORACLE_SIDE_RPC_URL.split(' ').filter(url => url.length > 0)
|
const sideUrls = ORACLE_SIDE_RPC_URL.split(' ').filter(url => url.length > 0)
|
||||||
@ -83,6 +96,7 @@ if (foreignUrls.length > 1) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
web3Home,
|
web3Home,
|
||||||
web3Foreign,
|
web3Foreign,
|
||||||
|
web3ForeignArchive,
|
||||||
web3Side,
|
web3Side,
|
||||||
web3HomeRedundant,
|
web3HomeRedundant,
|
||||||
web3ForeignRedundant,
|
web3ForeignRedundant,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const logger = require('../services/logger').child({
|
const logger = require('../services/logger').child({
|
||||||
module: 'web3'
|
module: 'web3'
|
||||||
})
|
})
|
||||||
|
const { BRIDGE_VALIDATORS_ABI } = require('../../../commons')
|
||||||
|
|
||||||
async function getNonce(web3, address) {
|
async function getNonce(web3, address) {
|
||||||
try {
|
try {
|
||||||
@ -9,6 +10,7 @@ async function getNonce(web3, address) {
|
|||||||
logger.debug({ address, transactionCount }, 'Transaction count obtained')
|
logger.debug({ address, transactionCount }, 'Transaction count obtained')
|
||||||
return transactionCount
|
return transactionCount
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logger.error(e.message)
|
||||||
throw new Error(`Nonce cannot be obtained`)
|
throw new Error(`Nonce cannot be obtained`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -20,10 +22,23 @@ async function getBlockNumber(web3) {
|
|||||||
logger.debug({ blockNumber }, 'Block number obtained')
|
logger.debug({ blockNumber }, 'Block number obtained')
|
||||||
return blockNumber
|
return blockNumber
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logger.error(e.message)
|
||||||
throw new Error(`Block Number cannot be obtained`)
|
throw new Error(`Block Number cannot be obtained`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getBlock(web3, number) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Getting block ${number}`)
|
||||||
|
const block = await web3.eth.getBlock(number)
|
||||||
|
logger.debug({ number: block.number, timestamp: block.timestamp, hash: block.hash }, 'Block obtained')
|
||||||
|
return block
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e.message)
|
||||||
|
throw new Error(`Block cannot be obtained`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function getChainId(web3) {
|
async function getChainId(web3) {
|
||||||
try {
|
try {
|
||||||
logger.debug('Getting chain id')
|
logger.debug('Getting chain id')
|
||||||
@ -31,6 +46,7 @@ async function getChainId(web3) {
|
|||||||
logger.debug({ chainId }, 'Chain id obtained')
|
logger.debug({ chainId }, 'Chain id obtained')
|
||||||
return chainId
|
return chainId
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logger.error(e.message)
|
||||||
throw new Error(`Chain Id cannot be obtained`)
|
throw new Error(`Chain Id cannot be obtained`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,14 +55,29 @@ async function getRequiredBlockConfirmations(contract) {
|
|||||||
try {
|
try {
|
||||||
const contractAddress = contract.options.address
|
const contractAddress = contract.options.address
|
||||||
logger.debug({ contractAddress }, 'Getting required block confirmations')
|
logger.debug({ contractAddress }, 'Getting required block confirmations')
|
||||||
const requiredBlockConfirmations = await contract.methods.requiredBlockConfirmations().call()
|
const requiredBlockConfirmations = parseInt(await contract.methods.requiredBlockConfirmations().call(), 10)
|
||||||
logger.debug({ contractAddress, requiredBlockConfirmations }, 'Required block confirmations obtained')
|
logger.debug({ contractAddress, requiredBlockConfirmations }, 'Required block confirmations obtained')
|
||||||
return requiredBlockConfirmations
|
return requiredBlockConfirmations
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logger.error(e.message)
|
||||||
throw new Error(`Required block confirmations cannot be obtained`)
|
throw new Error(`Required block confirmations cannot be obtained`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getValidatorContract(contract, web3) {
|
||||||
|
try {
|
||||||
|
const contractAddress = contract.options.address
|
||||||
|
logger.debug({ contractAddress }, 'Getting validator contract address')
|
||||||
|
const validatorContractAddress = await contract.methods.validatorContract().call()
|
||||||
|
logger.debug({ contractAddress, validatorContractAddress }, 'Validator contract address obtained')
|
||||||
|
|
||||||
|
return new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e.message)
|
||||||
|
throw new Error(`Validator cannot be obtained`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function getEvents({ contract, event, fromBlock, toBlock, filter }) {
|
async function getEvents({ contract, event, fromBlock, toBlock, filter }) {
|
||||||
try {
|
try {
|
||||||
const contractAddress = contract.options.address
|
const contractAddress = contract.options.address
|
||||||
@ -58,6 +89,7 @@ async function getEvents({ contract, event, fromBlock, toBlock, filter }) {
|
|||||||
logger.debug({ contractAddress, event, count: pastEvents.length }, 'Past events obtained')
|
logger.debug({ contractAddress, event, count: pastEvents.length }, 'Past events obtained')
|
||||||
return pastEvents
|
return pastEvents
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logger.error(e.message)
|
||||||
throw new Error(`${event} events cannot be obtained`)
|
throw new Error(`${event} events cannot be obtained`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,6 +113,7 @@ async function getEventsFromTx({ web3, contract, event, txHash, filter }) {
|
|||||||
logger.debug({ contractAddress, event, count: pastEvents.length }, 'Past events obtained')
|
logger.debug({ contractAddress, event, count: pastEvents.length }, 'Past events obtained')
|
||||||
return pastEvents
|
return pastEvents
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logger.error(e.message)
|
||||||
throw new Error(`${event} events cannot be obtained`)
|
throw new Error(`${event} events cannot be obtained`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,8 +121,10 @@ async function getEventsFromTx({ web3, contract, event, txHash, filter }) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
getNonce,
|
getNonce,
|
||||||
getBlockNumber,
|
getBlockNumber,
|
||||||
|
getBlock,
|
||||||
getChainId,
|
getChainId,
|
||||||
getRequiredBlockConfirmations,
|
getRequiredBlockConfirmations,
|
||||||
|
getValidatorContract,
|
||||||
getEvents,
|
getEvents,
|
||||||
getEventsFromTx
|
getEventsFromTx
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,14 @@ async function readAccessListFile(fileName, logger) {
|
|||||||
return readAccessLists[fileName]
|
return readAccessLists[fileName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function zipToObject(keys, values) {
|
||||||
|
const res = {}
|
||||||
|
keys.forEach((key, i) => {
|
||||||
|
res[key] = values[i]
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
syncForEach,
|
syncForEach,
|
||||||
checkHTTPS,
|
checkHTTPS,
|
||||||
@ -149,5 +157,6 @@ module.exports = {
|
|||||||
nonceError,
|
nonceError,
|
||||||
getRetrySequence,
|
getRetrySequence,
|
||||||
promiseAny,
|
promiseAny,
|
||||||
readAccessListFile
|
readAccessListFile,
|
||||||
|
zipToObject
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
require('../env')
|
require('../env')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const { BN, toBN } = require('web3').utils
|
|
||||||
const { connectWatcherToQueue, connection } = require('./services/amqpClient')
|
const { connectWatcherToQueue, connection } = require('./services/amqpClient')
|
||||||
const { getBlockNumber } = require('./tx/web3')
|
|
||||||
const { redis } = require('./services/redisClient')
|
const { redis } = require('./services/redisClient')
|
||||||
const logger = require('./services/logger')
|
const logger = require('./services/logger')
|
||||||
const { getShutdownFlag } = require('./services/shutdownState')
|
const { getShutdownFlag } = require('./services/shutdownState')
|
||||||
const { getRequiredBlockConfirmations, getEvents } = require('./tx/web3')
|
const { getBlockNumber, getRequiredBlockConfirmations, getEvents } = require('./tx/web3')
|
||||||
const { checkHTTPS, watchdog } = require('./utils/utils')
|
const { checkHTTPS, watchdog } = require('./utils/utils')
|
||||||
const { EXIT_CODES } = require('./utils/constants')
|
const { EXIT_CODES } = require('./utils/constants')
|
||||||
|
|
||||||
@ -24,24 +22,19 @@ const processTransfers = require('./events/processTransfers')(config)
|
|||||||
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
|
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
|
||||||
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
|
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
|
||||||
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
|
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
|
||||||
|
const processAMBInformationRequests = require('./events/processAMBInformationRequests')(config)
|
||||||
|
|
||||||
const { getTokensState } = require('./utils/tokenState')
|
const { getTokensState } = require('./utils/tokenState')
|
||||||
|
|
||||||
const ZERO = toBN(0)
|
const { web3, bridgeContract, eventContract, startBlock, pollingInterval, chain } = config.main
|
||||||
const ONE = toBN(1)
|
|
||||||
|
|
||||||
const web3Instance = config.web3
|
|
||||||
const bridgeContract = new web3Instance.eth.Contract(config.bridgeAbi, config.bridgeContractAddress)
|
|
||||||
let { eventContractAddress } = config
|
|
||||||
let eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress)
|
|
||||||
const lastBlockRedisKey = `${config.id}:lastProcessedBlock`
|
const lastBlockRedisKey = `${config.id}:lastProcessedBlock`
|
||||||
let lastProcessedBlock = BN.max(config.startBlock.sub(ONE), ZERO)
|
let lastProcessedBlock = Math.max(startBlock - 1, 0)
|
||||||
|
|
||||||
async function initialize() {
|
async function initialize() {
|
||||||
try {
|
try {
|
||||||
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
|
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
|
||||||
|
|
||||||
web3Instance.currentProvider.urls.forEach(checkHttps(config.chain))
|
web3.currentProvider.urls.forEach(checkHttps(chain))
|
||||||
|
|
||||||
await getLastProcessedBlock()
|
await getLastProcessedBlock()
|
||||||
connectWatcherToQueue({
|
connectWatcherToQueue({
|
||||||
@ -72,18 +65,18 @@ async function runMain({ sendToQueue }) {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
runMain({ sendToQueue })
|
runMain({ sendToQueue })
|
||||||
}, config.pollingInterval)
|
}, pollingInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLastProcessedBlock() {
|
async function getLastProcessedBlock() {
|
||||||
const result = await redis.get(lastBlockRedisKey)
|
const result = await redis.get(lastBlockRedisKey)
|
||||||
logger.debug({ fromRedis: result, fromConfig: lastProcessedBlock.toString() }, 'Last Processed block obtained')
|
logger.debug({ fromRedis: result, fromConfig: lastProcessedBlock }, 'Last Processed block obtained')
|
||||||
lastProcessedBlock = result ? toBN(result) : lastProcessedBlock
|
lastProcessedBlock = result ? parseInt(result, 10) : lastProcessedBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLastProcessedBlock(lastBlockNumber) {
|
function updateLastProcessedBlock(lastBlockNumber) {
|
||||||
lastProcessedBlock = lastBlockNumber
|
lastProcessedBlock = lastBlockNumber
|
||||||
return redis.set(lastBlockRedisKey, lastProcessedBlock.toString())
|
return redis.set(lastBlockRedisKey, lastProcessedBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
function processEvents(events) {
|
function processEvents(events) {
|
||||||
@ -113,28 +106,18 @@ async function checkConditions() {
|
|||||||
case 'erc-native-transfer':
|
case 'erc-native-transfer':
|
||||||
logger.debug('Getting token address to listen Transfer events')
|
logger.debug('Getting token address to listen Transfer events')
|
||||||
state = await getTokensState(bridgeContract, logger)
|
state = await getTokensState(bridgeContract, logger)
|
||||||
updateEventContract(state.bridgeableTokenAddress)
|
eventContract.options.address = state.bridgeableTokenAddress
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateEventContract(address) {
|
async function getLastBlockToProcess(web3, bridgeContract) {
|
||||||
if (eventContractAddress !== address) {
|
|
||||||
eventContractAddress = address
|
|
||||||
eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getLastBlockToProcess() {
|
|
||||||
const lastBlockNumberPromise = getBlockNumber(web3Instance).then(toBN)
|
|
||||||
const requiredBlockConfirmationsPromise = getRequiredBlockConfirmations(bridgeContract).then(toBN)
|
|
||||||
const [lastBlockNumber, requiredBlockConfirmations] = await Promise.all([
|
const [lastBlockNumber, requiredBlockConfirmations] = await Promise.all([
|
||||||
lastBlockNumberPromise,
|
getBlockNumber(web3),
|
||||||
requiredBlockConfirmationsPromise
|
getRequiredBlockConfirmations(bridgeContract)
|
||||||
])
|
])
|
||||||
|
return lastBlockNumber - requiredBlockConfirmations
|
||||||
return lastBlockNumber.sub(requiredBlockConfirmations)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main({ sendToQueue }) {
|
async function main({ sendToQueue }) {
|
||||||
@ -151,28 +134,49 @@ async function main({ sendToQueue }) {
|
|||||||
|
|
||||||
await checkConditions()
|
await checkConditions()
|
||||||
|
|
||||||
const lastBlockToProcess = await getLastBlockToProcess()
|
const lastBlockToProcess = await getLastBlockToProcess(web3, bridgeContract)
|
||||||
|
|
||||||
if (lastBlockToProcess.lte(lastProcessedBlock)) {
|
if (lastBlockToProcess <= lastProcessedBlock) {
|
||||||
logger.debug('All blocks already processed')
|
logger.debug('All blocks already processed')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const fromBlock = lastProcessedBlock.add(ONE)
|
const fromBlock = lastProcessedBlock + 1
|
||||||
const rangeEndBlock = config.blockPollingLimit ? fromBlock.add(config.blockPollingLimit) : lastBlockToProcess
|
const rangeEndBlock = config.blockPollingLimit ? fromBlock + config.blockPollingLimit : lastBlockToProcess
|
||||||
const toBlock = BN.min(lastBlockToProcess, rangeEndBlock)
|
let toBlock = Math.min(lastBlockToProcess, rangeEndBlock)
|
||||||
|
|
||||||
const events = await getEvents({
|
const events = (await getEvents({
|
||||||
contract: eventContract,
|
contract: eventContract,
|
||||||
event: config.event,
|
event: config.event,
|
||||||
fromBlock,
|
fromBlock,
|
||||||
toBlock,
|
toBlock,
|
||||||
filter: config.eventFilter
|
filter: config.eventFilter
|
||||||
})
|
})).sort((a, b) => a.blockNumber - b.blockNumber)
|
||||||
logger.info(`Found ${events.length} ${config.event} events`)
|
logger.info(`Found ${events.length} ${config.event} events`)
|
||||||
|
|
||||||
if (events.length) {
|
if (events.length) {
|
||||||
const job = await processEvents(events)
|
let job
|
||||||
|
|
||||||
|
// for async information requests, requests are processed in batches only if they are located in the same block
|
||||||
|
if (config.id === 'amb-information-request') {
|
||||||
|
// obtain block number and events from the earliest block
|
||||||
|
const batchBlockNumber = events[0].blockNumber
|
||||||
|
const batchEvents = events.filter(event => event.blockNumber === batchBlockNumber)
|
||||||
|
|
||||||
|
// if there are some other events in the later blocks,
|
||||||
|
// adjust lastProcessedBlock so that these events will be processed again on the next iteration
|
||||||
|
if (batchEvents.length < events.length) {
|
||||||
|
// pick event outside from the batch
|
||||||
|
toBlock = events[batchEvents.length].blockNumber - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
job = await processAMBInformationRequests(batchEvents)
|
||||||
|
if (job === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
job = await processEvents(events)
|
||||||
|
}
|
||||||
logger.info('Transactions to send:', job.length)
|
logger.info('Transactions to send:', job.length)
|
||||||
|
|
||||||
if (job.length) {
|
if (job.length) {
|
||||||
|
42
oracle/test/blockFinder.test.js
Normal file
42
oracle/test/blockFinder.test.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const { expect } = require('chai')
|
||||||
|
const makeBlockFinder = require('../src/services/blockFinder')
|
||||||
|
|
||||||
|
const mockBlock = n => ({ timestamp: n > 500 ? n * 10 : n + 4500, number: n })
|
||||||
|
const latestBlock = mockBlock(1000)
|
||||||
|
const web3Mock = {
|
||||||
|
eth: {
|
||||||
|
async getBlock(n) {
|
||||||
|
return n === 'latest' ? latestBlock : mockBlock(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('blockFinder', () => {
|
||||||
|
it('get recent blocks', async () => {
|
||||||
|
const blockFinder = await makeBlockFinder('test', web3Mock)
|
||||||
|
|
||||||
|
expect(await blockFinder(10000, latestBlock)).to.eql(latestBlock)
|
||||||
|
expect(await blockFinder(9999, latestBlock)).to.eql(mockBlock(999))
|
||||||
|
expect(await blockFinder(9995, latestBlock)).to.eql(mockBlock(999))
|
||||||
|
expect(await blockFinder(9991, latestBlock)).to.eql(mockBlock(999))
|
||||||
|
expect(await blockFinder(9990, latestBlock)).to.eql(mockBlock(999))
|
||||||
|
expect(await blockFinder(9989, latestBlock)).to.eql(mockBlock(998))
|
||||||
|
expect(await blockFinder(9981, latestBlock)).to.eql(mockBlock(998))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('get older blocks', async () => {
|
||||||
|
const blockFinder = await makeBlockFinder('test', web3Mock)
|
||||||
|
|
||||||
|
expect(await blockFinder(7500, latestBlock)).to.eql(mockBlock(750))
|
||||||
|
expect(await blockFinder(7497, latestBlock)).to.eql(mockBlock(749))
|
||||||
|
expect(await blockFinder(7511, latestBlock)).to.eql(mockBlock(751))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('get ancient blocks with different time period', async () => {
|
||||||
|
const blockFinder = await makeBlockFinder('test', web3Mock)
|
||||||
|
|
||||||
|
expect(await blockFinder(4600, latestBlock)).to.eql(mockBlock(100))
|
||||||
|
expect(await blockFinder(4601, latestBlock)).to.eql(mockBlock(101))
|
||||||
|
expect(await blockFinder(4602, latestBlock)).to.eql(mockBlock(102))
|
||||||
|
})
|
||||||
|
})
|
@ -34,7 +34,7 @@
|
|||||||
"timestamp": "0x00",
|
"timestamp": "0x00",
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"extraData": "0x",
|
"extraData": "0x",
|
||||||
"gasLimit": "0x7A1200"
|
"gasLimit": "0xb71b00"
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": {
|
"0000000000000000000000000000000000000001": {
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"timestamp": "0x00",
|
"timestamp": "0x00",
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"extraData": "0x",
|
"extraData": "0x",
|
||||||
"gasLimit": "0x7A1200"
|
"gasLimit": "0xb71b00"
|
||||||
},
|
},
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
Loading…
Reference in New Issue
Block a user