Compare commits

...

1 Commits

Author SHA1 Message Date
Kirill Fedoseev
c2429880ab Remove deprecated CHAI swap worker 2021-05-03 09:42:34 +03:00
11 changed files with 7 additions and 483 deletions

@ -1,6 +1,5 @@
const Web3 = require('web3')
const assert = require('assert')
const promiseRetry = require('promise-retry')
const {
user,
secondUser,
@ -196,211 +195,4 @@ describe('erc to native', () => {
)
})
}
it('should not invest dai when chai token is disabled', async () => {
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
await foreignBridge.methods.setMinDaiTokenBalance(foreignWeb3.utils.toWei('2', 'ether')).send({
from: validator.address,
gas: '1000000'
}) // set min limit for automatic investment to 2*2 dai
const valueToTransfer = foreignWeb3.utils.toWei('5', 'ether')
// this transfer won't trigger a call to convert to chai
await erc20Token.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
from: user.address,
gas: '1000000'
})
await promiseRetry(async (retry, number) => {
if (number < 4) {
retry()
} else {
const updatedBridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
assert(
toBN(bridgeDaiTokenBalance)
.add(toBN(valueToTransfer))
.eq(toBN(updatedBridgeDaiTokenBalance)),
'Dai tokens should not be when chai is disabled'
)
}
})
})
it('should invest dai after enough tokens are collected on bridge account', async () => {
await foreignBridge.methods.initializeChaiToken().send({
from: validator.address,
gas: '1000000'
}) // initialize chai token
await foreignBridge.methods.setMinDaiTokenBalance('0').send({
from: validator.address,
gas: '1000000'
}) // set investing limit to 0
await foreignBridge.methods.convertDaiToChai().send({
from: validator.address,
gas: '1000000'
}) // convert all existing dai tokens on bridge account to chai, in order to start from zero balance
await foreignBridge.methods.setMinDaiTokenBalance(foreignWeb3.utils.toWei('2', 'ether')).send({
from: validator.address,
gas: '1000000'
}) // set investing limit to 2 dai, automatically invest should happen after 4 dai
const valueToTransfer = foreignWeb3.utils.toWei('3', 'ether')
// this transfer won't trigger a call to convert to chai
await erc20Token.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
from: user.address,
gas: '1000000'
})
await promiseRetry(async (retry, number) => {
if (number < 4) {
retry()
} else {
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
assert(
valueToTransfer === bridgeDaiTokenBalance,
'Dai tokens should not be invested automatically before twice limit is reached'
)
}
})
// this transfer will trigger call to convert to chai
await erc20Token.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
from: user.address,
gas: '1000000'
})
await promiseRetry(async retry => {
const updatedBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
if (toBN(updatedBalance).gte(toBN(valueToTransfer).add(toBN(valueToTransfer)))) {
retry()
} else {
const updatedBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
assert(
toBN(updatedBalance).eq(toBN(foreignWeb3.utils.toWei('2', 'ether'))),
'Dai bridge balance should be equal to limit'
)
}
})
})
if (process.env.ULTIMATE !== 'true') {
describe('handling of chai swaps', async () => {
before(async () => {
// Next tests check validator nonces, this will force every validator to submit signature/affirmation
// Set 3 required signatures for home bridge
await setRequiredSignatures({
bridgeContract: homeBridge,
web3: homeWeb3,
requiredSignatures: 3,
options: {
from: validator.address,
gas: '4000000'
}
})
// Set 3 required signatures for foreign bridge
await setRequiredSignatures({
bridgeContract: foreignBridge,
web3: foreignWeb3,
requiredSignatures: 3,
options: {
from: validator.address,
gas: '4000000'
}
})
})
it('should not handle transfer event in paying interest', async () => {
await foreignBridge.methods.setInterestReceiver(user.address).send({
from: validator.address,
gas: '1000000'
})
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
await foreignBridge.methods.payInterest().send({
from: user.address,
gas: '1000000'
})
await promiseRetry(async (retry, number) => {
if (number < 6) {
retry()
} else {
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
assert(
nonce === initialNonce,
'Validator should not process transfer event originated during converting Chai => Dai'
)
}
})
})
it('should not handle chai withdrawal transfer event in executeSignatures as a regular transfer', async () => {
await foreignBridge.methods.setMinDaiTokenBalance('0').send({
from: validator.address,
gas: '1000000'
}) // set investing limit to 0
await foreignBridge.methods.convertDaiToChai().send({
from: validator.address,
gas: '1000000'
}) // convert all existing dai tokens on bridge account to chai, in order to start from zero balance
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
// send transaction to home bridge
await homeWeb3.eth.sendTransaction({
from: user.address,
to: COMMON_HOME_BRIDGE_ADDRESS,
gasPrice: '1',
gas: '1000000',
value: homeWeb3.utils.toWei('0.01')
})
// check that balance increases
await uniformRetry(async retry => {
const balance = await erc20Token.methods.balanceOf(user.address).call()
if (toBN(balance).lte(toBN(originalBalance))) {
retry()
}
})
await promiseRetry(async (retry, number) => {
if (number < 6) {
retry()
} else {
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
assert(
nonce === initialNonce + 1,
'Validator should not process transfer event originated during converting Chai => Dai'
)
}
})
})
after(async () => {
// Set 2 required signatures for home bridge
await setRequiredSignatures({
bridgeContract: homeBridge,
web3: homeWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
// Set 2 required signatures for foreign bridge
await setRequiredSignatures({
bridgeContract: foreignBridge,
web3: foreignWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
})
})
}
})

@ -1,20 +0,0 @@
const baseConfig = require('./base.config')
const { EXIT_CODES } = require('../src/utils/constants')
const id = `${baseConfig.id}-convert-to-chai`
const workerRequired = baseConfig.id === 'erc-native'
if (!workerRequired) {
console.error(`Convert to chai tokens worker not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`)
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
}
module.exports = {
...baseConfig.bridgeConfig,
...baseConfig.foreignConfig,
workerQueue: 'convert-to-chai',
senderQueue: 'foreign-prioritized',
name: `worker-${id}`,
id
}

@ -29,11 +29,6 @@ if (!transferWatcherRequired) {
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
}
const workerQueueConfig = {}
if (baseConfig.id === 'erc-native') {
workerQueueConfig.workerQueue = 'convert-to-chai'
}
module.exports = {
...baseConfig.bridgeConfig,
...baseConfig.foreignConfig,
@ -42,7 +37,6 @@ module.exports = {
eventAbi: ERC20_ABI,
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
queue: 'home-prioritized',
...workerQueueConfig,
name: `watcher-${id}`,
id
}

@ -7,7 +7,6 @@ services:
service: rabbit
networks:
- net_rabbit_bridge_transfer
- net_rabbit_bridge_convert_to_chai_worker
redis:
extends:
file: docker-compose.yml
@ -51,18 +50,6 @@ services:
networks:
- net_db_bridge_transfer
- net_rabbit_bridge_transfer
bridge_convert_to_chai_worker:
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 worker:convert-to-chai
networks:
- net_rabbit_bridge_convert_to_chai_worker
bridge_senderhome:
extends:
file: docker-compose.yml
@ -111,5 +98,3 @@ networks:
driver: bridge
net_rabbit_bridge_senderforeign:
driver: bridge
net_rabbit_bridge_convert_to_chai_worker:
driver: bridge

@ -9,7 +9,6 @@
"watcher:collected-signatures": "./scripts/start-worker.sh watcher collected-signatures-watcher",
"watcher:affirmation-request": "./scripts/start-worker.sh watcher affirmation-request-watcher",
"watcher:transfer": "./scripts/start-worker.sh watcher transfer-watcher",
"worker:convert-to-chai": "./scripts/start-worker.sh worker convert-to-chai-worker",
"sender:home": "./scripts/start-worker.sh sender home-sender",
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
"confirm:transfer": "./scripts/start-worker.sh confirmRelay transfer-watcher",

@ -47,7 +47,6 @@ async function initialize() {
connectWatcherToQueue({
queueName: config.queue,
workerQueue: config.workerQueue,
cb: runMain
})
} catch (e) {

@ -26,25 +26,18 @@ async function isAttached() {
return new Promise(res => dns.lookup(amqpHost, err => res(err === null)))
}
function connectWatcherToQueue({ queueName, workerQueue, cb }) {
function connectWatcherToQueue({ queueName, cb }) {
const channelWrapper = connection.createChannel({
json: true,
async setup(channel) {
await channel.assertQueue(queueName, { durable: true, maxPriority: SENDER_QUEUE_MAX_PRIORITY })
if (workerQueue) {
await channel.assertQueue(workerQueue, { durable: true })
}
}
})
const sendToQueue = data =>
channelWrapper.sendToQueue(queueName, data, { persistent: true, priority: SENDER_QUEUE_SEND_PRIORITY })
let sendToWorker
if (workerQueue) {
sendToWorker = data => channelWrapper.sendToQueue(workerQueue, data, { persistent: true })
}
cb({ sendToQueue, sendToWorker, channel: channelWrapper })
cb({ sendToQueue, channel: channelWrapper })
}
function connectSenderToQueue({ queueName, oldQueueName, cb, resendInterval }) {
@ -105,42 +98,6 @@ function connectSenderToQueue({ queueName, oldQueueName, cb, resendInterval }) {
})
}
function connectWorkerToQueue({ queueName, senderQueue, cb }) {
const deadLetterExchange = `${queueName}-retry`
const channelWrapper = connection.createChannel({
json: true
})
channelWrapper.addSetup(async channel => {
await channel.assertExchange(deadLetterExchange, 'fanout', { durable: true })
await channel.assertQueue(queueName, { durable: true })
await channel.assertQueue(senderQueue, { durable: true, maxPriority: SENDER_QUEUE_MAX_PRIORITY })
await channel.bindQueue(queueName, deadLetterExchange)
await channel.prefetch(1)
await channel.consume(queueName, msg =>
cb({
msg,
channel: channelWrapper,
ackMsg: job => channelWrapper.ack(job),
nackMsg: job => channelWrapper.nack(job, false, true),
sendToSenderQueue: data =>
channelWrapper.sendToQueue(senderQueue, data, { persistent: true, priority: SENDER_QUEUE_SEND_PRIORITY }),
scheduleForRetry: async (data, msgRetries = 0) => {
await generateRetry({
data,
msgRetries,
channelWrapper,
channel,
queueName,
deadLetterExchange
})
}
})
)
})
}
async function generateRetry({ data, msgRetries, channelWrapper, channel, queueName, deadLetterExchange }) {
const retries = msgRetries + 1
const delay = getRetrySequence(retries) * 1000
@ -183,7 +140,6 @@ module.exports = {
isAttached,
connectWatcherToQueue,
connectSenderToQueue,
connectWorkerToQueue,
connection,
generateRetry
}

@ -1,13 +0,0 @@
async function isChaiTokenEnabled(bridgeContract, logger) {
logger.debug('Checking Chai availability')
try {
return await bridgeContract.methods.isChaiTokenEnabled().call()
} catch (e) {
logger.debug('Method isChaiTokenEnabled is not supported')
return false
}
}
module.exports = {
isChaiTokenEnabled
}

@ -9,7 +9,6 @@ const { getShutdownFlag } = require('./services/shutdownState')
const { getRequiredBlockConfirmations, getEvents } = require('./tx/web3')
const { checkHTTPS, watchdog } = require('./utils/utils')
const { EXIT_CODES } = require('./utils/constants')
const { isChaiTokenEnabled } = require('./utils/chaiUtils')
if (process.argv.length < 3) {
logger.error('Please check the number of arguments, config file was not provided')
@ -47,7 +46,6 @@ async function initialize() {
await getLastProcessedBlock()
connectWatcherToQueue({
queueName: config.queue,
workerQueue: config.workerQueue,
cb: runMain
})
} catch (e) {
@ -56,16 +54,16 @@ async function initialize() {
}
}
async function runMain({ sendToQueue, sendToWorker }) {
async function runMain({ sendToQueue }) {
try {
if (connection.isConnected() && redis.status === 'ready') {
if (config.maxProcessingTime) {
await watchdog(() => main({ sendToQueue, sendToWorker }), config.maxProcessingTime, () => {
await watchdog(() => main({ sendToQueue }), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED)
})
} else {
await main({ sendToQueue, sendToWorker })
await main({ sendToQueue })
}
}
} catch (e) {
@ -73,7 +71,7 @@ async function runMain({ sendToQueue, sendToWorker }) {
}
setTimeout(() => {
runMain({ sendToQueue, sendToWorker })
runMain({ sendToQueue })
}, config.pollingInterval)
}
@ -147,16 +145,7 @@ async function getLastBlockToProcess() {
return lastBlockNumber.sub(requiredBlockConfirmations)
}
async function isWorkerNeeded() {
switch (config.id) {
case 'erc-native-transfer':
return isChaiTokenEnabled(bridgeContract, logger)
default:
return true
}
}
async function main({ sendToQueue, sendToWorker }) {
async function main({ sendToQueue }) {
try {
const wasShutdown = await getShutdownFlag(logger, config.shutdownKey, false)
if (await getShutdownFlag(logger, config.shutdownKey, true)) {
@ -191,10 +180,6 @@ async function main({ sendToQueue, sendToWorker }) {
logger.info(`Found ${events.length} ${config.event} events`)
if (events.length) {
if (sendToWorker && (await isWorkerNeeded())) {
await sendToWorker({ blockNumber: toBlock.toString() })
}
const job = await processEvents(events)
logger.info('Transactions to send:', job.length)

@ -1,73 +0,0 @@
const path = require('path')
const logger = require('./services/logger')
const { checkHTTPS, watchdog } = require('./utils/utils')
const { EXIT_CODES } = require('./utils/constants')
const { connectWorkerToQueue } = require('./services/amqpClient')
const config = require(path.join('../config/', process.argv[2]))
const convertToChai = require('./workers/convertToChai')(config)
const web3Instance = config.web3
async function initialize() {
try {
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
web3Instance.currentProvider.urls.forEach(checkHttps(config.chain))
connectWorkerToQueue({
queueName: config.workerQueue,
senderQueue: config.senderQueue,
cb: options => {
if (config.maxProcessingTime) {
return watchdog(() => main(options), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED)
})
}
return main(options)
}
})
} catch (e) {
logger.error(e.message)
process.exit(EXIT_CODES.GENERAL_ERROR)
}
}
async function run(blockNumber) {
if (config.id === 'erc-native-convert-to-chai') {
return convertToChai(blockNumber)
} else {
return []
}
}
async function main({ msg, ackMsg, nackMsg, sendToSenderQueue, scheduleForRetry }) {
try {
const { blockNumber } = JSON.parse(msg.content)
logger.info(`Msg received with block number ${blockNumber}`)
try {
const job = await run(blockNumber)
logger.info('Transactions to send:', job.length)
if (job.length) {
await sendToSenderQueue(job)
}
} catch (e) {
logger.info(`Sending failed msg to retry`)
await scheduleForRetry({ blockNumber }, msg.properties.headers['x-retries'])
}
ackMsg(msg)
} catch (e) {
logger.error(e)
nackMsg(msg)
}
logger.debug(`Finished worker operation`)
}
initialize()

@ -1,80 +0,0 @@
require('../../env')
const { HttpListProviderError } = require('../services/HttpListProvider')
const rootLogger = require('../services/logger')
const { web3Foreign } = require('../services/web3')
const { BRIDGE_VALIDATORS_ABI } = require('../../../commons')
let validatorContract = null
function convertToChaiBuilder(config) {
const foreignBridge = new web3Foreign.eth.Contract(config.foreignBridgeAbi, config.foreignBridgeAddress)
return async function convertToChai(blockNumber) {
const txToSend = []
const logger = rootLogger.child({
blockNumber: blockNumber.toString()
})
logger.debug(`Starting convert to chai operation`)
if (validatorContract === null) {
logger.debug('Getting validator contract address')
const validatorContractAddress = await foreignBridge.methods.validatorContract().call()
logger.debug({ validatorContractAddress }, 'Validator contract address obtained')
validatorContract = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress)
}
logger.debug(`Checking if is validator duty`)
const validatorDuty = await validatorContract.methods.isValidatorDuty(config.validatorAddress).call()
if (!validatorDuty) {
logger.info(`Convert to chai discarded because is not validator duty`)
return txToSend
}
logger.debug(`Checking if dai token balance is above the threshold`)
const daiNeedsToBeInvested = await foreignBridge.methods.isDaiNeedsToBeInvested().call()
if (!daiNeedsToBeInvested) {
logger.info(`Convert to chai discarded because dai balance is below the threshold or chai token is not set`)
return txToSend
}
let gasEstimate
try {
logger.debug(`Estimate gas`)
gasEstimate = await foreignBridge.methods.convertDaiToChai().estimateGas({
from: config.validatorAddress
})
logger.debug({ gasEstimate }, 'Gas estimated')
} catch (e) {
if (e instanceof HttpListProviderError) {
const errorMsg = 'RPC Connection Error: convertToChai Gas Estimate cannot be obtained.'
logger.error(e, errorMsg)
throw new Error(errorMsg)
} else {
logger.error(e, 'Unknown error while processing transaction')
throw e
}
}
// generate data
const data = await foreignBridge.methods.convertDaiToChai().encodeABI()
// push to job
txToSend.push({
data,
gasEstimate,
transactionReference: `convert to chai operation for block number ${blockNumber.toString()}`,
to: config.foreignBridgeAddress
})
return txToSend
}
}
module.exports = convertToChaiBuilder