different input counts

This commit is contained in:
poma 2021-06-09 13:58:57 +03:00
parent cd18bef60b
commit cb2a587540
No known key found for this signature in database
GPG Key ID: BA20CB01FE165657
6 changed files with 47 additions and 37 deletions

View File

@ -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);

View 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);

View 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);

View File

@ -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",

View File

@ -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"

View File

@ -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),