mirror of
https://github.com/tornadocash/tornado-relayer
synced 2024-02-02 15:04:06 +01:00
bit of refactor, add RelayerError class
This commit is contained in:
parent
76209e11c0
commit
50054e0516
@ -2,9 +2,9 @@ const {
|
||||
getTornadoWithdrawInputError,
|
||||
getMiningRewardInputError,
|
||||
getMiningWithdrawInputError,
|
||||
} = require('./validator')
|
||||
const { postJob } = require('./queue')
|
||||
const { jobType } = require('./constants')
|
||||
} = require('../modules/validator')
|
||||
const { postJob } = require('../queue')
|
||||
const { jobType } = require('../constants')
|
||||
|
||||
async function tornadoWithdraw(req, res) {
|
||||
const inputError = getTornadoWithdrawInputError(req.body)
|
4
src/contollers/index.js
Normal file
4
src/contollers/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
controller: require('./controller'),
|
||||
status: require('./status'),
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
const queue = require('./queue')
|
||||
const { netId, tornadoServiceFee, miningServiceFee, instances, redisUrl, rewardAccount } = require('./config')
|
||||
const { version } = require('../package.json')
|
||||
const Redis = require('ioredis')
|
||||
const redis = new Redis(redisUrl)
|
||||
const queue = require('../queue')
|
||||
const { netId, tornadoServiceFee, miningServiceFee, instances, rewardAccount } = require('../config')
|
||||
const { version } = require('../../package.json')
|
||||
const { redis } = require('../modules/redis')
|
||||
|
||||
async function status(req, res) {
|
||||
const ethPrices = await redis.hgetall('prices')
|
@ -1,18 +1,18 @@
|
||||
const Web3 = require('web3')
|
||||
const Redis = require('ioredis')
|
||||
const { toBN, fromWei } = require('web3-utils')
|
||||
|
||||
const { setSafeInterval } = require('./utils')
|
||||
const { redisUrl, httpRpcUrl, privateKey, minimumBalance } = require('./config')
|
||||
|
||||
const web3 = new Web3(httpRpcUrl)
|
||||
const redis = new Redis(redisUrl)
|
||||
const { setSafeInterval, toBN, fromWei } = require('./utils')
|
||||
const { privateKey, minimumBalance } = require('./config')
|
||||
const { redis } = require('./modules/redis')
|
||||
const web3 = require('./modules/web3')()
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
const { address } = web3.eth.accounts.privateKeyToAccount(privateKey)
|
||||
const balance = await web3.eth.getBalance(address)
|
||||
|
||||
const errors = await redis.zrevrange('errors', 0, -1)
|
||||
if (errors.length > 3) {
|
||||
console.log({ errors })
|
||||
throw new Error('Too many errors on relayer')
|
||||
}
|
||||
if (toBN(balance).lt(toBN(minimumBalance))) {
|
||||
throw new Error(`Not enough balance, less than ${fromWei(minimumBalance)} ETH`)
|
||||
}
|
||||
|
11
src/modules/redis.js
Normal file
11
src/modules/redis.js
Normal file
@ -0,0 +1,11 @@
|
||||
const Redis = require('ioredis')
|
||||
const { redisUrl } = require('../config')
|
||||
|
||||
const redis = new Redis(redisUrl)
|
||||
const redisSubscribe = new Redis(redisUrl)
|
||||
|
||||
module.exports = {
|
||||
redis,
|
||||
redisSubscribe,
|
||||
redisUrl,
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
const { httpRpcUrl, aggregatorAddress } = require('./config')
|
||||
const Web3 = require('web3')
|
||||
const web3 = new Web3(httpRpcUrl)
|
||||
const aggregator = new web3.eth.Contract(require('../abis/Aggregator.abi.json'), aggregatorAddress)
|
||||
const { aggregatorAddress } = require('../config')
|
||||
const web3 = require('./web3')()
|
||||
|
||||
const aggregator = new web3.eth.Contract(require('../../abis/Aggregator.abi.json'), aggregatorAddress)
|
||||
const ens = require('eth-ens-namehash')
|
||||
|
||||
class ENSResolver {
|
||||
@ -26,5 +26,4 @@ class ENSResolver {
|
||||
return addresses.length === 1 ? addresses[0] : addresses
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ENSResolver
|
||||
module.exports = new ENSResolver()
|
@ -1,6 +1,6 @@
|
||||
const { isAddress, toChecksumAddress } = require('web3-utils')
|
||||
const { getInstance } = require('./utils')
|
||||
const { rewardAccount } = require('./config')
|
||||
const { getInstance } = require('../utils')
|
||||
const { rewardAccount } = require('../config')
|
||||
|
||||
const Ajv = require('ajv')
|
||||
const ajv = new Ajv({ format: 'fast' })
|
30
src/modules/web3.js
Normal file
30
src/modules/web3.js
Normal file
@ -0,0 +1,30 @@
|
||||
const Web3 = require('web3')
|
||||
const { oracleRpcUrl, httpRpcUrl, wsRpcUrl } = require('../config')
|
||||
const getWeb3 = (type = 'http') => {
|
||||
let url
|
||||
switch (type) {
|
||||
case 'oracle':
|
||||
url = oracleRpcUrl
|
||||
break
|
||||
case 'ws':
|
||||
url = wsRpcUrl
|
||||
return new Web3(
|
||||
new Web3.providers.WebsocketProvider(wsRpcUrl, {
|
||||
clientConfig: {
|
||||
maxReceivedFrameSize: 100000000,
|
||||
maxReceivedMessageSize: 100000000,
|
||||
},
|
||||
}),
|
||||
)
|
||||
case 'http':
|
||||
default:
|
||||
url = httpRpcUrl
|
||||
break
|
||||
}
|
||||
return new Web3(
|
||||
new Web3.providers.HttpProvider(url, {
|
||||
timeout: 200000, // ms
|
||||
}),
|
||||
)
|
||||
}
|
||||
module.exports = getWeb3
|
@ -1,22 +1,13 @@
|
||||
const Redis = require('ioredis')
|
||||
const { redisUrl, offchainOracleAddress, oracleRpcUrl } = require('./config')
|
||||
const { getArgsForOracle, setSafeInterval } = require('./utils')
|
||||
const { toChecksumAddress } = require('web3-utils')
|
||||
const redis = new Redis(redisUrl)
|
||||
const Web3 = require('web3')
|
||||
const web3 = new Web3(
|
||||
new Web3.providers.HttpProvider(oracleRpcUrl, {
|
||||
timeout: 200000, // ms
|
||||
}),
|
||||
)
|
||||
const { offchainOracleAddress } = require('./config')
|
||||
const { getArgsForOracle, setSafeInterval, toChecksumAddress, toBN } = require('./utils')
|
||||
const { redis } = require('./modules/redis')
|
||||
const web3 = require('./modules/web3')()
|
||||
|
||||
const offchainOracleABI = require('../abis/OffchainOracle.abi.json')
|
||||
|
||||
const offchainOracle = new web3.eth.Contract(offchainOracleABI, offchainOracleAddress)
|
||||
const { tokenAddresses, oneUintAmount, currencyLookup } = getArgsForOracle()
|
||||
|
||||
const { toBN } = require('web3-utils')
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
const ethPrices = {}
|
||||
@ -40,6 +31,7 @@ async function main() {
|
||||
await redis.hmset('prices', ethPrices)
|
||||
console.log('Wrote following prices to redis', ethPrices)
|
||||
} catch (e) {
|
||||
redis.zadd('errors', new Date().getTime(), e.message)
|
||||
console.error('priceWatcher error', e)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
const { v4: uuid } = require('uuid')
|
||||
const Queue = require('bull')
|
||||
const Redis = require('ioredis')
|
||||
const { redisUrl } = require('./config')
|
||||
const { status } = require('./constants')
|
||||
const redis = new Redis(redisUrl)
|
||||
const { redis, redisUrl } = require('./modules/redis')
|
||||
|
||||
const queue = new Queue('proofs', redisUrl, {
|
||||
lockDuration: 300000, // Key expiration time for job locks.
|
||||
|
29
src/router.js
Normal file
29
src/router.js
Normal file
@ -0,0 +1,29 @@
|
||||
const { controller, status } = require('./contollers')
|
||||
const router = require('express').Router()
|
||||
|
||||
// Add CORS headers
|
||||
router.use((req, res, next) => {
|
||||
res.header('Access-Control-Allow-Origin', '*')
|
||||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
|
||||
next()
|
||||
})
|
||||
|
||||
// Log error to console but don't send it to the client to avoid leaking data
|
||||
router.use((err, req, res, next) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
router.get('/', status.index)
|
||||
router.get('/v1/status', status.status)
|
||||
router.get('/v1/jobs/:id', status.getJob)
|
||||
router.post('/v1/tornadoWithdraw', controller.tornadoWithdraw)
|
||||
router.get('/status', status.status)
|
||||
router.post('/relay', controller.tornadoWithdraw)
|
||||
router.post('/v1/miningReward', controller.miningReward)
|
||||
router.post('/v1/miningWithdraw', controller.miningWithdraw)
|
||||
|
||||
module.exports = router
|
@ -1,41 +1,14 @@
|
||||
const express = require('express')
|
||||
const status = require('./status')
|
||||
const controller = require('./controller')
|
||||
const { port, rewardAccount } = require('./config')
|
||||
const { version } = require('../package.json')
|
||||
const { isAddress } = require('web3-utils')
|
||||
|
||||
const app = express()
|
||||
app.use(express.json())
|
||||
|
||||
// Add CORS headers
|
||||
app.use((req, res, next) => {
|
||||
res.header('Access-Control-Allow-Origin', '*')
|
||||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
|
||||
next()
|
||||
})
|
||||
|
||||
// Log error to console but don't send it to the client to avoid leaking data
|
||||
app.use((err, req, res, next) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
app.get('/', status.index)
|
||||
app.get('/v1/status', status.status)
|
||||
app.get('/v1/jobs/:id', status.getJob)
|
||||
app.post('/v1/tornadoWithdraw', controller.tornadoWithdraw)
|
||||
app.get('/status', status.status)
|
||||
app.post('/relay', controller.tornadoWithdraw)
|
||||
app.post('/v1/miningReward', controller.miningReward)
|
||||
app.post('/v1/miningWithdraw', controller.miningWithdraw)
|
||||
const { isAddress } = require('./utils')
|
||||
const router = require('./router')
|
||||
|
||||
if (!isAddress(rewardAccount)) {
|
||||
throw new Error('No REWARD_ACCOUNT specified')
|
||||
}
|
||||
|
||||
const app = express()
|
||||
app.use(express.json())
|
||||
app.use(router)
|
||||
app.listen(port)
|
||||
console.log(`Relayer ${version} started on port ${port}`)
|
||||
|
@ -1,21 +1,10 @@
|
||||
const MerkleTree = require('fixed-merkle-tree')
|
||||
const { redisUrl, wsRpcUrl, minerMerkleTreeHeight, torn, netId } = require('./config')
|
||||
const { poseidonHash2 } = require('./utils')
|
||||
const { toBN } = require('web3-utils')
|
||||
const Redis = require('ioredis')
|
||||
const redis = new Redis(redisUrl)
|
||||
const ENSResolver = require('./resolver')
|
||||
const resolver = new ENSResolver()
|
||||
const Web3 = require('web3')
|
||||
const web3 = new Web3(
|
||||
new Web3.providers.WebsocketProvider(wsRpcUrl, {
|
||||
clientConfig: {
|
||||
maxReceivedFrameSize: 100000000,
|
||||
maxReceivedMessageSize: 100000000,
|
||||
},
|
||||
}),
|
||||
)
|
||||
const { minerMerkleTreeHeight, torn, netId } = require('./config')
|
||||
const { poseidonHash2, toBN } = require('./utils')
|
||||
const resolver = require('./modules/resolver')
|
||||
const web3 = require('./modules/web3')('ws')
|
||||
const MinerABI = require('../abis/mining.abi.json')
|
||||
const { redis } = require('./modules/redis')
|
||||
let contract
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
@ -123,7 +112,7 @@ async function init() {
|
||||
const newCommitments = newEvents
|
||||
.sort((a, b) => a.returnValues.index - b.returnValues.index)
|
||||
.map(e => toBN(e.returnValues.commitment))
|
||||
.filter((item, index, arr) => !index || item != arr[index - 1])
|
||||
.filter((item, index, arr) => !index || item !== arr[index - 1])
|
||||
|
||||
const commitments = cachedCommitments.concat(newCommitments)
|
||||
|
||||
@ -134,6 +123,7 @@ async function init() {
|
||||
eventSubscription = contract.events.NewAccount({ fromBlock: toBlock + 1 }, processNewEvent)
|
||||
blockSubscription = web3.eth.subscribe('newBlockHeaders', processNewBlock)
|
||||
} catch (e) {
|
||||
redis.zadd('errors', new Date().getTime(), e.message)
|
||||
console.error('error on init treeWatcher', e.message)
|
||||
}
|
||||
}
|
||||
|
16
src/utils.js
16
src/utils.js
@ -1,6 +1,6 @@
|
||||
const { instances, netId } = require('./config')
|
||||
const { poseidon } = require('circomlib')
|
||||
const { toBN, toChecksumAddress, BN } = require('web3-utils')
|
||||
const { toBN, toChecksumAddress, BN, fromWei, isAddress, toWei } = require('web3-utils')
|
||||
|
||||
const TOKENS = {
|
||||
torn: {
|
||||
@ -118,6 +118,13 @@ function fromDecimals(value, decimals) {
|
||||
return new BN(wei.toString(10), 10)
|
||||
}
|
||||
|
||||
class RelayerError extends Error {
|
||||
constructor(message, score = 0) {
|
||||
super(message)
|
||||
this.score = score
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getInstance,
|
||||
setSafeInterval,
|
||||
@ -126,4 +133,11 @@ module.exports = {
|
||||
when,
|
||||
getArgsForOracle,
|
||||
fromDecimals,
|
||||
toBN,
|
||||
toChecksumAddress,
|
||||
fromWei,
|
||||
toWei,
|
||||
BN,
|
||||
isAddress,
|
||||
RelayerError,
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
const fs = require('fs')
|
||||
const Web3 = require('web3')
|
||||
const { toBN, toWei, fromWei, toChecksumAddress } = require('web3-utils')
|
||||
const MerkleTree = require('fixed-merkle-tree')
|
||||
const Redis = require('ioredis')
|
||||
const { GasPriceOracle } = require('gas-price-oracle')
|
||||
const { Utils, Controller } = require('tornado-anonymity-mining')
|
||||
|
||||
@ -10,14 +7,22 @@ const swapABI = require('../abis/swap.abi.json')
|
||||
const miningABI = require('../abis/mining.abi.json')
|
||||
const tornadoABI = require('../abis/tornadoABI.json')
|
||||
const tornadoProxyABI = require('../abis/tornadoProxyABI.json')
|
||||
const aggregatorAbi = require('../abis/Aggregator.abi.json')
|
||||
const { queue } = require('./queue')
|
||||
const { poseidonHash2, getInstance, fromDecimals, sleep } = require('./utils')
|
||||
const {
|
||||
poseidonHash2,
|
||||
getInstance,
|
||||
fromDecimals,
|
||||
sleep,
|
||||
toBN,
|
||||
toWei,
|
||||
fromWei,
|
||||
toChecksumAddress,
|
||||
RelayerError,
|
||||
} = require('./utils')
|
||||
const { jobType, status } = require('./constants')
|
||||
const {
|
||||
torn,
|
||||
netId,
|
||||
redisUrl,
|
||||
gasLimits,
|
||||
instances,
|
||||
privateKey,
|
||||
@ -28,9 +33,10 @@ const {
|
||||
tornadoServiceFee,
|
||||
tornadoGoerliProxy,
|
||||
} = require('./config')
|
||||
const ENSResolver = require('./resolver')
|
||||
const resolver = new ENSResolver()
|
||||
const resolver = require('./modules/resolver')
|
||||
const { TxManager } = require('tx-manager')
|
||||
const { redis, redisSubscribe } = require('./modules/redis')
|
||||
const getWeb3 = require('./modules/web3')
|
||||
|
||||
let web3
|
||||
let currentTx
|
||||
@ -40,8 +46,6 @@ let txManager
|
||||
let controller
|
||||
let swap
|
||||
let minerContract
|
||||
const redis = new Redis(redisUrl)
|
||||
const redisSubscribe = new Redis(redisUrl)
|
||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: oracleRpcUrl })
|
||||
|
||||
async function fetchTree() {
|
||||
@ -76,7 +80,8 @@ async function fetchTree() {
|
||||
|
||||
async function start() {
|
||||
try {
|
||||
web3 = new Web3(httpRpcUrl)
|
||||
await clearErrors()
|
||||
web3 = getWeb3()
|
||||
const { CONFIRMATIONS, MAX_GAS_PRICE } = process.env
|
||||
txManager = new TxManager({
|
||||
privateKey,
|
||||
@ -101,6 +106,7 @@ async function start() {
|
||||
queue.process(processJob)
|
||||
console.log('Worker started')
|
||||
} catch (e) {
|
||||
redis.zadd('errors', new Date().getTime(), e.message)
|
||||
console.error('error on start worker', e.message)
|
||||
}
|
||||
}
|
||||
@ -116,13 +122,11 @@ async function getGasPrice() {
|
||||
const block = await web3.eth.getBlock('latest')
|
||||
|
||||
if (block && block.baseFeePerGas) {
|
||||
const baseFeePerGas = toBN(block.baseFeePerGas)
|
||||
return baseFeePerGas
|
||||
return toBN(block.baseFeePerGas)
|
||||
}
|
||||
|
||||
const { fast } = await gasPriceOracle.gasPrices()
|
||||
const gasPrice = toBN(toWei(fast.toString(), 'gwei'))
|
||||
return gasPrice
|
||||
return toBN(toWei(fast.toString(), 'gwei'))
|
||||
}
|
||||
|
||||
async function checkTornadoFee({ args, contract }) {
|
||||
@ -160,7 +164,10 @@ async function checkTornadoFee({ args, contract }) {
|
||||
fromWei(feePercent.toString()),
|
||||
)
|
||||
if (fee.lt(desiredFee)) {
|
||||
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.')
|
||||
throw new RelayerError(
|
||||
'Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.',
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +203,6 @@ async function checkMiningFee({ args }) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getProxyContract() {
|
||||
let proxyAddress
|
||||
if (netId === 5) {
|
||||
@ -262,7 +268,7 @@ async function isOutdatedTreeRevert(receipt, currentTx) {
|
||||
async function processJob(job) {
|
||||
try {
|
||||
if (!jobType[job.data.type]) {
|
||||
throw new Error(`Unknown job type: ${job.data.type}`)
|
||||
throw new RelayerError(`Unknown job type: ${job.data.type}`)
|
||||
}
|
||||
currentJob = job
|
||||
await updateStatus(status.ACCEPTED)
|
||||
@ -304,10 +310,10 @@ async function submitTx(job, retry = 0) {
|
||||
await updateStatus(status.RESUBMITTED)
|
||||
await submitTx(job, retry + 1)
|
||||
} else {
|
||||
throw new Error('Tree update retry limit exceeded')
|
||||
throw new RelayerError('Tree update retry limit exceeded')
|
||||
}
|
||||
} else {
|
||||
throw new Error('Submitted transaction failed')
|
||||
throw new RelayerError('Submitted transaction failed')
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@ -323,10 +329,10 @@ async function submitTx(job, retry = 0) {
|
||||
console.log('Tree is still not up to date, resubmitting')
|
||||
await submitTx(job, retry + 1)
|
||||
} else {
|
||||
throw new Error('Tree update retry limit exceeded')
|
||||
throw new RelayerError('Tree update retry limit exceeded')
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Revert by smart contract ${e.message}`)
|
||||
throw new RelayerError(`Revert by smart contract ${e.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -349,4 +355,9 @@ async function updateStatus(status) {
|
||||
await currentJob.update(currentJob.data)
|
||||
}
|
||||
|
||||
async function clearErrors() {
|
||||
console.log('Errors list cleared')
|
||||
await redis.del('errors')
|
||||
}
|
||||
|
||||
start()
|
||||
|
@ -4,7 +4,7 @@ const {
|
||||
getTornadoWithdrawInputError,
|
||||
getMiningRewardInputError,
|
||||
getMiningWithdrawInputError,
|
||||
} = require('../src/validator')
|
||||
} = require('../src/modules/validator')
|
||||
|
||||
describe('Validator', () => {
|
||||
describe('#getTornadoWithdrawInputError', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user