Common Validator utils (#181)

* Extracted parseValidatorEvent to commons

* Extracted processValidatorsEvents to commons

* Extracted validatorList to commons.

* refactorings

* Fixed imports, lint

* UI using getValidatorList

* Monitor using getValidatorList from commons

* Lint

* UI using properly getPastEvents

* Default options

* Final changes

* Corrected invocation of getPastEvents

* Correct usage of options in getPastEvents

* Changed expected message from infura

* Change usage of fromBlock and toBlock

* Default parameters
This commit is contained in:
Przemyslaw Rzad 2019-08-08 15:27:09 +02:00 committed by GitHub
parent 055a444fae
commit c865198290
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 166 additions and 245 deletions

@ -1,4 +1,5 @@
import { processValidatorsEvents, parseValidatorEvent } from '../contract'
const { expect } = require('chai')
const { processValidatorsEvents, parseValidatorEvent } = require('..')
describe('parseValidatorEvent', () => {
it('should parse ValidatorAdded event from v1', () => {
@ -14,8 +15,8 @@ describe('parseValidatorEvent', () => {
parseValidatorEvent(event)
// Then
expect(event.event).toBe('ValidatorAdded')
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
expect(event.event).to.be.equal('ValidatorAdded')
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
})
it('should parse ValidatorAdded event', () => {
// Given
@ -33,8 +34,8 @@ describe('parseValidatorEvent', () => {
parseValidatorEvent(event)
// Then
expect(event.event).toBe('ValidatorAdded')
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
expect(event.event).to.be.equal('ValidatorAdded')
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
})
it('should parse ValidatorAdded event from rewardableValidators', () => {
// Given
@ -52,8 +53,8 @@ describe('parseValidatorEvent', () => {
parseValidatorEvent(event)
// Then
expect(event.event).toBe('ValidatorAdded')
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
expect(event.event).to.be.equal('ValidatorAdded')
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
})
it('should parse ValidatorRemoved event', () => {
// Given
@ -71,8 +72,8 @@ describe('parseValidatorEvent', () => {
parseValidatorEvent(event)
// Then
expect(event.event).toBe('ValidatorRemoved')
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
expect(event.event).to.be.equal('ValidatorRemoved')
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
})
it('should parse ValidatorRemoved event from v1', () => {
// Given
@ -87,8 +88,8 @@ describe('parseValidatorEvent', () => {
parseValidatorEvent(event)
// Then
expect(event.event).toBe('ValidatorRemoved')
expect(event.returnValues.validator).toBe('0xcfef0c6bb765321529ffe81507f6d099693cd225')
expect(event.event).to.be.equal('ValidatorRemoved')
expect(event.returnValues.validator).to.be.equal('0xcfef0c6bb765321529ffe81507f6d099693cd225')
})
})
describe('processValidatorsEvents', () => {
@ -169,9 +170,9 @@ describe('processValidatorsEvents', () => {
const validatorList = processValidatorsEvents(events)
// Then
expect(validatorList.length).toBe(3)
expect(validatorList[0]).toBe('0xCbd25A2a5708051747a052dBB1b291865Fc0e474')
expect(validatorList[1]).toBe('0xBac68A86Cf596E3b124781E0bdbC47bb458bec62')
expect(validatorList[2]).toBe('0xf4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955')
expect(validatorList.length).to.be.equal(3)
expect(validatorList[0]).to.be.equal('0xCbd25A2a5708051747a052dBB1b291865Fc0e474')
expect(validatorList[1]).to.be.equal('0xBac68A86Cf596E3b124781E0bdbC47bb458bec62')
expect(validatorList[2]).to.be.equal('0xf4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955')
})
})

@ -1,5 +1,6 @@
const { toWei, toBN } = require('web3-utils')
const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants')
const { REWARDABLE_VALIDATORS_ABI } = require('./abis')
function decodeBridgeMode(bridgeModeHash) {
switch (bridgeModeHash) {
@ -66,6 +67,120 @@ const getUnit = bridgeMode => {
return { unitHome, unitForeign }
}
const parseValidatorEvent = event => {
if (
event.event === undefined &&
event.raw &&
event.raw.topics &&
(event.raw.topics[0] === '0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987' ||
event.raw.topics[0] === '0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83')
) {
const rawAddress = event.raw.topics.length > 1 ? event.raw.topics[1] : event.raw.data
const address = '0x' + rawAddress.slice(26)
event.event = 'ValidatorAdded'
event.returnValues.validator = address
} else if (
event.event === undefined &&
event.raw &&
event.raw.topics &&
event.raw.topics[0] === '0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1'
) {
const rawAddress = event.raw.data === '0x' ? event.raw.topics[1] : event.raw.data
const address = '0x' + rawAddress.slice(26)
event.event = 'ValidatorRemoved'
event.returnValues.validator = address
}
}
const processValidatorsEvents = events => {
const validatorList = new Set()
events.forEach(event => {
parseValidatorEvent(event)
if (event.event === 'ValidatorAdded') {
validatorList.add(event.returnValues.validator)
} else if (event.event === 'ValidatorRemoved') {
validatorList.delete(event.returnValues.validator)
}
})
return Array.from(validatorList)
}
const tryCall = async (method, fallbackValue) => {
try {
return await method.call()
} catch (e) {
return fallbackValue
}
}
const getDeployedAtBlock = async contract => tryCall(contract.methods.deployedAtBlock(), 0)
const getPastEvents = async (
contract,
{ event = 'allEvents', fromBlock = toBN(0), toBlock = 'latest', options = {} }
) => {
let events
try {
events = await contract.getPastEvents(event, {
...options,
fromBlock,
toBlock
})
} catch (e) {
if (e.message.includes('query returned more than') && toBlock !== 'latest') {
const middle = toBN(fromBlock)
.add(toBlock)
.divRound(toBN(2))
const middlePlusOne = middle.add(toBN(1))
const firstHalfEvents = await getPastEvents(contract, {
...options,
event,
fromBlock,
toBlock: middle
})
const secondHalfEvents = await getPastEvents(contract, {
...options,
event,
fromBlock: middlePlusOne,
toBlock
})
events = [...firstHalfEvents, ...secondHalfEvents]
} else {
throw new Error(e)
}
}
return events
}
const getValidatorList = async (address, eth, options) => {
options.logger && options.logger.debug && options.logger.debug('getting validatorList')
const validatorsContract = new eth.Contract(REWARDABLE_VALIDATORS_ABI, address) // in monitor, BRIDGE_VALIDATORS_ABI was used
const validators = await tryCall(validatorsContract.methods.validatorList(), [])
if (validators.length) {
return validators
}
options.logger && options.logger.debug && options.logger.debug('getting validatorsEvents')
const deployedAtBlock = await tryCall(validatorsContract.methods.deployedAtBlock(), 0)
const fromBlock = options.fromBlock || Number(deployedAtBlock) || 0
const toBlock = options.toBlock || 'latest'
const validatorsEvents = await getPastEvents(new eth.Contract([], address), {
event: 'allEvents',
fromBlock,
toBlock,
options: {}
})
return processValidatorsEvents(validatorsEvents)
}
const gasPriceWithinLimits = (gasPrice, limits) => {
if (!limits) {
return gasPrice
@ -135,6 +250,11 @@ module.exports = {
getBridgeMode,
getTokenType,
getUnit,
parseValidatorEvent,
processValidatorsEvents,
getValidatorList,
getPastEvents,
getDeployedAtBlock,
normalizeGasPrice,
gasPriceFromOracle,
gasPriceFromContract,

@ -1,43 +1,5 @@
const { toBN } = require('web3').utils
const ONE = toBN(1)
const TWO = toBN(2)
async function getPastEvents({ contract, event, fromBlock, toBlock, options }) {
let events
try {
events = await contract.getPastEvents(event, {
...options,
fromBlock,
toBlock
})
} catch (e) {
if (e.message.includes('query returned more than 1000 results')) {
const middle = fromBlock.add(toBlock).divRound(TWO)
const middlePlusOne = middle.add(ONE)
const firstHalfEvents = await getPastEvents({
contract,
event,
fromBlock,
toBlock: middle,
options
})
const secondHalfEvents = await getPastEvents({
contract,
event,
fromBlock: middlePlusOne,
toBlock,
options
})
events = [...firstHalfEvents, ...secondHalfEvents]
} else {
throw new Error(e)
}
}
return events
}
const getBlockNumberCall = web3 => web3.eth.getBlockNumber()
async function getBlockNumber(web3Home, web3Foreign) {
@ -45,6 +7,5 @@ async function getBlockNumber(web3Home, web3Foreign) {
}
module.exports = {
getPastEvents,
getBlockNumber
}

@ -10,7 +10,8 @@ const {
HOME_ERC_TO_ERC_ABI,
ERC20_ABI,
ERC677_BRIDGE_TOKEN_ABI,
getTokenType
getTokenType,
getPastEvents
} = require('../../commons')
const { HOME_RPC_URL, FOREIGN_RPC_URL, HOME_BRIDGE_ADDRESS, FOREIGN_BRIDGE_ADDRESS } = process.env
@ -23,7 +24,7 @@ const web3Home = new Web3(homeProvider)
const foreignProvider = new Web3.providers.HttpProvider(FOREIGN_RPC_URL)
const web3Foreign = new Web3(foreignProvider)
const { getPastEvents, getBlockNumber } = require('./contract')
const { getBlockNumber } = require('./contract')
async function main(mode) {
const homeErcBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, HOME_BRIDGE_ADDRESS)
@ -45,36 +46,29 @@ async function main(mode) {
const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
logger.debug("calling homeBridge.getPastEvents('UserRequestForSignature')")
const homeDeposits = await getPastEvents({
contract: homeBridge,
const homeDeposits = await getPastEvents(homeBridge, {
event: v1Bridge ? 'Deposit' : 'UserRequestForSignature',
fromBlock: HOME_DEPLOYMENT_BLOCK,
toBlock: homeBlockNumber,
options: {}
toBlock: homeBlockNumber
})
logger.debug("calling foreignBridge.getPastEvents('RelayedMessage')")
const foreignDeposits = await getPastEvents({
contract: foreignBridge,
const foreignDeposits = await getPastEvents(foreignBridge, {
event: v1Bridge ? 'Deposit' : 'RelayedMessage',
fromBlock: FOREIGN_DEPLOYMENT_BLOCK,
toBlock: foreignBlockNumber,
options: {}
toBlock: foreignBlockNumber
})
logger.debug("calling homeBridge.getPastEvents('AffirmationCompleted')")
const homeWithdrawals = await getPastEvents({
contract: homeBridge,
const homeWithdrawals = await getPastEvents(homeBridge, {
event: v1Bridge ? 'Withdraw' : 'AffirmationCompleted',
fromBlock: HOME_DEPLOYMENT_BLOCK,
toBlock: homeBlockNumber,
options: {}
toBlock: homeBlockNumber
})
logger.debug("calling foreignBridge.getPastEvents('UserRequestForAffirmation')")
const foreignWithdrawals = isExternalErc20
? await getPastEvents({
contract: erc20Contract,
? await getPastEvents(erc20Contract, {
event: 'Transfer',
fromBlock: FOREIGN_DEPLOYMENT_BLOCK,
toBlock: foreignBlockNumber,
@ -82,12 +76,10 @@ async function main(mode) {
filter: { to: FOREIGN_BRIDGE_ADDRESS }
}
})
: await getPastEvents({
contract: foreignBridge,
: await getPastEvents(foreignBridge, {
event: v1Bridge ? 'Withdraw' : 'UserRequestForAffirmation',
fromBlock: FOREIGN_DEPLOYMENT_BLOCK,
toBlock: foreignBlockNumber,
options: {}
toBlock: foreignBlockNumber
})
logger.debug('Done')
return {

@ -1,79 +0,0 @@
/* eslint no-param-reassign: ["error", { "props": false }] */
const { BRIDGE_VALIDATORS_ABI } = require('../../commons')
const logger = require('../logger')('validatorsUtils')
const { getPastEvents } = require('./contract')
const parseValidatorEvent = event => {
if (
event.event === undefined &&
event.raw &&
event.raw.topics &&
(event.raw.topics[0] === '0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987' ||
event.raw.topics[0] === '0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83')
) {
const rawAddress = event.raw.topics.length > 1 ? event.raw.topics[1] : event.raw.data
const address = '0x' + rawAddress.slice(26)
event.event = 'ValidatorAdded'
event.returnValues.validator = address
} else if (
event.event === undefined &&
event.raw &&
event.raw.topics &&
event.raw.topics[0] === '0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1'
) {
const rawAddress = event.raw.data === '0x' ? event.raw.topics[1] : event.raw.data
const address = '0x' + rawAddress.slice(26)
event.event = 'ValidatorRemoved'
event.returnValues.validator = address
}
}
const processValidatorsEvents = events => {
const validatorList = new Set()
events.forEach(event => {
parseValidatorEvent(event)
if (event.event === 'ValidatorAdded') {
validatorList.add(event.returnValues.validator)
} else if (event.event === 'ValidatorRemoved') {
validatorList.delete(event.returnValues.validator)
}
})
return Array.from(validatorList)
}
const validatorList = async contract => {
try {
return await contract.methods.validatorList().call()
} catch (e) {
return []
}
}
const getValidatorList = async (address, eth, fromBlock, toBlock) => {
logger.debug('getting validatorList')
const validatorsContract = new eth.Contract(BRIDGE_VALIDATORS_ABI, address)
const validators = await validatorList(validatorsContract)
if (validators.length) {
return validators
}
logger.debug('getting validatorsEvents')
const contract = new eth.Contract([], address)
const validatorsEvents = await getPastEvents({
contract,
event: 'allEvents',
fromBlock,
toBlock,
options: {}
})
return processValidatorsEvents(validatorsEvents)
}
module.exports = {
getValidatorList
}

@ -2,8 +2,7 @@ require('dotenv').config()
const Web3 = require('web3')
const fetch = require('node-fetch')
const logger = require('./logger')('validators')
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, gasPriceFromOracle } = require('../commons')
const { getValidatorList } = require('./utils/validatorUtils')
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, getValidatorList, gasPriceFromOracle } = require('../commons')
const { getBlockNumber } = require('./utils/contract')
const {
@ -66,20 +65,18 @@ async function main(bridgeMode) {
const foreignBridgeValidators = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, foreignValidatorsAddress)
logger.debug('calling foreignBridgeValidators getValidatorList()')
const foreignValidators = await getValidatorList(
foreignValidatorsAddress,
web3Foreign.eth,
FOREIGN_DEPLOYMENT_BLOCK,
foreignBlockNumber
)
const foreignValidators = await getValidatorList(foreignValidatorsAddress, web3Foreign.eth, {
from: FOREIGN_DEPLOYMENT_BLOCK,
to: foreignBlockNumber,
logger
})
logger.debug('calling homeBridgeValidators getValidatorList()')
const homeValidators = await getValidatorList(
homeValidatorsAddress,
web3Home.eth,
HOME_DEPLOYMENT_BLOCK,
homeBlockNumber
)
const homeValidators = await getValidatorList(homeValidatorsAddress, web3Home.eth, {
from: HOME_DEPLOYMENT_BLOCK,
to: homeBlockNumber,
logger
})
const homeBalances = {}
logger.debug('calling asyncForEach homeValidators homeBalances')

@ -9,7 +9,8 @@ import {
decodeFeeManagerMode,
getBridgeABIs,
getTokenType,
ERC20_BYTES32_ABI
ERC20_BYTES32_ABI,
getDeployedAtBlock
} from '../../../commons'
import {
getMaxPerTxLimit,
@ -27,7 +28,6 @@ import {
getHomeFee,
getFeeManagerMode,
ZERO_ADDRESS,
getDeployedAtBlock,
getValidatorList,
getValidatorContract,
getRequiredSignatures,

@ -11,7 +11,8 @@ import {
decodeFeeManagerMode,
getBridgeABIs,
HOME_V1_ABI,
ERC20_BYTES32_ABI
ERC20_BYTES32_ABI,
getDeployedAtBlock
} from '../../../commons'
import {
getMaxPerTxLimit,
@ -33,7 +34,6 @@ import {
getFeeManagerMode,
ZERO_ADDRESS,
getValidatorList,
getDeployedAtBlock,
getBlockRewardContract,
getValidatorContract,
getRequiredSignatures,

@ -1,7 +1,7 @@
import BN from 'bignumber.js'
import { fromDecimals } from './decimals'
import { fromWei } from 'web3-utils'
import { ERC_TYPES, REWARDABLE_VALIDATORS_ABI } from '../../../../commons'
import { getValidatorList as commonGetValidatorList, getPastEvents as commonGetPastEvents } from '../../../../commons'
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
@ -28,7 +28,7 @@ export const getCurrentLimit = async (contract, decimals) => {
}
export const getPastEvents = (contract, fromBlock, toBlock, event = 'allEvents') =>
contract.getPastEvents(event, { fromBlock, toBlock })
commonGetPastEvents(contract, { fromBlock, toBlock, event })
export const getErc677TokenAddress = contract => contract.methods.erc677token().call()
@ -62,70 +62,7 @@ export const totalBurntCoins = async contract => {
return new BN(burntCoins)
}
export const getValidatorList = async (address, eth) => {
const validatorsContract = new eth.Contract(REWARDABLE_VALIDATORS_ABI, address)
const validators = await validatorList(validatorsContract)
if (validators.length) {
return validators
}
const deployedAtBlock = await getDeployedAtBlock(validatorsContract)
const contract = new eth.Contract([], address)
const validatorsEvents = await contract.getPastEvents('allEvents', {
fromBlock: Number(deployedAtBlock)
})
return processValidatorsEvents(validatorsEvents)
}
export const validatorList = async contract => {
try {
return await contract.methods.validatorList().call()
} catch (e) {
return []
}
}
export const processValidatorsEvents = events => {
const validatorList = new Set()
events.forEach(event => {
parseValidatorEvent(event)
if (event.event === 'ValidatorAdded') {
validatorList.add(event.returnValues.validator)
} else if (event.event === 'ValidatorRemoved') {
validatorList.delete(event.returnValues.validator)
}
})
return Array.from(validatorList)
}
export const parseValidatorEvent = event => {
if (
event.event === undefined &&
event.raw &&
event.raw.topics &&
(event.raw.topics[0] === '0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987' ||
event.raw.topics[0] === '0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83')
) {
const rawAddress = event.raw.topics.length > 1 ? event.raw.topics[1] : event.raw.data
const address = '0x' + rawAddress.slice(26)
event.event = 'ValidatorAdded'
event.returnValues.validator = address
} else if (
event.event === undefined &&
event.raw &&
event.raw.topics &&
event.raw.topics[0] === '0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1'
) {
const rawAddress = event.raw.data === '0x' ? event.raw.topics[1] : event.raw.data
const address = '0x' + rawAddress.slice(26)
event.event = 'ValidatorRemoved'
event.returnValues.validator = address
}
}
export const getValidatorList = async (address, eth) => commonGetValidatorList(address, eth, { logger: console })
export const getName = contract => contract.methods.name().call()
@ -149,14 +86,6 @@ export const getForeignFee = async contract => {
return new BN(fromWei(feeInWei.toString()))
}
export const getDeployedAtBlock = async contract => {
try {
return await contract.methods.deployedAtBlock().call()
} catch (e) {
return 0
}
}
export const getBlockRewardContract = contract => contract.methods.blockRewardContract().call()
export const getValidatorContract = contract => contract.methods.validatorContract().call()