Refactor/async call serializers (#588)

This commit is contained in:
Kirill Fedoseev 2021-07-13 12:25:05 +03:00 committed by GitHub
parent d53452675e
commit 4c06329153
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 60 deletions

@ -27,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 () => {
@ -340,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 () => {
@ -358,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 () => {
@ -370,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 () => {
@ -475,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 () => {
@ -508,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,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')
} }
} }

@ -108,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) {