mirror of
https://github.com/tornadocash/tornado-nova
synced 2024-02-02 14:53:56 +01:00
different input counts
This commit is contained in:
parent
cd18bef60b
commit
cb2a587540
@ -15,42 +15,41 @@ nullifier = hash(commitment, privKey, merklePath)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Universal JoinSplit transaction with 2 inputs and 2 outputs
|
// Universal JoinSplit transaction with 2 inputs and 2 outputs
|
||||||
template Transaction(levels, zeroLeaf) {
|
template Transaction(levels, nIns, nOuts, zeroLeaf) {
|
||||||
signal input root;
|
signal input root;
|
||||||
signal input newRoot;
|
signal input newRoot;
|
||||||
signal input inputNullifier[2];
|
signal input inputNullifier[nIns];
|
||||||
signal input outputCommitment[2];
|
signal input outputCommitment[nOuts];
|
||||||
// external amount used for deposits and withdrawals
|
// external amount used for deposits and withdrawals
|
||||||
// correct extAmount range is enforced on the smart contract
|
// correct extAmount range is enforced on the smart contract
|
||||||
signal input extAmount;
|
signal input extAmount;
|
||||||
signal input fee;
|
signal input fee;
|
||||||
signal input extDataHash;
|
signal input extDataHash;
|
||||||
|
|
||||||
// data for 2 transaction inputs
|
// data for transaction inputs
|
||||||
signal private input inAmount[2];
|
signal private input inAmount[nIns];
|
||||||
signal private input inBlinding[2];
|
signal private input inBlinding[nIns];
|
||||||
signal private input inPrivateKey[2];
|
signal private input inPrivateKey[nIns];
|
||||||
signal private input inPathIndices[2];
|
signal private input inPathIndices[nIns];
|
||||||
signal private input inPathElements[2][levels];
|
signal private input inPathElements[nIns][levels];
|
||||||
|
|
||||||
// data for 2 transaction outputs
|
// data for transaction outputs
|
||||||
signal private input outAmount[2];
|
signal private input outAmount[nOuts];
|
||||||
signal private input outBlinding[2];
|
signal private input outBlinding[nOuts];
|
||||||
signal private input outPubkey[2];
|
signal private input outPubkey[nOuts];
|
||||||
signal private input outPathIndices;
|
signal private input outPathIndices;
|
||||||
signal private input outPathElements[levels - 1];
|
signal private input outPathElements[levels - 1];
|
||||||
|
|
||||||
component inUtxoHasher[2];
|
component inKeypair[nIns];
|
||||||
component inKeypair[2];
|
component inUtxoHasher[nIns];
|
||||||
component outUtxoHasher[2];
|
component nullifierHasher[nIns];
|
||||||
component nullifierHasher[2];
|
component inAmountCheck[nIns];
|
||||||
component checkRoot[2]
|
component tree[nIns];
|
||||||
component tree[2];
|
component checkRoot[nIns];
|
||||||
component inAmountCheck[2];
|
var sumIns = 0;
|
||||||
component outAmountCheck[2];
|
|
||||||
|
|
||||||
// verify correctness of transaction inputs
|
// verify correctness of transaction inputs
|
||||||
for (var tx = 0; tx < 2; tx++) {
|
for (var tx = 0; tx < nIns; tx++) {
|
||||||
inKeypair[tx] = Keypair();
|
inKeypair[tx] = Keypair();
|
||||||
inKeypair[tx].privateKey <== inPrivateKey[tx];
|
inKeypair[tx].privateKey <== inPrivateKey[tx];
|
||||||
|
|
||||||
@ -81,10 +80,16 @@ template Transaction(levels, zeroLeaf) {
|
|||||||
// Check that amount fits into 248 bits to prevent overflow
|
// Check that amount fits into 248 bits to prevent overflow
|
||||||
inAmountCheck[tx] = Num2Bits(248);
|
inAmountCheck[tx] = Num2Bits(248);
|
||||||
inAmountCheck[tx].in <== inAmount[tx];
|
inAmountCheck[tx].in <== inAmount[tx];
|
||||||
|
|
||||||
|
sumIns += inAmount[tx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component outUtxoHasher[nOuts];
|
||||||
|
component outAmountCheck[nOuts];
|
||||||
|
var sumOuts = 0;
|
||||||
|
|
||||||
// verify correctness of transaction outputs
|
// verify correctness of transaction outputs
|
||||||
for (var tx = 0; tx < 2; tx++) {
|
for (var tx = 0; tx < nOuts; tx++) {
|
||||||
outUtxoHasher[tx] = TransactionHasher();
|
outUtxoHasher[tx] = TransactionHasher();
|
||||||
outUtxoHasher[tx].amount <== outAmount[tx];
|
outUtxoHasher[tx].amount <== outAmount[tx];
|
||||||
outUtxoHasher[tx].blinding <== outBlinding[tx];
|
outUtxoHasher[tx].blinding <== outBlinding[tx];
|
||||||
@ -94,6 +99,8 @@ template Transaction(levels, zeroLeaf) {
|
|||||||
// Check that amount fits into 248 bits to prevent overflow
|
// Check that amount fits into 248 bits to prevent overflow
|
||||||
outAmountCheck[tx] = Num2Bits(248);
|
outAmountCheck[tx] = Num2Bits(248);
|
||||||
outAmountCheck[tx].in <== outAmount[tx];
|
outAmountCheck[tx].in <== outAmount[tx];
|
||||||
|
|
||||||
|
sumOuts += outAmount[tx];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that fee fits into 248 bits to prevent overflow
|
// Check that fee fits into 248 bits to prevent overflow
|
||||||
@ -106,7 +113,7 @@ template Transaction(levels, zeroLeaf) {
|
|||||||
sameNullifiers.out === 0;
|
sameNullifiers.out === 0;
|
||||||
|
|
||||||
// verify amount invariant
|
// verify amount invariant
|
||||||
inAmount[0] + inAmount[1] + extAmount === outAmount[0] + outAmount[1] + fee;
|
sumIns + extAmount === sumOuts + fee;
|
||||||
|
|
||||||
// Check merkle tree update with inserted transaction outputs
|
// Check merkle tree update with inserted transaction outputs
|
||||||
component treeUpdater = TreeUpdater(levels, zeroLeaf);
|
component treeUpdater = TreeUpdater(levels, zeroLeaf);
|
||||||
@ -119,7 +126,3 @@ template Transaction(levels, zeroLeaf) {
|
|||||||
treeUpdater.pathElements[i] <== outPathElements[i];
|
treeUpdater.pathElements[i] <== outPathElements[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// zeroLeaf = Poseidon(zero, zero)
|
|
||||||
// default `zero` value is keccak256("tornado") % FIELD_SIZE = 21663839004416932945382355908790599225266501822907911457504978515578255421292
|
|
||||||
component main = Transaction(5, 11850551329423159860688778991827824730037759162201783566284850822760196767874);
|
|
||||||
|
5
circuits/transaction16.circom
Normal file
5
circuits/transaction16.circom
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include "./transaction.circom"
|
||||||
|
|
||||||
|
// zeroLeaf = Poseidon(zero, zero)
|
||||||
|
// default `zero` value is keccak256("tornado") % FIELD_SIZE = 21663839004416932945382355908790599225266501822907911457504978515578255421292
|
||||||
|
component main = Transaction(5, 16, 2, 11850551329423159860688778991827824730037759162201783566284850822760196767874);
|
5
circuits/transaction2.circom
Normal file
5
circuits/transaction2.circom
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include "./transaction.circom"
|
||||||
|
|
||||||
|
// zeroLeaf = Poseidon(zero, zero)
|
||||||
|
// default `zero` value is keccak256("tornado") % FIELD_SIZE = 21663839004416932945382355908790599225266501822907911457504978515578255421292
|
||||||
|
component main = Transaction(5, 2, 2, 11850551329423159860688778991827824730037759162201783566284850822760196767874);
|
@ -7,7 +7,7 @@
|
|||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"circuit": "./scripts/buildCircuit.sh transaction",
|
"circuit": "./scripts/buildCircuit.sh transaction2 && ./scripts/buildCircuit.sh transaction16",
|
||||||
"compile": "npx hardhat compile",
|
"compile": "npx hardhat compile",
|
||||||
"build": "npm run circuit && npm run compile",
|
"build": "npm run circuit && npm run compile",
|
||||||
"migrate": "npx hardhat run scripts/deploy.js --network localhost",
|
"migrate": "npx hardhat run scripts/deploy.js --network localhost",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
POWERS_OF_TAU=14 # circuit will support max 2^POWERS_OF_TAU constraints
|
POWERS_OF_TAU=15 # circuit will support max 2^POWERS_OF_TAU constraints
|
||||||
mkdir -p artifacts/circuits
|
mkdir -p artifacts/circuits
|
||||||
if [ ! -f artifacts/circuits/ptau$POWERS_OF_TAU ]; then
|
if [ ! -f artifacts/circuits/ptau$POWERS_OF_TAU ]; then
|
||||||
echo "Generating powers of tau file"
|
echo "Generating powers of tau file"
|
||||||
|
11
src/index.js
11
src/index.js
@ -21,9 +21,6 @@ async function buildMerkleTree({ tornadoPool }) {
|
|||||||
|
|
||||||
async function getProof({ inputs, outputs, tree, extAmount, fee, recipient, relayer }) {
|
async function getProof({ inputs, outputs, tree, extAmount, fee, recipient, relayer }) {
|
||||||
// todo shuffle inputs and outputs
|
// todo shuffle inputs and outputs
|
||||||
if (inputs.length !== 2 || outputs.length !== 2) {
|
|
||||||
throw new Error('Unsupported number of inputs/outputs')
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputMerklePathIndices = []
|
let inputMerklePathIndices = []
|
||||||
let inputMerklePathElements = []
|
let inputMerklePathElements = []
|
||||||
@ -48,7 +45,7 @@ async function getProof({ inputs, outputs, tree, extAmount, fee, recipient, rela
|
|||||||
tree.insert(output.getCommitment())
|
tree.insert(output.getCommitment())
|
||||||
}
|
}
|
||||||
const outputIndex = tree.elements().length - 1
|
const outputIndex = tree.elements().length - 1
|
||||||
const outputPath = tree.path(outputIndex).pathElements.slice(1)
|
const outputPath = tree.path(outputIndex).pathElements
|
||||||
|
|
||||||
const extData = {
|
const extData = {
|
||||||
recipient: toFixedHex(recipient, 20),
|
recipient: toFixedHex(recipient, 20),
|
||||||
@ -78,14 +75,14 @@ async function getProof({ inputs, outputs, tree, extAmount, fee, recipient, rela
|
|||||||
outAmount: outputs.map((x) => x.amount),
|
outAmount: outputs.map((x) => x.amount),
|
||||||
outBlinding: outputs.map((x) => x.blinding),
|
outBlinding: outputs.map((x) => x.blinding),
|
||||||
outPubkey: outputs.map((x) => x.pubkey),
|
outPubkey: outputs.map((x) => x.pubkey),
|
||||||
outPathIndices: outputIndex >> 1,
|
outPathIndices: outputIndex >> Math.log2(outputs.length),
|
||||||
outPathElements: outputPath,
|
outPathElements: outputPath.slice(Math.log2(outputs.length)),
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('SNARK input', input)
|
//console.log('SNARK input', input)
|
||||||
|
|
||||||
console.log('Generating SNARK proof...')
|
console.log('Generating SNARK proof...')
|
||||||
const proof = await prove(input, './artifacts/circuits/transaction')
|
const proof = await prove(input, `./artifacts/circuits/transaction${inputs.length}`)
|
||||||
|
|
||||||
const args = [
|
const args = [
|
||||||
toFixedHex(input.root),
|
toFixedHex(input.root),
|
||||||
|
Loading…
Reference in New Issue
Block a user