Send tx using raw RPC method call for efficiency
This commit is contained in:
parent
f4150a3324
commit
dc668f452b
|
@ -17,6 +17,8 @@ services:
|
|||
- FOREIGN_URL
|
||||
- FOREIGN_ASSET
|
||||
- LOG_LEVEL
|
||||
- "GAS_LIMIT_FACTOR=3"
|
||||
- "MAX_GAS_LIMIT=6000000"
|
||||
volumes:
|
||||
- '../deploy/deploy-test/build/contracts/IERC20.json:/proxy/contracts_data/IERC20.json'
|
||||
- '../deploy/deploy-home/build/contracts/Bridge.json:/proxy/contracts_data/Bridge.json'
|
||||
|
|
|
@ -17,6 +17,8 @@ services:
|
|||
- FOREIGN_URL
|
||||
- FOREIGN_ASSET
|
||||
- LOG_LEVEL
|
||||
- "GAS_LIMIT_FACTOR=3"
|
||||
- "MAX_GAS_LIMIT=6000000"
|
||||
volumes:
|
||||
- '../deploy/deploy-test/build/contracts/IERC20.json:/proxy/contracts_data/IERC20.json'
|
||||
- '../deploy/deploy-home/build/contracts/Bridge.json:/proxy/contracts_data/Bridge.json'
|
||||
|
|
|
@ -6,6 +6,6 @@ COPY ./proxy/package.json /proxy/
|
|||
|
||||
RUN npm install
|
||||
|
||||
COPY ./proxy/index.js ./proxy/encode.js ./proxy/decode.js ./shared/logger.js ./shared/crypto.js /proxy/
|
||||
COPY ./proxy/index.js ./proxy/encode.js ./proxy/decode.js ./proxy/sendTx.js ./shared/logger.js ./shared/crypto.js /proxy/
|
||||
|
||||
ENTRYPOINT ["node", "index.js"]
|
||||
|
|
|
@ -7,6 +7,7 @@ const { utils } = require('ethers')
|
|||
|
||||
const encode = require('./encode')
|
||||
const decode = require('./decode')
|
||||
const { createSender, waitForReceipt } = require('./sendTx')
|
||||
const logger = require('./logger')
|
||||
const { publicKeyToAddress } = require('./crypto')
|
||||
|
||||
|
@ -31,8 +32,8 @@ const lock = new AsyncLock()
|
|||
|
||||
let homeValidatorNonce
|
||||
let sideValidatorNonce
|
||||
let homeBlockGasLimit
|
||||
let sideBlockGasLimit
|
||||
let homeSender
|
||||
let sideSender
|
||||
|
||||
const app = express()
|
||||
app.use(express.json())
|
||||
|
@ -63,8 +64,8 @@ async function main () {
|
|||
homeValidatorNonce = await homeWeb3.eth.getTransactionCount(validatorAddress)
|
||||
sideValidatorNonce = await sideWeb3.eth.getTransactionCount(validatorAddress)
|
||||
|
||||
homeBlockGasLimit = (await homeWeb3.eth.getBlock('latest', false)).gasLimit
|
||||
sideBlockGasLimit = (await sideWeb3.eth.getBlock('latest', false)).gasLimit
|
||||
homeSender = await createSender(HOME_RPC_URL, VALIDATOR_PRIVATE_KEY)
|
||||
sideSender = await createSender(SIDE_RPC_URL, VALIDATOR_PRIVATE_KEY)
|
||||
|
||||
logger.warn(`My validator address in home and side networks is ${validatorAddress}`)
|
||||
|
||||
|
@ -151,10 +152,11 @@ async function signupSign (req, res) {
|
|||
logger.debug('SignupSign call')
|
||||
const hash = sideWeb3.utils.sha3(`0x${req.body.third}`)
|
||||
const query = sharedDb.methods.signupSign(hash)
|
||||
const receipt = await sideSendQuery(query)
|
||||
const txHash = await sideSendQuery(query)
|
||||
const receipt = await waitForReceipt(SIDE_RPC_URL, txHash)
|
||||
|
||||
// Already have signup
|
||||
if (receipt === false) {
|
||||
if (receipt.status === false) {
|
||||
res.send(Ok({ uuid: hash, number: 0 }))
|
||||
logger.debug('Already have signup')
|
||||
return
|
||||
|
@ -185,81 +187,27 @@ async function confirmFundsTransfer (req, res) {
|
|||
}
|
||||
|
||||
function sideSendQuery (query) {
|
||||
return lock.acquire('side', async () => {
|
||||
logger.debug('Sending query')
|
||||
return lock.acquire('home', async () => {
|
||||
logger.debug('Sending side query')
|
||||
const encodedABI = query.encodeABI()
|
||||
const tx = {
|
||||
return await sideSender({
|
||||
data: encodedABI,
|
||||
from: validatorAddress,
|
||||
to: SIDE_SHARED_DB_ADDRESS,
|
||||
nonce: sideValidatorNonce++,
|
||||
chainId: await sideWeb3.eth.net.getId()
|
||||
}
|
||||
tx.gas = Math.min(Math.ceil(await query.estimateGas({
|
||||
from: validatorAddress
|
||||
}) * 1.5), sideBlockGasLimit)
|
||||
const signedTx = await sideWeb3.eth.accounts.signTransaction(tx, VALIDATOR_PRIVATE_KEY)
|
||||
|
||||
return sideWeb3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
||||
.catch(e => {
|
||||
const error = parseError(e.message)
|
||||
const reason = parseReason(e.message)
|
||||
if (error === 'revert' && reason.length) {
|
||||
logger.debug(reason)
|
||||
return false
|
||||
} else if (error === 'out of gas') {
|
||||
logger.debug('Out of gas, retrying')
|
||||
return true
|
||||
} else {
|
||||
logger.debug('Side tx failed, retrying, %o', e.message)
|
||||
return true
|
||||
}
|
||||
nonce: sideValidatorNonce++
|
||||
})
|
||||
})
|
||||
.then(result => {
|
||||
if (result === true)
|
||||
return sideSendQuery(query)
|
||||
return result !== false
|
||||
})
|
||||
}
|
||||
|
||||
function homeSendQuery (query) {
|
||||
return lock.acquire('home', async () => {
|
||||
logger.debug('Sending query')
|
||||
logger.debug('Sending home query')
|
||||
const encodedABI = query.encodeABI()
|
||||
const tx = {
|
||||
return await homeSender({
|
||||
data: encodedABI,
|
||||
from: validatorAddress,
|
||||
to: HOME_BRIDGE_ADDRESS,
|
||||
nonce: homeValidatorNonce++,
|
||||
chainId: await homeWeb3.eth.net.getId()
|
||||
}
|
||||
tx.gas = Math.min(Math.ceil(await query.estimateGas({
|
||||
from: validatorAddress
|
||||
}) * 1.5), homeBlockGasLimit)
|
||||
const signedTx = await homeWeb3.eth.accounts.signTransaction(tx, VALIDATOR_PRIVATE_KEY)
|
||||
|
||||
return homeWeb3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
||||
.catch(e => {
|
||||
const error = parseError(e.message)
|
||||
const reason = parseReason(e.message)
|
||||
if (error === 'revert' && reason.length) {
|
||||
logger.debug(reason)
|
||||
return false
|
||||
} else if (error === 'out of gas') {
|
||||
logger.debug('Out of gas, retrying')
|
||||
return true
|
||||
} else {
|
||||
logger.debug('Home tx failed, retrying, %o', e.message)
|
||||
return true
|
||||
}
|
||||
nonce: homeValidatorNonce++
|
||||
})
|
||||
})
|
||||
.then(result => {
|
||||
if (result === true)
|
||||
return homeSendQuery(query)
|
||||
return result !== false
|
||||
})
|
||||
}
|
||||
|
||||
function parseReason (message) {
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
const Web3 = require('web3')
|
||||
const axios = require('axios')
|
||||
const ethers = require('ethers')
|
||||
const BN = require('bignumber.js')
|
||||
|
||||
const logger = require('./logger')
|
||||
|
||||
const { GAS_LIMIT_FACTOR, MAX_GAS_LIMIT } = process.env
|
||||
|
||||
function sendRpcRequest (url, method, params) {
|
||||
return axios.post(url, {
|
||||
jsonrpc: '2.0',
|
||||
method,
|
||||
params,
|
||||
id: 1
|
||||
})
|
||||
.then(res => res.data)
|
||||
.catch(async e => {
|
||||
logger.warn(`Request to ${url}, method ${method} failed, retrying`)
|
||||
await new Promise(res => setTimeout(res, 1000))
|
||||
return sendRpcRequest(url, method, params)
|
||||
})
|
||||
}
|
||||
|
||||
async function createSender (url, privateKey) {
|
||||
const web3 = new Web3(url, null, { transactionConfirmationBlocks: 1 })
|
||||
const signer = new ethers.utils.SigningKey(privateKey)
|
||||
|
||||
const chainId = await web3.eth.net.getId()
|
||||
return async function (tx) {
|
||||
tx = {
|
||||
data: tx.data,
|
||||
to: tx.to,
|
||||
nonce: tx.nonce,
|
||||
chainId,
|
||||
value: `0x${new BN(tx.value || 0).toString(16)}`,
|
||||
gasPrice: `0x${new BN(tx.gasPrice || 1000000000).toString(16)}`
|
||||
}
|
||||
|
||||
try {
|
||||
logger.trace(`Preparing and sending transaction %o on ${url}`, tx)
|
||||
const estimate = await sendRpcRequest(url, 'eth_estimateGas', [ {
|
||||
from: signer.address,
|
||||
to: tx.to,
|
||||
data: tx.data,
|
||||
gasPrice: tx.gasPrice,
|
||||
value: tx.value,
|
||||
gas: `0x${new BN(MAX_GAS_LIMIT).toString(16)}`
|
||||
} ])
|
||||
|
||||
if (estimate.error) {
|
||||
logger.debug('Gas estimate failed %o', estimate.error)
|
||||
return false
|
||||
}
|
||||
const gasLimit = BN.min(new BN(estimate.result, 16).multipliedBy(GAS_LIMIT_FACTOR), MAX_GAS_LIMIT)
|
||||
tx.gasLimit = `0x${new BN(gasLimit).toString(16)}`
|
||||
logger.trace(`Estimated gas to ${gasLimit}`)
|
||||
|
||||
const hash = web3.utils.sha3(ethers.utils.serializeTransaction(tx))
|
||||
const signature = signer.signDigest(hash)
|
||||
const signedTx = ethers.utils.serializeTransaction(tx, signature)
|
||||
|
||||
const { result, error } = await sendRpcRequest(url, 'eth_sendRawTransaction', [ signedTx ])
|
||||
// handle nonce error
|
||||
// handle insufficient funds error
|
||||
if (error) {
|
||||
logger.debug('Sending signed tx %o failed, %o', tx, error)
|
||||
return false
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (e) {
|
||||
logger.warn('Something failed, %o', e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForReceipt (url, txHash) {
|
||||
while (true) {
|
||||
const { result, error } = await sendRpcRequest(url, 'eth_getTransactionReceipt', [ txHash ])
|
||||
|
||||
if(result === null || error) {
|
||||
await new Promise(res => setTimeout(res, 1000))
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { createSender, waitForReceipt }
|
Loading…
Reference in New Issue