Merge the develop branch to the master branch, preparation to v2.6.0-rc0

This commit is contained in:
Alexander Kolotov 2020-10-02 20:34:31 +03:00 committed by GitHub
commit 44ca0d71ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 195 additions and 56 deletions

@ -22,7 +22,7 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR | A value that will multiply the gas price of th
name | description | value name | description | value
--- | --- | --- --- | --- | ---
ORACLE_BRIDGE_MODE | The bridge mode. The bridge starts listening to a different set of events based on this parameter. | NATIVE_TO_ERC / ERC_TO_ERC / ERC_TO_NATIVE ORACLE_BRIDGE_MODE | The bridge mode. The bridge starts listening to a different set of events based on this parameter. | NATIVE_TO_ERC / ERC_TO_ERC / ERC_TO_NATIVE / ARBITRARY_MESSAGE
ORACLE_ALLOW_HTTP_FOR_RPC | **Only use in test environments - must be omitted in production environments.**. If this parameter is specified and set to `yes`, RPC URLs can be specified in form of HTTP links. A warning that the connection is insecure will be written to the logs. | `yes` / `no` ORACLE_ALLOW_HTTP_FOR_RPC | **Only use in test environments - must be omitted in production environments.**. If this parameter is specified and set to `yes`, RPC URLs can be specified in form of HTTP links. A warning that the connection is insecure will be written to the logs. | `yes` / `no`
ORACLE_HOME_RPC_POLLING_INTERVAL | The interval in milliseconds used to request the RPC node in the Home network for new blocks. The interval should match the average production time for a new block. | integer ORACLE_HOME_RPC_POLLING_INTERVAL | The interval in milliseconds used to request the RPC node in the Home network for new blocks. The interval should match the average production time for a new block. | integer
ORACLE_FOREIGN_RPC_POLLING_INTERVAL | The interval in milliseconds used to request the RPC node in the Foreign network for new blocks. The interval should match the average production time for a new block. | integer ORACLE_FOREIGN_RPC_POLLING_INTERVAL | The interval in milliseconds used to request the RPC node in the Foreign network for new blocks. The interval should match the average production time for a new block. | integer
@ -37,6 +37,10 @@ ORACLE_MAX_PROCESSING_TIME | The workers processes will be killed if this amount
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY | The private key of the bridge validator used to sign confirmations before sending transactions to the bridge contracts. The validator account is calculated automatically from the private key. Every bridge instance (set of watchers and senders) must have its own unique private key. The specified private key is used to sign transactions on both sides of the bridge. | hexidecimal without "0x" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY | The private key of the bridge validator used to sign confirmations before sending transactions to the bridge contracts. The validator account is calculated automatically from the private key. Every bridge instance (set of watchers and senders) must have its own unique private key. The specified private key is used to sign transactions on both sides of the bridge. | hexidecimal without "0x"
ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexidecimal with "0x" ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexidecimal with "0x"
ORACLE_TX_REDUNDANCY | If set to `true`, instructs oracle to send `eth_sendRawTransaction` requests through all available RPC urls defined in `COMMON_HOME_RPC_URL` and `COMMON_FOREIGN_RPC_URL` variables instead of using first available one ORACLE_TX_REDUNDANCY | If set to `true`, instructs oracle to send `eth_sendRawTransaction` requests through all available RPC urls defined in `COMMON_HOME_RPC_URL` and `COMMON_FOREIGN_RPC_URL` variables instead of using first available one
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST | Filename with a list of addresses, separated by newlines. If set, determines the privileged set of accounts whose requests will be automatically processed by the CollectedSignatures watcher. | string
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST | Filename with a list of addresses, separated by newlines. If set, determines the blocked set of accounts whose requests will not be automatically processed by the CollectedSignatures watcher. Has a lower priority than the `ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST` | string
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER | If set to `true`, instructs the oracle to do an extra check for transaction origin in the block/allowance list. `false` by default. | `true` / `false`
ORACLE_ALWAYS_RELAY_SIGNATURES | If set to `true`, the oracle will always relay signatures even if it was not the last who finilized the signatures collecting process. The default is `false`. | `true` / `false`
## UI configuration ## UI configuration

@ -19,6 +19,6 @@
"eslint-plugin-jest": "^23.18.0" "eslint-plugin-jest": "^23.18.0"
}, },
"engines": { "engines": {
"node": ">= 8.9" "node": ">= 10.18"
} }
} }

@ -59,7 +59,7 @@ export const CONFIRMATIONS_STATUS_DESCRIPTION_HOME: { [key: string]: string } =
EXECUTION_PENDING: EXECUTION_PENDING:
'The specified transaction was included in a block\nand the validators collected signatures. The\nvalidators transaction with collected signatures was\nsent but is not yet added to a block.', 'The specified transaction was included in a block\nand the validators collected signatures. The\nvalidators transaction with collected signatures was\nsent but is not yet added to a block.',
EXECUTION_WAITING: EXECUTION_WAITING:
'The specified transaction was included in a block\nand the validators collected signatures. Either\n1. One of the validators is waiting for chain finalization.\n2. A validator skipped its duty to relay signatures.\nCheck status again after a few blocks. If the issue still persists contact to the validators by messaging on %linkhttps://forum.poa.network/c/support', 'The specified transaction was included in a block\nand the validators collected signatures. Either\n1. One of the validators is waiting for chain finalization.\n2. A validator skipped its duty to relay signatures.\n3. The execution transaction is still pending (e.g. due to the gas price spike).\nCheck status again after a few blocks. If the issue still persists contact to the validators by messaging on %linkhttps://forum.poa.network/c/support',
FAILED: FAILED:
'The specified transaction was included in a block,\nbut transactions with signatures sent by a majority of\nvalidators failed. The cross-chain relay request will\nnot be processed. Contact to the validators by\nmessaging on %linkhttps://forum.poa.network/c/support', 'The specified transaction was included in a block,\nbut transactions with signatures sent by a majority of\nvalidators failed. The cross-chain relay request will\nnot be processed. Contact to the validators by\nmessaging on %linkhttps://forum.poa.network/c/support',
PENDING: PENDING:

@ -0,0 +1 @@
0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04

@ -23,3 +23,4 @@ ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
ORACLE_ALLOW_HTTP_FOR_RPC=yes ORACLE_ALLOW_HTTP_FOR_RPC=yes
ORACLE_HOME_START_BLOCK=1 ORACLE_HOME_START_BLOCK=1
ORACLE_FOREIGN_START_BLOCK=1 ORACLE_FOREIGN_START_BLOCK=1
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt

@ -15,6 +15,10 @@
"address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B", "address": "0x3CC5baAB679eC0732C175760079Bf48F564ad26B",
"privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1" "privateKey": "0xedb53ee050631b7914d5f1a66c2f0d2df3ec85a9ed2a9616b16a7b1b7a10b8d1"
}, },
"blockedUser": {
"address": "0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04",
"privateKey": "0x65df4ea787916f6ed9660f0b0fe36858a65735ad0dcd34527497f4ce32e53883"
},
"validator": { "validator": {
"address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b", "address": "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b",
"privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" "privateKey": "0x8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"

@ -50,6 +50,9 @@ services:
environment: environment:
- NODE_ENV=production - NODE_ENV=production
command: "true" command: "true"
volumes:
- '../e2e-commons/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks: networks:
- ultimate - ultimate
oracle-amb: oracle-amb:

@ -14,7 +14,7 @@
"axios": "0.19.0" "axios": "0.19.0"
}, },
"engines": { "engines": {
"node": ">= 8.9" "node": ">= 10.18"
}, },
"devDependencies": {} "devDependencies": {}
} }

@ -21,7 +21,7 @@
"web3": "1.0.0-beta.34" "web3": "1.0.0-beta.34"
}, },
"engines": { "engines": {
"node": ">=8.9" "node": ">= 10.18"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.2.0" "chai": "^4.2.0"

@ -22,7 +22,7 @@
"websocket": "^1.0.28" "websocket": "^1.0.28"
}, },
"engines": { "engines": {
"node": ">= 8.9" "node": ">= 10.18"
}, },
"devDependencies": {} "devDependencies": {}
} }

@ -4,13 +4,14 @@ const promiseRetry = require('promise-retry')
const { const {
user, user,
secondUser, secondUser,
blockedUser,
validator, validator,
ercToNativeBridge, ercToNativeBridge,
homeRPC, homeRPC,
foreignRPC foreignRPC
} = require('../../e2e-commons/constants.json') } = require('../../e2e-commons/constants.json')
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_NATIVE_ABI } = require('../../commons') const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_NATIVE_ABI } = require('../../commons')
const { uniformRetry } = require('../../e2e-commons/utils') const { uniformRetry, sleep } = require('../../e2e-commons/utils')
const { setRequiredSignatures } = require('./utils') const { setRequiredSignatures } = require('./utils')
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL)) const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
@ -22,6 +23,7 @@ const COMMON_FOREIGN_BRIDGE_ADDRESS = ercToNativeBridge.foreign
const { toBN } = foreignWeb3.utils const { toBN } = foreignWeb3.utils
homeWeb3.eth.accounts.wallet.add(user.privateKey) homeWeb3.eth.accounts.wallet.add(user.privateKey)
homeWeb3.eth.accounts.wallet.add(blockedUser.privateKey)
homeWeb3.eth.accounts.wallet.add(validator.privateKey) homeWeb3.eth.accounts.wallet.add(validator.privateKey)
foreignWeb3.eth.accounts.wallet.add(user.privateKey) foreignWeb3.eth.accounts.wallet.add(user.privateKey)
foreignWeb3.eth.accounts.wallet.add(validator.privateKey) foreignWeb3.eth.accounts.wallet.add(validator.privateKey)
@ -142,6 +144,47 @@ describe('erc to native', () => {
} }
}) })
}) })
it('should not process transaction from blocked users', async () => {
const originalBalance1 = await erc20Token.methods.balanceOf(user.address).call()
const originalBalance2 = await erc20Token.methods.balanceOf(blockedUser.address).call()
// check that account has tokens in home chain
const balance1 = await homeWeb3.eth.getBalance(user.address)
const balance2 = await homeWeb3.eth.getBalance(blockedUser.address)
assert(!toBN(balance1).isZero(), 'Account should have tokens')
assert(!toBN(balance2).isZero(), 'Account should have tokens')
// 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')
})
// send transaction to home bridge
await homeWeb3.eth.sendTransaction({
from: blockedUser.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(originalBalance1))) {
retry()
}
})
await sleep(3000)
const balance = await erc20Token.methods.balanceOf(blockedUser.address).call()
assert(toBN(balance).eq(toBN(originalBalance2)), 'Bridge should not process collected signatures from blocked user')
})
it('should not invest dai when chai token is disabled', async () => { it('should not invest dai when chai token is disabled', async () => {
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()

@ -30,6 +30,10 @@ ORACLE_FOREIGN_START_BLOCK=
ORACLE_LOG_LEVEL=debug ORACLE_LOG_LEVEL=debug
ORACLE_MAX_PROCESSING_TIME=20000 ORACLE_MAX_PROCESSING_TIME=20000
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST=access-lists/allowance_list.txt
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=access-lists/block_list.txt
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER=false
#Uncomment these lines only if you are going to send transaction by testing scripts #Uncomment these lines only if you are going to send transaction by testing scripts
#USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370 #USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370
#USER_ADDRESS_PRIVATE_KEY= #USER_ADDRESS_PRIVATE_KEY=

@ -25,6 +25,9 @@ services:
extends: extends:
file: docker-compose.yml file: docker-compose.yml
service: bridge_collected service: bridge_collected
volumes:
- '~/bridge_data/access-lists/block_list.txt:/mono/oracle/access-lists/block_list.txt'
- '~/bridge_data/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks: networks:
- net_db_bridge_request - net_db_bridge_request
- net_rabbit_bridge_request - net_rabbit_bridge_request

@ -48,6 +48,6 @@
"sinon": "^6.1.0" "sinon": "^6.1.0"
}, },
"engines": { "engines": {
"node": ">= 8.9" "node": ">= 10.18"
} }
} }

@ -177,7 +177,7 @@ async function sendJobTx(jobs) {
e.message e.message
) )
if (e.message.includes('Insufficient funds')) { if (e.message.toLowerCase().includes('insufficient funds')) {
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS) const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
const minimumBalance = gasLimit.multipliedBy(gasPrice) const minimumBalance = gasLimit.multipliedBy(gasPrice)
logger.error( logger.error(

@ -12,6 +12,8 @@ const { MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/const
const limit = promiseLimit(MAX_CONCURRENT_EVENTS) const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
const { ORACLE_ALWAYS_RELAY_SIGNATURES } = process.env
let validatorContract = null let validatorContract = null
function processCollectedSignaturesBuilder(config) { function processCollectedSignaturesBuilder(config) {
@ -39,7 +41,9 @@ function processCollectedSignaturesBuilder(config) {
eventTransactionHash: colSignature.transactionHash eventTransactionHash: colSignature.transactionHash
}) })
if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) { if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
logger.debug('Validator handles all CollectedSignature requests')
} else if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) {
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`) logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
return return
} }

@ -4,11 +4,19 @@ const { HttpListProviderError } = require('http-list-provider')
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons') const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
const rootLogger = require('../../services/logger') const rootLogger = require('../../services/logger')
const { web3Home, web3Foreign } = require('../../services/web3') const { web3Home, web3Foreign } = require('../../services/web3')
const { signatureToVRS, packSignatures } = require('../../utils/message') const { signatureToVRS, packSignatures, parseMessage } = require('../../utils/message')
const { readAccessListFile } = require('../../utils/utils')
const estimateGas = require('./estimateGas') const estimateGas = require('./estimateGas')
const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors') const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors')
const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const {
ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST,
ORACLE_HOME_TO_FOREIGN_BLOCK_LIST,
ORACLE_HOME_TO_FOREIGN_CHECK_SENDER,
ORACLE_ALWAYS_RELAY_SIGNATURES
} = process.env
const limit = promiseLimit(MAX_CONCURRENT_EVENTS) const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
let validatorContract = null let validatorContract = null
@ -38,7 +46,9 @@ function processCollectedSignaturesBuilder(config) {
eventTransactionHash: colSignature.transactionHash eventTransactionHash: colSignature.transactionHash
}) })
if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) { if (ORACLE_ALWAYS_RELAY_SIGNATURES && ORACLE_ALWAYS_RELAY_SIGNATURES === 'true') {
logger.debug('Validator handles all CollectedSignature requests')
} else if (authorityResponsibleForRelay !== web3Home.utils.toChecksumAddress(config.validatorAddress)) {
logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`) logger.info(`Validator not responsible for relaying CollectedSignatures ${colSignature.transactionHash}`)
return return
} }
@ -46,6 +56,49 @@ function processCollectedSignaturesBuilder(config) {
logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`) logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`)
const message = await homeBridge.methods.message(messageHash).call() const message = await homeBridge.methods.message(messageHash).call()
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
const parsedMessage = parseMessage(message)
const recipient = parsedMessage.recipient.toLowerCase()
const originalTxHash = parsedMessage.txHash
if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST) {
const allowanceList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST, logger)
if (allowanceList.indexOf(recipient) === -1) {
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
if (allowanceList.indexOf(sender) === -1) {
logger.info(
{ sender, recipient },
'Validator skips a transaction. Neither sender nor recipient addresses are in the allowance list.'
)
return
}
} else {
logger.info(
{ recipient },
'Validator skips a transaction. Recipient address is not in the allowance list.'
)
return
}
}
} else if (ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) {
const blockList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_BLOCK_LIST, logger)
if (blockList.indexOf(recipient) > -1) {
logger.info({ recipient }, 'Validator skips a transaction. Recipient address is in the block list.')
return
}
if (ORACLE_HOME_TO_FOREIGN_CHECK_SENDER === 'true') {
logger.debug({ txHash: originalTxHash }, 'Requested sender of an original withdrawal transaction')
const sender = (await web3Home.eth.getTransaction(originalTxHash)).from.toLowerCase()
if (blockList.indexOf(sender) > -1) {
logger.info({ sender }, 'Validator skips a transaction. Sender address is in the block list.')
return
}
}
}
}
logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get') logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get')
const requiredSignatures = [] const requiredSignatures = []

@ -1,6 +1,5 @@
require('../env') require('../env')
const path = require('path') const path = require('path')
const { toBN } = require('web3-utils')
const { connectSenderToQueue } = require('./services/amqpClient') const { connectSenderToQueue } = require('./services/amqpClient')
const { redis } = require('./services/redisClient') const { redis } = require('./services/redisClient')
const GasPrice = require('./services/gasPrice') const GasPrice = require('./services/gasPrice')
@ -98,10 +97,10 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
} }
const txArray = JSON.parse(msg.content) const txArray = JSON.parse(msg.content)
logger.info(`Msg received with ${txArray.length} Tx to send`) logger.debug(`Msg received with ${txArray.length} Tx to send`)
const gasPrice = GasPrice.getPrice() const gasPrice = GasPrice.getPrice()
let nonce = await readNonce() let nonce
let insufficientFunds = false let insufficientFunds = false
let minimumBalance = null let minimumBalance = null
const failedTx = [] const failedTx = []
@ -110,9 +109,11 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
const isResend = txArray.length > 0 && !!txArray[0].txHash const isResend = txArray.length > 0 && !!txArray[0].txHash
if (isResend) { if (isResend) {
logger.debug(`Checking status of ${txArray.length} transactions`) logger.info(`Checking status of ${txArray.length} transactions`)
nonce = null
} else { } else {
logger.debug(`Sending ${txArray.length} transactions`) logger.info(`Sending ${txArray.length} transactions`)
nonce = await readNonce()
} }
await syncForEach(txArray, async job => { await syncForEach(txArray, async job => {
let gasLimit let gasLimit
@ -123,38 +124,29 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
} }
try { try {
let txNonce
if (isResend) { if (isResend) {
const tx = await web3Instance.eth.getTransaction(job.txHash) const tx = await web3Instance.eth.getTransaction(job.txHash)
if (tx === null) { if (tx && tx.blockNumber !== null) {
logger.info(`Transaction ${job.txHash} was not found, dropping it`) logger.debug(`Transaction ${job.txHash} was successfully mined`)
return return
} }
if (tx.blockNumber !== null) {
logger.info(`Transaction ${job.txHash} was successfully mined`) if (nonce === null) {
return nonce = await readNonce(true)
} }
logger.info( logger.info(
`Previously sent transaction is stuck, updating gasPrice: ${tx.gasPrice} -> ${gasPrice.toString(10)}` `Transaction ${job.txHash} was not mined, updating gasPrice: ${job.gasPrice} -> ${gasPrice.toString(10)}`
) )
if (toBN(tx.gasPrice).gte(toBN(gasPrice))) {
logger.info("Gas price returned from the oracle didn't increase, will reinspect this transaction later")
sentTx.push(job)
return
} }
logger.info(`Sending transaction with nonce ${nonce}`)
txNonce = tx.nonce job.gasPrice = gasPrice.toString(10)
} else { job.txHash = await sendTx({
txNonce = nonce++
}
logger.info(`Sending transaction with nonce ${txNonce}`)
const txHash = await sendTx({
chain: config.id, chain: config.id,
data: job.data, data: job.data,
nonce: txNonce, nonce,
gasPrice: gasPrice.toString(10), gasPrice: job.gasPrice,
amount: '0', amount: '0',
gasLimit, gasLimit,
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
@ -162,14 +154,12 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
chainId, chainId,
web3: web3Instance web3: web3Instance
}) })
sentTx.push({ sentTx.push(job)
...job,
txHash
})
nonce++
logger.info( logger.info(
{ eventTransactionHash: job.transactionReference, generatedTransactionHash: txHash }, { eventTransactionHash: job.transactionReference, generatedTransactionHash: job.txHash },
`Tx generated ${txHash} for event Tx ${job.transactionReference}` `Tx generated ${job.txHash} for event Tx ${job.transactionReference}`
) )
} catch (e) { } catch (e) {
logger.error( logger.error(
@ -177,11 +167,11 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
`Tx Failed for event Tx ${job.transactionReference}.`, `Tx Failed for event Tx ${job.transactionReference}.`,
e.message e.message
) )
if (!e.message.includes('Transaction with the same hash was already imported')) { if (!e.message.toLowerCase().includes('transaction with the same hash was already imported')) {
failedTx.push(job) failedTx.push(job)
} }
if (e.message.includes('Insufficient funds')) { if (e.message.toLowerCase().includes('insufficient funds')) {
insufficientFunds = true insufficientFunds = true
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS) const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
minimumBalance = gasLimit.multipliedBy(gasPrice) minimumBalance = gasLimit.multipliedBy(gasPrice)

@ -21,7 +21,7 @@ module.exports = {
}, },
GAS_PRICE_BOUNDARIES: { GAS_PRICE_BOUNDARIES: {
MIN: 1, MIN: 1,
MAX: 250 MAX: 1000
}, },
TRANSACTION_RESEND_TIMEOUT: 20 * 60 * 1000, TRANSACTION_RESEND_TIMEOUT: 20 * 60 * 1000,
SENDER_QUEUE_MAX_PRIORITY: 10, SENDER_QUEUE_MAX_PRIORITY: 10,

@ -1,3 +1,4 @@
const fs = require('fs')
const BigNumber = require('bignumber.js') const BigNumber = require('bignumber.js')
const promiseRetry = require('promise-retry') const promiseRetry = require('promise-retry')
const Web3 = require('web3') const Web3 = require('web3')
@ -93,10 +94,11 @@ function privateKeyToAddress(privateKey) {
} }
function nonceError(e) { function nonceError(e) {
const message = e.message.toLowerCase()
return ( return (
e.message.includes('Transaction nonce is too low') || message.includes('transaction nonce is too low') ||
e.message.includes('nonce too low') || message.includes('nonce too low') ||
e.message.includes('transaction with same nonce in the queue') message.includes('transaction with same nonce in the queue')
) )
} }
@ -105,6 +107,27 @@ function nonceError(e) {
const invert = p => new Promise((res, rej) => p.then(rej, res)) const invert = p => new Promise((res, rej) => p.then(rej, res))
const promiseAny = ps => invert(Promise.all(ps.map(invert))) const promiseAny = ps => invert(Promise.all(ps.map(invert)))
const readAccessLists = {}
async function readAccessListFile(fileName, logger) {
if (!readAccessLists[fileName]) {
logger.debug({ fileName }, 'Access list file read requested')
try {
const data = await fs.promises.readFile(fileName)
readAccessLists[fileName] = data
.toString()
.split('\n')
.map(addr => addr.trim().toLowerCase())
.filter(addr => addr.length === 42)
logger.info({ fileName }, `Access list was read successfully, ${data.length} addresses found`)
logger.debug({ addresses: readAccessLists[fileName] }, `Read addresses from the file`)
} catch (e) {
readAccessLists[fileName] = []
logger.error({ fileName, error: e }, `Failed to read access list from the file`)
}
}
return readAccessLists[fileName]
}
module.exports = { module.exports = {
syncForEach, syncForEach,
checkHTTPS, checkHTTPS,
@ -115,5 +138,6 @@ module.exports = {
privateKeyToAddress, privateKeyToAddress,
nonceError, nonceError,
getRetrySequence, getRetrySequence,
promiseAny promiseAny,
readAccessListFile
} }

@ -51,6 +51,7 @@
"Dcef88209a20D52165230104B245803C3269454d": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, "Dcef88209a20D52165230104B245803C3269454d": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
"bb140FbA6242a1c3887A7823F7750a73101383e3": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, "bb140FbA6242a1c3887A7823F7750a73101383e3": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
"7FC1442AB55Da569940Eb750AaD2BAA63DA4010E": { "balance": "500000000000000000000" }, "7FC1442AB55Da569940Eb750AaD2BAA63DA4010E": { "balance": "500000000000000000000" },
"B4579fd5AfEaB60B03Db3F408AAdD315035943f7": { "balance": "500000000000000000000" } "B4579fd5AfEaB60B03Db3F408AAdD315035943f7": { "balance": "500000000000000000000" },
"c9e38bfdB9c635F0796ad83CC8705dc379F41c04": { "balance": "500000000000000000000" }
} }
} }

@ -11,6 +11,6 @@
"lint": "eslint . --ignore-path ../.eslintignore" "lint": "eslint . --ignore-path ../.eslintignore"
}, },
"engines": { "engines": {
"node": ">= 8.9" "node": ">= 10.18"
} }
} }

@ -325,7 +325,7 @@ class ForeignStore {
@action @action
async getCurrentLimit() { async getCurrentLimit() {
try { try {
const result = await getCurrentLimit(this.foreignBridge, this.tokenDecimals) const result = await getCurrentLimit(this.foreignBridge, this.homeStore.homeBridge, this.tokenDecimals)
this.maxCurrentDeposit = result.maxCurrentDeposit this.maxCurrentDeposit = result.maxCurrentDeposit
this.dailyLimit = result.dailyLimit this.dailyLimit = result.dailyLimit
this.totalSpentPerDay = result.totalSpentPerDay this.totalSpentPerDay = result.totalSpentPerDay

@ -429,7 +429,11 @@ class HomeStore {
@action @action
async getCurrentLimit() { async getCurrentLimit() {
try { try {
const result = await getCurrentLimit(this.homeBridge, this.tokenDecimals) const result = await getCurrentLimit(
this.homeBridge,
this.rootStore.foreignStore.foreignBridge,
this.tokenDecimals
)
this.maxCurrentDeposit = result.maxCurrentDeposit this.maxCurrentDeposit = result.maxCurrentDeposit
this.dailyLimit = result.dailyLimit this.dailyLimit = result.dailyLimit
this.totalSpentPerDay = result.totalSpentPerDay this.totalSpentPerDay = result.totalSpentPerDay

@ -21,10 +21,10 @@ export const getMinPerTxLimit = async (contract, decimals) => {
return fromDecimals(minPerTx, decimals) return fromDecimals(minPerTx, decimals)
} }
export const getCurrentLimit = async (contract, decimals) => { export const getCurrentLimit = async (contract, otherContract, decimals) => {
const currentDay = await contract.methods.getCurrentDay().call() const currentDay = await contract.methods.getCurrentDay().call()
const dailyLimit = await contract.methods.dailyLimit().call() const dailyLimit = await contract.methods.dailyLimit().call()
const totalSpentPerDay = await contract.methods.totalSpentPerDay(currentDay).call() const totalSpentPerDay = await otherContract.methods.totalExecutedPerDay(currentDay).call()
const maxCurrentDeposit = new BN(dailyLimit).minus(new BN(totalSpentPerDay)).toString(10) const maxCurrentDeposit = new BN(dailyLimit).minus(new BN(totalSpentPerDay)).toString(10)
return { return {
maxCurrentDeposit: fromDecimals(maxCurrentDeposit, decimals), maxCurrentDeposit: fromDecimals(maxCurrentDeposit, decimals),