Update watcher to be able to skip events

This commit is contained in:
Gerardo Nardelli 2019-12-04 17:18:14 -03:00
parent 0eeae74ffa
commit 3cd53f7bda
5 changed files with 169 additions and 18 deletions

@ -32,7 +32,7 @@ FOREIGN_GAS_PRICE=10000000000
FOREIGN_REWARDABLE=false FOREIGN_REWARDABLE=false
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977 BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
ERC20_TOKEN_ADDRESS=0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9 ERC20_TOKEN_ADDRESS=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359
REQUIRED_NUMBER_OF_VALIDATORS=1 REQUIRED_NUMBER_OF_VALIDATORS=1
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"

@ -33,12 +33,122 @@ describe('erc to native', () => {
before(async () => { before(async () => {
halfDuplexTokenAddress = await foreignBridge.methods.halfDuplexErc20token().call() halfDuplexTokenAddress = await foreignBridge.methods.halfDuplexErc20token().call()
halfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, halfDuplexTokenAddress) halfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, halfDuplexTokenAddress)
})
it('should continue working after migration', async () => {
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
// set min threshold for swap const transferValue = homeWeb3.utils.toWei('0.01')
// erc20 token address and half duplex address are the same before migration
const tokenAddress = await foreignBridge.methods.erc20token().call()
const erc20AndhalfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, tokenAddress)
// send tokens to foreign bridge
await erc20AndhalfDuplexToken.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
.send({
from: user.address,
gas: '1000000'
})
.catch(e => {
console.error(e)
})
// Send a trivial transaction to generate a new block since the watcher
// is configured to wait 1 confirmation block
await generateNewBlock(foreignWeb3, user.address)
// check that balance increases
await promiseRetry(async (retry, number) => {
const balance = await homeWeb3.eth.getBalance(user.address)
await generateNewBlock(foreignWeb3, user.address)
// retry at least 4 times to check transfer is not double processed by the two watchers
if (toBN(balance).lte(toBN(originalBalanceOnHome)) || number < 4) {
retry()
} else {
assert(
toBN(balance).eq(toBN(originalBalanceOnHome).add(toBN(transferValue))),
'User balance should be increased only by second transfer'
)
}
})
// call migration
await foreignBridge.methods.migrateToMCD().send({
from: validator.address,
gas: '4000000'
})
// update min threshold for swap
await foreignBridge.methods.setMinHDTokenBalance(foreignWeb3.utils.toWei('1', 'ether')).send({ await foreignBridge.methods.setMinHDTokenBalance(foreignWeb3.utils.toWei('1', 'ether')).send({
from: validator.address, from: validator.address,
gas: '1000000' gas: '1000000'
}) })
const AfterMigrateBalance = await homeWeb3.eth.getBalance(user.address)
// send tokens to foreign bridge
await erc20Token.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
.send({
from: user.address,
gas: '1000000'
})
.catch(e => {
console.error(e)
})
// Send a trivial transaction to generate a new block since the watcher
// is configured to wait 1 confirmation block
await generateNewBlock(foreignWeb3, user.address)
// check that balance increases
await promiseRetry(async (retry, number) => {
const balance = await homeWeb3.eth.getBalance(user.address)
await generateNewBlock(foreignWeb3, user.address)
// retry at least 4 times to check transfer is not double processed by the two watchers
if (toBN(balance).lte(toBN(AfterMigrateBalance)) || number < 4) {
retry()
} else {
assert(
toBN(balance).eq(toBN(AfterMigrateBalance).add(toBN(transferValue))),
'User balance should be increased only by second transfer'
)
}
})
const afterMigrateAndTransferBalance = await homeWeb3.eth.getBalance(user.address)
// send tokens to foreign bridge
await halfDuplexToken.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
.send({
from: user.address,
gas: '1000000'
})
.catch(e => {
console.error(e)
})
// Send a trivial transaction to generate a new block since the watcher
// is configured to wait 1 confirmation block
await generateNewBlock(foreignWeb3, user.address)
// check that balance increases
await promiseRetry(async (retry, number) => {
const balance = await homeWeb3.eth.getBalance(user.address)
await generateNewBlock(foreignWeb3, user.address)
// retry at least 4 times to check transfer is not double processed by the two watchers
if (toBN(balance).lte(toBN(afterMigrateAndTransferBalance)) || number < 4) {
retry()
} else {
assert(
toBN(balance).eq(toBN(afterMigrateAndTransferBalance).add(toBN(transferValue))),
'User balance should be increased only by second transfer'
)
}
})
}) })
it('should convert tokens in foreign to coins in home', async () => { it('should convert tokens in foreign to coins in home', async () => {
const balance = await erc20Token.methods.balanceOf(user.address).call() const balance = await erc20Token.methods.balanceOf(user.address).call()
@ -116,7 +226,6 @@ describe('erc to native', () => {
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address) const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
const bridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() const bridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
assert(toBN(bridgeHalfDuplexBalance).isZero(), 'Bridge balance should be zero')
const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether') const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether')
@ -176,7 +285,9 @@ describe('erc to native', () => {
assert(toBN(updatedBalance).isZero(), 'Half duplex bridge balance should be zero') assert(toBN(updatedBalance).isZero(), 'Half duplex bridge balance should be zero')
assert( assert(
toBN(updatedBridgeErc20TokenBalance).eq( toBN(updatedBridgeErc20TokenBalance).eq(
toBN(bridgeErc20TokenBalance).add(toBN(foreignWeb3.utils.toWei('2', 'ether'))) toBN(bridgeErc20TokenBalance)
.add(toBN(bridgeHalfDuplexBalance))
.add(toBN(foreignWeb3.utils.toWei('2', 'ether')))
), ),
'Erc20 token balance should be correctly increased by the token swap' 'Erc20 token balance should be correctly increased by the token swap'
) )

@ -1,5 +1,6 @@
require('../env') require('../env')
const Web3 = require('web3') const Web3 = require('web3')
const { getTokensState } = require('../src/events/processTransfers/tokenState')
const { const {
ERC677_BRIDGE_TOKEN_ABI, ERC677_BRIDGE_TOKEN_ABI,
FOREIGN_ERC_TO_ERC_ABI, FOREIGN_ERC_TO_ERC_ABI,
@ -9,7 +10,7 @@ const {
async function initialChecks() { async function initialChecks() {
const { ORACLE_BRIDGE_MODE, COMMON_FOREIGN_RPC_URL, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env const { ORACLE_BRIDGE_MODE, COMMON_FOREIGN_RPC_URL, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
const result = {} let result = {}
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)) const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL))
if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') { if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') {
@ -17,17 +18,7 @@ async function initialChecks() {
result.bridgeableTokenAddress = await bridge.methods.erc20token().call() result.bridgeableTokenAddress = await bridge.methods.erc20token().call()
} else if (ORACLE_BRIDGE_MODE === 'ERC_TO_NATIVE') { } else if (ORACLE_BRIDGE_MODE === 'ERC_TO_NATIVE') {
const bridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) const bridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
result.bridgeableTokenAddress = await bridge.methods.erc20token().call() result = await getTokensState(bridge)
try {
const halfDuplexErc20tokenAddress = await bridge.methods.halfDuplexErc20token().call()
if (halfDuplexErc20tokenAddress !== result.bridgeableTokenAddress) {
result.halfDuplexTokenAddress = halfDuplexErc20tokenAddress
} else {
result.idle = true
}
} catch (e) {
result.idle = true
}
} }
if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') { if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') {

@ -0,0 +1,20 @@
async function getTokensState(bridgeContract) {
const context = {}
context.bridgeableTokenAddress = await bridgeContract.methods.erc20token().call()
try {
const halfDuplexErc20tokenAddress = await bridgeContract.methods.halfDuplexErc20token().call()
if (halfDuplexErc20tokenAddress !== context.bridgeableTokenAddress) {
context.halfDuplexTokenAddress = halfDuplexErc20tokenAddress
} else {
context.idle = true
}
} catch (e) {
context.idle = true
}
return context
}
module.exports = {
getTokensState
}

@ -26,12 +26,16 @@ const processAMBSignatureRequests = require('./events/processAMBSignatureRequest
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config) const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config) const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
const { getTokensState } = require('./events/processTransfers/tokenState')
const ZERO = toBN(0) const ZERO = toBN(0)
const ONE = toBN(1) const ONE = toBN(1)
const web3Instance = config.web3 const web3Instance = config.web3
const bridgeContract = new web3Instance.eth.Contract(config.bridgeAbi, config.bridgeContractAddress) const bridgeContract = new web3Instance.eth.Contract(config.bridgeAbi, config.bridgeContractAddress)
const eventContract = new web3Instance.eth.Contract(config.eventAbi, config.eventContractAddress) let { eventContractAddress } = config
let eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress)
let skipEvents = config.idle
const lastBlockRedisKey = `${config.id}:lastProcessedBlock` const lastBlockRedisKey = `${config.id}:lastProcessedBlock`
let lastProcessedBlock = BN.max(config.startBlock.sub(ONE), ZERO) let lastProcessedBlock = BN.max(config.startBlock.sub(ONE), ZERO)
@ -117,6 +121,29 @@ function processEvents(events, blockNumber) {
} }
} }
async function checkConditions() {
let state
switch (config.id) {
case 'erc-native-transfer':
state = await getTokensState(bridgeContract)
updateEventContract(state.bridgeableTokenAddress)
break
case 'erc-native-half-duplex-transfer':
state = await getTokensState(bridgeContract)
skipEvents = state.idle
updateEventContract(state.halfDuplexTokenAddress)
break
default:
}
}
function updateEventContract(address) {
if (eventContractAddress !== address) {
eventContractAddress = address
eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress)
}
}
async function getLastBlockToProcess() { async function getLastBlockToProcess() {
const lastBlockNumberPromise = getBlockNumber(web3Instance).then(toBN) const lastBlockNumberPromise = getBlockNumber(web3Instance).then(toBN)
const requiredBlockConfirmationsPromise = getRequiredBlockConfirmations(bridgeContract).then(toBN) const requiredBlockConfirmationsPromise = getRequiredBlockConfirmations(bridgeContract).then(toBN)
@ -130,7 +157,9 @@ async function getLastBlockToProcess() {
async function main({ sendToQueue, sendToWorker }) { async function main({ sendToQueue, sendToWorker }) {
try { try {
if (config.idle) { await checkConditions()
if (skipEvents) {
logger.debug('Watcher in idle mode, skipping getting events') logger.debug('Watcher in idle mode, skipping getting events')
return return
} }