From 1dd9cf70d9c0aae43023e6eb358a213dc020ba27 Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 16 Jun 2021 11:28:39 +0300 Subject: [PATCH] refactor --- contracts/TornadoPool.sol | 128 ++++++++++++++++++++------------------ src/index.js | 13 ++-- src/utxo.js | 2 +- test/full.test.js | 2 +- 4 files changed, 75 insertions(+), 70 deletions(-) diff --git a/contracts/TornadoPool.sol b/contracts/TornadoPool.sol index a3f4596..39501f1 100644 --- a/contracts/TornadoPool.sol +++ b/contracts/TornadoPool.sol @@ -16,9 +16,9 @@ pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; // todo: maybe remove? interface IVerifier { - function verifyProof(bytes memory _proof, uint256[9] memory _input) external returns (bool); + function verifyProof(bytes memory _proof, uint256[9] memory _input) external view returns (bool); - function verifyProof(bytes memory _proof, uint256[23] memory _input) external returns (bool); + function verifyProof(bytes memory _proof, uint256[23] memory _input) external view returns (bool); } contract TornadoPool is ReentrancyGuard { @@ -38,15 +38,13 @@ contract TornadoPool is ReentrancyGuard { bytes encryptedOutput2; } - // todo: event Transaction(); event NewCommitment(bytes32 commitment, uint256 index, bytes encryptedOutput); event NewNullifier(bytes32 nullifier); - event Withdraw(bytes32 indexed nullifier); // todo emit it on withdraw so we can easily find the withdraw tx for user on UI /** @dev The constructor - @param _verifier2 the address of SNARK verifier for this contract - @param _verifier16 the address of SNARK verifier for this contract + @param _verifier2 the address of SNARK verifier for 2 inputs + @param _verifier16 the address of SNARK verifier for 16 inputs */ constructor( IVerifier _verifier2, @@ -74,59 +72,10 @@ contract TornadoPool is ReentrancyGuard { require(!isSpent(_inputNullifiers[i]), "Input is already spent"); } require(uint256(_extDataHash) == uint256(keccak256(abi.encode(_extData))) % FIELD_SIZE, "Incorrect external data hash"); - if (_inputNullifiers.length == 2) { - require( - verifier2.verifyProof( - _proof, - [ - uint256(_root), - uint256(_newRoot), - uint256(_inputNullifiers[0]), - uint256(_inputNullifiers[1]), - uint256(_outputCommitments[0]), - uint256(_outputCommitments[1]), - _extAmount, - _fee, - uint256(_extDataHash) - ] - ), - "Invalid transaction proof" - ); - } else if (_inputNullifiers.length == 16) { - require( - verifier16.verifyProof( - _proof, - [ - uint256(_root), - uint256(_newRoot), - uint256(_inputNullifiers[0]), - uint256(_inputNullifiers[1]), - uint256(_inputNullifiers[2]), - uint256(_inputNullifiers[3]), - uint256(_inputNullifiers[4]), - uint256(_inputNullifiers[5]), - uint256(_inputNullifiers[6]), - uint256(_inputNullifiers[7]), - uint256(_inputNullifiers[8]), - uint256(_inputNullifiers[9]), - uint256(_inputNullifiers[10]), - uint256(_inputNullifiers[11]), - uint256(_inputNullifiers[12]), - uint256(_inputNullifiers[13]), - uint256(_inputNullifiers[14]), - uint256(_inputNullifiers[15]), - uint256(_outputCommitments[0]), - uint256(_outputCommitments[1]), - _extAmount, - _fee, - uint256(_extDataHash) - ] - ), - "Invalid transaction proof" - ); - } else { - revert("unsupported input count"); - } + require( + verifyProof(_proof, _root, _newRoot, _inputNullifiers, _outputCommitments, _extAmount, _fee, _extDataHash), + "Invalid transaction proof" + ); currentRoot = _newRoot; for (uint256 i = 0; i < _inputNullifiers.length; i++) { @@ -172,4 +121,65 @@ contract TornadoPool is ReentrancyGuard { function isSpent(bytes32 _nullifierHash) public view returns (bool) { return nullifierHashes[_nullifierHash]; } + + function verifyProof( + bytes memory _proof, + bytes32 _root, + bytes32 _newRoot, + bytes32[] memory _inputNullifiers, + bytes32[2] memory _outputCommitments, + uint256 _extAmount, + uint256 _fee, + bytes32 _extDataHash + ) public view returns (bool) { + if (_inputNullifiers.length == 2) { + return + verifier2.verifyProof( + _proof, + [ + uint256(_root), + uint256(_newRoot), + uint256(_inputNullifiers[0]), + uint256(_inputNullifiers[1]), + uint256(_outputCommitments[0]), + uint256(_outputCommitments[1]), + _extAmount, + _fee, + uint256(_extDataHash) + ] + ); + } else if (_inputNullifiers.length == 16) { + return + verifier16.verifyProof( + _proof, + [ + uint256(_root), + uint256(_newRoot), + uint256(_inputNullifiers[0]), + uint256(_inputNullifiers[1]), + uint256(_inputNullifiers[2]), + uint256(_inputNullifiers[3]), + uint256(_inputNullifiers[4]), + uint256(_inputNullifiers[5]), + uint256(_inputNullifiers[6]), + uint256(_inputNullifiers[7]), + uint256(_inputNullifiers[8]), + uint256(_inputNullifiers[9]), + uint256(_inputNullifiers[10]), + uint256(_inputNullifiers[11]), + uint256(_inputNullifiers[12]), + uint256(_inputNullifiers[13]), + uint256(_inputNullifiers[14]), + uint256(_inputNullifiers[15]), + uint256(_outputCommitments[0]), + uint256(_outputCommitments[1]), + _extAmount, + _fee, + uint256(_extDataHash) + ] + ); + } else { + revert("unsupported input count"); + } + } } diff --git a/src/index.js b/src/index.js index 1df96ab..ff215fb 100644 --- a/src/index.js +++ b/src/index.js @@ -9,7 +9,6 @@ const { prove } = require('./prover') const MERKLE_TREE_HEIGHT = 5 async function buildMerkleTree({ tornadoPool }) { - console.log('Getting contract state...') const filter = tornadoPool.filters.NewCommitment() const events = await tornadoPool.queryFilter(filter, 0) @@ -48,8 +47,6 @@ async function getProof({ inputs, outputs, tree, extAmount, fee, recipient, rela const outputIndex = tree.elements().length - 1 const outputPath = tree.path(outputIndex).pathElements - //encrypt(encryptedPublicKey, { data }, 'x25519-xsalsa20-poly1305') - const extData = { recipient: toFixedHex(recipient, 20), relayer: toFixedHex(relayer, 20), @@ -82,9 +79,6 @@ async function getProof({ inputs, outputs, tree, extAmount, fee, recipient, rela outPathElements: outputPath.slice(Math.log2(outputs.length)), } - //console.log('SNARK input', input) - - console.log('Generating SNARK proof...') const proof = await prove(input, `./artifacts/circuits/transaction${inputs.length}`) const args = [ @@ -119,7 +113,8 @@ async function transaction({ tornadoPool, inputs = [], outputs = [], fee = 0, re let extAmount = BigNumber.from(fee) .add(outputs.reduce((sum, x) => sum.add(x.amount), BigNumber.from(0))) .sub(inputs.reduce((sum, x) => sum.add(x.amount), BigNumber.from(0))) - const amount = extAmount > 0 ? extAmount : 0 + + const amount = extAmount > 0 ? extAmount : 0 // extAmount will be positive for a deposit, zero for a transact and negative for withdraw if (extAmount < 0) { extAmount = FIELD_SIZE.add(extAmount) } @@ -134,12 +129,12 @@ async function transaction({ tornadoPool, inputs = [], outputs = [], fee = 0, re relayer, }) - console.log('Sending transaction...') const receipt = await tornadoPool.transaction(proof, ...args, { value: amount, gasLimit: 1e6, }) - console.log(`Receipt ${receipt.hash}`) + const { gasUsed } = await receipt.wait() + // console.log(`Gas Used ${gasUsed}`) } module.exports = { transaction } diff --git a/src/utxo.js b/src/utxo.js index 7bddfa8..24015fd 100644 --- a/src/utxo.js +++ b/src/utxo.js @@ -4,7 +4,7 @@ const { randomBN, poseidonHash, toBuffer } = require('./utils') const Keypair = require('./keypair') class Utxo { - /** + /** Initialize a new UTXO - unspent transaction output or input. Note, a full TX consists of 2/16 inputs and 2 outputs * * @param {BigNumber | BigInt | number | string} amount UTXO amount * @param {BigNumber | BigInt | number | string} blinding Blinding factor diff --git a/test/full.test.js b/test/full.test.js index 08c8f7a..4903cb8 100644 --- a/test/full.test.js +++ b/test/full.test.js @@ -68,7 +68,7 @@ describe('TornadoPool', () => { const bobReceiveUtxo = Utxo.decrypt(bobKeypair, events[0].args.encryptedOutput, events[0].args.index) expect(bobReceiveUtxo.amount).to.be.equal(bobSendAmount) - // Bob withdraws part of his funds from the shielded pool + // Bob withdraws a part of his funds from the shielded pool const bobWithdrawAmount = 2e6 const bobEthAddress = '0xDeaD00000000000000000000000000000000BEEf' const bobChangeUtxo = new Utxo({ amount: bobSendAmount - bobWithdrawAmount, keypair: bobKeypair })