mirror of
https://github.com/tornadocash/tornado-relayer.git
synced 2025-05-08 17:44:59 -04:00
works for regular tornado. dirty and WIP though
This commit is contained in:
parent
d888fdbd44
commit
c16164876e
7 changed files with 114 additions and 144 deletions
132
src/worker.js
132
src/worker.js
|
@ -1,27 +1,27 @@
|
|||
const { queue } = require('./queue')
|
||||
const Web3 = require('web3')
|
||||
const { rpcUrl, redisUrl, privateKey, netId, gasBumpInterval, gasBumpPercentage, maxGasPrice } = require('../config')
|
||||
const { numberToHex, toWei, toHex, toBN, fromWei, toChecksumAddress, BN } = require('web3-utils')
|
||||
const tornadoABI = require('../abis/tornadoABI.json')
|
||||
const { numberToHex, toBN } = require('web3-utils')
|
||||
const MerkleTree = require('fixed-merkle-tree')
|
||||
const { setSafeInterval, poseidonHash2 } = require('./utils')
|
||||
const Redis = require('ioredis')
|
||||
const redis = new Redis(redisUrl)
|
||||
const redisSubscribe = new Redis(redisUrl)
|
||||
const { GasPriceOracle } = require('gas-price-oracle')
|
||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: rpcUrl })
|
||||
queue.process(process)
|
||||
redisSubscribe.subscribe('treeUpdate', fetchTree)
|
||||
|
||||
const tornadoABI = require('../abis/tornadoABI.json')
|
||||
const { queue } = require('./queue')
|
||||
const { poseidonHash2 } = require('./utils')
|
||||
const { rpcUrl, redisUrl, privateKey, updateConfig, rewardAccount } = require('../config')
|
||||
const TxManager = require('./TxManager')
|
||||
|
||||
let web3
|
||||
let nonce
|
||||
let currentTx
|
||||
let currentJob
|
||||
let tree
|
||||
let txManager
|
||||
const redis = new Redis(redisUrl)
|
||||
const redisSubscribe = new Redis(redisUrl)
|
||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: rpcUrl })
|
||||
|
||||
async function fetchTree() {
|
||||
const elements = await redis.get('tree:elements')
|
||||
const convert = (_, val) => typeof(val) === 'string' ? toBN(val) : val
|
||||
const convert = (_, val) => (typeof val === 'string' ? toBN(val) : val)
|
||||
tree = MerkleTree.deserialize(JSON.parse(elements, convert), poseidonHash2)
|
||||
|
||||
if (currentTx) {
|
||||
|
@ -29,75 +29,55 @@ async function fetchTree() {
|
|||
}
|
||||
}
|
||||
|
||||
async function watcher() {
|
||||
if (currentTx && Date.now() - currentTx.date > gasBumpInterval) {
|
||||
bumpGasPrice()
|
||||
}
|
||||
}
|
||||
|
||||
async function bumpGasPrice() {
|
||||
const newGasPrice = toBN(currentTx.gasPrice).mul(toBN(gasBumpPercentage)).div(toBN(100))
|
||||
const maxGasPrice = toBN(toWei(maxGasPrice.toString(), 'Gwei'))
|
||||
currentTx.gasPrice = toHex(BN.min(newGasPrice, maxGasPrice))
|
||||
currentTx.date = Date.now()
|
||||
console.log(`Resubmitting with gas price ${fromWei(currentTx.gasPrice.toString(), 'gwei')} gwei`)
|
||||
await sendTx(currentTx, updateTxHash)
|
||||
}
|
||||
|
||||
async function init() {
|
||||
async function start() {
|
||||
web3 = new Web3(rpcUrl, null, { transactionConfirmationBlocks: 1 })
|
||||
const account = web3.eth.accounts.privateKeyToAccount('0x' + privateKey)
|
||||
web3.eth.accounts.wallet.add('0x' + privateKey)
|
||||
web3.eth.defaultAccount = account.address
|
||||
nonce = await web3.eth.getTransactionCount(account.address, 'latest')
|
||||
updateConfig({ rewardAccount: account.address })
|
||||
txManager = new TxManager({ privateKey, rpcUrl })
|
||||
queue.process(process)
|
||||
redisSubscribe.subscribe('treeUpdate', fetchTree)
|
||||
await fetchTree()
|
||||
setSafeInterval(watcher, 1000)
|
||||
console.log('Worker started')
|
||||
}
|
||||
|
||||
async function checkTornadoFee(contract, fee, refund) {
|
||||
|
||||
async function checkTornadoFee(/* contract, fee, refund*/) {
|
||||
const { fast } = await gasPriceOracle.gasPrices()
|
||||
console.log('fast', fast)
|
||||
}
|
||||
|
||||
async function process(job) {
|
||||
if (job.type !== 'tornadoWithdraw') {
|
||||
if (job.data.type !== 'tornadoWithdraw') {
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
currentJob = job
|
||||
console.log(Date.now(), ' withdraw started', job.id)
|
||||
const { proof, args, contract } = job.data
|
||||
const { proof, args, contract } = job.data.data
|
||||
const fee = toBN(args[4])
|
||||
const refund = toBN(args[5])
|
||||
await checkTornadoFee(contract, fee, refund)
|
||||
|
||||
|
||||
const instance = new web3.eth.Contract(tornadoABI, contract)
|
||||
const data = instance.methods.withdraw(proof, ...args).encodeABI()
|
||||
const gasPrices = await gasPriceOracle.gasPrices()
|
||||
currentTx = {
|
||||
from: web3.eth.defaultAccount,
|
||||
currentTx = await txManager.createTx({
|
||||
value: numberToHex(refund),
|
||||
gasPrice: toHex(toWei(gasPrices.fast.toString(), 'gwei')),
|
||||
to: contract,
|
||||
netId,
|
||||
data,
|
||||
nonce,
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
currentTx.gas = await web3.eth.estimateGas(currentTx)
|
||||
}
|
||||
catch (e) {
|
||||
await currentTx
|
||||
.send()
|
||||
.on('transactionHash', updateTxHash)
|
||||
.on('mined', (receipt) => {
|
||||
console.log('Mined in block', receipt.blockNumber)
|
||||
})
|
||||
.on('confirmations', updateConfirmations)
|
||||
} catch (e) {
|
||||
console.error('Revert', e)
|
||||
throw new Error(`Revert by smart contract ${e.message}`)
|
||||
}
|
||||
|
||||
nonce++
|
||||
await sendTx(currentTx, updateTxHash)
|
||||
}
|
||||
|
||||
async function waitForTx(hash) {
|
||||
|
||||
}
|
||||
|
||||
async function updateTxHash(txHash) {
|
||||
|
@ -106,46 +86,10 @@ async function updateTxHash(txHash) {
|
|||
await currentJob.update(currentJob.data)
|
||||
}
|
||||
|
||||
async function sendTx(tx, onTxHash, retryAttempt) {
|
||||
let signedTx = await this.web3.eth.accounts.signTransaction(tx, privateKey)
|
||||
let result = this.web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
||||
|
||||
if (onTxHash) {
|
||||
result.once('transactionHash', onTxHash)
|
||||
}
|
||||
|
||||
try { // await returns once tx is mined
|
||||
await result
|
||||
} catch (e) {
|
||||
console.log(`Error for tx with nonce ${tx.nonce}\n${e.message}`)
|
||||
if (nonceErrors.includes(e.message)) {
|
||||
console.log('nonce too low, retrying')
|
||||
if (retryAttempt <= 10) {
|
||||
tx.nonce++
|
||||
return sendTx(tx, onTxHash, retryAttempt + 1)
|
||||
}
|
||||
}
|
||||
if (gasPriceErrors.includes(e.message)) {
|
||||
return bumpGasPrice()
|
||||
}
|
||||
throw new Error(e)
|
||||
}
|
||||
async function updateConfirmations(confirmations) {
|
||||
console.log(`Confirmations count ${confirmations}`)
|
||||
currentJob.data.confirmations = confirmations
|
||||
await currentJob.update(currentJob.data)
|
||||
}
|
||||
|
||||
const nonceErrors = [
|
||||
'Returned error: Transaction nonce is too low. Try incrementing the nonce.',
|
||||
'Returned error: nonce too low',
|
||||
]
|
||||
|
||||
const gasPriceErrors = [
|
||||
'Returned error: Transaction gas price supplied is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce.',
|
||||
'Returned error: replacement transaction underpriced',
|
||||
]
|
||||
|
||||
async function main() {
|
||||
await init()
|
||||
|
||||
}
|
||||
|
||||
// main()
|
||||
fetchTree()
|
||||
module.exports = { start, process }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue