From cab8a2b8dbed1b5bfc0ae78f6f889d3823dda257 Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 4 Nov 2020 22:23:14 +0300 Subject: [PATCH] use ens --- abis/Aggregator.abi.json | 319 +++++++++++++++++++++++++++++++++++++++ docker-compose.test.yml | 24 +-- docker-compose.yml | 8 +- package.json | 3 +- src/config.js | 8 +- src/resolver.js | 30 ++++ src/treeWatcher.js | 8 +- src/worker.js | 17 ++- yarn.lock | 6 +- 9 files changed, 390 insertions(+), 33 deletions(-) create mode 100644 abis/Aggregator.abi.json create mode 100644 src/resolver.js diff --git a/abis/Aggregator.abi.json b/abis/Aggregator.abi.json new file mode 100644 index 0000000..595ef1e --- /dev/null +++ b/abis/Aggregator.abi.json @@ -0,0 +1,319 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "domains", + "type": "bytes32[]" + } + ], + "name": "bulkResolve", + "outputs": [ + { + "internalType": "address[]", + "name": "result", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract Governance", + "name": "governance", + "type": "address" + } + ], + "name": "getAllProposals", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "forVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "againstVotes", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "executed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "extended", + "type": "bool" + }, + { + "internalType": "enum Governance.ProposalState", + "name": "state", + "type": "uint8" + } + ], + "internalType": "struct GovernanceAggregator.Proposal[]", + "name": "proposals", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract Governance", + "name": "governance", + "type": "address" + }, + { + "internalType": "address[]", + "name": "accs", + "type": "address[]" + } + ], + "name": "getGovernanceBalances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "fromTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "oneUnitAmounts", + "type": "uint256[]" + } + ], + "name": "getPricesInETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "prices", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract Governance", + "name": "governance", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getUserData", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "latestProposalId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "latestProposalIdState", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timelock", + "type": "uint256" + }, + { + "internalType": "address", + "name": "delegatee", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract Miner", + "name": "miner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "instances", + "type": "address[]" + } + ], + "name": "minerRates", + "outputs": [ + { + "internalType": "uint256[]", + "name": "_rates", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "resolve", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract RewardSwap", + "name": "swap", + "type": "address" + } + ], + "name": "swapState", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolWeight", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract Miner", + "name": "miner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "instances", + "type": "address[]" + }, + { + "internalType": "contract RewardSwap", + "name": "swap", + "type": "address" + } + ], + "name": "miningData", + "outputs": [ + { + "internalType": "uint256[]", + "name": "_rates", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolWeight", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "fromTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "oneUnitAmounts", + "type": "uint256[]" + }, + { + "internalType": "contract RewardSwap", + "name": "swap", + "type": "address" + } + ], + "name": "marketData", + "outputs": [ + { + "internalType": "uint256[]", + "name": "prices", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 90bf5dd..f139f00 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -13,7 +13,7 @@ services: environment: REDIS_URL: redis://redis/0 nginx_proxy_read_timeout: 600 - depends_on: [ redis ] + depends_on: [redis] treeWatcher: image: tornadocash/relayer @@ -22,7 +22,7 @@ services: env_file: .env environment: REDIS_URL: redis://redis/0 - depends_on: [ redis ] + depends_on: [redis] priceWatcher: image: tornadocash/relayer @@ -31,7 +31,7 @@ services: env_file: .env environment: REDIS_URL: redis://redis/0 - depends_on: [ redis ] + depends_on: [redis] worker1: image: tornadocash/relayer @@ -40,16 +40,16 @@ services: env_file: .env environment: REDIS_URL: redis://redis/0 - depends_on: [ redis ] + depends_on: [redis] -# worker2: -# image: tornadocash/relayer -# restart: always -# command: worker -# env_file: .env -# environment: -# PRIVATE_KEY: qwe -# REDIS_URL: redis://redis/0 + # worker2: + # image: tornadocash/relayer + # restart: always + # command: worker + # env_file: .env + # environment: + # PRIVATE_KEY: qwe + # REDIS_URL: redis://redis/0 redis: image: redis diff --git a/docker-compose.yml b/docker-compose.yml index 79dd8f4..90e63c6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: LETSENCRYPT_HOST: example.duckdns.org REDIS_URL: redis://redis/0 nginx_proxy_read_timeout: 600 - depends_on: [ redis ] + depends_on: [redis] treeWatcher: image: tornadocash/relayer @@ -20,7 +20,7 @@ services: env_file: .env environment: REDIS_URL: redis://redis/0 - depends_on: [ redis ] + depends_on: [redis] priceWatcher: image: tornadocash/relayer @@ -29,7 +29,7 @@ services: env_file: .env environment: REDIS_URL: redis://redis/0 - depends_on: [ redis ] + depends_on: [redis] worker1: image: tornadocash/relayer @@ -38,7 +38,7 @@ services: env_file: .env environment: REDIS_URL: redis://redis/0 - depends_on: [ redis ] + depends_on: [redis] # worker2: # image: tornadocash/relayer diff --git a/package.json b/package.json index 1208aae..ddb6765 100644 --- a/package.json +++ b/package.json @@ -21,14 +21,15 @@ "bull": "^3.12.1", "circomlib": "git+ssh://git@github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c", "dotenv": "^8.2.0", + "eth-ens-namehash": "^2.0.8", "express": "^4.17.1", "fixed-merkle-tree": "^0.4.0", "gas-price-oracle": "^0.2.2", "ioredis": "^4.14.1", "node-fetch": "^2.6.0", + "torn-token": "git+ssh://git@github.com/tornadocash/torn-token.git#e119ff634fbe1b33b9643074bc2f6b08741c635f", "tornado-cash-anonymity-mining": "git+ssh://git@github.com/tornadocash/tornado-anonymity-mining.git#02ff4c9061eef94617b3c1b91a02c0d25bb1c410", "tx-manager": "git+ssh://git@github.com/tornadocash/tx-manager.git#df118be318b007e597e157504d105dfa3e6322a1", - "torn-token": "git+ssh://git@github.com/tornadocash/torn-token.git#b90fc5adccbf9a87c3bdac6175a0e038a9ed3dd8", "uuid": "^8.3.0", "web3": "^1.3.0", "web3-core-promievent": "^1.3.0", diff --git a/src/config.js b/src/config.js index 648f6ea..3e14c5c 100644 --- a/src/config.js +++ b/src/config.js @@ -1,6 +1,7 @@ require('dotenv').config() const jobType = require('./jobTypes') +const tornConfig = require('torn-token') module.exports = { netId: Number(process.env.NET_ID) || 42, @@ -9,12 +10,11 @@ module.exports = { wsRpcUrl: process.env.WS_RPC_URL, oracleRpcUrl: process.env.ORACLE_RPC_URL || 'https://mainnet.infura.io/', oracleAddress: '0xA2b8E7ee7c8a18ea561A5CF7C9C365592026E374', - minerAddress: '0x3E0a9C6Cf76136862De28ce25a56cDBF38EF9D37', // each network has its own instance - tornadoProxyAddress: '0x98529F6FaE5AdaFfa0AaDA37d9017AF9a4281E13', // each network has its own instance - swapAddress: '0x1E73e0a484a595B692f3d212642AE4B3BF30E7e3', + aggregatorAddress: '0x748F37B04eAB454Ad8cF4F5d6814984448c79B35', minerMerkleTreeHeight: 20, privateKey: process.env.PRIVATE_KEY, - instances: require('torn-token').instances, + instances: tornConfig.instances, + torn: tornConfig, port: process.env.APP_PORT || 8000, tornadoServiceFee: Number(process.env.REGULAR_TORNADO_WITHDRAW_FEE), miningServiceFee: Number(process.env.MINING_SERVICE_FEE), diff --git a/src/resolver.js b/src/resolver.js new file mode 100644 index 0000000..edc9466 --- /dev/null +++ b/src/resolver.js @@ -0,0 +1,30 @@ +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 ens = require('eth-ens-namehash') + +class ENSResolver { + constructor() { + this.addresses = {} + } + + async resolve(domains) { + if (!Array.isArray(domains)) { + domains = [domains] + } + + const unresolved = domains.filter(d => !this.addresses[d]) + if (unresolved.length) { + const resolved = await aggregator.methods.bulkResolve(unresolved.map(ens.hash)).call() + for (let i = 0; i < resolved.length; i++) { + this.addresses[domains[i]] = resolved[i] + } + } + + const addresses = domains.map(domain => this.addresses[domain]) + return addresses.length === 1 ? addresses[0] : addresses + } +} + +module.exports = ENSResolver diff --git a/src/treeWatcher.js b/src/treeWatcher.js index b3fcf2f..55f8cc7 100644 --- a/src/treeWatcher.js +++ b/src/treeWatcher.js @@ -1,12 +1,15 @@ const MerkleTree = require('fixed-merkle-tree') -const { redisUrl, wsRpcUrl, minerMerkleTreeHeight, minerAddress } = require('./config') +const { redisUrl, wsRpcUrl, minerMerkleTreeHeight, torn } = 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(wsRpcUrl) -const contract = new web3.eth.Contract(require('../abis/mining.abi.json'), minerAddress) +const MinerABI = require('../abis/mining.abi.json') +let contract let tree, eventSubscription, blockSubscription @@ -86,6 +89,7 @@ async function rebuild() { async function init() { console.log('Initializing') + contract = new web3.eth.Contract(MinerABI, await resolver.resolve(torn.miningV2.address)) const block = await web3.eth.getBlockNumber() const events = await fetchEvents(0, block) tree = new MerkleTree(minerMerkleTreeHeight, events, { hashFunction: poseidonHash2 }) diff --git a/src/worker.js b/src/worker.js index 26922a5..0e04c1a 100644 --- a/src/worker.js +++ b/src/worker.js @@ -14,17 +14,17 @@ const { poseidonHash2, getInstance, fromDecimals } = require('./utils') const jobType = require('./jobTypes') const { netId, + torn, httpRpcUrl, redisUrl, privateKey, - swapAddress, - minerAddress, - tornadoProxyAddress, gasLimits, instances, tornadoServiceFee, miningServiceFee, } = require('./config') +const ENSResolver = require('./resolver') +const resolver = new ENSResolver() const { TxManager } = require('tx-manager') const { Controller } = require('tornado-cash-anonymity-mining') @@ -63,6 +63,7 @@ async function fetchTree() { const update = await controller.treeUpdate(args.account.outputCommitment, tree) + const minerAddress = await resolver.resolve(torn.miningV2.address) const instance = new web3.eth.Contract(miningABI, minerAddress) const data = currentJob.data.type === 'MINING_REWARD' @@ -79,7 +80,7 @@ async function fetchTree() { async function start() { web3 = new Web3(httpRpcUrl) txManager = new TxManager({ privateKey, rpcUrl: httpRpcUrl, config: { CONFIRMATIONS: 6 } }) - swap = new web3.eth.Contract(swapABI, swapAddress) + swap = new web3.eth.Contract(swapABI, resolver.resolve(torn.rewardSwap.address)) redisSubscribe.subscribe('treeUpdate', fetchTree) await fetchTree() const provingKeys = { @@ -168,16 +169,18 @@ async function checkMiningFee({ args }) { } } -function getTxObject({ data }) { +async function getTxObject({ data }) { if (data.type === jobType.TORNADO_WITHDRAW) { + const tornadoProxyAddress = await resolver.resolve(torn.tornadoProxy.address) const contract = new web3.eth.Contract(tornadoProxyABI, tornadoProxyAddress) const calldata = contract.methods.withdraw(data.contract, data.proof, ...data.args).encodeABI() return { value: data.args[5], to: tornadoProxyAddress, - data: calldata + 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() @@ -197,7 +200,7 @@ async function process(job) { await updateStatus(status.ACCEPTED) console.log(`Start processing a new ${job.data.type} job #${job.id}`) await checkFee(job) - currentTx = await txManager.createTx(getTxObject(job)) + currentTx = await txManager.createTx(await getTxObject(job)) if (job.data.type !== jobType.TORNADO_WITHDRAW) { await fetchTree() diff --git a/yarn.lock b/yarn.lock index 0aac533..c0286cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1466,7 +1466,7 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eth-ens-namehash@2.0.8: +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= @@ -3928,9 +3928,9 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -"torn-token@git+ssh://git@github.com/tornadocash/torn-token.git#b90fc5adccbf9a87c3bdac6175a0e038a9ed3dd8": +"torn-token@git+ssh://git@github.com/tornadocash/torn-token.git#e119ff634fbe1b33b9643074bc2f6b08741c635f": version "1.0.0" - resolved "git+ssh://git@github.com/tornadocash/torn-token.git#b90fc5adccbf9a87c3bdac6175a0e038a9ed3dd8" + resolved "git+ssh://git@github.com/tornadocash/torn-token.git#e119ff634fbe1b33b9643074bc2f6b08741c635f" dependencies: "@openzeppelin/contracts" "^3.1.0" dotenv "^8.2.0"