tokenbridge/oracle/src/confirmRelay.js

210 lines
6.7 KiB
JavaScript

require('../env')
const path = require('path')
const fs = require('fs')
const { isAttached, connectWatcherToQueue, connection } = require('./services/amqpClient')
const logger = require('./services/logger')
const GasPrice = require('./services/gasPrice')
const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3')
const { sendTx } = require('./tx/sendTx')
const { getTokensState } = require('./utils/tokenState')
const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants')
const { ORACLE_ALLOW_HTTP_FOR_RPC } = process.env
if (process.argv.length < 4) {
logger.error('Please check the number of arguments, transaction hash is not present')
process.exit(EXIT_CODES.GENERAL_ERROR)
}
const config = require(path.join('../config/', process.argv[2]))
const { web3, eventContract, chain, bridgeContract } = config.main
const isTxHash = txHash => txHash.length === 66 && web3.utils.isHexStrict(txHash)
function readTxHashes(filePath) {
return fs
.readFileSync(filePath)
.toString()
.split('\n')
.map(v => v.trim())
.filter(isTxHash)
}
const txHashesArgs = process.argv.slice(3)
const rawTxHashes = txHashesArgs.filter(isTxHash)
const txHashesFiles = txHashesArgs.filter(path => fs.existsSync(path)).flatMap(readTxHashes)
const txHashes = [...rawTxHashes, ...txHashesFiles]
const processSignatureRequests = require('./events/processSignatureRequests')(config)
const processCollectedSignatures = require('./events/processCollectedSignatures')(config)
const processAffirmationRequests = require('./events/processAffirmationRequests')(config)
const processTransfers = require('./events/processTransfers')(config)
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
const processAMBInformationRequests = require('./events/processAMBInformationRequests')(config)
let attached
async function initialize() {
try {
const checkHttps = checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger)
web3.currentProvider.urls.forEach(checkHttps(chain))
attached = await isAttached()
if (attached) {
logger.info('RabbitMQ container is available, using oracle sender')
} else {
logger.info('RabbitMQ container is not available, using internal sender')
}
connectWatcherToQueue({
queueName: config.queue,
cb: runMain
})
} catch (e) {
logger.error(e)
process.exit(EXIT_CODES.GENERAL_ERROR)
}
}
async function runMain({ sendToQueue }) {
try {
const sendJob = attached ? sendToQueue : sendJobTx
if (!attached || connection.isConnected()) {
if (config.maxProcessingTime) {
await watchdog(() => main({ sendJob, txHashes }), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED)
})
} else {
await main({ sendJob, txHashes })
}
} else {
setTimeout(() => {
runMain({ sendToQueue })
}, config.pollingInterval)
}
} catch (e) {
logger.error(e)
}
}
function processEvents(events) {
switch (config.id) {
case 'erc-native-signature-request':
return processSignatureRequests(events)
case 'erc-native-collected-signatures':
return processCollectedSignatures(events)
case 'erc-native-affirmation-request':
return processAffirmationRequests(events)
case 'erc-native-transfer':
return processTransfers(events)
case 'amb-signature-request':
return processAMBSignatureRequests(events)
case 'amb-collected-signatures':
return processAMBCollectedSignatures(events)
case 'amb-affirmation-request':
return processAMBAffirmationRequests(events)
case 'amb-information-request':
return processAMBInformationRequests(events)
default:
return []
}
}
async function main({ sendJob, txHashes }) {
if (config.id === 'erc-native-transfer') {
logger.debug('Getting token address to listen Transfer events')
const state = await getTokensState(bridgeContract, logger)
eventContract.options.address = state.bridgeableTokenAddress
}
logger.info(`Processing ${txHashes.length} input transactions`)
for (const txHash of txHashes) {
try {
logger.info({ txHash }, `Processing transaction`)
const events = await getEventsFromTx({
web3,
contract: eventContract,
event: config.event,
txHash,
filter: config.eventFilter
})
logger.info({ txHash }, `Found ${events.length} ${config.event} events`)
if (events.length) {
const job = await processEvents(events)
logger.info({ txHash }, 'Transactions to send:', job.length)
if (job.length) {
await sendJob(job)
}
}
} catch (e) {
logger.error(e)
}
}
await connection.close()
logger.debug('Finished')
}
async function sendJobTx(jobs) {
await GasPrice.start(chain, true)
const gasPrice = GasPrice.getPrice().toString(10)
const { web3 } = config.sender === 'foreign' ? config.foreign : config.home
const chainId = await getChainId(web3)
let nonce = await getNonce(web3, config.validatorAddress)
await syncForEach(jobs, async job => {
let gasLimit
if (typeof job.extraGas === 'number') {
gasLimit = addExtraGas(job.gasEstimate + job.extraGas, 0, MAX_GAS_LIMIT)
} else {
gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT)
}
try {
logger.info(`Sending transaction with nonce ${nonce}`)
const txHash = await sendTx({
data: job.data,
nonce,
gasPrice,
amount: '0',
gasLimit,
privateKey: config.validatorPrivateKey,
to: job.to,
chainId,
web3
})
nonce++
logger.info(
{ eventTransactionHash: job.transactionReference, generatedTransactionHash: txHash },
`Tx generated ${txHash} for event Tx ${job.transactionReference}`
)
} catch (e) {
logger.error(
{ eventTransactionHash: job.transactionReference, error: e.message },
`Tx Failed for event Tx ${job.transactionReference}.`,
e.message
)
if (e.message.toLowerCase().includes('insufficient funds')) {
const currentBalance = await web3.eth.getBalance(config.validatorAddress)
const minimumBalance = gasLimit.multipliedBy(gasPrice)
logger.error(
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`
)
}
}
})
}
initialize()