commit
f011b6f9c5
@ -18,4 +18,4 @@ REWARD_ACCOUNT=
|
||||
# in GWEI
|
||||
MAX_GAS_PRICE=1000
|
||||
CONFIRMATIONS=8
|
||||
AGGREGATOR=0x9125921ab88429c81d50b6d7e97231cddd181542
|
||||
AGGREGATOR=0x466121060aD4dCE1E421027297d7e263236cbfc3
|
||||
|
@ -29,7 +29,7 @@
|
||||
"node-fetch": "^2.6.0",
|
||||
"torn-token": "git+ssh://git@github.com/tornadocash/torn-token.git#04c4df88d470ca7503ef5d97882c56cba4f3647d",
|
||||
"tornado-cash-anonymity-mining": "git+ssh://git@github.com/tornadocash/tornado-anonymity-mining.git#b13228c20126f212ebbcc5a8493ce2105210739e",
|
||||
"tx-manager": "^0.2.6",
|
||||
"tx-manager": "^0.2.9",
|
||||
"uuid": "^8.3.0",
|
||||
"web3": "^1.3.0",
|
||||
"web3-core-promievent": "^1.3.0",
|
||||
|
106
src/worker.js
106
src/worker.js
@ -36,6 +36,8 @@ let tree
|
||||
let txManager
|
||||
let controller
|
||||
let swap
|
||||
let minerContract
|
||||
let proxyContract
|
||||
const redis = new Redis(redisUrl)
|
||||
const redisSubscribe = new Redis(redisUrl)
|
||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: httpRpcUrl })
|
||||
@ -46,6 +48,7 @@ const status = Object.freeze({
|
||||
MINED: 'MINED',
|
||||
CONFIRMED: 'CONFIRMED',
|
||||
FAILED: 'FAILED',
|
||||
RESUBMITTED: 'RESUBMITTED',
|
||||
})
|
||||
|
||||
async function fetchTree() {
|
||||
@ -81,8 +84,14 @@ async function fetchTree() {
|
||||
async function start() {
|
||||
web3 = new Web3(httpRpcUrl)
|
||||
const { CONFIRMATIONS, MAX_GAS_PRICE } = process.env
|
||||
txManager = new TxManager({ privateKey, rpcUrl: httpRpcUrl, config: { CONFIRMATIONS, MAX_GAS_PRICE } })
|
||||
txManager = new TxManager({
|
||||
privateKey,
|
||||
rpcUrl: httpRpcUrl,
|
||||
config: { CONFIRMATIONS, MAX_GAS_PRICE, THROW_ON_REVERT: false },
|
||||
})
|
||||
swap = new web3.eth.Contract(swapABI, await resolver.resolve(torn.rewardSwap.address))
|
||||
minerContract = new web3.eth.Contract(miningABI, await resolver.resolve(torn.miningV2.address))
|
||||
proxyContract = new web3.eth.Contract(tornadoProxyABI, await resolver.resolve(torn.tornadoProxy.address))
|
||||
redisSubscribe.subscribe('treeUpdate', fetchTree)
|
||||
await fetchTree()
|
||||
const provingKeys = {
|
||||
@ -171,12 +180,11 @@ async function checkMiningFee({ args }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getTxObject({ data }) {
|
||||
function getTxObject({ data }) {
|
||||
if (data.type === jobType.TORNADO_WITHDRAW) {
|
||||
let contract, calldata
|
||||
if (getInstance(data.contract).currency === 'eth') {
|
||||
const tornadoProxyAddress = await resolver.resolve(torn.tornadoProxy.address)
|
||||
contract = new web3.eth.Contract(tornadoProxyABI, tornadoProxyAddress)
|
||||
contract = proxyContract
|
||||
calldata = contract.methods.withdraw(data.contract, data.proof, ...data.args).encodeABI()
|
||||
} else {
|
||||
contract = new web3.eth.Contract(tornadoABI, data.contract)
|
||||
@ -188,17 +196,29 @@ async function getTxObject({ data }) {
|
||||
data: calldata,
|
||||
}
|
||||
} else {
|
||||
const minerAddress = await resolver.resolve(torn.miningV2.address)
|
||||
const contract = new web3.eth.Contract(miningABI, minerAddress)
|
||||
const method = data.type === jobType.MINING_REWARD ? 'reward' : 'withdraw'
|
||||
const calldata = contract.methods[method](data.proof, data.args).encodeABI()
|
||||
const calldata = minerContract.methods[method](data.proof, data.args).encodeABI()
|
||||
return {
|
||||
to: minerAddress,
|
||||
to: minerContract._address,
|
||||
data: calldata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function isOutdatedTreeRevert(receipt, currentTx) {
|
||||
try {
|
||||
await web3.eth.call(currentTx.tx, receipt.blockNumber)
|
||||
console.log('Simulated call successful')
|
||||
return false
|
||||
} catch (e) {
|
||||
console.log('Decoded revert reason:', e.message)
|
||||
return (
|
||||
e.message.indexOf('Outdated account merkle root') !== -1 ||
|
||||
e.message.indexOf('Outdated tree update merkle root') !== -1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function processJob(job) {
|
||||
try {
|
||||
if (!jobType[job.data.type]) {
|
||||
@ -207,38 +227,56 @@ async function processJob(job) {
|
||||
currentJob = job
|
||||
await updateStatus(status.ACCEPTED)
|
||||
console.log(`Start processing a new ${job.data.type} job #${job.id}`)
|
||||
await checkFee(job)
|
||||
currentTx = await txManager.createTx(await getTxObject(job))
|
||||
|
||||
if (job.data.type !== jobType.TORNADO_WITHDRAW) {
|
||||
await fetchTree()
|
||||
}
|
||||
|
||||
try {
|
||||
await currentTx
|
||||
.send()
|
||||
.on('transactionHash', txHash => {
|
||||
updateTxHash(txHash)
|
||||
updateStatus(status.SENT)
|
||||
})
|
||||
.on('mined', receipt => {
|
||||
console.log('Mined in block', receipt.blockNumber)
|
||||
updateStatus(status.MINED)
|
||||
})
|
||||
.on('confirmations', updateConfirmations)
|
||||
|
||||
await updateStatus(status.CONFIRMED)
|
||||
} catch (e) {
|
||||
console.error('Revert', e)
|
||||
throw new Error(`Revert by smart contract ${e.message}`)
|
||||
}
|
||||
await submitTx(job)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.error('processJob', e.message)
|
||||
await updateStatus(status.FAILED)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function submitTx(job, retry = 0) {
|
||||
await checkFee(job)
|
||||
currentTx = await txManager.createTx(getTxObject(job))
|
||||
|
||||
if (job.data.type !== jobType.TORNADO_WITHDRAW) {
|
||||
await fetchTree()
|
||||
}
|
||||
|
||||
try {
|
||||
const receipt = await currentTx
|
||||
.send()
|
||||
.on('transactionHash', txHash => {
|
||||
updateTxHash(txHash)
|
||||
updateStatus(status.SENT)
|
||||
})
|
||||
.on('mined', receipt => {
|
||||
console.log('Mined in block', receipt.blockNumber)
|
||||
updateStatus(status.MINED)
|
||||
})
|
||||
.on('confirmations', updateConfirmations)
|
||||
|
||||
if (receipt.status === 1) {
|
||||
await updateStatus(status.CONFIRMED)
|
||||
} else {
|
||||
if (job.data.type !== jobType.TORNADO_WITHDRAW && (await isOutdatedTreeRevert(receipt, currentTx))) {
|
||||
if (retry < 3) {
|
||||
await updateStatus(status.RESUBMITTED)
|
||||
await submitTx(job, retry + 1)
|
||||
} else {
|
||||
throw new Error('Tree update retry limit exceeded')
|
||||
}
|
||||
} else {
|
||||
throw new Error('Submitted transaction failed')
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// todo this could result in duplicated error logs
|
||||
// todo handle a case where account tree is still not up to date (wait and retry)?
|
||||
throw new Error(`Revert by smart contract ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateTxHash(txHash) {
|
||||
console.log(`A new successfully sent tx ${txHash}`)
|
||||
currentJob.data.txHash = txHash
|
||||
|
@ -4384,10 +4384,10 @@ tweetnacl@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
||||
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||
|
||||
tx-manager@^0.2.6:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/tx-manager/-/tx-manager-0.2.6.tgz#05f49794695ebd71c58b794bb213927efdbf6d3e"
|
||||
integrity sha512-19h+PacgoUuXbN2wKTpb4R+MxD9fOxil3ZsvnDRG/Z4pPboUzd/D2+VBdcH9+EEVGXovZ6bcVggIZ4RIO7LO1w==
|
||||
tx-manager@^0.2.9:
|
||||
version "0.2.9"
|
||||
resolved "https://registry.yarnpkg.com/tx-manager/-/tx-manager-0.2.9.tgz#dc949d7f1a3ed3a3517384f6ef89b10c0a140e02"
|
||||
integrity sha512-IQvDo/j+K9JG1NjLOYUUegyCdfsn0D8sTjQU5w2/Mj30GB/MVPmMqWlPIuu2bwhIJY78WpOln25i60TaT5Sawg==
|
||||
dependencies:
|
||||
async-mutex "^0.2.4"
|
||||
ethers "^5.0.17"
|
||||
|
Loading…
Reference in New Issue
Block a user