Compare commits

...

3 Commits
2.3.0 ... 2.3.1

Author SHA1 Message Date
Alexander Kolotov
c2f6b5e8ba Merge the develop branch to the master branch, preparation to v2.3.1 2020-08-11 20:13:58 +03:00
Alexander Kolotov
f3f226afdf Correction of the message for a transaction without the bridge requests (#416) 2020-08-06 23:43:13 +03:00
Kirill Fedoseev
1eb8a8b1dc Use requestGasLimit parameter in AMB oracle in estimateGas (#415) 2020-08-06 16:15:51 +03:00
13 changed files with 113 additions and 19 deletions

View File

@@ -64,10 +64,6 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
const displayReference = multiMessageSelected ? messages[selectedMessageId].id : txHash const displayReference = multiMessageSelected ? messages[selectedMessageId].id : txHash
const formattedMessageId = formatTxHash(displayReference) const formattedMessageId = formatTxHash(displayReference)
const displayedDescription = multiMessageSelected
? getTransactionStatusDescription(TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE, timestamp)
: description
const isHome = chainId === home.chainId.toString() const isHome = chainId === home.chainId.toString()
const txExplorerLink = getExplorerTxUrl(txHash, isHome) const txExplorerLink = getExplorerTxUrl(txHash, isHome)
const displayExplorerLink = status !== TRANSACTION_STATUS.NOT_FOUND const displayExplorerLink = status !== TRANSACTION_STATUS.NOT_FOUND
@@ -75,17 +71,32 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
const displayConfirmations = status === TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE || multiMessageSelected const displayConfirmations = status === TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE || multiMessageSelected
const messageToConfirm = const messageToConfirm =
messages.length > 1 ? messages[selectedMessageId] : messages.length > 0 ? messages[0] : { id: '', data: '' } messages.length > 1 ? messages[selectedMessageId] : messages.length > 0 ? messages[0] : { id: '', data: '' }
let displayedDescription: string = multiMessageSelected
? getTransactionStatusDescription(TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE, timestamp)
: description
let link
const descArray = displayedDescription.split('%link')
if (descArray.length > 1) {
displayedDescription = descArray[0]
link = (
<ExplorerTxLink href={descArray[1]} target="_blank" rel="noopener noreferrer">
{descArray[1]}
</ExplorerTxLink>
)
}
return ( return (
<div> <div>
{status && ( {status && (
<p> <p>
The request{' '} The transaction{' '}
{displayExplorerLink && ( {displayExplorerLink && (
<ExplorerTxLink href={txExplorerLink} target="_blank"> <ExplorerTxLink href={txExplorerLink} target="_blank">
{formattedMessageId} {formattedMessageId}
</ExplorerTxLink> </ExplorerTxLink>
)} )}
{!displayExplorerLink && <label>{formattedMessageId}</label>} {displayedDescription} {!displayExplorerLink && <label>{formattedMessageId}</label>} {displayedDescription} {link}
</p> </p>
)} )}
{displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />} {displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />}

View File

@@ -2,7 +2,8 @@
export const TRANSACTION_STATUS_DESCRIPTION: { [key: string]: string } = { export const TRANSACTION_STATUS_DESCRIPTION: { [key: string]: string } = {
SUCCESS_MULTIPLE_MESSAGES: 'was initiated %t and contains several bridge messages. Specify one of them:', SUCCESS_MULTIPLE_MESSAGES: 'was initiated %t and contains several bridge messages. Specify one of them:',
SUCCESS_ONE_MESSAGE: 'was initiated %t', SUCCESS_ONE_MESSAGE: 'was initiated %t',
SUCCESS_NO_MESSAGES: 'execution succeeded %t but it does not contain any bridge messages', SUCCESS_NO_MESSAGES:
'successfully mined %t but it does not seem to contain any request to the bridge, \nso nothing needs to be confirmed by the validators. \nIf you are sure that the transaction should contain a request to the bridge,\ncontact to the validators by \nmessaging on %linkhttps://forum.poa.network/c/support',
FAILED: 'failed %t', FAILED: 'failed %t',
NOT_FOUND: NOT_FOUND:
'Transaction not found. \n1. Check that the transaction hash is correct. \n2. Wait several blocks for the transaction to be\nmined, gas price affects mining speed.' 'Transaction not found. \n1. Check that the transaction hash is correct. \n2. Wait several blocks for the transaction to be\nmined, gas price affects mining speed.'

View File

@@ -7,7 +7,7 @@ const rpcUrlsManager = require('./services/getRpcUrlsManager')
const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3') const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3')
const { sendTx } = require('./tx/sendTx') const { sendTx } = require('./tx/sendTx')
const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils') const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE } = require('./utils/constants') const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants')
const { ORACLE_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_ALLOW_HTTP_FOR_RPC } = process.env const { ORACLE_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_ALLOW_HTTP_FOR_RPC } = process.env
@@ -143,7 +143,12 @@ async function sendJobTx(jobs) {
let nonce = await getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS) let nonce = await getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
await syncForEach(jobs, async job => { await syncForEach(jobs, async job => {
const gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE) 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 { try {
logger.info(`Sending transaction with nonce ${nonce}`) logger.info(`Sending transaction with nonce ${nonce}`)

View File

@@ -3,14 +3,23 @@ const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = req
const logger = require('../../services/logger').child({ const logger = require('../../services/logger').child({
module: 'processAffirmationRequests:estimateGas' module: 'processAffirmationRequests:estimateGas'
}) })
const { parseAMBHeader } = require('../../utils/message')
const { strip0x } = require('../../../../commons')
const {
AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: estimateExtraGas,
MIN_AMB_HEADER_LENGTH
} = require('../../utils/constants')
async function estimateGas({ web3, homeBridge, validatorContract, message, address }) { async function estimateGas({ web3, homeBridge, validatorContract, message, address }) {
try { try {
const gasEstimate = await homeBridge.methods.executeAffirmation(message).estimateGas({ const gasEstimate = await homeBridge.methods.executeAffirmation(message).estimateGas({
from: address from: address
}) })
const msgGasLimit = parseAMBHeader(message).gasLimit
// message length in bytes
const len = strip0x(message).length / 2 - MIN_AMB_HEADER_LENGTH
return gasEstimate return gasEstimate + msgGasLimit + estimateExtraGas(len)
} catch (e) { } catch (e) {
if (e instanceof HttpListProviderError) { if (e instanceof HttpListProviderError) {
throw e throw e

View File

@@ -4,7 +4,7 @@ const promiseLimit = require('promise-limit')
const rootLogger = require('../../services/logger') const rootLogger = require('../../services/logger')
const { web3Home } = require('../../services/web3') const { web3Home } = require('../../services/web3')
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
const estimateGas = require('./estimateGas') const estimateGas = require('./estimateGas')
const { parseAMBMessage } = require('../../../../commons') const { parseAMBMessage } = require('../../../../commons')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
@@ -75,6 +75,7 @@ function processAffirmationRequestsBuilder(config) {
txToSend.push({ txToSend.push({
data, data,
gasEstimate, gasEstimate,
extraGas: EXTRA_GAS_ABSOLUTE,
transactionReference: affirmationRequest.transactionHash, transactionReference: affirmationRequest.transactionHash,
to: config.homeBridgeAddress to: config.homeBridgeAddress
}) })

View File

@@ -4,6 +4,7 @@ const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError
const logger = require('../../services/logger').child({ const logger = require('../../services/logger').child({
module: 'processCollectedSignatures:estimateGas' module: 'processCollectedSignatures:estimateGas'
}) })
const { parseAMBHeader } = require('../../utils/message')
const web3 = new Web3() const web3 = new Web3()
const { toBN } = Web3.utils const { toBN } = Web3.utils
@@ -24,7 +25,12 @@ async function estimateGas({
const gasEstimate = await foreignBridge.methods.executeSignatures(message, signatures).estimateGas({ const gasEstimate = await foreignBridge.methods.executeSignatures(message, signatures).estimateGas({
from: address from: address
}) })
return gasEstimate const msgGasLimit = parseAMBHeader(message).gasLimit
// + estimateExtraGas(len)
// is not needed here, since estimateGas will already take into account gas
// needed for memory expansion, message processing, etc.
return gasEstimate + msgGasLimit
} catch (e) { } catch (e) {
if (e instanceof HttpListProviderError) { if (e instanceof HttpListProviderError) {
throw e throw e

View File

@@ -8,7 +8,7 @@ const { signatureToVRS, packSignatures } = require('../../utils/message')
const { parseAMBMessage } = require('../../../../commons') const { parseAMBMessage } = require('../../../../commons')
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, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
const limit = promiseLimit(MAX_CONCURRENT_EVENTS) const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
@@ -107,6 +107,7 @@ function processCollectedSignaturesBuilder(config) {
txToSend.push({ txToSend.push({
data, data,
gasEstimate, gasEstimate,
extraGas: EXTRA_GAS_ABSOLUTE,
transactionReference: colSignature.transactionHash, transactionReference: colSignature.transactionHash,
to: config.foreignBridgeAddress to: config.foreignBridgeAddress
}) })

View File

@@ -16,7 +16,7 @@ const {
watchdog, watchdog,
nonceError nonceError
} = require('./utils/utils') } = require('./utils/utils')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE } = require('./utils/constants') const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants')
const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
@@ -106,7 +106,12 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry }) {
logger.debug(`Sending ${txArray.length} transactions`) logger.debug(`Sending ${txArray.length} transactions`)
await syncForEach(txArray, async job => { await syncForEach(txArray, async job => {
const gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE) 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 { try {
logger.info(`Sending transaction with nonce ${nonce}`) logger.info(`Sending transaction with nonce ${nonce}`)

View File

@@ -1,5 +1,9 @@
module.exports = { module.exports = {
EXTRA_GAS_PERCENTAGE: 4, EXTRA_GAS_PERCENTAGE: 4,
EXTRA_GAS_ABSOLUTE: 200000,
AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: len => Math.floor(0.0035 * len ** 2 + 40 * len),
MIN_AMB_HEADER_LENGTH: 32 + 20 + 20 + 4 + 2 + 1 + 2,
MAX_GAS_LIMIT: 10000000,
MAX_CONCURRENT_EVENTS: 50, MAX_CONCURRENT_EVENTS: 50,
RETRY_CONFIG: { RETRY_CONFIG: {
retries: 20, retries: 20,

View File

@@ -73,9 +73,37 @@ function packSignatures(array) {
return `0x${msgLength}${v}${r}${s}` return `0x${msgLength}${v}${r}${s}`
} }
function parseAMBHeader(message) {
message = strip0x(message)
const messageIdStart = 0
const messageIdLength = 32 * 2
const messageId = `0x${message.slice(messageIdStart, messageIdStart + messageIdLength)}`
const senderStart = messageIdStart + messageIdLength
const senderLength = 20 * 2
const sender = `0x${message.slice(senderStart, senderStart + senderLength)}`
const executorStart = senderStart + senderLength
const executorLength = 20 * 2
const executor = `0x${message.slice(executorStart, executorStart + executorLength)}`
const gasLimitStart = executorStart + executorLength
const gasLimitLength = 4 * 2
const gasLimit = parseInt(message.slice(gasLimitStart, gasLimitStart + gasLimitLength), 16)
return {
messageId,
sender,
executor,
gasLimit
}
}
module.exports = { module.exports = {
createMessage, createMessage,
parseMessage, parseMessage,
signatureToVRS, signatureToVRS,
packSignatures packSignatures,
parseAMBHeader
} }

View File

@@ -48,13 +48,13 @@ async function waitForFunds(web3, address, minimumBalance, cb, logger) {
) )
} }
function addExtraGas(gas, extraPercentage) { function addExtraGas(gas, extraPercentage, maxGasLimit = Infinity) {
gas = BigNumber(gas) gas = BigNumber(gas)
extraPercentage = BigNumber(1 + extraPercentage) extraPercentage = BigNumber(1 + extraPercentage)
const gasWithExtra = gas.multipliedBy(extraPercentage).toFixed(0) const gasWithExtra = gas.multipliedBy(extraPercentage).toFixed(0)
return BigNumber(gasWithExtra) return BigNumber.min(maxGasLimit, gasWithExtra)
} }
function setIntervalAndRun(f, interval) { function setIntervalAndRun(f, interval) {

View File

@@ -1,6 +1,6 @@
const { BN, toBN } = require('web3').utils const { BN, toBN } = require('web3').utils
const { expect } = require('chai').use(require('bn-chai')(BN)) const { expect } = require('chai').use(require('bn-chai')(BN))
const { createMessage, parseMessage, signatureToVRS } = require('../src/utils/message') const { createMessage, parseMessage, signatureToVRS, parseAMBHeader } = require('../src/utils/message')
describe('message utils', () => { describe('message utils', () => {
const expectedMessageLength = 104 const expectedMessageLength = 104
@@ -288,4 +288,19 @@ describe('message utils', () => {
expect(signatureThunk).to.throw() expect(signatureThunk).to.throw()
}) })
}) })
describe('parseAMBHeader', () => {
it('should return correct values for parsed headers', () => {
// given
const message =
'0x000500009a6ff99b356dd998260582be7d95a4d08b2132600000000000000061339d0e6f308a410f18888932bdf661636a0f538f34718200957aeadd6bece186e61b95618e73a6dc000f42400101002a4d2fb102cf00000000000000000000000081c770bbe8f5f41b4642ed575e630c911c94e4070000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002e516d543162324178466b6643456a6f715861547148734370666f724a4c66765667434853516e513847575a347662000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e516d5a7543586f693378487338486e4c325a423539316674466f4c454471516b473655746e47324d6d513147614e000000000000000000000000000000000000'
const { messageId, sender, executor, gasLimit } = parseAMBHeader(message)
// then
expect(messageId).to.equal('0x000500009a6ff99b356dd998260582be7d95a4d08b2132600000000000000061')
expect(sender).to.equal('0x339d0e6f308a410f18888932bdf661636a0f538f')
expect(executor).to.equal('0x34718200957aeadd6bece186e61b95618e73a6dc')
expect(gasLimit).to.equal(1000000)
})
})
}) })

View File

@@ -34,6 +34,14 @@ describe('utils', () => {
expect(result.toString()).to.equal('225') expect(result.toString()).to.equal('225')
}) })
it('should handle maxGasLimit', () => {
const result1 = addExtraGas(new BigNumber(100), 0.25, 110)
const result2 = addExtraGas(new BigNumber(100), 0.25, 150)
expect(result1.toString()).to.equal('110')
expect(result2.toString()).to.equal('125')
})
}) })
describe('checkHTTPS', () => { describe('checkHTTPS', () => {