Merge the develop branch to the master branch, preparation to v3.0.0
This merge contains the following set of changes: * [Oracle, Improvement] Async calls error codes (#587) * [Oracle, Improvement] Refactor/async call serializers (#588)
This commit is contained in:
commit
4f5e3c47be
@ -19,6 +19,7 @@ COPY --from=contracts /mono/contracts/build ./contracts/build
|
|||||||
COPY commons/package.json ./commons/
|
COPY commons/package.json ./commons/
|
||||||
COPY oracle-e2e/package.json ./oracle-e2e/
|
COPY oracle-e2e/package.json ./oracle-e2e/
|
||||||
COPY monitor-e2e/package.json ./monitor-e2e/
|
COPY monitor-e2e/package.json ./monitor-e2e/
|
||||||
|
COPY oracle/src/utils/constants.js ./oracle/src/utils/constants.js
|
||||||
|
|
||||||
COPY yarn.lock .
|
COPY yarn.lock .
|
||||||
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production
|
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
|
const { ASYNC_CALL_ERRORS } = require('../../oracle/src/utils/constants')
|
||||||
const { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json')
|
const { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json')
|
||||||
const { uniformRetry } = require('../../e2e-commons/utils')
|
const { uniformRetry } = require('../../e2e-commons/utils')
|
||||||
const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI, ambInformationSignatures } = require('../../commons')
|
const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI, ambInformationSignatures } = require('../../commons')
|
||||||
@ -26,6 +27,45 @@ const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox, opts)
|
|||||||
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, amb.home, 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)
|
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, amb.foreign, opts)
|
||||||
|
|
||||||
|
function validateBlock(web3, serialized, block) {
|
||||||
|
assert.strictEqual(serialized.length, 2 + 64 * 12)
|
||||||
|
const values = web3.eth.abi.decodeParameter(
|
||||||
|
'(uint256,bytes32,address,uint256,uint256,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256)',
|
||||||
|
serialized
|
||||||
|
)
|
||||||
|
assert.strictEqual(values[0], block.number.toString(), 'wrong block number returned')
|
||||||
|
assert.strictEqual(values[1], block.hash, 'wrong block hash returned')
|
||||||
|
assert.strictEqual(values[2], block.miner, 'wrong block miner returned')
|
||||||
|
assert.strictEqual(values[3], block.gasUsed.toString(), 'wrong block gasUsed returned')
|
||||||
|
assert.strictEqual(values[4], block.gasLimit.toString(), 'wrong block gasLimit returned')
|
||||||
|
assert.strictEqual(values[5], block.parentHash, 'wrong block parentHash returned')
|
||||||
|
assert.strictEqual(values[6], block.receiptsRoot, 'wrong block receiptsRoot returned')
|
||||||
|
assert.strictEqual(values[7], block.stateRoot, 'wrong block stateRoot returned')
|
||||||
|
assert.strictEqual(values[8], block.transactionsRoot, 'wrong block transactionsRoot returned')
|
||||||
|
assert.strictEqual(values[9], block.timestamp.toString(), 'wrong block timestamp returned')
|
||||||
|
assert.strictEqual(values[10], block.difficulty, 'wrong block difficulty returned')
|
||||||
|
assert.strictEqual(values[11], block.totalDifficulty, 'wrong block totalDifficulty returned')
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateTransaction(web3, serialized, tx) {
|
||||||
|
assert.strictEqual(serialized.length, 64 * 13 + tx.input.length + 56)
|
||||||
|
const values = web3.eth.abi.decodeParameter(
|
||||||
|
'(bytes32,uint256,bytes32,uint256,address,address,uint256,uint256,uint256,uint256,bytes)',
|
||||||
|
serialized
|
||||||
|
)
|
||||||
|
assert.strictEqual(values[0], tx.hash, 'wrong txHash returned')
|
||||||
|
assert.strictEqual(values[1], tx.blockNumber.toString(), 'wrong tx blockNumber returned')
|
||||||
|
assert.strictEqual(values[2], tx.blockHash.toString(), 'wrong tx blockHash returned')
|
||||||
|
assert.strictEqual(values[3], tx.transactionIndex.toString(), 'wrong tx transactionIndex returned')
|
||||||
|
assert.strictEqual(values[4], tx.from, 'wrong tx from returned')
|
||||||
|
assert.strictEqual(values[5], tx.to, 'wrong tx to returned')
|
||||||
|
assert.strictEqual(values[6], tx.value, 'wrong tx value returned')
|
||||||
|
assert.strictEqual(values[7], tx.nonce.toString(), 'wrong tx nonce returned')
|
||||||
|
assert.strictEqual(values[8], tx.gas.toString(), 'wrong tx gas returned')
|
||||||
|
assert.strictEqual(values[9], tx.gasPrice, 'wrong tx gasPrice returned')
|
||||||
|
assert.strictEqual(values[10], tx.input, 'wrong tx data returned')
|
||||||
|
}
|
||||||
|
|
||||||
describe('arbitrary message bridging', () => {
|
describe('arbitrary message bridging', () => {
|
||||||
let requiredSignatures = 1
|
let requiredSignatures = 1
|
||||||
before(async () => {
|
before(async () => {
|
||||||
@ -264,7 +304,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
await makeAsyncCall(selector, data2)
|
await makeAsyncCall(selector, data2)
|
||||||
|
|
||||||
assert(!(await homeBox.methods.status().call()), 'status is true')
|
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||||
assert.strictEqual(await homeBox.methods.data().call(), null, 'returned data is incorrect')
|
assert.strictEqual(await homeBox.methods.data().call(), ASYNC_CALL_ERRORS.REVERT, 'returned data is incorrect')
|
||||||
|
|
||||||
const data3 = homeWeb3.eth.abi.encodeParameters(
|
const data3 = homeWeb3.eth.abi.encodeParameters(
|
||||||
['address', 'address', 'uint256', 'bytes'],
|
['address', 'address', 'uint256', 'bytes'],
|
||||||
@ -274,7 +314,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
await makeAsyncCall(selector, data3)
|
await makeAsyncCall(selector, data3)
|
||||||
|
|
||||||
assert(!(await homeBox.methods.status().call()), 'status is true')
|
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||||
assert.strictEqual(await homeBox.methods.data().call(), null, 'returned data is incorrect')
|
assert.strictEqual(await homeBox.methods.data().call(), ASYNC_CALL_ERRORS.REVERT, 'returned data is incorrect')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should make async eth_call for specific block', async () => {
|
it('should make async eth_call for specific block', async () => {
|
||||||
@ -315,6 +355,11 @@ describe('arbitrary message bridging', () => {
|
|||||||
await makeAsyncCall(selector, data3)
|
await makeAsyncCall(selector, data3)
|
||||||
|
|
||||||
assert(!(await homeBox.methods.status().call()), 'status is true')
|
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||||
|
assert.strictEqual(
|
||||||
|
await homeBox.methods.data().call(),
|
||||||
|
ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE,
|
||||||
|
'returned data is incorrect'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should make async eth_blockNumber', async () => {
|
it('should make async eth_blockNumber', async () => {
|
||||||
@ -334,15 +379,8 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
assert(await homeBox.methods.status().call(), 'status is false')
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
const data = await homeBox.methods.data().call()
|
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)
|
const block = await foreignWeb3.eth.getBlock(blockNumber)
|
||||||
assert.strictEqual(number, blockNumber, 'wrong block number returned')
|
validateBlock(homeWeb3, data, block)
|
||||||
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 () => {
|
it('should make async eth_getBlockByNumber and return latest block', async () => {
|
||||||
@ -352,7 +390,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
assert(await homeBox.methods.status().call(), 'status is false')
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
const data = await homeBox.methods.data().call()
|
const data = await homeBox.methods.data().call()
|
||||||
assert.strictEqual(data.length, 2 + 64 * 3)
|
assert.strictEqual(data.length, 2 + 64 * 12)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should make async eth_getBlockByHash', async () => {
|
it('should make async eth_getBlockByHash', async () => {
|
||||||
@ -364,16 +402,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
assert(await homeBox.methods.status().call(), 'status is false')
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
const data = await homeBox.methods.data().call()
|
const data = await homeBox.methods.data().call()
|
||||||
assert.strictEqual(data.length, 2 + 64 * 3)
|
validateBlock(homeWeb3, data, block)
|
||||||
|
|
||||||
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 () => {
|
it('should make async eth_getBalance', async () => {
|
||||||
@ -469,28 +498,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
assert(await homeBox.methods.status().call(), 'status is false')
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
const data = await homeBox.methods.data().call()
|
const data = await homeBox.methods.data().call()
|
||||||
const dataTypes = [
|
validateTransaction(homeWeb3, data, tx)
|
||||||
'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 () => {
|
it('should make async eth_getTransactionReceipt', async () => {
|
||||||
@ -502,18 +510,25 @@ describe('arbitrary message bridging', () => {
|
|||||||
|
|
||||||
assert(await homeBox.methods.status().call(), 'status is false')
|
assert(await homeBox.methods.status().call(), 'status is false')
|
||||||
const data = await homeBox.methods.data().call()
|
const data = await homeBox.methods.data().call()
|
||||||
const dataTypes = ['bytes32', 'uint256', 'bool', '(address,bytes32[],bytes)[]']
|
const values = homeWeb3.eth.abi.decodeParameter(
|
||||||
const values = homeWeb3.eth.abi.decodeParameters(dataTypes, data)
|
'(bytes32,uint256,bytes32,uint256,address,address,uint256,bool,(address,bytes32[],bytes)[])',
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
assert.strictEqual(values[0], txHash, 'wrong txHash returned')
|
assert.strictEqual(values[0], txHash, 'wrong txHash returned')
|
||||||
assert.strictEqual(values[1], receipt.blockNumber.toString(), 'wrong tx blockNumber 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[2], receipt.blockHash, 'wrong tx blockHash returned')
|
||||||
assert.strictEqual(values[3].length, 1, 'wrong logs length returned')
|
assert.strictEqual(values[3], receipt.transactionIndex.toString(), 'wrong tx transactionIndex returned')
|
||||||
assert.strictEqual(values[3][0][0], receipt.logs[0].address, 'wrong log address returned')
|
assert.strictEqual(values[4].toLowerCase(), receipt.from, 'wrong tx from returned')
|
||||||
assert.strictEqual(values[3][0][1].length, 2, 'wrong log topics length returned')
|
assert.strictEqual(values[5].toLowerCase(), receipt.to, 'wrong tx to returned')
|
||||||
assert.strictEqual(values[3][0][1][0], receipt.logs[0].topics[0], 'wrong event signature returned')
|
assert.strictEqual(values[6], receipt.gasUsed.toString(), 'wrong gasUsed to returned')
|
||||||
assert.strictEqual(values[3][0][1][1], receipt.logs[0].topics[1], 'wrong message id returned')
|
assert.strictEqual(values[7], receipt.status, 'wrong tx status returned')
|
||||||
assert.strictEqual(values[3][0][2], receipt.logs[0].data, 'wrong log data returned')
|
assert.strictEqual(values[8].length, 1, 'wrong logs length returned')
|
||||||
|
assert.strictEqual(values[8][0][0], receipt.logs[0].address, 'wrong log address returned')
|
||||||
|
assert.strictEqual(values[8][0][1].length, 2, 'wrong log topics length returned')
|
||||||
|
assert.strictEqual(values[8][0][1][0], receipt.logs[0].topics[0], 'wrong event signature returned')
|
||||||
|
assert.strictEqual(values[8][0][1][1], receipt.logs[0].topics[1], 'wrong message id returned')
|
||||||
|
assert.strictEqual(values[8][0][2], receipt.logs[0].data, 'wrong log data returned')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should make async eth_getStorageAt', async () => {
|
it('should make async eth_getStorageAt', async () => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const { toBN } = require('web3').utils
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
const { zipToObject } = require('../../../utils/utils')
|
const { ASYNC_CALL_ERRORS, ASYNC_ETH_CALL_MAX_GAS_LIMIT } = require('../../../utils/constants')
|
||||||
|
const { zipToObject, isRevertError } = require('../../../utils/utils')
|
||||||
|
|
||||||
const argTypes = {
|
const argTypes = {
|
||||||
to: 'address',
|
to: 'address',
|
||||||
@ -17,14 +18,23 @@ function makeCall(argNames) {
|
|||||||
const { blockNumber, ...opts } = zipToObject(argNames, args)
|
const { blockNumber, ...opts } = zipToObject(argNames, args)
|
||||||
|
|
||||||
if (blockNumber && toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
if (blockNumber && toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
return [false, '0x']
|
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||||
}
|
}
|
||||||
|
|
||||||
const [status, result] = await web3.eth
|
// different clients might use different default gas limits, so it makes sense to limit it by some large number
|
||||||
.call(opts, blockNumber || foreignBlock.number)
|
if (!opts.gas || toBN(opts.gas).gt(toBN(ASYNC_ETH_CALL_MAX_GAS_LIMIT))) {
|
||||||
.then(result => [true, result], err => [false, err.data])
|
opts.gas = ASYNC_ETH_CALL_MAX_GAS_LIMIT
|
||||||
|
}
|
||||||
|
|
||||||
return [status, web3.eth.abi.encodeParameter('bytes', result)]
|
return web3.eth
|
||||||
|
.call(opts, blockNumber || foreignBlock.number)
|
||||||
|
.then(result => [true, web3.eth.abi.encodeParameter('bytes', result)])
|
||||||
|
.catch(e => {
|
||||||
|
if (isRevertError(e)) {
|
||||||
|
return [false, ASYNC_CALL_ERRORS.REVERT]
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
const { toBN } = require('web3').utils
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||||
|
|
||||||
async function call(web3, data, foreignBlock) {
|
async function call(web3, data, foreignBlock) {
|
||||||
const address = web3.eth.abi.decodeParameter('address', data)
|
const address = web3.eth.abi.decodeParameter('address', data)
|
||||||
|
|
||||||
@ -12,7 +14,7 @@ async function callArchive(web3, data, foreignBlock) {
|
|||||||
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
|
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
|
||||||
|
|
||||||
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
return [false, '0x']
|
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||||
}
|
}
|
||||||
|
|
||||||
const balance = await web3.eth.getBalance(address, blockNumber)
|
const balance = await web3.eth.getBalance(address, blockNumber)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||||
const { serializeBlock } = require('./serializers')
|
const { serializeBlock } = require('./serializers')
|
||||||
|
|
||||||
async function call(web3, data, foreignBlock) {
|
async function call(web3, data, foreignBlock) {
|
||||||
@ -6,7 +7,7 @@ async function call(web3, data, foreignBlock) {
|
|||||||
const block = await web3.eth.getBlock(blockHash)
|
const block = await web3.eth.getBlock(blockHash)
|
||||||
|
|
||||||
if (block === null || block.number > foreignBlock.number) {
|
if (block === null || block.number > foreignBlock.number) {
|
||||||
return [false, '0x']
|
return [false, ASYNC_CALL_ERRORS.NOT_FOUND]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [true, serializeBlock(web3, block)]
|
return [true, serializeBlock(web3, block)]
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
const { toBN } = require('web3').utils
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||||
const { serializeBlock } = require('./serializers')
|
const { serializeBlock } = require('./serializers')
|
||||||
|
|
||||||
async function call(web3, data, foreignBlock) {
|
async function call(web3, data, foreignBlock) {
|
||||||
const blockNumber = web3.eth.abi.decodeParameter('uint256', data)
|
const blockNumber = web3.eth.abi.decodeParameter('uint256', data)
|
||||||
|
|
||||||
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
return [false, '0x']
|
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||||
}
|
}
|
||||||
|
|
||||||
const block = await web3.eth.getBlock(blockNumber)
|
const block = await web3.eth.getBlock(blockNumber)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
const { toBN } = require('web3').utils
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||||
|
|
||||||
async function call(web3, data, foreignBlock) {
|
async function call(web3, data, foreignBlock) {
|
||||||
const { 0: address, 1: slot } = web3.eth.abi.decodeParameters(['address', 'bytes32'], data)
|
const { 0: address, 1: slot } = web3.eth.abi.decodeParameters(['address', 'bytes32'], data)
|
||||||
|
|
||||||
@ -12,7 +14,7 @@ async function callArchive(web3, data, foreignBlock) {
|
|||||||
const { 0: address, 1: slot, 2: blockNumber } = web3.eth.abi.decodeParameters(['address', 'bytes32', 'uint256'], data)
|
const { 0: address, 1: slot, 2: blockNumber } = web3.eth.abi.decodeParameters(['address', 'bytes32', 'uint256'], data)
|
||||||
|
|
||||||
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
return [false, '0x']
|
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = await web3.eth.getStorageAt(address, slot, blockNumber)
|
const value = await web3.eth.getStorageAt(address, slot, blockNumber)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||||
const { serializeTx } = require('./serializers')
|
const { serializeTx } = require('./serializers')
|
||||||
|
|
||||||
async function call(web3, data, foreignBlock) {
|
async function call(web3, data, foreignBlock) {
|
||||||
@ -6,7 +7,7 @@ async function call(web3, data, foreignBlock) {
|
|||||||
const tx = await web3.eth.getTransaction(hash)
|
const tx = await web3.eth.getTransaction(hash)
|
||||||
|
|
||||||
if (tx === null || tx.blockNumber > foreignBlock.number) {
|
if (tx === null || tx.blockNumber > foreignBlock.number) {
|
||||||
return [false, '0x']
|
return [false, ASYNC_CALL_ERRORS.NOT_FOUND]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [true, serializeTx(web3, tx)]
|
return [true, serializeTx(web3, tx)]
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
const { toBN } = require('web3').utils
|
const { toBN } = require('web3').utils
|
||||||
|
|
||||||
|
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||||
|
|
||||||
async function call(web3, data, foreignBlock) {
|
async function call(web3, data, foreignBlock) {
|
||||||
const address = web3.eth.abi.decodeParameter('address', data)
|
const address = web3.eth.abi.decodeParameter('address', data)
|
||||||
|
|
||||||
@ -12,7 +14,7 @@ async function callArchive(web3, data, foreignBlock) {
|
|||||||
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
|
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
|
||||||
|
|
||||||
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||||
return [false, '0x']
|
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonce = await web3.eth.getTransactionCount(address, blockNumber)
|
const nonce = await web3.eth.getTransactionCount(address, blockNumber)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||||
const { serializeReceipt } = require('./serializers')
|
const { serializeReceipt } = require('./serializers')
|
||||||
|
|
||||||
async function call(web3, data, foreignBlock) {
|
async function call(web3, data, foreignBlock) {
|
||||||
@ -6,7 +7,7 @@ async function call(web3, data, foreignBlock) {
|
|||||||
const receipt = await web3.eth.getTransactionReceipt(hash)
|
const receipt = await web3.eth.getTransactionReceipt(hash)
|
||||||
|
|
||||||
if (receipt === null || receipt.blockNumber > foreignBlock.number) {
|
if (receipt === null || receipt.blockNumber > foreignBlock.number) {
|
||||||
return [false, '0x']
|
return [false, ASYNC_CALL_ERRORS.NOT_FOUND]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [true, serializeReceipt(web3, receipt)]
|
return [true, serializeReceipt(web3, receipt)]
|
||||||
|
@ -1,15 +1,30 @@
|
|||||||
const { ZERO_ADDRESS } = require('../../../../../commons')
|
const { ZERO_ADDRESS } = require('../../../../../commons')
|
||||||
|
|
||||||
const serializeBlock = (web3, block) => {
|
const serializeBlock = (web3, block) => {
|
||||||
const args = [block.number, block.hash, block.miner]
|
const args = [
|
||||||
const types = ['uint256', 'bytes32', 'address']
|
block.number,
|
||||||
return web3.eth.abi.encodeParameters(types, args)
|
block.hash,
|
||||||
|
block.miner,
|
||||||
|
block.gasUsed,
|
||||||
|
block.gasLimit,
|
||||||
|
block.parentHash,
|
||||||
|
block.receiptsRoot,
|
||||||
|
block.stateRoot,
|
||||||
|
block.transactionsRoot,
|
||||||
|
block.timestamp,
|
||||||
|
block.difficulty,
|
||||||
|
block.totalDifficulty
|
||||||
|
]
|
||||||
|
const type = '(uint256,bytes32,address,uint256,uint256,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256)'
|
||||||
|
return web3.eth.abi.encodeParameter(type, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
const serializeTx = (web3, tx) => {
|
const serializeTx = (web3, tx) => {
|
||||||
const args = [
|
const args = [
|
||||||
tx.hash,
|
tx.hash,
|
||||||
tx.blockNumber,
|
tx.blockNumber,
|
||||||
|
tx.blockHash,
|
||||||
|
tx.transactionIndex,
|
||||||
tx.from,
|
tx.from,
|
||||||
tx.to || ZERO_ADDRESS,
|
tx.to || ZERO_ADDRESS,
|
||||||
tx.value,
|
tx.value,
|
||||||
@ -18,16 +33,26 @@ const serializeTx = (web3, tx) => {
|
|||||||
tx.gasPrice,
|
tx.gasPrice,
|
||||||
tx.input
|
tx.input
|
||||||
]
|
]
|
||||||
const types = ['bytes32', 'uint256', 'address', 'address', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes']
|
const type = '(bytes32,uint256,bytes32,uint256,address,address,uint256,uint256,uint256,uint256,bytes)'
|
||||||
return web3.eth.abi.encodeParameters(types, args)
|
return web3.eth.abi.encodeParameter(type, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeLog = log => [log.address, log.topics, log.data]
|
const normalizeLog = log => [log.address, log.topics, log.data]
|
||||||
|
|
||||||
const serializeReceipt = (web3, receipt) => {
|
const serializeReceipt = (web3, receipt) => {
|
||||||
const args = [receipt.transactionHash, receipt.blockNumber, receipt.status, receipt.logs.map(normalizeLog)]
|
const args = [
|
||||||
const types = ['bytes32', 'uint256', 'bool', '(address,bytes32[],bytes)[]']
|
receipt.transactionHash,
|
||||||
return web3.eth.abi.encodeParameters(types, args)
|
receipt.blockNumber,
|
||||||
|
receipt.blockHash,
|
||||||
|
receipt.transactionIndex,
|
||||||
|
receipt.from,
|
||||||
|
receipt.to || ZERO_ADDRESS,
|
||||||
|
receipt.gasUsed,
|
||||||
|
receipt.status,
|
||||||
|
receipt.logs.map(normalizeLog)
|
||||||
|
]
|
||||||
|
const type = '(bytes32,uint256,bytes32,uint256,address,address,uint256,bool,(address,bytes32[],bytes)[])'
|
||||||
|
return web3.eth.abi.encodeParameter(type, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -6,7 +6,16 @@ const logger = require('../../services/logger').child({
|
|||||||
const { strip0x } = require('../../../../commons')
|
const { strip0x } = require('../../../../commons')
|
||||||
const { AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: estimateExtraGas } = require('../../utils/constants')
|
const { AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: estimateExtraGas } = require('../../utils/constants')
|
||||||
|
|
||||||
async function estimateGas({ web3, homeBridge, validatorContract, messageId, status, result, address }) {
|
async function estimateGas({
|
||||||
|
web3,
|
||||||
|
homeBridge,
|
||||||
|
validatorContract,
|
||||||
|
messageId,
|
||||||
|
status,
|
||||||
|
result,
|
||||||
|
address,
|
||||||
|
homeBlockNumber
|
||||||
|
}) {
|
||||||
try {
|
try {
|
||||||
const gasEstimate = await homeBridge.methods.confirmInformation(messageId, status, result).estimateGas({
|
const gasEstimate = await homeBridge.methods.confirmInformation(messageId, status, result).estimateGas({
|
||||||
from: address
|
from: address
|
||||||
@ -51,6 +60,20 @@ async function estimateGas({ web3, homeBridge, validatorContract, messageId, sta
|
|||||||
throw new InvalidValidatorError(`${address} is not a validator`)
|
throw new InvalidValidatorError(`${address} is not a validator`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug('Check if InformationRetrieved event for this message already exists')
|
||||||
|
const logs = await homeBridge.getPastEvents('InformationRetrieved', {
|
||||||
|
fromBlock: homeBlockNumber,
|
||||||
|
toBlock: 'latest',
|
||||||
|
filter: { messageId }
|
||||||
|
})
|
||||||
|
if (logs.length > 0) {
|
||||||
|
logger.warn(
|
||||||
|
'This particular message was already signed and processed by other validators.' +
|
||||||
|
'However, evaluated async call result is different from the one recorded on-chain.'
|
||||||
|
)
|
||||||
|
throw new AlreadyProcessedError(e.message)
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error('Unknown error while processing message')
|
throw new Error('Unknown error while processing message')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,13 @@ const { soliditySha3 } = require('web3').utils
|
|||||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||||
const rootLogger = require('../../services/logger')
|
const rootLogger = require('../../services/logger')
|
||||||
const makeBlockFinder = require('../../services/blockFinder')
|
const makeBlockFinder = require('../../services/blockFinder')
|
||||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
|
const {
|
||||||
|
EXIT_CODES,
|
||||||
|
MAX_CONCURRENT_EVENTS,
|
||||||
|
EXTRA_GAS_ABSOLUTE,
|
||||||
|
ASYNC_CALL_ERRORS,
|
||||||
|
MAX_ASYNC_CALL_RESULT_LENGTH
|
||||||
|
} = require('../../utils/constants')
|
||||||
const estimateGas = require('./estimateGas')
|
const estimateGas = require('./estimateGas')
|
||||||
const { getValidatorContract, getBlock, getBlockNumber, getRequiredBlockConfirmations } = require('../../tx/web3')
|
const { getValidatorContract, getBlock, getBlockNumber, getRequiredBlockConfirmations } = require('../../tx/web3')
|
||||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||||
@ -79,12 +85,17 @@ function processInformationRequestsBuilder(config) {
|
|||||||
logger.info({ requestSelector, method: asyncCallMethod, data }, 'Processing async request')
|
logger.info({ requestSelector, method: asyncCallMethod, data }, 'Processing async request')
|
||||||
|
|
||||||
const call = asyncCalls[asyncCallMethod]
|
const call = asyncCalls[asyncCallMethod]
|
||||||
const [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => {
|
let [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => {
|
||||||
if (e instanceof HttpListProviderError) {
|
if (e instanceof HttpListProviderError) {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
return [false, '0x']
|
logger.error({ error: e.message }, 'Unknown error during async call execution')
|
||||||
|
throw e
|
||||||
})
|
})
|
||||||
|
if (result.length > 2 + MAX_ASYNC_CALL_RESULT_LENGTH * 2) {
|
||||||
|
status = false
|
||||||
|
result = ASYNC_CALL_ERRORS.RESULT_IS_TOO_LONG
|
||||||
|
}
|
||||||
logger.info({ requestSelector, method: asyncCallMethod, status, result }, 'Request result obtained')
|
logger.info({ requestSelector, method: asyncCallMethod, status, result }, 'Request result obtained')
|
||||||
|
|
||||||
let gasEstimate
|
let gasEstimate
|
||||||
@ -97,7 +108,8 @@ function processInformationRequestsBuilder(config) {
|
|||||||
messageId,
|
messageId,
|
||||||
status,
|
status,
|
||||||
result,
|
result,
|
||||||
address: config.validatorAddress
|
address: config.validatorAddress,
|
||||||
|
homeBlockNumber: homeBlock.number
|
||||||
})
|
})
|
||||||
logger.debug({ gasEstimate }, 'Gas estimated')
|
logger.debug({ gasEstimate }, 'Gas estimated')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -27,5 +27,17 @@ module.exports = {
|
|||||||
FALLBACK_RPC_URL_SWITCH_TIMEOUT: 60 * 60 * 1000,
|
FALLBACK_RPC_URL_SWITCH_TIMEOUT: 60 * 60 * 1000,
|
||||||
SENDER_QUEUE_MAX_PRIORITY: 10,
|
SENDER_QUEUE_MAX_PRIORITY: 10,
|
||||||
SENDER_QUEUE_SEND_PRIORITY: 5,
|
SENDER_QUEUE_SEND_PRIORITY: 5,
|
||||||
SENDER_QUEUE_CHECK_STATUS_PRIORITY: 1
|
SENDER_QUEUE_CHECK_STATUS_PRIORITY: 1,
|
||||||
|
ASYNC_CALL_ERRORS: {
|
||||||
|
// requested transaction/block/receipt does not exist
|
||||||
|
NOT_FOUND: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
// requested custom block does not exist yet or its timestamp is greater than the home block timestamp
|
||||||
|
BLOCK_IS_IN_THE_FUTURE: '0x0000000000000000000000000000000000000000000000000000000000000001',
|
||||||
|
// eth_call has reverted or finished with OOG error
|
||||||
|
REVERT: '0x0000000000000000000000000000000000000000000000000000000000000002',
|
||||||
|
// evaluated output length exceeds allowed length of 64 KB
|
||||||
|
RESULT_IS_TOO_LONG: '0x0000000000000000000000000000000000000000000000000000000000000003'
|
||||||
|
},
|
||||||
|
MAX_ASYNC_CALL_RESULT_LENGTH: 64 * 1024,
|
||||||
|
ASYNC_ETH_CALL_MAX_GAS_LIMIT: 100000000
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,18 @@ function isNonceError(e) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRevertError(e) {
|
||||||
|
const message = e.message.toLowerCase()
|
||||||
|
// OE and NE returns "VM execution error"/"Transaction execution error"
|
||||||
|
// Geth returns "out of gas"/"intrinsic gas too low"/"execution reverted"
|
||||||
|
return (
|
||||||
|
message.includes('execution error') ||
|
||||||
|
message.includes('intrinsic gas too low') ||
|
||||||
|
message.includes('out of gas') ||
|
||||||
|
message.includes('execution reverted')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Promise.all rejects on the first rejected Promise or fulfills with the list of results
|
// Promise.all rejects on the first rejected Promise or fulfills with the list of results
|
||||||
// inverted Promise.all fulfills with the first obtained result or rejects with the list of errors
|
// inverted Promise.all fulfills with the first obtained result or rejects with the list of errors
|
||||||
const invert = p => new Promise((res, rej) => p.then(rej, res))
|
const invert = p => new Promise((res, rej) => p.then(rej, res))
|
||||||
@ -174,6 +186,7 @@ module.exports = {
|
|||||||
isSameTransactionError,
|
isSameTransactionError,
|
||||||
isInsufficientBalanceError,
|
isInsufficientBalanceError,
|
||||||
isNonceError,
|
isNonceError,
|
||||||
|
isRevertError,
|
||||||
getRetrySequence,
|
getRetrySequence,
|
||||||
promiseAny,
|
promiseAny,
|
||||||
readAccessListFile,
|
readAccessListFile,
|
||||||
|
Loading…
Reference in New Issue
Block a user