migrationDeposits.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. require('dotenv').config()
  2. const Web3 = require('web3')
  3. // 1. edit here
  4. const SOURCE_RPC = 'https://mainnet.infura.io/v3/c7463beadf2144e68646ff049917b716'
  5. const TARGET_RPC = 'https://mainnet.infura.io/v3/c7463beadf2144e68646ff049917b716'
  6. const HELPER_RPC = 'https://kovan.poa.network'
  7. const web3Source = new Web3(SOURCE_RPC, null, { transactionConfirmationBlocks: 1 })
  8. const web3Target = new Web3(TARGET_RPC, null, { transactionConfirmationBlocks: 1 })
  9. const web3Helper = new Web3(HELPER_RPC, null, { transactionConfirmationBlocks: 1 })
  10. const ABI = [{ 'constant':false,'inputs':[{ 'name':'_newAccount','type':'address' }],'name':'changeOperator','outputs':[],'payable':false,'stateMutability':'nonpayable','type':'function' },{ 'constant':true,'inputs':[],'name':'filled_subtrees','outputs':[{ 'name':'','type':'uint256[]' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[{ 'name':'','type':'uint256' }],'name':'nullifierHashes','outputs':[{ 'name':'','type':'bool' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[],'name':'verifier','outputs':[{ 'name':'','type':'address' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[],'name':'transferValue','outputs':[{ 'name':'','type':'uint256' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':false,'inputs':[{ 'name':'_commitments','type':'uint256[]' },{ 'name':'_nullifierHashes','type':'uint256[]' }],'name':'migrateState','outputs':[],'payable':false,'stateMutability':'nonpayable','type':'function' },{ 'constant':true,'inputs':[],'name':'roots','outputs':[{ 'name':'','type':'uint256[]' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':false,'inputs':[{ 'name':'_verifier','type':'address' },{ 'name':'_transferValue','type':'uint256' },{ 'name':'_merkleTreeHeight','type':'uint8' },{ 'name':'_emptyElement','type':'uint256' },{ 'name':'_operator','type':'address' },{ 'name':'_filled_subtrees','type':'uint256[]' },{ 'name':'_lastRoot','type':'uint256' }],'name':'initialize','outputs':[],'payable':false,'stateMutability':'nonpayable','type':'function' },{ 'constant':true,'inputs':[{ 'name':'','type':'uint256' }],'name':'commitments','outputs':[{ 'name':'','type':'bool' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[],'name':'zeros','outputs':[{ 'name':'','type':'uint256[]' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[],'name':'levels','outputs':[{ 'name':'','type':'uint256' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':false,'inputs':[{ 'name':'a','type':'uint256[2]' },{ 'name':'b','type':'uint256[2][2]' },{ 'name':'c','type':'uint256[2]' },{ 'name':'input','type':'uint256[4]' }],'name':'withdraw','outputs':[],'payable':false,'stateMutability':'nonpayable','type':'function' },{ 'constant':true,'inputs':[],'name':'operator','outputs':[{ 'name':'','type':'address' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[],'name':'isDepositsEnabled','outputs':[{ 'name':'','type':'bool' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[{ 'name':'nullifier','type':'uint256' }],'name':'isSpent','outputs':[{ 'name':'','type':'bool' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[{ 'name':'left','type':'uint256' },{ 'name':'right','type':'uint256' }],'name':'hashLeftRight','outputs':[{ 'name':'mimc_hash','type':'uint256' }],'payable':false,'stateMutability':'pure','type':'function' },{ 'constant':true,'inputs':[],'name':'next_index','outputs':[{ 'name':'','type':'uint32' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':true,'inputs':[],'name':'current_root','outputs':[{ 'name':'','type':'uint256' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':false,'inputs':[{ 'name':'tree_levels','type':'uint256' },{ 'name':'zero_value','type':'uint256' },{ 'name':'filled_subtrees','type':'uint256[]' },{ 'name':'lastRoot','type':'uint256' }],'name':'initialize','outputs':[],'payable':false,'stateMutability':'nonpayable','type':'function' },{ 'constant':true,'inputs':[{ 'name':'root','type':'uint256' }],'name':'isKnownRoot','outputs':[{ 'name':'','type':'bool' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':false,'inputs':[{ 'name':'commitment','type':'uint256' }],'name':'deposit','outputs':[],'payable':true,'stateMutability':'payable','type':'function' },{ 'constant':true,'inputs':[],'name':'getLastRoot','outputs':[{ 'name':'','type':'uint256' }],'payable':false,'stateMutability':'view','type':'function' },{ 'constant':false,'inputs':[],'name':'toggleDeposits','outputs':[],'payable':false,'stateMutability':'nonpayable','type':'function' },{ 'constant':false,'inputs':[],'name':'stopMigration','outputs':[],'payable':false,'stateMutability':'nonpayable','type':'function' },{ 'constant':true,'inputs':[],'name':'isMigrating','outputs':[{ 'name':'','type':'bool' }],'payable':false,'stateMutability':'view','type':'function' },{ 'payable':true,'stateMutability':'payable','type':'fallback' },{ 'anonymous':false,'inputs':[{ 'indexed':true,'name':'commitment','type':'uint256' },{ 'indexed':false,'name':'leafIndex','type':'uint256' },{ 'indexed':false,'name':'timestamp','type':'uint256' }],'name':'Deposit','type':'event' },{ 'anonymous':false,'inputs':[{ 'indexed':false,'name':'to','type':'address' },{ 'indexed':false,'name':'nullifierHash','type':'uint256' },{ 'indexed':false,'name':'fee','type':'uint256' }],'name':'Withdraw','type':'event' }]
  11. const ABIv2 = require('./build/contracts/MigratableETHTornado.json').abi
  12. const snarkjs = require('snarkjs')
  13. const bigInt = snarkjs.bigInt
  14. const { numberToHex, toWei } = require('web3-utils')
  15. // 2. edit here
  16. const PREVIOUS_INSTANCE = '0xb541fc07bC7619fD4062A54d96268525cBC6FfEF'
  17. const HELPER_INSTANCE = '0xAF1a4734e1234898a7e000fC7eBE8dcb2ca83472'
  18. const NEW_INSTANCE = '0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc'
  19. function toHex(number, length = 32) {
  20. let str = number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16)
  21. return '0x' + str.padStart(length * 2, '0')
  22. }
  23. const previousInstance = new web3Source.eth.Contract(ABI, PREVIOUS_INSTANCE)
  24. previousInstance.deployedBlock = 0
  25. async function loadDeposits() {
  26. const depositEvents = await previousInstance.getPastEvents('Deposit', { fromBlock: previousInstance.deployedBlock, toBlock: 'latest' })
  27. console.log('deposits length', depositEvents.length)
  28. const withdrawEvents = await previousInstance.getPastEvents('Withdraw', { fromBlock: previousInstance.deployedBlock, toBlock: 'latest' })
  29. console.log('withdrawEvents length', withdrawEvents.length)
  30. const commitments = depositEvents
  31. .sort((a, b) => a.returnValues.leafIndex.sub(b.returnValues.leafIndex))
  32. .map(e => toHex(e.returnValues.commitment))
  33. const nullifiers = withdrawEvents
  34. .map(e => toHex(e.returnValues.nullifierHash))
  35. const { root, subtrees } = await getSubtrees({ commitments })
  36. console.log(root, subtrees)
  37. // console.log(JSON.stringify({ subtrees, lastRoot, commitments, nullifiers }, null, 2))
  38. return { subtrees, lastRoot: toHex(root), commitments, nullifiers }
  39. }
  40. async function makeDeposit({ web3, privKey, instance, nonce, commitment }) {
  41. const data = instance.methods.deposit(commitment).encodeABI()
  42. const tx = {
  43. from: web3Helper.eth.defaultAccount,
  44. value: toWei('0.1'),
  45. gas: 2e6,
  46. gasPrice: toHex(toWei('12', 'gwei')),
  47. to: instance._address,
  48. netId: await web3.eth.net.getId(),
  49. data,
  50. nonce
  51. }
  52. let signedTx = await web3.eth.accounts.signTransaction(tx, privKey)
  53. return web3.eth.sendSignedTransaction(signedTx.rawTransaction)
  54. }
  55. async function getSubtrees({ commitments }) {
  56. const tempInstance = new web3Helper.eth.Contract(ABIv2, HELPER_INSTANCE)
  57. let nonce = await web3Helper.eth.getTransactionCount(web3Helper.eth.defaultAccount)
  58. const chunkSize = 80
  59. let chunks = Math.ceil(commitments.length / chunkSize)
  60. for(let c = 0; c < chunks; c++) {
  61. let lastTransaction
  62. for(let i = c * chunkSize; i < Math.min(commitments.length, (c + 1) * chunkSize); i++) {
  63. const n = toHex(nonce)
  64. lastTransaction = makeDeposit({ web3: web3Helper, privKey: process.env.PRIVATE_KEY, instance: tempInstance, nonce: n, commitment: commitments[i] })
  65. nonce = bigInt(nonce).add(bigInt('1'))
  66. }
  67. console.log(`Waiting for the ${c}th chunk`)
  68. await lastTransaction
  69. }
  70. let subtrees = []
  71. for (let i = 0; i < process.env.MERKLE_TREE_HEIGHT; i++) {
  72. subtrees.push(await tempInstance.methods.filledSubtrees(i).call())
  73. }
  74. subtrees = subtrees.map(x => toHex(x))
  75. const lastRoot = await tempInstance.methods.getLastRoot().call()
  76. return { subtrees, root: toHex(lastRoot) }
  77. }
  78. async function migrateState({ subtrees, lastRoot, commitments, nullifiers, newInstance }) {
  79. const loadBy = 100
  80. let commitmentsToLoad
  81. let nullifiersToLoad
  82. await newInstance.methods.initializeTreeForMigration(subtrees, lastRoot).send({
  83. gas: numberToHex(2500000),
  84. gasPrice: toHex(toWei('12', 'gwei')),
  85. from: web3Target.eth.defaultAccount
  86. })
  87. for(let i=0; i < commitments.length / loadBy; i++) {
  88. commitmentsToLoad = commitments.slice(i*loadBy, (i+1)*loadBy)
  89. nullifiersToLoad = nullifiers.slice(i*loadBy, (i+1)*loadBy)
  90. console.log(`Uploading commitments and nullifiers from ${i*loadBy} to ${(i+1)*loadBy}:`)
  91. // console.log('Commitments:\n', commitmentsToLoad)
  92. // console.log('Nullifiers:\n', nullifiersToLoad)
  93. const tx = await newInstance.methods.migrateState(
  94. commitmentsToLoad,
  95. nullifiersToLoad
  96. ).send({
  97. gas: numberToHex(6500000),
  98. gasPrice: toHex(toWei('12', 'gwei')),
  99. from: web3Target.eth.defaultAccount
  100. })
  101. console.log('Gas used:', tx.gasUsed)
  102. }
  103. const balance = await web3Source.eth.getBalance(PREVIOUS_INSTANCE)
  104. console.log('balance to send in wei', balance)
  105. // 3. Decide should we uncomment here
  106. // const finish = await newInstance.methods.finishMigration().send({
  107. // gas: numberToHex(1500000),
  108. // gasPrice: toHex(toWei('10', 'gwei')),
  109. // from: web3Target.eth.defaultAccount,
  110. // value: balance
  111. // })
  112. // console.log('migration completed', finish.transactionHash)
  113. }
  114. async function main() {
  115. const helperAccount = web3Helper.eth.accounts.privateKeyToAccount('0x' + process.env.PRIVATE_KEY)
  116. web3Helper.eth.accounts.wallet.add('0x' + process.env.PRIVATE_KEY)
  117. web3Helper.eth.defaultAccount = helperAccount.address
  118. const targetAccount = web3Target.eth.accounts.privateKeyToAccount('0x' + process.env.PRIVATE_KEY)
  119. web3Target.eth.accounts.wallet.add('0x' + process.env.PRIVATE_KEY)
  120. web3Target.eth.defaultAccount = targetAccount.address
  121. const { subtrees, lastRoot, commitments, nullifiers } = await loadDeposits()
  122. const newInstance = new web3Target.eth.Contract(ABIv2, NEW_INSTANCE)
  123. console.log('commitments length ', commitments.length)
  124. console.log('nullifiers length ', nullifiers.length)
  125. await migrateState({ subtrees, lastRoot, commitments, nullifiers, newInstance })
  126. }
  127. main()