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_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_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_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`
|
||||
|
@ -9,6 +9,7 @@
|
||||
- oracle_net_db_bridge_request
|
||||
- oracle_net_db_bridge_collected
|
||||
- oracle_net_db_bridge_affirmation
|
||||
- oracle_net_db_bridge_information
|
||||
- oracle_net_db_bridge_transfer
|
||||
- oracle_net_db_bridge_senderhome
|
||||
- oracle_net_db_bridge_senderforeign
|
||||
@ -16,6 +17,7 @@
|
||||
- oracle_net_rabbit_bridge_request
|
||||
- oracle_net_rabbit_bridge_collected
|
||||
- oracle_net_rabbit_bridge_affirmation
|
||||
- oracle_net_rabbit_bridge_information
|
||||
- oracle_net_rabbit_bridge_transfer
|
||||
- oracle_net_rabbit_bridge_senderhome
|
||||
- oracle_net_rabbit_bridge_senderforeign
|
||||
|
@ -3,6 +3,7 @@
|
||||
with_items:
|
||||
- docker-compose
|
||||
- docker-compose-transfer
|
||||
- docker-compose-amb
|
||||
loop_control:
|
||||
loop_var: file
|
||||
|
||||
|
@ -42,6 +42,10 @@
|
||||
set_fact: composefileoverride="-f docker-compose-transfer.yml"
|
||||
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
|
||||
template:
|
||||
src: key.j2
|
||||
|
@ -20,3 +20,4 @@
|
||||
with_items:
|
||||
- docker-compose.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_FOREIGN_START_BLOCK=1
|
||||
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_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_MAX_AMOUNT_PER_TX=8000000
|
||||
HOME_MAX_AMOUNT_PER_TX=2000000
|
||||
HOME_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
HOME_GAS_PRICE=1000000000
|
||||
|
||||
@ -17,7 +17,7 @@ FOREIGN_RPC_URL=http://parity2:8545
|
||||
FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_MAX_AMOUNT_PER_TX=8000000
|
||||
FOREIGN_MAX_AMOUNT_PER_TX=2000000
|
||||
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
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
|
||||
docker-compose down
|
||||
docker-compose -p validator1 down
|
||||
docker-compose -p validator2 down
|
||||
docker-compose -p validator3 down
|
||||
docker network rm ultimate || true
|
||||
|
@ -15,52 +15,42 @@ docker network create --driver bridge ultimate || true
|
||||
docker-compose up -d parity1 parity2 e2e
|
||||
|
||||
startValidator () {
|
||||
docker-compose $1 run -d --name $4 redis
|
||||
docker-compose $1 run -d --name $5 rabbit
|
||||
db_env="-e ORACLE_QUEUE_URL=amqp://$4 -e ORACLE_REDIS_URL=redis://$3"
|
||||
|
||||
docker-compose $1 run -d --name $3 redis
|
||||
docker-compose $1 run -d --name $4 rabbit
|
||||
|
||||
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $3 -d oracle-erc20-native yarn watcher:transfer
|
||||
docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:transfer
|
||||
fi
|
||||
if [[ -z "$MODE" || "$MODE" == amb ]]; then
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:collected-signatures
|
||||
docker-compose $1 run $2 $3 -d oracle-amb yarn watcher:affirmation-request
|
||||
docker-compose $1 run $2 $db_env -d oracle-amb yarn watcher:signature-request
|
||||
docker-compose $1 run $2 $db_env -d oracle-amb yarn watcher:collected-signatures
|
||||
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
|
||||
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 -d --name $4 redis
|
||||
docker-compose $1 run -d --name $5 rabbit
|
||||
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
|
||||
docker-compose $1 run $2 $db_env -d oracle-amb yarn sender:home
|
||||
docker-compose $1 run $2 $db_env -d oracle-amb yarn sender:foreign
|
||||
docker-compose $1 run $2 $db_env -d oracle-amb yarn manager:shutdown
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
if [ "$1" == "oracle" ]; then
|
||||
startValidator "" "" "" "redis" "rabbit"
|
||||
startValidator "-p validator1" "" redis rabbit
|
||||
fi
|
||||
|
||||
if [ "$1" == "oracle-validator-2" ]; then
|
||||
oracle2name="-p validator2"
|
||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||
oracle2comp="-e ORACLE_QUEUE_URL=amqp://rabbit2 -e ORACLE_REDIS_URL=redis://redis2"
|
||||
startValidator "$oracle2name" "$oracle2Values" "$oracle2comp" "redis2" "rabbit2"
|
||||
startValidator "-p validator2" "$oracle2Values" redis2 rabbit2
|
||||
fi
|
||||
|
||||
if [ "$1" == "oracle-validator-3" ]; then
|
||||
oracle3name="-p validator3"
|
||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||
oracle3comp="-e ORACLE_QUEUE_URL=amqp://rabbit3 -e ORACLE_REDIS_URL=redis://redis3"
|
||||
startValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3"
|
||||
startValidator "-p validator3" "$oracle3Values" redis3 rabbit3
|
||||
fi
|
||||
|
||||
if [ "$1" == "alm" ]; then
|
||||
@ -92,17 +82,15 @@ while [ "$1" != "" ]; do
|
||||
fi
|
||||
|
||||
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"
|
||||
oracle2comp="-e ORACLE_QUEUE_URL=amqp://rabbit2 -e ORACLE_REDIS_URL=redis://redis2"
|
||||
startAMBValidator "$oracle2name" "$oracle2Values" "$oracle2comp" "redis2" "rabbit2"
|
||||
startValidator "-p validator2" "$oracle2Values" redis2 rabbit2
|
||||
|
||||
oracle3name="-p validator3"
|
||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||
oracle3comp="-e ORACLE_QUEUE_URL=amqp://rabbit3 -e ORACLE_REDIS_URL=redis://redis3"
|
||||
startAMBValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3"
|
||||
startValidator "-p validator3" "$oracle3Values" redis3 rabbit3
|
||||
fi
|
||||
|
||||
shift # Shift all the parameters down by one
|
||||
|
@ -4,7 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "mocha",
|
||||
"start": "mocha --exit",
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
"amb": "mocha test/amb.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 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(validator.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
|
||||
|
||||
const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox)
|
||||
const blockHomeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.blockedHomeBox)
|
||||
const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox)
|
||||
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const opts = {
|
||||
from: user.address,
|
||||
gas: 400000,
|
||||
gasPrice: '1'
|
||||
}
|
||||
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', () => {
|
||||
let requiredSignatures = 1
|
||||
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
|
||||
if (process.env.ULTIMATE === 'true') {
|
||||
return
|
||||
@ -66,10 +90,7 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
await homeBox.methods
|
||||
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '400000'
|
||||
})
|
||||
.send()
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
@ -98,10 +119,7 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
await blockHomeBox.methods
|
||||
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '400000'
|
||||
})
|
||||
.send()
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
@ -137,10 +155,7 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
await homeBox.methods
|
||||
.setValueOnOtherNetworkUsingManualLane(newValue, amb.home, amb.foreignBox)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '400000'
|
||||
})
|
||||
.send()
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
@ -173,10 +188,7 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
await foreignBox.methods
|
||||
.setValueOnOtherNetwork(newValue, amb.foreign, amb.homeBox)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '400000'
|
||||
})
|
||||
.send()
|
||||
.catch(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"],
|
||||
"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`
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
...baseConfig,
|
||||
main: baseConfig.foreign,
|
||||
event: 'UserRequestForAffirmation',
|
||||
queue: 'home-prioritized',
|
||||
name: `watcher-${id}`,
|
||||
|
@ -1,6 +1,5 @@
|
||||
require('../env')
|
||||
|
||||
const { toBN } = require('web3').utils
|
||||
const {
|
||||
BRIDGE_MODES,
|
||||
HOME_ERC_TO_NATIVE_ABI,
|
||||
@ -11,13 +10,26 @@ const {
|
||||
const { web3Home, web3Foreign } = require('../src/services/web3')
|
||||
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 foreignAbi
|
||||
let id
|
||||
|
||||
switch (process.env.ORACLE_BRIDGE_MODE) {
|
||||
switch (ORACLE_BRIDGE_MODE) {
|
||||
case BRIDGE_MODES.ERC_TO_NATIVE:
|
||||
homeAbi = HOME_ERC_TO_NATIVE_ABI
|
||||
foreignAbi = FOREIGN_ERC_TO_NATIVE_ABI
|
||||
@ -30,7 +42,7 @@ switch (process.env.ORACLE_BRIDGE_MODE) {
|
||||
break
|
||||
default:
|
||||
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 {
|
||||
homeAbi = HOME_ERC_TO_NATIVE_ABI
|
||||
foreignAbi = FOREIGN_ERC_TO_NATIVE_ABI
|
||||
@ -38,56 +50,41 @@ switch (process.env.ORACLE_BRIDGE_MODE) {
|
||||
}
|
||||
}
|
||||
|
||||
let maxProcessingTime = null
|
||||
if (String(process.env.ORACLE_MAX_PROCESSING_TIME) === '0') {
|
||||
maxProcessingTime = 0
|
||||
} else if (!process.env.ORACLE_MAX_PROCESSING_TIME) {
|
||||
maxProcessingTime =
|
||||
4 * Math.max(process.env.ORACLE_HOME_RPC_POLLING_INTERVAL, process.env.ORACLE_FOREIGN_RPC_POLLING_INTERVAL)
|
||||
} else {
|
||||
maxProcessingTime = Number(process.env.ORACLE_MAX_PROCESSING_TIME)
|
||||
const homeContract = new web3Home.eth.Contract(homeAbi, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const homeConfig = {
|
||||
chain: 'home',
|
||||
bridgeAddress: COMMON_HOME_BRIDGE_ADDRESS,
|
||||
bridgeABI: homeAbi,
|
||||
pollingInterval: parseInt(ORACLE_HOME_RPC_POLLING_INTERVAL, 10),
|
||||
startBlock: parseInt(ORACLE_HOME_START_BLOCK, 10) || 0,
|
||||
blockPollingLimit: parseInt(ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT, 10),
|
||||
web3: web3Home,
|
||||
bridgeContract: homeContract,
|
||||
eventContract: homeContract
|
||||
}
|
||||
|
||||
const bridgeConfig = {
|
||||
homeBridgeAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS,
|
||||
homeBridgeAbi: homeAbi,
|
||||
foreignBridgeAddress: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||
foreignBridgeAbi: foreignAbi,
|
||||
const foreignContract = new web3Foreign.eth.Contract(foreignAbi, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const foreignConfig = {
|
||||
chain: 'foreign',
|
||||
bridgeAddress: COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||
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: {},
|
||||
validatorAddress: ORACLE_VALIDATOR_ADDRESS || privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY),
|
||||
maxProcessingTime,
|
||||
shutdownKey: 'oracle-shutdown'
|
||||
}
|
||||
|
||||
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,
|
||||
shutdownKey: 'oracle-shutdown',
|
||||
home: homeConfig,
|
||||
foreign: foreignConfig,
|
||||
id
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ const baseConfig = require('./base.config')
|
||||
const id = `${baseConfig.id}-collected-signatures`
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.homeConfig,
|
||||
...baseConfig,
|
||||
main: baseConfig.home,
|
||||
event: 'CollectedSignatures',
|
||||
queue: 'foreign-prioritized',
|
||||
name: `watcher-${id}`,
|
||||
|
@ -6,7 +6,7 @@ const { web3Foreign, web3ForeignRedundant, web3ForeignFallback } = require('../s
|
||||
const { ORACLE_FOREIGN_TX_RESEND_INTERVAL } = process.env
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig,
|
||||
queue: 'foreign-prioritized',
|
||||
oldQueue: 'foreign',
|
||||
id: 'foreign',
|
||||
|
@ -6,7 +6,7 @@ const { web3Home, web3HomeRedundant, web3HomeFallback } = require('../src/servic
|
||||
const { ORACLE_HOME_TX_RESEND_INTERVAL } = process.env
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig,
|
||||
queue: 'home-prioritized',
|
||||
oldQueue: '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
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig,
|
||||
id: 'shutdown-manager',
|
||||
name: 'shutdown-manager',
|
||||
pollingInterval: ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL || 120000,
|
||||
@ -16,5 +16,6 @@ module.exports = {
|
||||
checksBeforeStop: 1,
|
||||
shutdownServiceURL: ORACLE_SHUTDOWN_SERVICE_URL,
|
||||
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`
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.homeConfig,
|
||||
...baseConfig,
|
||||
main: baseConfig.home,
|
||||
event: 'UserRequestForSignature',
|
||||
queue: 'home-prioritized',
|
||||
name: `watcher-${id}`,
|
||||
|
@ -23,11 +23,12 @@ if (baseConfig.id !== 'erc-native') {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
...baseConfig,
|
||||
main: {
|
||||
...baseConfig.foreign,
|
||||
eventContract: new baseConfig.foreign.web3.eth.Contract(ERC20_ABI, initialChecks.bridgeableTokenAddress)
|
||||
},
|
||||
event: 'Transfer',
|
||||
eventContractAddress: initialChecks.bridgeableTokenAddress,
|
||||
eventAbi: ERC20_ABI,
|
||||
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
||||
queue: 'home-prioritized',
|
||||
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:collected-signatures": "./scripts/start-worker.sh watcher collected-signatures-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",
|
||||
"sender:home": "./scripts/start-worker.sh sender home-sender",
|
||||
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
|
||||
|
@ -1,16 +1,10 @@
|
||||
require('../env')
|
||||
|
||||
const { BRIDGE_VALIDATORS_ABI } = require('../../commons')
|
||||
const { web3Home, web3Foreign } = require('../src/services/web3')
|
||||
const { bridgeConfig } = require('../config/base.config')
|
||||
const { home, foreign } = require('../config/base.config')
|
||||
|
||||
const homeABI = bridgeConfig.homeBridgeAbi
|
||||
const foreignABI = bridgeConfig.foreignBridgeAbi
|
||||
|
||||
async function getStartBlock(web3, bridgeAddress, bridgeAbi) {
|
||||
async function getStartBlock(bridgeContract, web3) {
|
||||
try {
|
||||
const bridgeContract = new web3.eth.Contract(bridgeAbi, bridgeAddress)
|
||||
|
||||
const deployedAtBlock = await bridgeContract.methods.deployedAtBlock().call()
|
||||
|
||||
const validatorContractAddress = await bridgeContract.methods.validatorContract().call()
|
||||
@ -30,10 +24,8 @@ async function getStartBlock(web3, bridgeAddress, bridgeAbi) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { COMMON_HOME_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
|
||||
|
||||
const homeStartBlock = await getStartBlock(web3Home, COMMON_HOME_BRIDGE_ADDRESS, homeABI)
|
||||
const foreignStartBlock = await getStartBlock(web3Foreign, COMMON_FOREIGN_BRIDGE_ADDRESS, foreignABI)
|
||||
const homeStartBlock = await getStartBlock(home.bridgeContract, home.web3)
|
||||
const foreignStartBlock = await getStartBlock(foreign.bridgeContract, foreign.web3)
|
||||
const result = {
|
||||
homeStartBlock,
|
||||
foreignStartBlock
|
||||
|
@ -25,10 +25,9 @@ const processTransfers = require('./events/processTransfers')(config)
|
||||
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
|
||||
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
|
||||
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
|
||||
const processAMBInformationRequests = require('./events/processAMBInformationRequests')(config)
|
||||
|
||||
const web3Instance = config.web3
|
||||
const { eventContractAddress } = config
|
||||
const eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress)
|
||||
const { web3, eventContract } = config.main
|
||||
|
||||
let attached
|
||||
|
||||
@ -36,7 +35,7 @@ async function initialize() {
|
||||
try {
|
||||
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()
|
||||
if (attached) {
|
||||
@ -93,6 +92,8 @@ function processEvents(events) {
|
||||
return processAMBCollectedSignatures(events)
|
||||
case 'amb-affirmation-request':
|
||||
return processAMBAffirmationRequests(events)
|
||||
case 'amb-information-request':
|
||||
return processAMBInformationRequests(events)
|
||||
default:
|
||||
return []
|
||||
}
|
||||
@ -101,7 +102,7 @@ function processEvents(events) {
|
||||
async function main({ sendJob, txHash }) {
|
||||
try {
|
||||
const events = await getEventsFromTx({
|
||||
web3: web3Instance,
|
||||
web3,
|
||||
contract: eventContract,
|
||||
event: config.event,
|
||||
txHash,
|
||||
@ -128,8 +129,8 @@ async function main({ sendJob, txHash }) {
|
||||
|
||||
async function sendJobTx(jobs) {
|
||||
const gasPrice = await GasPrice.start(config.chain, true)
|
||||
const chainId = await getChainId(web3Instance)
|
||||
let nonce = await getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
|
||||
const chainId = await getChainId(web3)
|
||||
let nonce = await getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
|
||||
|
||||
await syncForEach(jobs, async job => {
|
||||
let gasLimit
|
||||
@ -150,7 +151,7 @@ async function sendJobTx(jobs) {
|
||||
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
|
||||
to: job.to,
|
||||
chainId,
|
||||
web3: web3Instance
|
||||
web3
|
||||
})
|
||||
|
||||
nonce++
|
||||
@ -166,7 +167,7 @@ async function sendJobTx(jobs) {
|
||||
)
|
||||
|
||||
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)
|
||||
logger.error(
|
||||
`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 { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||
const rootLogger = require('../../services/logger')
|
||||
const { web3Home } = require('../../services/web3')
|
||||
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
|
||||
const { getValidatorContract } = require('../../tx/web3')
|
||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
|
||||
const estimateGas = require('./estimateGas')
|
||||
const { parseAMBMessage } = require('../../../../commons')
|
||||
@ -11,20 +10,16 @@ const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = req
|
||||
|
||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||
|
||||
let validatorContract = null
|
||||
|
||||
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) {
|
||||
const txToSend = []
|
||||
|
||||
if (validatorContract === null) {
|
||||
rootLogger.debug('Getting validator contract address')
|
||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
||||
|
||||
validatorContract = new web3Home.eth.Contract(bridgeValidatorsABI, validatorContractAddress)
|
||||
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||
}
|
||||
|
||||
rootLogger.debug(`Processing ${affirmationRequests.length} AffirmationRequest events`)
|
||||
@ -45,8 +40,8 @@ function processAffirmationRequestsBuilder(config) {
|
||||
try {
|
||||
logger.debug('Estimate gas')
|
||||
gasEstimate = await estimateGas({
|
||||
web3: web3Home,
|
||||
homeBridge,
|
||||
web3,
|
||||
homeBridge: bridgeContract,
|
||||
validatorContract,
|
||||
message,
|
||||
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({
|
||||
data,
|
||||
gasEstimate,
|
||||
extraGas: EXTRA_GAS_ABSOLUTE,
|
||||
transactionReference: affirmationRequest.transactionHash,
|
||||
to: config.homeBridgeAddress
|
||||
to: config.home.bridgeAddress
|
||||
})
|
||||
})
|
||||
.map(promise => limit(promise))
|
||||
|
@ -1,9 +1,8 @@
|
||||
require('dotenv').config()
|
||||
const promiseLimit = require('promise-limit')
|
||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
|
||||
const { getValidatorContract } = require('../../tx/web3')
|
||||
const rootLogger = require('../../services/logger')
|
||||
const { web3Home, web3Foreign } = require('../../services/web3')
|
||||
const { signatureToVRS, packSignatures } = require('../../utils/message')
|
||||
const { readAccessListFile } = require('../../utils/utils')
|
||||
const { parseAMBMessage } = require('../../../../commons')
|
||||
@ -19,22 +18,16 @@ const {
|
||||
ORACLE_ALWAYS_RELAY_SIGNATURES
|
||||
} = process.env
|
||||
|
||||
let validatorContract = null
|
||||
|
||||
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) {
|
||||
const txToSend = []
|
||||
|
||||
if (validatorContract === null) {
|
||||
rootLogger.debug('Getting validator contract address')
|
||||
const validatorContractAddress = await foreignBridge.methods.validatorContract().call()
|
||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
||||
|
||||
validatorContract = new web3Foreign.eth.Contract(bridgeValidatorsABI, validatorContractAddress)
|
||||
validatorContract = await getValidatorContract(foreign.bridgeContract, foreign.web3)
|
||||
}
|
||||
|
||||
rootLogger.debug(`Processing ${signatures.length} CollectedSignatures events`)
|
||||
@ -48,13 +41,13 @@ function processCollectedSignaturesBuilder(config) {
|
||||
|
||||
if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
|
||||
logger.debug('Validator handles all CollectedSignature requests')
|
||||
} else if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) {
|
||||
} else if (authorityResponsibleForRelay !== home.web3.utils.toChecksumAddress(config.validatorAddress)) {
|
||||
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
|
||||
return
|
||||
}
|
||||
|
||||
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
|
||||
const message = await homeBridge.methods.message(messageHash).call()
|
||||
const message = await home.bridgeContract.methods.message(messageHash).call()
|
||||
const parsedMessage = parseAMBMessage(message)
|
||||
|
||||
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')
|
||||
const signaturePromises = requiredSignatures.map(async (el, index) => {
|
||||
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)
|
||||
v.push(vrs.v)
|
||||
r.push(vrs.r)
|
||||
@ -120,7 +113,7 @@ function processCollectedSignaturesBuilder(config) {
|
||||
try {
|
||||
logger.debug('Estimate gas')
|
||||
gasEstimate = await estimateGas({
|
||||
foreignBridge,
|
||||
foreignBridge: foreign.bridgeContract,
|
||||
validatorContract,
|
||||
v,
|
||||
r,
|
||||
@ -146,14 +139,13 @@ function processCollectedSignaturesBuilder(config) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
const data = await foreignBridge.methods.executeSignatures(message, signatures).encodeABI()
|
||||
|
||||
const data = foreign.bridgeContract.methods.executeSignatures(message, signatures).encodeABI()
|
||||
txToSend.push({
|
||||
data,
|
||||
gasEstimate,
|
||||
extraGas: EXTRA_GAS_ABSOLUTE,
|
||||
transactionReference: colSignature.transactionHash,
|
||||
to: config.foreignBridgeAddress
|
||||
to: config.foreign.bridgeAddress
|
||||
})
|
||||
})
|
||||
.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()
|
||||
const promiseLimit = require('promise-limit')
|
||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
|
||||
const rootLogger = require('../../services/logger')
|
||||
const { web3Home } = require('../../services/web3')
|
||||
const { getValidatorContract } = require('../../tx/web3')
|
||||
const { parseAMBMessage } = require('../../../../commons')
|
||||
const estimateGas = require('../processSignatureRequests/estimateGas')
|
||||
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)
|
||||
|
||||
let validatorContract = null
|
||||
|
||||
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) {
|
||||
const txToSend = []
|
||||
|
||||
if (validatorContract === null) {
|
||||
rootLogger.debug('Getting validator contract address')
|
||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
||||
|
||||
validatorContract = new web3Home.eth.Contract(bridgeValidatorsABI, validatorContractAddress)
|
||||
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||
}
|
||||
|
||||
rootLogger.debug(`Processing ${signatureRequests.length} SignatureRequest events`)
|
||||
@ -42,14 +37,14 @@ function processSignatureRequestsBuilder(config) {
|
||||
const { sender, executor } = parseAMBMessage(message)
|
||||
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
|
||||
try {
|
||||
logger.debug('Estimate gas')
|
||||
gasEstimate = await estimateGas({
|
||||
web3: web3Home,
|
||||
homeBridge,
|
||||
web3,
|
||||
homeBridge: bridgeContract,
|
||||
validatorContract,
|
||||
signature: signature.signature,
|
||||
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({
|
||||
data,
|
||||
gasEstimate,
|
||||
transactionReference: signatureRequest.transactionHash,
|
||||
to: config.homeBridgeAddress
|
||||
to: config.home.bridgeAddress
|
||||
})
|
||||
})
|
||||
.map(promise => limit(promise))
|
||||
|
@ -2,29 +2,23 @@ require('../../../env')
|
||||
const promiseLimit = require('promise-limit')
|
||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||
const rootLogger = require('../../services/logger')
|
||||
const { web3Home } = require('../../services/web3')
|
||||
|
||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
||||
const { getValidatorContract } = require('../../tx/web3')
|
||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
||||
const estimateGas = require('./estimateGas')
|
||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||
|
||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||
|
||||
let validatorContract = null
|
||||
|
||||
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) {
|
||||
const txToSend = []
|
||||
|
||||
if (validatorContract === null) {
|
||||
rootLogger.debug('Getting validator contract address')
|
||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
||||
|
||||
validatorContract = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
||||
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||
}
|
||||
|
||||
rootLogger.debug(`Processing ${affirmationRequests.length} AffirmationRequest events`)
|
||||
@ -42,8 +36,8 @@ function processAffirmationRequestsBuilder(config) {
|
||||
try {
|
||||
logger.debug('Estimate gas')
|
||||
gasEstimate = await estimateGas({
|
||||
web3: web3Home,
|
||||
homeBridge,
|
||||
web3,
|
||||
homeBridge: bridgeContract,
|
||||
validatorContract,
|
||||
recipient,
|
||||
value,
|
||||
@ -71,15 +65,14 @@ function processAffirmationRequestsBuilder(config) {
|
||||
}
|
||||
}
|
||||
|
||||
const data = await homeBridge.methods
|
||||
const data = bridgeContract.methods
|
||||
.executeAffirmation(recipient, value, affirmationRequest.transactionHash)
|
||||
.encodeABI({ from: config.validatorAddress })
|
||||
|
||||
.encodeABI()
|
||||
txToSend.push({
|
||||
data,
|
||||
gasEstimate,
|
||||
transactionReference: affirmationRequest.transactionHash,
|
||||
to: config.homeBridgeAddress
|
||||
to: config.home.bridgeAddress
|
||||
})
|
||||
})
|
||||
.map(promise => limit(promise))
|
||||
|
@ -1,9 +1,8 @@
|
||||
require('../../../env')
|
||||
const promiseLimit = require('promise-limit')
|
||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
||||
const rootLogger = require('../../services/logger')
|
||||
const { web3Home, web3Foreign } = require('../../services/web3')
|
||||
const { getValidatorContract } = require('../../tx/web3')
|
||||
const { signatureToVRS, packSignatures, parseMessage } = require('../../utils/message')
|
||||
const { readAccessListFile } = require('../../utils/utils')
|
||||
const estimateGas = require('./estimateGas')
|
||||
@ -19,22 +18,16 @@ const {
|
||||
|
||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||
|
||||
let validatorContract = null
|
||||
|
||||
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) {
|
||||
const txToSend = []
|
||||
|
||||
if (validatorContract === null) {
|
||||
rootLogger.debug('Getting validator contract address')
|
||||
const validatorContractAddress = await foreignBridge.methods.validatorContract().call()
|
||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
||||
|
||||
validatorContract = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
||||
validatorContract = await getValidatorContract(foreign.bridgeContract, foreign.web3)
|
||||
}
|
||||
|
||||
rootLogger.debug(`Processing ${signatures.length} CollectedSignatures events`)
|
||||
@ -48,13 +41,13 @@ function processCollectedSignaturesBuilder(config) {
|
||||
|
||||
if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
|
||||
logger.debug('Validator handles all CollectedSignature requests')
|
||||
} else if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) {
|
||||
} else if (authorityResponsibleForRelay !== home.web3.utils.toChecksumAddress(config.validatorAddress)) {
|
||||
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
|
||||
return
|
||||
}
|
||||
|
||||
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
|
||||
const message = await homeBridge.methods.message(messageHash).call()
|
||||
const message = await home.bridgeContract.methods.message(messageHash).call()
|
||||
|
||||
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
|
||||
const parsedMessage = parseMessage(message)
|
||||
@ -66,7 +59,7 @@ function processCollectedSignaturesBuilder(config) {
|
||||
if (allowanceList.indexOf(recipient) === -1) {
|
||||
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
|
||||
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
|
||||
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
||||
const sender = (await home.web3.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
||||
if (allowanceList.indexOf(sender) === -1) {
|
||||
logger.info(
|
||||
{ sender, recipient },
|
||||
@ -90,7 +83,7 @@ function processCollectedSignaturesBuilder(config) {
|
||||
}
|
||||
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
|
||||
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
|
||||
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
||||
const sender = (await home.bridgeContract.eth.getTransaction(originalTxHash)).from.toLowerCase()
|
||||
if (blockList.indexOf(sender) > -1) {
|
||||
logger.info({ sender }, 'Validator skips a transaction. Sender address is in the block list.')
|
||||
return
|
||||
@ -110,7 +103,7 @@ function processCollectedSignaturesBuilder(config) {
|
||||
logger.debug('Getting message signatures')
|
||||
const signaturePromises = requiredSignatures.map(async (el, index) => {
|
||||
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)
|
||||
v.push(vrs.v)
|
||||
r.push(vrs.r)
|
||||
@ -125,7 +118,7 @@ function processCollectedSignaturesBuilder(config) {
|
||||
try {
|
||||
logger.debug('Estimate gas')
|
||||
gasEstimate = await estimateGas({
|
||||
foreignBridge,
|
||||
foreignBridge: foreign.bridgeContract,
|
||||
validatorContract,
|
||||
v,
|
||||
r,
|
||||
@ -149,12 +142,12 @@ function processCollectedSignaturesBuilder(config) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
const data = await foreignBridge.methods.executeSignatures(message, signatures).encodeABI()
|
||||
const data = foreign.bridgeContract.methods.executeSignatures(message, signatures).encodeABI()
|
||||
txToSend.push({
|
||||
data,
|
||||
gasEstimate,
|
||||
transactionReference: colSignature.transactionHash,
|
||||
to: config.foreignBridgeAddress
|
||||
to: config.foreign.bridgeAddress
|
||||
})
|
||||
})
|
||||
.map(promise => limit(promise))
|
||||
|
@ -1,9 +1,8 @@
|
||||
require('../../../env')
|
||||
const promiseLimit = require('promise-limit')
|
||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
||||
const rootLogger = require('../../services/logger')
|
||||
const { web3Home } = require('../../services/web3')
|
||||
const { getValidatorContract } = require('../../tx/web3')
|
||||
const { createMessage } = require('../../utils/message')
|
||||
const estimateGas = require('./estimateGas')
|
||||
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)
|
||||
|
||||
function processSignatureRequestsBuilder(config) {
|
||||
const { bridgeContract, web3 } = config.home
|
||||
|
||||
let expectedMessageLength = null
|
||||
let validatorContract = null
|
||||
|
||||
function processSignatureRequestsBuilder(config) {
|
||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
||||
|
||||
return async function processSignatureRequests(signatureRequests) {
|
||||
const txToSend = []
|
||||
|
||||
if (expectedMessageLength === null) {
|
||||
expectedMessageLength = await homeBridge.methods.requiredMessageLength().call()
|
||||
expectedMessageLength = await bridgeContract.methods.requiredMessageLength().call()
|
||||
}
|
||||
|
||||
if (validatorContract === null) {
|
||||
rootLogger.debug('Getting validator contract address')
|
||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
||||
|
||||
validatorContract = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
||||
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||
}
|
||||
|
||||
rootLogger.debug(`Processing ${signatureRequests.length} SignatureRequest events`)
|
||||
@ -49,18 +44,18 @@ function processSignatureRequestsBuilder(config) {
|
||||
recipient,
|
||||
value,
|
||||
transactionHash: signatureRequest.transactionHash,
|
||||
bridgeAddress: config.foreignBridgeAddress,
|
||||
bridgeAddress: config.foreign.bridgeAddress,
|
||||
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
|
||||
try {
|
||||
logger.debug('Estimate gas')
|
||||
gasEstimate = await estimateGas({
|
||||
web3: web3Home,
|
||||
homeBridge,
|
||||
web3,
|
||||
homeBridge: bridgeContract,
|
||||
validatorContract,
|
||||
signature: signature.signature,
|
||||
message,
|
||||
@ -87,15 +82,12 @@ function processSignatureRequestsBuilder(config) {
|
||||
}
|
||||
}
|
||||
|
||||
const data = await homeBridge.methods
|
||||
.submitSignature(signature.signature, message)
|
||||
.encodeABI({ from: config.validatorAddress })
|
||||
|
||||
const data = bridgeContract.methods.submitSignature(signature.signature, message).encodeABI()
|
||||
txToSend.push({
|
||||
data,
|
||||
gasEstimate,
|
||||
transactionReference: signatureRequest.transactionHash,
|
||||
to: config.homeBridgeAddress
|
||||
to: config.home.bridgeAddress
|
||||
})
|
||||
})
|
||||
.map(promise => limit(promise))
|
||||
|
@ -1,35 +1,32 @@
|
||||
require('../../../env')
|
||||
const promiseLimit = require('promise-limit')
|
||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||
const { BRIDGE_VALIDATORS_ABI, ZERO_ADDRESS } = require('../../../../commons')
|
||||
const { ZERO_ADDRESS } = require('../../../../commons')
|
||||
const rootLogger = require('../../services/logger')
|
||||
const { web3Home, web3Foreign } = require('../../services/web3')
|
||||
const { getValidatorContract } = require('../../tx/web3')
|
||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
||||
const estimateGas = require('../processAffirmationRequests/estimateGas')
|
||||
|
||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||
|
||||
let validatorContract = null
|
||||
|
||||
function processTransfersBuilder(config) {
|
||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
||||
const userRequestForAffirmationAbi = config.foreignBridgeAbi.filter(
|
||||
const { bridgeContract, web3 } = config.home
|
||||
|
||||
const userRequestForAffirmationAbi = config.foreign.bridgeABI.find(
|
||||
e => e.type === 'event' && e.name === 'UserRequestForAffirmation'
|
||||
)[0]
|
||||
const tokensSwappedAbi = config.foreignBridgeAbi.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0]
|
||||
const userRequestForAffirmationHash = web3Home.eth.abi.encodeEventSignature(userRequestForAffirmationAbi)
|
||||
const tokensSwappedHash = tokensSwappedAbi ? web3Home.eth.abi.encodeEventSignature(tokensSwappedAbi) : '0x'
|
||||
)
|
||||
const tokensSwappedAbi = config.foreign.bridgeABI.find(e => e.type === 'event' && e.name === 'TokensSwapped')
|
||||
const userRequestForAffirmationHash = web3.eth.abi.encodeEventSignature(userRequestForAffirmationAbi)
|
||||
const tokensSwappedHash = tokensSwappedAbi ? web3.eth.abi.encodeEventSignature(tokensSwappedAbi) : '0x'
|
||||
|
||||
let validatorContract = null
|
||||
|
||||
return async function processTransfers(transfers) {
|
||||
const txToSend = []
|
||||
|
||||
if (validatorContract === null) {
|
||||
rootLogger.debug('Getting validator contract address')
|
||||
const validatorContractAddress = await homeBridge.methods.validatorContract().call()
|
||||
rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained')
|
||||
|
||||
validatorContract = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
|
||||
validatorContract = await getValidatorContract(bridgeContract, web3)
|
||||
}
|
||||
|
||||
rootLogger.debug(`Processing ${transfers.length} Transfer events`)
|
||||
@ -43,10 +40,10 @@ function processTransfersBuilder(config) {
|
||||
|
||||
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(
|
||||
e => e.address === config.foreignBridgeAddress && e.topics[0] === userRequestForAffirmationHash
|
||||
e => e.address === config.foreign.bridgeAddress && e.topics[0] === userRequestForAffirmationHash
|
||||
)
|
||||
|
||||
if (existsAffirmationEvent) {
|
||||
@ -59,7 +56,7 @@ function processTransfersBuilder(config) {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if (from === ZERO_ADDRESS && existsTokensSwappedEvent) {
|
||||
@ -73,8 +70,8 @@ function processTransfersBuilder(config) {
|
||||
try {
|
||||
logger.debug('Estimate gas')
|
||||
gasEstimate = await estimateGas({
|
||||
web3: web3Home,
|
||||
homeBridge,
|
||||
web3,
|
||||
homeBridge: bridgeContract,
|
||||
validatorContract,
|
||||
recipient: from,
|
||||
value,
|
||||
@ -100,15 +97,12 @@ function processTransfersBuilder(config) {
|
||||
}
|
||||
}
|
||||
|
||||
const data = await homeBridge.methods
|
||||
.executeAffirmation(from, value, transfer.transactionHash)
|
||||
.encodeABI({ from: config.validatorAddress })
|
||||
|
||||
const data = bridgeContract.methods.executeAffirmation(from, value, transfer.transactionHash).encodeABI()
|
||||
txToSend.push({
|
||||
data,
|
||||
gasEstimate,
|
||||
transactionReference: transfer.transactionHash,
|
||||
to: config.homeBridgeAddress
|
||||
to: config.home.bridgeAddress
|
||||
})
|
||||
})
|
||||
.map(promise => limit(promise))
|
||||
|
@ -30,9 +30,8 @@ if (process.argv.length < 3) {
|
||||
|
||||
const config = require(path.join('../config/', process.argv[2]))
|
||||
|
||||
const web3Instance = config.web3
|
||||
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.web3Redundant : config.web3
|
||||
const { web3Fallback } = config
|
||||
const { web3, web3Fallback } = config
|
||||
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.web3Redundant : web3
|
||||
|
||||
const nonceKey = `${config.id}:nonce`
|
||||
let chainId = 0
|
||||
@ -41,12 +40,11 @@ async function initialize() {
|
||||
try {
|
||||
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)
|
||||
|
||||
chainId = await getChainId(web3Instance)
|
||||
|
||||
chainId = await getChainId(web3)
|
||||
connectQueue()
|
||||
} catch (e) {
|
||||
logger.error(e.message)
|
||||
@ -86,7 +84,7 @@ async function readNonce(forceUpdate) {
|
||||
logger.debug('Reading nonce')
|
||||
if (forceUpdate) {
|
||||
logger.debug('Forcing update of nonce')
|
||||
return getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
|
||||
return getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
|
||||
}
|
||||
|
||||
const nonce = await redis.get(nonceKey)
|
||||
@ -95,7 +93,7 @@ async function readNonce(forceUpdate) {
|
||||
return Number(nonce)
|
||||
} else {
|
||||
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')) {
|
||||
insufficientFunds = true
|
||||
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
|
||||
const currentBalance = await web3.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
|
||||
minimumBalance = gasLimit.multipliedBy(gasPrice)
|
||||
logger.error(
|
||||
`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) {
|
||||
logger.warn('Insufficient funds. Stop sending transactions until the account has the minimum balance')
|
||||
channel.close()
|
||||
waitForFunds(web3Instance, ORACLE_VALIDATOR_ADDRESS, minimumBalance, resume, logger)
|
||||
waitForFunds(web3, ORACLE_VALIDATOR_ADDRESS, minimumBalance, resume, logger)
|
||||
}
|
||||
} catch (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')
|
||||
const fetch = require('node-fetch')
|
||||
const { web3Home, web3Foreign } = require('../services/web3')
|
||||
const { bridgeConfig } = require('../../config/base.config')
|
||||
const { home, foreign } = require('../../config/base.config')
|
||||
const logger = require('../services/logger').child({
|
||||
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 { gasPriceFromSupplier, gasPriceFromContract } = require('../../../commons')
|
||||
|
||||
const HomeABI = bridgeConfig.homeBridgeAbi
|
||||
const ForeignABI = bridgeConfig.foreignBridgeAbi
|
||||
|
||||
const {
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK,
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL,
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE,
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL,
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR,
|
||||
COMMON_HOME_BRIDGE_ADDRESS,
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK,
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL,
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE,
|
||||
@ -27,10 +21,6 @@ const {
|
||||
COMMON_HOME_GAS_PRICE_FACTOR
|
||||
} = 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 fetchGasPriceInterval = null
|
||||
@ -48,13 +38,13 @@ const fetchGasPrice = async (speedType, factor, bridgeContract, gasPriceSupplier
|
||||
async function start(chainId, fetchOnce) {
|
||||
clearInterval(fetchGasPriceInterval)
|
||||
|
||||
let bridgeContract = null
|
||||
let contract = null
|
||||
let gasPriceSupplierUrl = null
|
||||
let speedType = null
|
||||
let updateInterval = null
|
||||
let factor = null
|
||||
if (chainId === 'home') {
|
||||
bridgeContract = homeBridge
|
||||
contract = home.bridgeContract
|
||||
gasPriceSupplierUrl = COMMON_HOME_GAS_PRICE_SUPPLIER_URL
|
||||
speedType = COMMON_HOME_GAS_PRICE_SPEED_TYPE
|
||||
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
|
||||
} else if (chainId === 'foreign') {
|
||||
bridgeContract = foreignBridge
|
||||
contract = foreign.bridgeContract
|
||||
gasPriceSupplierUrl = COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL
|
||||
speedType = COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE
|
||||
updateInterval = ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL || DEFAULT_UPDATE_INTERVAL
|
||||
@ -79,14 +69,13 @@ async function start(chainId, fetchOnce) {
|
||||
}
|
||||
|
||||
if (fetchOnce) {
|
||||
await fetchGasPrice(speedType, factor, bridgeContract, fetchFn)
|
||||
await fetchGasPrice(speedType, factor, contract, fetchFn)
|
||||
} else {
|
||||
fetchGasPriceInterval = await setIntervalAndRun(
|
||||
() => fetchGasPrice(speedType, factor, bridgeContract, fetchFn),
|
||||
() => fetchGasPrice(speedType, factor, contract, fetchFn),
|
||||
updateInterval
|
||||
)
|
||||
}
|
||||
return getPrice()
|
||||
}
|
||||
|
||||
function getPrice() {
|
||||
|
@ -3,6 +3,7 @@ const path = require('path')
|
||||
const {
|
||||
web3Home,
|
||||
web3Foreign,
|
||||
web3ForeignArchive,
|
||||
web3Side,
|
||||
web3HomeFallback,
|
||||
web3ForeignFallback,
|
||||
@ -31,6 +32,10 @@ web3ForeignFallback.currentProvider.setLogger(logger)
|
||||
web3HomeRedundant.currentProvider.setLogger(logger)
|
||||
web3ForeignRedundant.currentProvider.setLogger(logger)
|
||||
|
||||
if (web3ForeignArchive) {
|
||||
web3ForeignArchive.currentProvider.setLogger(logger)
|
||||
}
|
||||
|
||||
if (web3Side) {
|
||||
web3Side.currentProvider.setLogger(logger)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ const {
|
||||
COMMON_HOME_RPC_URL,
|
||||
COMMON_FOREIGN_RPC_URL,
|
||||
ORACLE_SIDE_RPC_URL,
|
||||
ORACLE_FOREIGN_ARCHIVE_RPC_URL,
|
||||
ORACLE_RPC_REQUEST_TIMEOUT,
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL,
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL
|
||||
@ -42,6 +43,18 @@ const web3Home = new Web3(homeProvider)
|
||||
const foreignProvider = new HttpListProvider(foreignUrls, foreignOptions)
|
||||
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
|
||||
if (ORACLE_SIDE_RPC_URL) {
|
||||
const sideUrls = ORACLE_SIDE_RPC_URL.split(' ').filter(url => url.length > 0)
|
||||
@ -83,6 +96,7 @@ if (foreignUrls.length > 1) {
|
||||
module.exports = {
|
||||
web3Home,
|
||||
web3Foreign,
|
||||
web3ForeignArchive,
|
||||
web3Side,
|
||||
web3HomeRedundant,
|
||||
web3ForeignRedundant,
|
||||
|
@ -1,6 +1,7 @@
|
||||
const logger = require('../services/logger').child({
|
||||
module: 'web3'
|
||||
})
|
||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../commons')
|
||||
|
||||
async function getNonce(web3, address) {
|
||||
try {
|
||||
@ -9,6 +10,7 @@ async function getNonce(web3, address) {
|
||||
logger.debug({ address, transactionCount }, 'Transaction count obtained')
|
||||
return transactionCount
|
||||
} catch (e) {
|
||||
logger.error(e.message)
|
||||
throw new Error(`Nonce cannot be obtained`)
|
||||
}
|
||||
}
|
||||
@ -20,10 +22,23 @@ async function getBlockNumber(web3) {
|
||||
logger.debug({ blockNumber }, 'Block number obtained')
|
||||
return blockNumber
|
||||
} catch (e) {
|
||||
logger.error(e.message)
|
||||
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) {
|
||||
try {
|
||||
logger.debug('Getting chain id')
|
||||
@ -31,6 +46,7 @@ async function getChainId(web3) {
|
||||
logger.debug({ chainId }, 'Chain id obtained')
|
||||
return chainId
|
||||
} catch (e) {
|
||||
logger.error(e.message)
|
||||
throw new Error(`Chain Id cannot be obtained`)
|
||||
}
|
||||
}
|
||||
@ -39,14 +55,29 @@ async function getRequiredBlockConfirmations(contract) {
|
||||
try {
|
||||
const contractAddress = contract.options.address
|
||||
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')
|
||||
return requiredBlockConfirmations
|
||||
} catch (e) {
|
||||
logger.error(e.message)
|
||||
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 }) {
|
||||
try {
|
||||
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')
|
||||
return pastEvents
|
||||
} catch (e) {
|
||||
logger.error(e.message)
|
||||
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')
|
||||
return pastEvents
|
||||
} catch (e) {
|
||||
logger.error(e.message)
|
||||
throw new Error(`${event} events cannot be obtained`)
|
||||
}
|
||||
}
|
||||
@ -88,8 +121,10 @@ async function getEventsFromTx({ web3, contract, event, txHash, filter }) {
|
||||
module.exports = {
|
||||
getNonce,
|
||||
getBlockNumber,
|
||||
getBlock,
|
||||
getChainId,
|
||||
getRequiredBlockConfirmations,
|
||||
getValidatorContract,
|
||||
getEvents,
|
||||
getEventsFromTx
|
||||
}
|
||||
|
@ -137,6 +137,14 @@ async function readAccessListFile(fileName, logger) {
|
||||
return readAccessLists[fileName]
|
||||
}
|
||||
|
||||
function zipToObject(keys, values) {
|
||||
const res = {}
|
||||
keys.forEach((key, i) => {
|
||||
res[key] = values[i]
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
syncForEach,
|
||||
checkHTTPS,
|
||||
@ -149,5 +157,6 @@ module.exports = {
|
||||
nonceError,
|
||||
getRetrySequence,
|
||||
promiseAny,
|
||||
readAccessListFile
|
||||
readAccessListFile,
|
||||
zipToObject
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
require('../env')
|
||||
const path = require('path')
|
||||
const { BN, toBN } = require('web3').utils
|
||||
const { connectWatcherToQueue, connection } = require('./services/amqpClient')
|
||||
const { getBlockNumber } = require('./tx/web3')
|
||||
const { redis } = require('./services/redisClient')
|
||||
const logger = require('./services/logger')
|
||||
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 { EXIT_CODES } = require('./utils/constants')
|
||||
|
||||
@ -24,24 +22,19 @@ const processTransfers = require('./events/processTransfers')(config)
|
||||
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
|
||||
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
|
||||
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
|
||||
const processAMBInformationRequests = require('./events/processAMBInformationRequests')(config)
|
||||
|
||||
const { getTokensState } = require('./utils/tokenState')
|
||||
|
||||
const ZERO = toBN(0)
|
||||
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 { web3, bridgeContract, eventContract, startBlock, pollingInterval, chain } = config.main
|
||||
const lastBlockRedisKey = `${config.id}:lastProcessedBlock`
|
||||
let lastProcessedBlock = BN.max(config.startBlock.sub(ONE), ZERO)
|
||||
let lastProcessedBlock = Math.max(startBlock - 1, 0)
|
||||
|
||||
async function initialize() {
|
||||
try {
|
||||
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()
|
||||
connectWatcherToQueue({
|
||||
@ -72,18 +65,18 @@ async function runMain({ sendToQueue }) {
|
||||
|
||||
setTimeout(() => {
|
||||
runMain({ sendToQueue })
|
||||
}, config.pollingInterval)
|
||||
}, pollingInterval)
|
||||
}
|
||||
|
||||
async function getLastProcessedBlock() {
|
||||
const result = await redis.get(lastBlockRedisKey)
|
||||
logger.debug({ fromRedis: result, fromConfig: lastProcessedBlock.toString() }, 'Last Processed block obtained')
|
||||
lastProcessedBlock = result ? toBN(result) : lastProcessedBlock
|
||||
logger.debug({ fromRedis: result, fromConfig: lastProcessedBlock }, 'Last Processed block obtained')
|
||||
lastProcessedBlock = result ? parseInt(result, 10) : lastProcessedBlock
|
||||
}
|
||||
|
||||
function updateLastProcessedBlock(lastBlockNumber) {
|
||||
lastProcessedBlock = lastBlockNumber
|
||||
return redis.set(lastBlockRedisKey, lastProcessedBlock.toString())
|
||||
return redis.set(lastBlockRedisKey, lastProcessedBlock)
|
||||
}
|
||||
|
||||
function processEvents(events) {
|
||||
@ -113,28 +106,18 @@ async function checkConditions() {
|
||||
case 'erc-native-transfer':
|
||||
logger.debug('Getting token address to listen Transfer events')
|
||||
state = await getTokensState(bridgeContract, logger)
|
||||
updateEventContract(state.bridgeableTokenAddress)
|
||||
eventContract.options.address = state.bridgeableTokenAddress
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
function updateEventContract(address) {
|
||||
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)
|
||||
async function getLastBlockToProcess(web3, bridgeContract) {
|
||||
const [lastBlockNumber, requiredBlockConfirmations] = await Promise.all([
|
||||
lastBlockNumberPromise,
|
||||
requiredBlockConfirmationsPromise
|
||||
getBlockNumber(web3),
|
||||
getRequiredBlockConfirmations(bridgeContract)
|
||||
])
|
||||
|
||||
return lastBlockNumber.sub(requiredBlockConfirmations)
|
||||
return lastBlockNumber - requiredBlockConfirmations
|
||||
}
|
||||
|
||||
async function main({ sendToQueue }) {
|
||||
@ -151,28 +134,49 @@ async function main({ sendToQueue }) {
|
||||
|
||||
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')
|
||||
return
|
||||
}
|
||||
|
||||
const fromBlock = lastProcessedBlock.add(ONE)
|
||||
const rangeEndBlock = config.blockPollingLimit ? fromBlock.add(config.blockPollingLimit) : lastBlockToProcess
|
||||
const toBlock = BN.min(lastBlockToProcess, rangeEndBlock)
|
||||
const fromBlock = lastProcessedBlock + 1
|
||||
const rangeEndBlock = config.blockPollingLimit ? fromBlock + config.blockPollingLimit : lastBlockToProcess
|
||||
let toBlock = Math.min(lastBlockToProcess, rangeEndBlock)
|
||||
|
||||
const events = await getEvents({
|
||||
const events = (await getEvents({
|
||||
contract: eventContract,
|
||||
event: config.event,
|
||||
fromBlock,
|
||||
toBlock,
|
||||
filter: config.eventFilter
|
||||
})
|
||||
})).sort((a, b) => a.blockNumber - b.blockNumber)
|
||||
logger.info(`Found ${events.length} ${config.event} events`)
|
||||
|
||||
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)
|
||||
|
||||
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",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x7A1200"
|
||||
"gasLimit": "0xb71b00"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": {
|
||||
|
@ -34,7 +34,7 @@
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x7A1200"
|
||||
"gasLimit": "0xb71b00"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
Loading…
Reference in New Issue
Block a user