mirror of
https://github.com/tornadocash/tornado-nova
synced 2024-02-02 14:53:56 +01:00
refactor
This commit is contained in:
parent
4fded51106
commit
1dd9cf70d9
@ -16,9 +16,9 @@ pragma experimental ABIEncoderV2;
|
|||||||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; // todo: maybe remove?
|
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; // todo: maybe remove?
|
||||||
|
|
||||||
interface IVerifier {
|
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 {
|
contract TornadoPool is ReentrancyGuard {
|
||||||
@ -38,15 +38,13 @@ contract TornadoPool is ReentrancyGuard {
|
|||||||
bytes encryptedOutput2;
|
bytes encryptedOutput2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: event Transaction();
|
|
||||||
event NewCommitment(bytes32 commitment, uint256 index, bytes encryptedOutput);
|
event NewCommitment(bytes32 commitment, uint256 index, bytes encryptedOutput);
|
||||||
event NewNullifier(bytes32 nullifier);
|
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
|
@dev The constructor
|
||||||
@param _verifier2 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 this contract
|
@param _verifier16 the address of SNARK verifier for 16 inputs
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
IVerifier _verifier2,
|
IVerifier _verifier2,
|
||||||
@ -74,59 +72,10 @@ contract TornadoPool is ReentrancyGuard {
|
|||||||
require(!isSpent(_inputNullifiers[i]), "Input is already spent");
|
require(!isSpent(_inputNullifiers[i]), "Input is already spent");
|
||||||
}
|
}
|
||||||
require(uint256(_extDataHash) == uint256(keccak256(abi.encode(_extData))) % FIELD_SIZE, "Incorrect external data hash");
|
require(uint256(_extDataHash) == uint256(keccak256(abi.encode(_extData))) % FIELD_SIZE, "Incorrect external data hash");
|
||||||
if (_inputNullifiers.length == 2) {
|
require(
|
||||||
require(
|
verifyProof(_proof, _root, _newRoot, _inputNullifiers, _outputCommitments, _extAmount, _fee, _extDataHash),
|
||||||
verifier2.verifyProof(
|
"Invalid transaction proof"
|
||||||
_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");
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRoot = _newRoot;
|
currentRoot = _newRoot;
|
||||||
for (uint256 i = 0; i < _inputNullifiers.length; i++) {
|
for (uint256 i = 0; i < _inputNullifiers.length; i++) {
|
||||||
@ -172,4 +121,65 @@ contract TornadoPool is ReentrancyGuard {
|
|||||||
function isSpent(bytes32 _nullifierHash) public view returns (bool) {
|
function isSpent(bytes32 _nullifierHash) public view returns (bool) {
|
||||||
return nullifierHashes[_nullifierHash];
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
13
src/index.js
13
src/index.js
@ -9,7 +9,6 @@ const { prove } = require('./prover')
|
|||||||
const MERKLE_TREE_HEIGHT = 5
|
const MERKLE_TREE_HEIGHT = 5
|
||||||
|
|
||||||
async function buildMerkleTree({ tornadoPool }) {
|
async function buildMerkleTree({ tornadoPool }) {
|
||||||
console.log('Getting contract state...')
|
|
||||||
const filter = tornadoPool.filters.NewCommitment()
|
const filter = tornadoPool.filters.NewCommitment()
|
||||||
const events = await tornadoPool.queryFilter(filter, 0)
|
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 outputIndex = tree.elements().length - 1
|
||||||
const outputPath = tree.path(outputIndex).pathElements
|
const outputPath = tree.path(outputIndex).pathElements
|
||||||
|
|
||||||
//encrypt(encryptedPublicKey, { data }, 'x25519-xsalsa20-poly1305')
|
|
||||||
|
|
||||||
const extData = {
|
const extData = {
|
||||||
recipient: toFixedHex(recipient, 20),
|
recipient: toFixedHex(recipient, 20),
|
||||||
relayer: toFixedHex(relayer, 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)),
|
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 proof = await prove(input, `./artifacts/circuits/transaction${inputs.length}`)
|
||||||
|
|
||||||
const args = [
|
const args = [
|
||||||
@ -119,7 +113,8 @@ async function transaction({ tornadoPool, inputs = [], outputs = [], fee = 0, re
|
|||||||
let extAmount = BigNumber.from(fee)
|
let extAmount = BigNumber.from(fee)
|
||||||
.add(outputs.reduce((sum, x) => sum.add(x.amount), BigNumber.from(0)))
|
.add(outputs.reduce((sum, x) => sum.add(x.amount), BigNumber.from(0)))
|
||||||
.sub(inputs.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) {
|
if (extAmount < 0) {
|
||||||
extAmount = FIELD_SIZE.add(extAmount)
|
extAmount = FIELD_SIZE.add(extAmount)
|
||||||
}
|
}
|
||||||
@ -134,12 +129,12 @@ async function transaction({ tornadoPool, inputs = [], outputs = [], fee = 0, re
|
|||||||
relayer,
|
relayer,
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('Sending transaction...')
|
|
||||||
const receipt = await tornadoPool.transaction(proof, ...args, {
|
const receipt = await tornadoPool.transaction(proof, ...args, {
|
||||||
value: amount,
|
value: amount,
|
||||||
gasLimit: 1e6,
|
gasLimit: 1e6,
|
||||||
})
|
})
|
||||||
console.log(`Receipt ${receipt.hash}`)
|
const { gasUsed } = await receipt.wait()
|
||||||
|
// console.log(`Gas Used ${gasUsed}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { transaction }
|
module.exports = { transaction }
|
||||||
|
@ -4,7 +4,7 @@ const { randomBN, poseidonHash, toBuffer } = require('./utils')
|
|||||||
const Keypair = require('./keypair')
|
const Keypair = require('./keypair')
|
||||||
|
|
||||||
class Utxo {
|
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} amount UTXO amount
|
||||||
* @param {BigNumber | BigInt | number | string} blinding Blinding factor
|
* @param {BigNumber | BigInt | number | string} blinding Blinding factor
|
||||||
|
@ -68,7 +68,7 @@ describe('TornadoPool', () => {
|
|||||||
const bobReceiveUtxo = Utxo.decrypt(bobKeypair, events[0].args.encryptedOutput, events[0].args.index)
|
const bobReceiveUtxo = Utxo.decrypt(bobKeypair, events[0].args.encryptedOutput, events[0].args.index)
|
||||||
expect(bobReceiveUtxo.amount).to.be.equal(bobSendAmount)
|
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 bobWithdrawAmount = 2e6
|
||||||
const bobEthAddress = '0xDeaD00000000000000000000000000000000BEEf'
|
const bobEthAddress = '0xDeaD00000000000000000000000000000000BEEf'
|
||||||
const bobChangeUtxo = new Utxo({ amount: bobSendAmount - bobWithdrawAmount, keypair: bobKeypair })
|
const bobChangeUtxo = new Utxo({ amount: bobSendAmount - bobWithdrawAmount, keypair: bobKeypair })
|
||||||
|
Loading…
Reference in New Issue
Block a user