diff --git a/circuit/binary_compare.circom b/circuit/binary_compare.circom index b88b9be..472f0eb 100644 --- a/circuit/binary_compare.circom +++ b/circuit/binary_compare.circom @@ -3,8 +3,9 @@ pragma circom 2.0.0; //------------------------------------------------------------------------------ // -// given two numbers in `n`-bit binary decomposition (little-endian), we compute -// +// given two numbers in `n`-bit binary decomposition +// (least significant bit first), we compute +// // / -1 if A < B // out := { 0 if A == B // \ +1 if A > B @@ -12,7 +13,7 @@ pragma circom 2.0.0; // NOTE: we don't check that the digits are indeed binary; // that's the responsibility of the caller! // -// This version uses `(3*n-1)` nonlinear constraints. +// This version uses `(3*n-1)` nonlinear constraints. // Question: can we do better? (note that this has to work with n >= 254 digits too!) // @@ -42,11 +43,11 @@ template BinaryCompare(n) { //------------------------------------------------------------------------------ // -// given two numbers in `n`-bit binary decomposition (little-endian), we compute +// given two numbers in `n`-bit binary decomposition (little-endian), we compute // // out := (A <= B) ? 1 : 0 // -// NOTE: we don't check that the digits are indeed binary; +// NOTE: we don't check that the digits are indeed binary; // that's the responsibility of the caller! // @@ -69,11 +70,11 @@ template BinaryLessOrEqual(n) { //------------------------------------------------------------------------------ // -// given two numbers in `n`-bit binary decomposition (little-endian), we compute +// given two numbers in `n`-bit binary decomposition (little-endian), we compute // // out := (A >= B) ? 1 : 0 // -// NOTE: we don't check that the digits are indeed binary; +// NOTE: we don't check that the digits are indeed binary; // that's the responsibility of the caller! // diff --git a/circuit/extract_bits.circom b/circuit/extract_bits.circom index 6b28e6e..248ff35 100644 --- a/circuit/extract_bits.circom +++ b/circuit/extract_bits.circom @@ -5,12 +5,12 @@ include "misc.circom"; //------------------------------------------------------------------------------ -// +// // extract the lowest `n` bits from a field element. // -// NOTE: this is rather nontrivial, as everything is computed modulo `r`, +// NOTE: this is rather nontrivial, as everything is computed modulo `r`, // so naive bit decomposition does not work (there are multiple solutions). -// +// // TODO: optimize this // @@ -23,7 +23,7 @@ template ExtractLowerBits(n) { component tb = ToBits(254); // note: 2^253 < r < 2^254 tb.inp <== inp; - // bits of field prime `r` in little-endian order + // bits of field prime `r`, least significant bit first var primeBits[254] = [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,1,0,1,0,0,1,1,1,0,1,1,0,0,1,1,1,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,1,1,1,0,1,0,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,1,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,0,1,0,1,1,0,0,0,1,1,0,0,1,0,0,0,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,1,1]; // enforce that the binary representation is < r @@ -33,7 +33,7 @@ template ExtractLowerBits(n) { le.out === -1; // enforce `A < B`, that is, `bits < prime` // extract the lowest `n` bits - for(var i=0; i out[i]; } @@ -56,7 +56,7 @@ template ExtractLowerBits_testfield65537(n) { component tb = ToBits(18); // note: 2^16 < r < 2^18 tb.inp <== inp; - // bits of field prime `r` in little-endian order + // bits of field prime `r`, least significant bit first var primeBits[18] = [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0]; // enforce that the binary representation is < r @@ -66,7 +66,7 @@ template ExtractLowerBits_testfield65537(n) { le.out === -1; // enforce `A < B`, that is, `bits < prime` // extract the lowest `n` bits - for(var i=0; i out[i]; } diff --git a/circuit/log2.circom b/circuit/log2.circom index a0a6150..bb5d626 100644 --- a/circuit/log2.circom +++ b/circuit/log2.circom @@ -3,7 +3,7 @@ pragma circom 2.0.0; include "misc.circom"; //------------------------------------------------------------------------------ -// +// // given an input `inp`, this template checks that inp == 2^out // with 0 < out <= n // @@ -13,14 +13,14 @@ include "misc.circom"; template Log2(n) { signal input inp; - signal output out; + signal output out; signal output mask[n+1]; // mask will be a vector [1,1,1,...1,0,...,0,0] - // which can change only where inp == 2^out + // which can change only where index == out var log2 = -1; - for(var i=0; i<=n; i++) { + for(var i=0; i<=n; i++) { mask[i] <-- ((2**i) < inp) ? 1 : 0; if (2**i == inp) { log2 = i; } } @@ -31,19 +31,19 @@ template Log2(n) { mask[n] === 0; var sum = 0; - for(var i=0; i bits; signal aux[n+1]; diff --git a/circuit/merkle.circom b/circuit/merkle.circom index beab67b..0396436 100644 --- a/circuit/merkle.circom +++ b/circuit/merkle.circom @@ -1,7 +1,6 @@ pragma circom 2.0.0; include "poseidon2_perm.circom"; -include "poseidon2_hash.circom"; include "misc.circom"; @@ -15,7 +14,7 @@ include "misc.circom"; // // inputs and outputs: // - leaf: the leaf hash -// - pathBits: the linear index of the leaf, in binary decomposition (little-endian) +// - pathBits: the linear index of the leaf, in binary decomposition (least significant bit first) // - lastBits: the index of the last leaf (= nLeaves-1), in binary decomposition // - maskBits: the bits of the the mask `2^ceilingLog2(size) - 1` // - merklePath: the Merkle inclusion proof (required hashes, starting from the leaf and ending near the root) @@ -26,7 +25,7 @@ include "misc.circom"; // template RootFromMerklePath( maxDepth ) { - + signal input leaf; signal input pathBits[ maxDepth ]; // bits of the linear index signal input lastBits[ maxDepth ]; // bits of the last linear index `= size-1` @@ -38,14 +37,17 @@ template RootFromMerklePath( maxDepth ) { signal aux[ maxDepth+1 ]; aux[0] <== leaf; - // compute which prefixes (in big-endian) of the index is - // the same as the corresponding prefix of the last index + // Determine whether nodes from the path are last in their row and are odd, + // by computing which binary prefixes of the index are the same as the + // corresponding prefix of the last index. + // This is done in reverse bit order, because pathBits and lastBits have the + // least significant bit first. component eq[ maxDepth ]; signal isLast[ maxDepth+1 ]; isLast[ maxDepth ] <== 1; for(var i=maxDepth-1; i>=0; i--) { eq[i] = IsEqual(); - eq[i].A <== pathBits[i]; + eq[i].A <== pathBits[i]; eq[i].B <== lastBits[i]; isLast[i] <== isLast[i+1] * eq[i].out; } @@ -64,7 +66,7 @@ template RootFromMerklePath( maxDepth ) { var R = merklePath[i]; // based on pathBits[i], we switch or not - + switch[i] <== (R-L) * pathBits[i]; comp[i].key <== bottom + 2*odd; comp[i].inp[0] <== L + switch[i]; diff --git a/circuit/misc.circom b/circuit/misc.circom index c120261..d249553 100644 --- a/circuit/misc.circom +++ b/circuit/misc.circom @@ -1,7 +1,6 @@ pragma circom 2.0.0; //------------------------------------------------------------------------------ -// compute (compile time) the log2 of a number function FloorLog2(n) { return (n==0) ? -1 : (1 + FloorLog2(n>>1)); @@ -12,7 +11,7 @@ function CeilLog2(n) { } //------------------------------------------------------------------------------ -// decompose an n-bit number into bits +// decompose an n-bit number into bits (least significant bit first) template ToBits(n) { signal input inp; @@ -44,11 +43,11 @@ template IsZero() { out <== 1 - inp * inv; // enfore that either `inp` or `out` must be zero - inp*out === 0; + inp*out === 0; } //------------------------------------------------------------------------------ -// check equality of two field elements; that is, computes `(A==B) ? 1 : 0` +// check equality of two field elements; that is, computes `(A==B) ? 1 : 0` template IsEqual() { signal input A,B; diff --git a/circuit/poseidon2_hash.circom b/circuit/poseidon2_hash.circom index 7e58725..ba20f91 100644 --- a/circuit/poseidon2_hash.circom +++ b/circuit/poseidon2_hash.circom @@ -2,20 +2,6 @@ pragma circom 2.0.0; include "poseidon2_sponge.circom"; -//------------------------------------------------------------------------------ -// Hash `n` field elements into 1, with approximately 127-254 bits of preimage security -// Note that the output size must be at least twice than the desired security level (?) -// (assuming bn128 scalar field. We use capacity=2, rate=1, t=3). - -template Poseidon2_hash_rate1(n) { - signal input inp[n]; - signal output out; - - component sponge = PoseidonSponge(3,2,n,1); - sponge.inp <== inp; - sponge.out[0] ==> out; -} - //------------------------------------------------------------------------------ // Hash `n` field elements into 1, with approximately 127 bits of preimage security // (assuming bn128 scalar field. We use capacity=1, rate=2, t=3). diff --git a/circuit/poseidon2_perm.circom b/circuit/poseidon2_perm.circom index c518378..ff789fa 100644 --- a/circuit/poseidon2_perm.circom +++ b/circuit/poseidon2_perm.circom @@ -1,7 +1,7 @@ pragma circom 2.0.0; // -// The Poseidon2 permutation for bn128 and t=3 +// The Poseidon2 permutation for bn128 and t=3 // //------------------------------------------------------------------------------ @@ -24,7 +24,7 @@ template InternalRound(i) { signal input inp[3]; signal output out[3]; - var round_consts[56] = + var round_consts[56] = [ 0x15ce7e5ae220e8623a40b3a3b22d441eff0c9be1ae1d32f1b777af84eea7e38c , 0x1bf60ac8bfff0f631983c93e218ca0d4a4059c254b4299b1d9984a07edccfaf0 , 0x0fab0c9387cb2bec9dc11b2951088b9e1e1d2978542fc131f74a8f8fdac95b40 @@ -99,11 +99,11 @@ template ExternalRound(i) { signal input inp[3]; signal output out[3]; - var round_consts[8][3] = + var round_consts[8][3] = [ [ 0x2c4c51fd1bb9567c27e99f5712b49e0574178b41b6f0a476cddc41d242cf2b43 , 0x1c5f8d18acb9c61ec6fcbfcda5356f1b3fdee7dc22c99a5b73a2750e5b054104 - , 0x2d3c1988b4541e4c045595b8d574e98a7c2820314a82e67a4e380f1c4541ba90 + , 0x2d3c1988b4541e4c045595b8d574e98a7c2820314a82e67a4e380f1c4541ba90 ] , [ 0x052547dc9e6d936cab6680372f1734c39f490d0cb970e2077c82f7e4172943d3 , 0x29d967f4002adcbb5a6037d644d36db91f591b088f69d9b4257694f5f9456bc2 @@ -120,19 +120,19 @@ template ExternalRound(i) { , [ 0x25672a14b5d085e31a30a7e1d5675ebfab034fb04dc2ec5e544887523f98dede , 0x0cf702434b891e1b2f1d71883506d68cdb1be36fa125674a3019647b3a98accd , 0x1837e75235ff5d112a5eddf7a4939448748339e7b5f2de683cf0c0ae98bdfbb3 - ] + ] , [ 0x1cd8a14cff3a61f04197a083c6485581a7d836941f6832704837a24b2d15613a , 0x266f6d85be0cef2ece525ba6a54b647ff789785069882772e6cac8131eecc1e4 , 0x0538fde2183c3f5833ecd9e07edf30fe977d28dd6f246d7960889d9928b506b3 - ] + ] , [ 0x07a0693ff41476abb4664f3442596aa8399fdccf245d65882fce9a37c268aa04 , 0x11eb49b07d33de2bd60ea68e7f652beda15644ed7855ee5a45763b576d216e8e , 0x08f8887da6ce51a8c06041f64e22697895f34bacb8c0a39ec12bf597f7c67cfc - ] + ] , [ 0x2a912ec610191eb7662f86a52cc64c0122bd5ba762e1db8da79b5949fdd38092 , 0x2031d7fd91b80857aa1fef64e23cfad9a9ba8fe8c8d09de92b1edb592a44c290 , 0x0f81ebce43c47711751fa64d6c007221016d485641c28c507d04fd3dc7fba1d2 - ] + ] ]; component sb[3]; @@ -154,7 +154,7 @@ template LinearLayer() { signal output out[3]; out[0] <== 2*inp[0] + inp[1] + inp[2]; out[1] <== inp[0] + 2*inp[1] + inp[2]; - out[2] <== inp[0] + inp[1] + 2*inp[2]; + out[2] <== inp[0] + inp[1] + 2*inp[2]; } //------------------------------------------------------------------------------ @@ -172,7 +172,7 @@ template Permutation() { component ext[8]; for(var k=0; k<8; k++) { ext[k] = ExternalRound(k); } - + component int[56]; for(var k=0; k<56; k++) { int[k] = InternalRound(k); } @@ -201,20 +201,6 @@ template Permutation() { // the "compression function" takes 2 field elements as input and produces // 1 field element as output. It is a trivial application of the permutation. -template Compression() { - signal input inp[2]; - signal output out; - - component perm = Permutation(); - perm.inp[0] <== inp[0]; - perm.inp[1] <== inp[1]; - perm.inp[2] <== 0; - - perm.out[0] ==> out; -} - -//-------------------------------------- - template KeyedCompression() { signal input key; signal input inp[2]; diff --git a/circuit/poseidon2_sponge.circom b/circuit/poseidon2_sponge.circom index d7698bb..981bfc6 100644 --- a/circuit/poseidon2_sponge.circom +++ b/circuit/poseidon2_sponge.circom @@ -17,10 +17,10 @@ function min(a,b) { // c = capacity (1 or 2) // r = rate = t - c // -// everything is measured in number of field elements +// everything is measured in number of field elements // // we use the padding `10*` from the original Poseidon paper, -// and initial state constant zero. Note that this is different +// and initial state constant zero. Note that this is different // from the "SAFE padding" recommended in the Poseidon2 paper // (which uses `0*` padding and a nontrivial initial state) // @@ -47,7 +47,7 @@ template PoseidonSponge(t, capacity, input_len, output_len) { signal padded[padded_len]; for(var i=0; i