mirror of
https://github.com/logos-blockchain/logos-blockchain-pocs.git
synced 2026-01-02 05:03:09 +00:00
Clean the repo and push the new PoL
This commit is contained in:
parent
5c7ff0fad5
commit
6f94e8da43
19
circom_circuits/hash_bn/poseidon2_hash.circom
Normal file
19
circom_circuits/hash_bn/poseidon2_hash.circom
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "poseidon2_sponge.circom";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Hash `n` field elements into 1, with approximately 254 bits of preimage security (?)
|
||||
// (assuming bn128 scalar field. We use capacity=2, rate=1, t=3).
|
||||
|
||||
template Poseidon2_hash(n) {
|
||||
signal input inp[n];
|
||||
signal output out;
|
||||
|
||||
component sponge = PoseidonSponge(3,2,n,1);
|
||||
sponge.inp <== inp;
|
||||
sponge.out[0] ==> out;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
218
circom_circuits/hash_bn/poseidon2_perm.circom
Normal file
218
circom_circuits/hash_bn/poseidon2_perm.circom
Normal file
@ -0,0 +1,218 @@
|
||||
//
|
||||
pragma circom 2.0.0;
|
||||
|
||||
//
|
||||
// The Poseidon2 permutation for bn128 and t=3
|
||||
//
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// The S-box
|
||||
|
||||
template SBox() {
|
||||
signal input inp;
|
||||
signal output out;
|
||||
|
||||
signal x2 <== inp*inp;
|
||||
signal x4 <== x2*x2;
|
||||
|
||||
out <== inp*x4;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// partial or internal round
|
||||
|
||||
template InternalRound(i) {
|
||||
signal input inp[3];
|
||||
signal output out[3];
|
||||
|
||||
var round_consts[56] =
|
||||
[ 0x15ce7e5ae220e8623a40b3a3b22d441eff0c9be1ae1d32f1b777af84eea7e38c
|
||||
, 0x1bf60ac8bfff0f631983c93e218ca0d4a4059c254b4299b1d9984a07edccfaf0
|
||||
, 0x0fab0c9387cb2bec9dc11b2951088b9e1e1d2978542fc131f74a8f8fdac95b40
|
||||
, 0x07d085a48750738019784663bccd460656dc62c1b18964a0d27a5bd0c27ee453
|
||||
, 0x10d57b1fad99da9d3fe16cf7f5dae05be844f67b2e7db3472a2e96e167578bc4
|
||||
, 0x0c36c40f7bd1934b7d5525031467aa39aeaea461996a70eda5a2a704e1733bb0
|
||||
, 0x0e4b65a0f3e1f9d3166a2145063c999bd08a4679676d765f4d11f97ed5c080ae
|
||||
, 0x1ce5561061120d5c7ea09da2528c4c041b9ad0f05d655f38b10d79878b69f29d
|
||||
, 0x2d323f651c3da8f0e0754391a10fa111b25dfa00471edf5493c44dfc3f28add6
|
||||
, 0x05a0741ee5bdc3e099fd6bdad9a0865bc9ceecd13ea4e702e536dd370b8f1953
|
||||
, 0x176a2ec4746fc0e0eca9e5e11d6facaee05524a92e5785c8b8161780a4435136
|
||||
, 0x0691faf0f42a9ed97629b1ae0dc7f1b019c06dd852cb6efe57f7eeb1aa865aef
|
||||
, 0x0e46cf138dad09d61b9a7cab95a23b5c8cb276874f3715598bacb55d5ad271de
|
||||
, 0x0f18c3d95bac1ac424160d240cdffc2c44f7b6315ba65ed3ff2eff5b3e48b4f2
|
||||
, 0x2eea6af14b592ec45a4119ac1e6e6f0312ecd090a096e340d472283e543ddff7
|
||||
, 0x06b0d7a8f4ce97d049ae994139f5f71dca4899d4f1cd3dd83a32a89a58c0a8e6
|
||||
, 0x019df0b9828eed5892dd55c1ad6408196f6293d600ef4491703a1b37e119ba8e
|
||||
, 0x08ca5e3c93817cdb1c2b2a12d02c779d74c1bb12b6668f3ab3ddd7837f3a4a00
|
||||
, 0x28382d747e3fd6cb2e0d8e8edd79c5313eed307a3517c11046245b1476e4f701
|
||||
, 0x0ca89aecd5675b77c8271765da98cfcb6875b3053d4742c9ff502861bd16ad28
|
||||
, 0x19046bc0b03ca90802ec83f212001e7ffd7f9224cfffae523451deb52eab3787
|
||||
, 0x036fd7dfa1c05110b3428e6abcc43e1de9abba915320c4a600f843bfb676ca51
|
||||
, 0x08f0a7abcb1a2f6595a9b7380c5028e3999db4fe5cb21892e5bb5cb11a7757ba
|
||||
, 0x0b614acc1ce3fbe9048f8385e4ee24c3843deea186bacea3c904c9f6340ad8cb
|
||||
, 0x00b2d98c5d988f9b41f2c98e017fc954a6ae423b2261575941f8eac8835d985c
|
||||
, 0x1457f18555b7973ba5b311d57ec5d77e936980b97f5973875f1f7cc765a4fc95
|
||||
, 0x002b453debc1bee525cb751bc10641a6b86f847d696418cf1144950982591bfa
|
||||
, 0x0c2af1abcc6ece77218315d2af445ccbfc6647b7af2510682882cc792c6bb8cf
|
||||
, 0x0e2825d9eb84b59902a1adb49ac0c2c291dee7c45d2e8c30369a4d595039e8ad
|
||||
, 0x297e2e86a8c672d39f3343b8dfce7a6f20f3571bfd5c8a28e3905aa2dcfeca44
|
||||
, 0x00d397281d902e49ec6504ba9186e806db9ad4fc8f86e7277aa7f1467eb6f9de
|
||||
, 0x2fb7c89c372d7e2050e7377ed471000c73544a2b9fd66557f3577c09cac98b4b
|
||||
, 0x16125247be4387a8c3e62490167f0cffdba02eda4f018d0b40639a13bb0cfef9
|
||||
, 0x2291fd9d442f2d9b97ab22f7d4d52c2a82e41f852cf620b144612650a39e26e8
|
||||
, 0x1eec61f16a275ae238540feaeeadfec56d32171b1cc393729d06f37f476fde71
|
||||
, 0x259ce871ba5dacbb48d8aed3d8513eef51558dc0b360f28c1a15dbfc5e7f6ca2
|
||||
, 0x2d3376a14ddbf95587e2f7567ff04fe13a3c7cb17363c8b9c5dd1d9262a210cb
|
||||
, 0x13b843d9f65f4cddd7ce10d9cad9b8b99ac5e9a8c4269288173a91c0f3c3b084
|
||||
, 0x0b52e9b2f1aa9fd204e4a42c481cc76c704783e34114b8e93e026a50fa9764e8
|
||||
, 0x1fd083229276c7f27d3ad941476b394ff37bd44d3a1e9caca1400d9077a2056c
|
||||
, 0x22743c328a6283f3ba7379af22c684c498568fd7ad9fad5151368c913197cbd9
|
||||
, 0x043007aefd9741070d95caaaba0c1b070e4eec8eef8c1e512c8e579c6ed64f76
|
||||
, 0x17ab175144f64bc843074f6b3a0c57c5dd2c954af8723c029ee642539496a7b3
|
||||
, 0x2befcad3d53fba5eeef8cae9668fed5c1e9e596a46e8458e218f7a665fddf4eb
|
||||
, 0x15151c4116d97de74bfa6ca3178f73c8fe8fe612c70c6f85a7a1551942cb71cc
|
||||
, 0x2ac40bf6c3176300a6835d5fc7cc4fd5e5d299fb1baa86487268ec1b9eedfa97
|
||||
, 0x0f151de1f01b4e24ffe04279318f0a68efabb485188f191e37e6915ff6059f6e
|
||||
, 0x2e43dffc34537535182aebac1ad7bf0a5533b88f65f9652f0ad584e2ffc4dd1f
|
||||
, 0x2ebabc2c37ef53d8b13b24a2a2b729d536735f58956125a3876da0664c2442d7
|
||||
, 0x0dc3beceb34e49f5ad7226dd202c5cf879dffcc9a6dd32a300e8f2a4b59edf03
|
||||
, 0x2f1ddeccce83adf68779c53b639871a8f81d4d00aefe1e812efce8ec999d457d
|
||||
, 0x1f63e41280ff5c021715d52b19780298ed8bd3d5eb506316b527e24149d4d4f1
|
||||
, 0x1b8c1252a5888f8cb2672effb5df49c633d3fd7183271488a1c40d0f88e7636e
|
||||
, 0x0f45697130f5498e2940568ef0d5e9e16b1095a6cdbb6411df20a973c605e70b
|
||||
, 0x0780ccc403cdd68983acbd34cda41cacfb2cf911a93076bc25587b4b0aed4929
|
||||
, 0x238d26ca97c691591e929f32199a643550f325f23a85d420080b289d7cecc9d4
|
||||
];
|
||||
|
||||
component sb = SBox();
|
||||
sb.inp <== inp[0] + round_consts[i];
|
||||
|
||||
out[0] <== 2*sb.out + inp[1] + inp[2];
|
||||
out[1] <== sb.out + 2*inp[1] + inp[2];
|
||||
out[2] <== sb.out + inp[1] + 3*inp[2];
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// external rounds
|
||||
|
||||
template ExternalRound(i) {
|
||||
signal input inp[3];
|
||||
signal output out[3];
|
||||
|
||||
var round_consts[8][3] =
|
||||
|
||||
[ [ 0x2c4c51fd1bb9567c27e99f5712b49e0574178b41b6f0a476cddc41d242cf2b43
|
||||
, 0x1c5f8d18acb9c61ec6fcbfcda5356f1b3fdee7dc22c99a5b73a2750e5b054104
|
||||
, 0x2d3c1988b4541e4c045595b8d574e98a7c2820314a82e67a4e380f1c4541ba90
|
||||
]
|
||||
, [ 0x052547dc9e6d936cab6680372f1734c39f490d0cb970e2077c82f7e4172943d3
|
||||
, 0x29d967f4002adcbb5a6037d644d36db91f591b088f69d9b4257694f5f9456bc2
|
||||
, 0x0350084b8305b91c426c25aeeecafc83fc5feec44b9636cb3b17d2121ec5b88a
|
||||
]
|
||||
, [ 0x1815d1e52a8196127530cc1e79f07a0ccd815fb5d94d070631f89f6c724d4cbe
|
||||
, 0x17b5ba882530af5d70466e2b434b0ccb15b7a8c0138d64455281e7724a066272
|
||||
, 0x1c859b60226b443767b73cd1b08823620de310bc49ea48662626014cea449aee
|
||||
]
|
||||
, [ 0x1b26e7f0ac7dd8b64c2f7a1904c958bb48d2635478a90d926f5ff2364effab37
|
||||
, 0x2da7f36850e6c377bdcdd380efd9e7c419555d3062b0997952dfbe5c54b1a22e
|
||||
, 0x17803c56450e74bc6c7ff97275390c017f682db11f3f4ca6e1f714efdfb9bd66
|
||||
]
|
||||
, [ 0x25672a14b5d085e31a30a7e1d5675ebfab034fb04dc2ec5e544887523f98dede
|
||||
, 0x0cf702434b891e1b2f1d71883506d68cdb1be36fa125674a3019647b3a98accd
|
||||
, 0x1837e75235ff5d112a5eddf7a4939448748339e7b5f2de683cf0c0ae98bdfbb3
|
||||
]
|
||||
, [ 0x1cd8a14cff3a61f04197a083c6485581a7d836941f6832704837a24b2d15613a
|
||||
, 0x266f6d85be0cef2ece525ba6a54b647ff789785069882772e6cac8131eecc1e4
|
||||
, 0x0538fde2183c3f5833ecd9e07edf30fe977d28dd6f246d7960889d9928b506b3
|
||||
]
|
||||
, [ 0x07a0693ff41476abb4664f3442596aa8399fdccf245d65882fce9a37c268aa04
|
||||
, 0x11eb49b07d33de2bd60ea68e7f652beda15644ed7855ee5a45763b576d216e8e
|
||||
, 0x08f8887da6ce51a8c06041f64e22697895f34bacb8c0a39ec12bf597f7c67cfc
|
||||
]
|
||||
, [ 0x2a912ec610191eb7662f86a52cc64c0122bd5ba762e1db8da79b5949fdd38092
|
||||
, 0x2031d7fd91b80857aa1fef64e23cfad9a9ba8fe8c8d09de92b1edb592a44c290
|
||||
, 0x0f81ebce43c47711751fa64d6c007221016d485641c28c507d04fd3dc7fba1d2
|
||||
]
|
||||
];
|
||||
|
||||
component sb[3];
|
||||
for(var j=0; j<3; j++) {
|
||||
sb[j] = SBox();
|
||||
sb[j].inp <== inp[j] + round_consts[i][j];
|
||||
}
|
||||
|
||||
out[0] <== 2*sb[0].out + sb[1].out + sb[2].out;
|
||||
out[1] <== sb[0].out + 2*sb[1].out + sb[2].out;
|
||||
out[2] <== sb[0].out + sb[1].out + 2*sb[2].out;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// the initial linear layer
|
||||
|
||||
template LinearLayer() {
|
||||
signal input inp[3];
|
||||
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];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// the Poseidon2 permutation for t=3
|
||||
|
||||
template Permutation() {
|
||||
signal input inp[3];
|
||||
signal output out[3];
|
||||
|
||||
signal aux[65][3];
|
||||
|
||||
component ll = LinearLayer();
|
||||
for(var j=0; j<3; j++) { ll.inp[j] <== inp[j]; }
|
||||
for(var j=0; j<3; j++) { ll.out[j] ==> aux[0][j]; }
|
||||
|
||||
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); }
|
||||
|
||||
// first 4 external rounds
|
||||
for(var k=0; k<4; k++) {
|
||||
for(var j=0; j<3; j++) { ext[k].inp[j] <== aux[k ][j]; }
|
||||
for(var j=0; j<3; j++) { ext[k].out[j] ==> aux[k+1][j]; }
|
||||
}
|
||||
|
||||
// the 56 internal rounds
|
||||
for(var k=0; k<56; k++) {
|
||||
for(var j=0; j<3; j++) { int[k].inp[j] <== aux[k+4][j]; }
|
||||
for(var j=0; j<3; j++) { int[k].out[j] ==> aux[k+5][j]; }
|
||||
}
|
||||
|
||||
// last 4 external rounds
|
||||
for(var k=0; k<4; k++) {
|
||||
for(var j=0; j<3; j++) { ext[k+4].inp[j] <== aux[k+60][j]; }
|
||||
for(var j=0; j<3; j++) { ext[k+4].out[j] ==> aux[k+61][j]; }
|
||||
}
|
||||
|
||||
for(var j=0; j<3; j++) { out[j] <== aux[64][j]; }
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
127
circom_circuits/hash_bn/poseidon2_sponge.circom
Normal file
127
circom_circuits/hash_bn/poseidon2_sponge.circom
Normal file
@ -0,0 +1,127 @@
|
||||
//
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "poseidon2_perm.circom";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function min(a,b) {
|
||||
return (a <= b) ? a : b;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Poseidon sponge construction
|
||||
//
|
||||
// t = size of state (currently fixed to 3)
|
||||
// c = capacity (1 or 2)
|
||||
// r = rate = t - c
|
||||
//
|
||||
// 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
|
||||
// from the "SAFE padding" recommended in the Poseidon2 paper
|
||||
// (which uses `0*` padding and a nontrivial initial state)
|
||||
//
|
||||
|
||||
template PoseidonSponge(t, capacity, input_len, output_len) {
|
||||
|
||||
var rate = t - capacity;
|
||||
|
||||
assert( t == 3);
|
||||
|
||||
assert( capacity > 0 );
|
||||
assert( rate > 0 );
|
||||
assert( capacity < t );
|
||||
assert( rate < t );
|
||||
|
||||
signal input inp[ input_len];
|
||||
signal output out[output_len];
|
||||
|
||||
// round up to rate the input + 1 field element ("10*" padding)
|
||||
var nblocks = ((input_len + 1) + (rate-1)) \ rate;
|
||||
var nout = (output_len + (rate-1)) \ rate;
|
||||
var padded_len = nblocks * rate;
|
||||
|
||||
signal padded[padded_len];
|
||||
for(var i=0; i<input_len; i++) { padded[i] <== inp[i]; }
|
||||
padded[input_len ] <== 1;
|
||||
for(var i=input_len+1; i<padded_len; i++) { padded[i] <== 0; }
|
||||
|
||||
signal state [nblocks+nout][t ];
|
||||
signal sorbed[nblocks ][rate];
|
||||
|
||||
// domain separation, capacity IV:
|
||||
var civ = 2**64 + 256*t + rate;
|
||||
|
||||
// initialize state
|
||||
for(var i=0; i<t-1; i++) { state[0][i] <== 0; }
|
||||
state[0][t-1] <== civ;
|
||||
|
||||
component absorb [nblocks];
|
||||
component squeeze[nout-1];
|
||||
|
||||
for(var m=0; m<nblocks; m++) {
|
||||
|
||||
for(var i=0; i<rate; i++) {
|
||||
var a = state [m][i];
|
||||
var b = padded[m*rate+i];
|
||||
sorbed[m][i] <== a + b;
|
||||
}
|
||||
|
||||
absorb[m] = Permutation();
|
||||
for(var j=0 ; j<rate; j++) { absorb[m].inp[j] <== sorbed[m][j]; }
|
||||
for(var j=rate; j<t ; j++) { absorb[m].inp[j] <== state [m][j]; }
|
||||
absorb[m].out ==> state[m+1];
|
||||
|
||||
}
|
||||
|
||||
var q = min(rate, output_len);
|
||||
for(var i=0; i<q; i++) {
|
||||
state[nblocks][i] ==> out[i];
|
||||
}
|
||||
var out_ptr = rate;
|
||||
|
||||
for(var n=1; n<nout; n++) {
|
||||
squeeze[n-1] = Permutation();
|
||||
squeeze[n-1].inp <== state[nblocks+n-1];
|
||||
squeeze[n-1].out ==> state[nblocks+n ];
|
||||
|
||||
var q = min(rate, output_len-out_ptr);
|
||||
for(var i=0; i<q; i++) {
|
||||
state[nblocks+n][i] ==> out[out_ptr+i];
|
||||
}
|
||||
out_ptr += rate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// sponge hash with rate=1
|
||||
//
|
||||
|
||||
template Poseidon2_sponge_hash_rate_1(n) {
|
||||
signal input inp[n];
|
||||
signal output out;
|
||||
component sponge = PoseidonSponge(3, 2, n, 1);
|
||||
sponge.inp <== inp;
|
||||
sponge.out[0] ==> out;
|
||||
}
|
||||
|
||||
//
|
||||
// sponge hash with rate=2
|
||||
//
|
||||
|
||||
template Poseidon2_sponge_hash_rate_2(n) {
|
||||
signal input inp[n];
|
||||
signal output out;
|
||||
component sponge = PoseidonSponge(3, 1, n, 1);
|
||||
sponge.inp <== inp;
|
||||
sponge.out[0] ==> out;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
69
circom_circuits/ledger/merkle.circom
Normal file
69
circom_circuits/ledger/merkle.circom
Normal file
@ -0,0 +1,69 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "poseidon2_hash.circom";
|
||||
include "comparator.circom";
|
||||
|
||||
// proof of Merkle membership of depth n
|
||||
// /!\ To call this function, it's important to check that each selector is a bit before!!!
|
||||
template proof_of_membership(n) {
|
||||
signal input nodes[n]; // The path forming the Merkle proof
|
||||
signal input selector[n]; // it's the leaf's indice in big endian bits
|
||||
signal input root;
|
||||
signal input leaf;
|
||||
|
||||
|
||||
|
||||
component compression_hash[n];
|
||||
|
||||
compression_hash[0] = Poseidon2_hash(2);
|
||||
compression_hash[0].inp[0] <== leaf - selector[n-1] * (leaf - nodes[0]);
|
||||
compression_hash[0].inp[1] <== nodes[0] - selector[n-1] * (nodes[0] - leaf);
|
||||
|
||||
for(var i=1; i<n; i++){
|
||||
compression_hash[i] = Poseidon2_hash(2);
|
||||
compression_hash[i].inp[0] <== compression_hash[i-1].out - selector[n-1-i] * (compression_hash[i-1].out - nodes[i]);
|
||||
compression_hash[i].inp[1] <== nodes[i] - selector[n-1-i] * (nodes[i] - compression_hash[i-1].out);
|
||||
}
|
||||
|
||||
root === compression_hash[n-1].out;
|
||||
}
|
||||
|
||||
|
||||
// proof of Merkle non-membership using an IMT of depth n
|
||||
// /!\ To call this function, it's important to check that each selector is a bit before!!!
|
||||
template proof_of_non_membership(n) {
|
||||
signal input previous; // We prove that the nullifier isn't in the set because it falls between previous and next
|
||||
signal input nullifier;
|
||||
signal input next;
|
||||
signal input nodes[n];
|
||||
signal input selector[n];
|
||||
signal input root;
|
||||
|
||||
component hash = Poseidon2_hash(2);
|
||||
component comparator[2];
|
||||
component membership = proof_of_membership(n);
|
||||
|
||||
// Recover the leaf representing previous pointing to next in the IMT
|
||||
hash.inp[0] <== previous;
|
||||
hash.inp[1] <== next;
|
||||
|
||||
// Verify that the leaf computed is indeed in the IMT
|
||||
membership.root <== root;
|
||||
membership.leaf <== hash.out;
|
||||
for(var i =0; i < n; i++){
|
||||
membership.nodes[i] <== nodes[i];
|
||||
membership.selector[i] <== selector[i];
|
||||
}
|
||||
|
||||
// Check that nullifier stictly falls between previous and next.
|
||||
comparator[0] = SafeFullLessThan();
|
||||
comparator[0].a <== previous;
|
||||
comparator[0].b <== nullifier;
|
||||
comparator[0].out === 1;
|
||||
|
||||
comparator[1] = SafeFullLessThan();
|
||||
comparator[1].a <== nullifier;
|
||||
comparator[1].b <== next;
|
||||
comparator[1].out === 1;
|
||||
}
|
||||
51
circom_circuits/ledger/notes.circom
Normal file
51
circom_circuits/ledger/notes.circom
Normal file
@ -0,0 +1,51 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "poseidon2_hash.circom";
|
||||
include "merkle.circom";
|
||||
|
||||
// The unit of the note is supposed to be NMO
|
||||
template commitment(){
|
||||
signal input state;
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input nonce;
|
||||
signal input zoneID;
|
||||
signal input public_key;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(7);
|
||||
hash.inp[0] <== 78797779839578798469956777; //78797779839578798469956777 = NOMOS_NOTE_CM in ASCII
|
||||
hash.inp[1] <== state;
|
||||
hash.inp[2] <== value;
|
||||
hash.inp[3] <== unit;
|
||||
hash.inp[4] <== nonce;
|
||||
hash.inp[5] <== public_key;
|
||||
hash.inp[6] <== zoneID;
|
||||
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
template nullifier(){
|
||||
signal input commitment;
|
||||
signal input secret_key;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(3);
|
||||
hash.inp[0] <== 78797779839578798469957870; //78797779839578798469957870 = NOMOS_NOTE_NF in ASCII
|
||||
hash.inp[1] <== commitment;
|
||||
hash.inp[2] <== secret_key;
|
||||
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
template derive_public_key(){
|
||||
signal input secret_key;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(2);
|
||||
hash.inp[0] <== 787977798395756870; // 787977798395756870 = NOMOS_KDF in ASCII
|
||||
hash.inp[1] <== secret_key;
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
154
circom_circuits/misc/comparator.circom
Normal file
154
circom_circuits/misc/comparator.circom
Normal file
@ -0,0 +1,154 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../circomlib/circuits/bitify.circom";
|
||||
include "../circomlib/circuits/comparators.circom";
|
||||
|
||||
// If a or b isn't guaranteed to be less than p use SafeFullComparator
|
||||
template FullLessThan() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
component bitifier_a = Num2Bits(254);
|
||||
component bitifier_b = Num2Bits(254);
|
||||
|
||||
bitifier_a.in <== a;
|
||||
bitifier_b.in <== b;
|
||||
|
||||
component numifier_a = Bits2Num(252);
|
||||
component numifier_b = Bits2Num(252);
|
||||
|
||||
for(var i =0; i<252; i++){
|
||||
numifier_a.in[i] <== bitifier_a.out[i+2];
|
||||
numifier_b.in[i] <== bitifier_b.out[i+2];
|
||||
}
|
||||
|
||||
component A = LessThan(252);
|
||||
A.in[0] <== numifier_a.out;
|
||||
A.in[1] <== numifier_b.out;
|
||||
|
||||
component B = IsEqual();
|
||||
B.in[0] <== numifier_a.out;
|
||||
B.in[1] <== numifier_b.out;
|
||||
|
||||
component C = IsEqual();
|
||||
C.in[0] <== bitifier_a.out[1];
|
||||
C.in[1] <== bitifier_b.out[1];
|
||||
|
||||
component D = IsEqual();
|
||||
D.in[0] <== bitifier_a.out[1];
|
||||
D.in[1] <== 1;
|
||||
|
||||
component E = IsEqual();
|
||||
E.in[0] <== bitifier_a.out[0];
|
||||
E.in[1] <== bitifier_b.out[0];
|
||||
|
||||
component F = IsEqual();
|
||||
F.in[0] <== bitifier_a.out[0];
|
||||
F.in[1] <== 1;
|
||||
|
||||
signal intermediate_results[5];
|
||||
intermediate_results[0] <== (1 - A.out) * B.out;
|
||||
intermediate_results[1] <== C.out * (1-E.out);
|
||||
intermediate_results[2] <== intermediate_results[1] * F.out;
|
||||
intermediate_results[3] <== (1-C.out) * D.out;
|
||||
intermediate_results[4] <== A.out * (1-B.out);
|
||||
|
||||
out <== intermediate_results[0] * (intermediate_results[2] + intermediate_results[3]) + intermediate_results[4];
|
||||
|
||||
}
|
||||
|
||||
template SafeFullLessThan() {
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
|
||||
component bitifier_a = Num2Bits_strict();
|
||||
component bitifier_b = Num2Bits_strict();
|
||||
|
||||
bitifier_a.in <== a;
|
||||
bitifier_b.in <== b;
|
||||
|
||||
component numifier_a = Bits2Num(252);
|
||||
component numifier_b = Bits2Num(252);
|
||||
|
||||
for(var i =0; i<252; i++){
|
||||
numifier_a.in[i] <== bitifier_a.out[i+2];
|
||||
numifier_b.in[i] <== bitifier_b.out[i+2];
|
||||
}
|
||||
|
||||
component A = LessThan(252);
|
||||
A.in[0] <== numifier_a.out;
|
||||
A.in[1] <== numifier_b.out;
|
||||
|
||||
component B = IsEqual();
|
||||
B.in[0] <== numifier_a.out;
|
||||
B.in[1] <== numifier_b.out;
|
||||
|
||||
component C = IsEqual();
|
||||
C.in[0] <== bitifier_a.out[1];
|
||||
C.in[1] <== bitifier_b.out[1];
|
||||
|
||||
component D = IsEqual();
|
||||
D.in[0] <== bitifier_a.out[1];
|
||||
D.in[1] <== 1;
|
||||
|
||||
component E = IsEqual();
|
||||
E.in[0] <== bitifier_a.out[0];
|
||||
E.in[1] <== bitifier_b.out[0];
|
||||
|
||||
component F = IsEqual();
|
||||
F.in[0] <== bitifier_a.out[0];
|
||||
F.in[1] <== 1;
|
||||
|
||||
signal intermediate_results[5];
|
||||
intermediate_results[0] <== (1 - A.out) * B.out;
|
||||
intermediate_results[1] <== C.out * (1-E.out);
|
||||
intermediate_results[2] <== intermediate_results[1] * F.out;
|
||||
intermediate_results[3] <== (1-C.out) * D.out;
|
||||
intermediate_results[4] <== A.out * (1-B.out);
|
||||
|
||||
out <== intermediate_results[0] * (intermediate_results[2] + intermediate_results[3]) + intermediate_results[4];
|
||||
|
||||
}
|
||||
|
||||
// Safely compare two n-bit numbers
|
||||
// Performs range checks on the inputs to avoid overflow. Range is n <= 252
|
||||
template SafeLessThan(n) {
|
||||
assert(n <= 252);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component aInRange = Num2Bits(n);
|
||||
aInRange.in <== in[0];
|
||||
component bInRange = Num2Bits(n);
|
||||
bInRange.in <== in[1];
|
||||
|
||||
component lt = LessThan(n);
|
||||
|
||||
lt.in[0] <== in[0];
|
||||
lt.in[1] <== in[1];
|
||||
|
||||
out <== lt.out;
|
||||
}
|
||||
|
||||
// Safely compare two n-bit numbers
|
||||
// Performs range checks on the inputs to avoid overflow. Range is n <= 252
|
||||
template SafeLessEqThan(n) {
|
||||
assert(n <= 252);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component aInRange = Num2Bits(n);
|
||||
aInRange.in <== in[0];
|
||||
component bInRange = Num2Bits(n);
|
||||
bInRange.in <== in[1];
|
||||
|
||||
component lt = LessEqThan(n);
|
||||
|
||||
lt.in[0] <== in[0];
|
||||
lt.in[1] <== in[1];
|
||||
|
||||
out <== lt.out;
|
||||
}
|
||||
197
circom_circuits/proof_of_leadership/PoL.circom
Normal file
197
circom_circuits/proof_of_leadership/PoL.circom
Normal file
@ -0,0 +1,197 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../hash_bn/poseidon2_hash.circom";
|
||||
include "../ledger/notes.circom";
|
||||
include "../misc/comparator.circom";
|
||||
include "../circomlib/circuits/bitify.circom";
|
||||
|
||||
|
||||
template ticket_calculator(){
|
||||
signal input epoch_nonce;
|
||||
signal input slot;
|
||||
signal input commitment;
|
||||
signal input secret_key;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(5);
|
||||
hash.inp[0] <== 76696568; // 76696568 = LEAD in ASCII
|
||||
hash.inp[1] <== epoch_nonce;
|
||||
hash.inp[2] <== slot;
|
||||
hash.inp[3] <== commitment;
|
||||
hash.inp[4] <== secret_key;
|
||||
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
template derive_secret_key(){
|
||||
signal input starting_slot;
|
||||
signal input secrets_root;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(3);
|
||||
hash.inp[0] <== 78797779839583696782698495756989; //78797779839583696782698495756989 = NOMOS_SECRET_KEY in ASCII
|
||||
hash.inp[1] <== starting_slot;
|
||||
hash.inp[2] <== secrets_root;
|
||||
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
template derive_entropy(){
|
||||
signal input slot;
|
||||
signal input commitment;
|
||||
signal input secret_key;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(4);
|
||||
hash.inp[0] <== 78797779839578797867699567797884827366; // 78797779839578797867699567797884827366 = NOMOS_NONCE_CONTRIB
|
||||
hash.inp[1] <== slot;
|
||||
hash.inp[2] <== commitment;
|
||||
hash.inp[3] <== secret_key;
|
||||
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
|
||||
template payment_proof_of_leadership(){
|
||||
signal input slot;
|
||||
signal input epoch_nonce;
|
||||
signal input t0;
|
||||
signal input t1;
|
||||
signal input slot_secret;
|
||||
signal input slot_secret_path[25];
|
||||
|
||||
//Part of the commitment proof of membership
|
||||
signal input cm_nodes[32];
|
||||
signal input cm_selectors[32]; // must be bits
|
||||
signal input commitments_root;
|
||||
|
||||
//Part of the nullifier proof of non-membership
|
||||
signal input nf_previous;
|
||||
signal input nf_next;
|
||||
signal input nf_nodes[32];
|
||||
signal input nf_selectors[32];
|
||||
signal input nullifiers_root;
|
||||
|
||||
//Part of the secret key
|
||||
signal input starting_slot;
|
||||
signal input secrets_root;
|
||||
|
||||
// The winning note. The unit is supposed to be NMO and the ZoneID is PAYMENT
|
||||
signal input state;
|
||||
signal input value;
|
||||
signal input nonce;
|
||||
|
||||
signal output entropy_contrib;
|
||||
|
||||
|
||||
// Derive the secret key
|
||||
component sk = derive_secret_key();
|
||||
sk.starting_slot <== starting_slot;
|
||||
sk.secrets_root <== secrets_root;
|
||||
|
||||
|
||||
// Derive the public key from the secret key
|
||||
component pk = derive_public_key();
|
||||
pk.secret_key <== sk.out;
|
||||
|
||||
|
||||
// Derive the commitment from the note and the public key
|
||||
component cm = commitment();
|
||||
cm.state <== state;
|
||||
cm.value <== value;
|
||||
cm.unit <== 787779; // NMO in ASCII
|
||||
cm.nonce <== nonce;
|
||||
cm.zoneID <== 80658977697884; // PAYMENT in ASCII
|
||||
cm.public_key <== pk.out;
|
||||
|
||||
|
||||
// Derive the nullifier from the commitment and the secret key
|
||||
component nf = nullifier();
|
||||
nf.commitment <== cm.out;
|
||||
nf.secret_key <== sk.out;
|
||||
|
||||
|
||||
// Check commitment membership
|
||||
//First check selectors are indeed bits
|
||||
for(var i = 0; i < 32; i++){
|
||||
cm_selectors[i] * (1 - cm_selectors[i]) === 0;
|
||||
}
|
||||
//Then check the proof of membership
|
||||
component cm_membership = proof_of_membership(32);
|
||||
for(var i = 0; i < 32; i++){
|
||||
cm_membership.nodes[i] <== cm_nodes[i];
|
||||
cm_membership.selector[i] <== cm_selectors[i];
|
||||
}
|
||||
cm_membership.root <== commitments_root;
|
||||
cm_membership.leaf <== cm.out;
|
||||
|
||||
|
||||
// Compute the lottery ticket
|
||||
component ticket = ticket_calculator();
|
||||
ticket.epoch_nonce <== epoch_nonce;
|
||||
ticket.slot <== slot;
|
||||
ticket.commitment <== cm.out;
|
||||
ticket.secret_key <== sk.out;
|
||||
|
||||
|
||||
// Compute the lottery threshold
|
||||
signal intermediate;
|
||||
signal threshold;
|
||||
intermediate <== t1 * value;
|
||||
threshold <== value * (t0 + intermediate);
|
||||
|
||||
|
||||
// Check that the ticket is winning
|
||||
component winning = FullLessThan();
|
||||
winning.a <== ticket.out;
|
||||
winning.b <== threshold;
|
||||
winning.out === 1;
|
||||
|
||||
|
||||
// Check nullifier non-membership
|
||||
//First check selectors are indeed bits
|
||||
for(var i = 0; i < 32; i++){
|
||||
nf_selectors[i] * (1 - nf_selectors[i]) === 0;
|
||||
}
|
||||
//Then check the proof of non-membership
|
||||
component nf_membership = proof_of_non_membership(32);
|
||||
nf_membership.previous <== nf_previous;
|
||||
nf_membership.nullifier <== nf.out;
|
||||
nf_membership.next <== nf_next;
|
||||
nf_membership.root <== nullifiers_root;
|
||||
for(var i =0; i<32; i++){
|
||||
nf_membership.nodes[i] <== nf_nodes[i];
|
||||
nf_membership.selector[i] <== nf_selectors[i];
|
||||
}
|
||||
|
||||
|
||||
// Check the knowledge of the secret at position slot - starting_slot
|
||||
// Verify that the substraction wont underflow (starting_slot < slot)
|
||||
component checker = SafeLessEqThan(252);
|
||||
checker.in[0] <== starting_slot;
|
||||
checker.in[1] <== slot;
|
||||
checker.out === 1;
|
||||
// Compute the positions related to slot - starting_slot
|
||||
component bits = Num2Bits(25);
|
||||
bits.in <== slot - starting_slot;
|
||||
// Check the membership of the secret_slot against the secrets_root
|
||||
component secret_membership = proof_of_membership(25);
|
||||
for(var i =0; i<25; i++){
|
||||
secret_membership.nodes[i] <== slot_secret_path[i];
|
||||
secret_membership.selector[i] <== bits.out[24-i];
|
||||
}
|
||||
secret_membership.root <== secrets_root;
|
||||
secret_membership.leaf <== slot_secret;
|
||||
|
||||
|
||||
// Compute the entropy contribution
|
||||
component entropy = derive_entropy();
|
||||
entropy.slot <== slot;
|
||||
entropy.commitment <== cm.out;
|
||||
entropy.secret_key <== sk.out;
|
||||
|
||||
entropy_contrib <== entropy.out;
|
||||
}
|
||||
|
||||
component main = payment_proof_of_leadership();
|
||||
336
circom_circuits/proof_of_leadership/generate_inputs.py
Executable file
336
circom_circuits/proof_of_leadership/generate_inputs.py
Executable file
@ -0,0 +1,336 @@
|
||||
#!/usr/bin/sage
|
||||
# -*- mode: python ; -*-
|
||||
|
||||
|
||||
from sage.all import *
|
||||
|
||||
p = 21888242871839275222246405745257275088548364400416034343698204186575808495617
|
||||
F = FiniteField(p)
|
||||
|
||||
def poseidon2_hash(data):
|
||||
return PoseidonSponge(data,2,1)[0]
|
||||
|
||||
|
||||
|
||||
def Poseidon2_sponge_hash_rate_1(data, n):
|
||||
return PoseidonSponge(data,3,2,n,1)
|
||||
|
||||
def Poseidon2_sponge_hash_rate_2(data, n):
|
||||
return PoseidonSponge(data,3,1,n,1)
|
||||
|
||||
|
||||
def SBox(inp):
|
||||
return inp**5
|
||||
|
||||
def InternalRound(inp, i):
|
||||
round_consts = [ 0x15ce7e5ae220e8623a40b3a3b22d441eff0c9be1ae1d32f1b777af84eea7e38c
|
||||
, 0x1bf60ac8bfff0f631983c93e218ca0d4a4059c254b4299b1d9984a07edccfaf0
|
||||
, 0x0fab0c9387cb2bec9dc11b2951088b9e1e1d2978542fc131f74a8f8fdac95b40
|
||||
, 0x07d085a48750738019784663bccd460656dc62c1b18964a0d27a5bd0c27ee453
|
||||
, 0x10d57b1fad99da9d3fe16cf7f5dae05be844f67b2e7db3472a2e96e167578bc4
|
||||
, 0x0c36c40f7bd1934b7d5525031467aa39aeaea461996a70eda5a2a704e1733bb0
|
||||
, 0x0e4b65a0f3e1f9d3166a2145063c999bd08a4679676d765f4d11f97ed5c080ae
|
||||
, 0x1ce5561061120d5c7ea09da2528c4c041b9ad0f05d655f38b10d79878b69f29d
|
||||
, 0x2d323f651c3da8f0e0754391a10fa111b25dfa00471edf5493c44dfc3f28add6
|
||||
, 0x05a0741ee5bdc3e099fd6bdad9a0865bc9ceecd13ea4e702e536dd370b8f1953
|
||||
, 0x176a2ec4746fc0e0eca9e5e11d6facaee05524a92e5785c8b8161780a4435136
|
||||
, 0x0691faf0f42a9ed97629b1ae0dc7f1b019c06dd852cb6efe57f7eeb1aa865aef
|
||||
, 0x0e46cf138dad09d61b9a7cab95a23b5c8cb276874f3715598bacb55d5ad271de
|
||||
, 0x0f18c3d95bac1ac424160d240cdffc2c44f7b6315ba65ed3ff2eff5b3e48b4f2
|
||||
, 0x2eea6af14b592ec45a4119ac1e6e6f0312ecd090a096e340d472283e543ddff7
|
||||
, 0x06b0d7a8f4ce97d049ae994139f5f71dca4899d4f1cd3dd83a32a89a58c0a8e6
|
||||
, 0x019df0b9828eed5892dd55c1ad6408196f6293d600ef4491703a1b37e119ba8e
|
||||
, 0x08ca5e3c93817cdb1c2b2a12d02c779d74c1bb12b6668f3ab3ddd7837f3a4a00
|
||||
, 0x28382d747e3fd6cb2e0d8e8edd79c5313eed307a3517c11046245b1476e4f701
|
||||
, 0x0ca89aecd5675b77c8271765da98cfcb6875b3053d4742c9ff502861bd16ad28
|
||||
, 0x19046bc0b03ca90802ec83f212001e7ffd7f9224cfffae523451deb52eab3787
|
||||
, 0x036fd7dfa1c05110b3428e6abcc43e1de9abba915320c4a600f843bfb676ca51
|
||||
, 0x08f0a7abcb1a2f6595a9b7380c5028e3999db4fe5cb21892e5bb5cb11a7757ba
|
||||
, 0x0b614acc1ce3fbe9048f8385e4ee24c3843deea186bacea3c904c9f6340ad8cb
|
||||
, 0x00b2d98c5d988f9b41f2c98e017fc954a6ae423b2261575941f8eac8835d985c
|
||||
, 0x1457f18555b7973ba5b311d57ec5d77e936980b97f5973875f1f7cc765a4fc95
|
||||
, 0x002b453debc1bee525cb751bc10641a6b86f847d696418cf1144950982591bfa
|
||||
, 0x0c2af1abcc6ece77218315d2af445ccbfc6647b7af2510682882cc792c6bb8cf
|
||||
, 0x0e2825d9eb84b59902a1adb49ac0c2c291dee7c45d2e8c30369a4d595039e8ad
|
||||
, 0x297e2e86a8c672d39f3343b8dfce7a6f20f3571bfd5c8a28e3905aa2dcfeca44
|
||||
, 0x00d397281d902e49ec6504ba9186e806db9ad4fc8f86e7277aa7f1467eb6f9de
|
||||
, 0x2fb7c89c372d7e2050e7377ed471000c73544a2b9fd66557f3577c09cac98b4b
|
||||
, 0x16125247be4387a8c3e62490167f0cffdba02eda4f018d0b40639a13bb0cfef9
|
||||
, 0x2291fd9d442f2d9b97ab22f7d4d52c2a82e41f852cf620b144612650a39e26e8
|
||||
, 0x1eec61f16a275ae238540feaeeadfec56d32171b1cc393729d06f37f476fde71
|
||||
, 0x259ce871ba5dacbb48d8aed3d8513eef51558dc0b360f28c1a15dbfc5e7f6ca2
|
||||
, 0x2d3376a14ddbf95587e2f7567ff04fe13a3c7cb17363c8b9c5dd1d9262a210cb
|
||||
, 0x13b843d9f65f4cddd7ce10d9cad9b8b99ac5e9a8c4269288173a91c0f3c3b084
|
||||
, 0x0b52e9b2f1aa9fd204e4a42c481cc76c704783e34114b8e93e026a50fa9764e8
|
||||
, 0x1fd083229276c7f27d3ad941476b394ff37bd44d3a1e9caca1400d9077a2056c
|
||||
, 0x22743c328a6283f3ba7379af22c684c498568fd7ad9fad5151368c913197cbd9
|
||||
, 0x043007aefd9741070d95caaaba0c1b070e4eec8eef8c1e512c8e579c6ed64f76
|
||||
, 0x17ab175144f64bc843074f6b3a0c57c5dd2c954af8723c029ee642539496a7b3
|
||||
, 0x2befcad3d53fba5eeef8cae9668fed5c1e9e596a46e8458e218f7a665fddf4eb
|
||||
, 0x15151c4116d97de74bfa6ca3178f73c8fe8fe612c70c6f85a7a1551942cb71cc
|
||||
, 0x2ac40bf6c3176300a6835d5fc7cc4fd5e5d299fb1baa86487268ec1b9eedfa97
|
||||
, 0x0f151de1f01b4e24ffe04279318f0a68efabb485188f191e37e6915ff6059f6e
|
||||
, 0x2e43dffc34537535182aebac1ad7bf0a5533b88f65f9652f0ad584e2ffc4dd1f
|
||||
, 0x2ebabc2c37ef53d8b13b24a2a2b729d536735f58956125a3876da0664c2442d7
|
||||
, 0x0dc3beceb34e49f5ad7226dd202c5cf879dffcc9a6dd32a300e8f2a4b59edf03
|
||||
, 0x2f1ddeccce83adf68779c53b639871a8f81d4d00aefe1e812efce8ec999d457d
|
||||
, 0x1f63e41280ff5c021715d52b19780298ed8bd3d5eb506316b527e24149d4d4f1
|
||||
, 0x1b8c1252a5888f8cb2672effb5df49c633d3fd7183271488a1c40d0f88e7636e
|
||||
, 0x0f45697130f5498e2940568ef0d5e9e16b1095a6cdbb6411df20a973c605e70b
|
||||
, 0x0780ccc403cdd68983acbd34cda41cacfb2cf911a93076bc25587b4b0aed4929
|
||||
, 0x238d26ca97c691591e929f32199a643550f325f23a85d420080b289d7cecc9d4
|
||||
]
|
||||
|
||||
sb = SBox(inp[0] + round_consts[i])
|
||||
out = [F(0) for i in range(3)]
|
||||
out[0] = 2*sb + inp[1] + inp[2];
|
||||
out[1] = sb + 2*inp[1] + inp[2];
|
||||
out[2] = sb + inp[1] + 3*inp[2];
|
||||
return out
|
||||
|
||||
def ExternalRound(inp, i):
|
||||
out = [F(0) for j in range(3)]
|
||||
round_consts = [ [ F(0x2c4c51fd1bb9567c27e99f5712b49e0574178b41b6f0a476cddc41d242cf2b43)
|
||||
, F(0x1c5f8d18acb9c61ec6fcbfcda5356f1b3fdee7dc22c99a5b73a2750e5b054104)
|
||||
, F(0x2d3c1988b4541e4c045595b8d574e98a7c2820314a82e67a4e380f1c4541ba90 )
|
||||
]
|
||||
, [ F(0x052547dc9e6d936cab6680372f1734c39f490d0cb970e2077c82f7e4172943d3)
|
||||
, F(0x29d967f4002adcbb5a6037d644d36db91f591b088f69d9b4257694f5f9456bc2)
|
||||
, F(0x0350084b8305b91c426c25aeeecafc83fc5feec44b9636cb3b17d2121ec5b88a)
|
||||
]
|
||||
, [ F(0x1815d1e52a8196127530cc1e79f07a0ccd815fb5d94d070631f89f6c724d4cbe)
|
||||
, F(0x17b5ba882530af5d70466e2b434b0ccb15b7a8c0138d64455281e7724a066272)
|
||||
, F(0x1c859b60226b443767b73cd1b08823620de310bc49ea48662626014cea449aee)
|
||||
]
|
||||
, [ F(0x1b26e7f0ac7dd8b64c2f7a1904c958bb48d2635478a90d926f5ff2364effab37)
|
||||
, F(0x2da7f36850e6c377bdcdd380efd9e7c419555d3062b0997952dfbe5c54b1a22e)
|
||||
, F(0x17803c56450e74bc6c7ff97275390c017f682db11f3f4ca6e1f714efdfb9bd66)
|
||||
]
|
||||
, [ F(0x25672a14b5d085e31a30a7e1d5675ebfab034fb04dc2ec5e544887523f98dede)
|
||||
, F(0x0cf702434b891e1b2f1d71883506d68cdb1be36fa125674a3019647b3a98accd)
|
||||
, F(0x1837e75235ff5d112a5eddf7a4939448748339e7b5f2de683cf0c0ae98bdfbb3)
|
||||
]
|
||||
, [ F(0x1cd8a14cff3a61f04197a083c6485581a7d836941f6832704837a24b2d15613a)
|
||||
, F(0x266f6d85be0cef2ece525ba6a54b647ff789785069882772e6cac8131eecc1e4)
|
||||
, F(0x0538fde2183c3f5833ecd9e07edf30fe977d28dd6f246d7960889d9928b506b3)
|
||||
]
|
||||
, [ F(0x07a0693ff41476abb4664f3442596aa8399fdccf245d65882fce9a37c268aa04)
|
||||
, F(0x11eb49b07d33de2bd60ea68e7f652beda15644ed7855ee5a45763b576d216e8e)
|
||||
, F(0x08f8887da6ce51a8c06041f64e22697895f34bacb8c0a39ec12bf597f7c67cfc)
|
||||
]
|
||||
, [ F(0x2a912ec610191eb7662f86a52cc64c0122bd5ba762e1db8da79b5949fdd38092)
|
||||
, F(0x2031d7fd91b80857aa1fef64e23cfad9a9ba8fe8c8d09de92b1edb592a44c290)
|
||||
, F(0x0f81ebce43c47711751fa64d6c007221016d485641c28c507d04fd3dc7fba1d2)
|
||||
]
|
||||
]
|
||||
|
||||
sb = [F(0) for j in range(3)]
|
||||
for j in range(3):
|
||||
sb[j] = SBox(F(inp[j] + round_consts[i][j]))
|
||||
out = [F(0) for j in range(3)]
|
||||
out[0] = 2*sb[0] + sb[1] + sb[2]
|
||||
out[1] = sb[0] + 2*sb[1] + sb[2]
|
||||
out[2] = sb[0]+ sb[1] + 2*sb[2]
|
||||
return out
|
||||
|
||||
def LinearLayer(inp):
|
||||
out = [F(0) for i in range(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]
|
||||
return out
|
||||
|
||||
def Permutation(inp):
|
||||
out = [F(0) for i in range(3)]
|
||||
|
||||
state = LinearLayer(inp)
|
||||
|
||||
for k in range(4):
|
||||
state = ExternalRound(state, k)
|
||||
for k in range(56):
|
||||
state = InternalRound(state, k)
|
||||
for k in range(4):
|
||||
state = ExternalRound(state, k+4)
|
||||
return state
|
||||
|
||||
def Compression(inp):
|
||||
return Permutation([inp[0],inp[1],F(0)])
|
||||
|
||||
def PoseidonSponge(data, capacity, output_len):
|
||||
rate = 3 - capacity;
|
||||
output = [F(0) for i in range(output_len)]
|
||||
assert( capacity > 0 )
|
||||
assert( rate > 0 )
|
||||
assert( capacity < 3 )
|
||||
assert( rate < 3 )
|
||||
|
||||
# round up to rate the input + 1 field element ("10*" padding)
|
||||
nblocks = ((len(data) + 1) + (rate-1)) // rate;
|
||||
nout = (output_len + (rate-1)) // rate;
|
||||
padded_len = nblocks * rate;
|
||||
|
||||
padded = []
|
||||
for i in range(len(data)):
|
||||
padded.append(F(data[i]))
|
||||
padded.append(F(1))
|
||||
for i in range(len(data)+1,padded_len):
|
||||
padded.append(F(0))
|
||||
|
||||
civ = F(2**64 + 256*3 + rate)
|
||||
|
||||
state = [F(0),F(0),F(civ)]
|
||||
sorbed = [F(0) for j in range(rate)]
|
||||
|
||||
for m in range(nblocks):
|
||||
for i in range(rate):
|
||||
a = state[i]
|
||||
b = padded[m*rate+i]
|
||||
sorbed[i] = a + b
|
||||
state = Permutation(sorbed[0:rate] + state[rate:3])
|
||||
|
||||
q = min(rate, output_len)
|
||||
for i in range(q):
|
||||
output[i] = state[i]
|
||||
out_ptr = rate
|
||||
|
||||
for n in range(1,nout):
|
||||
state[nblocks+n] = Permutation(state[nblocks+n-1])
|
||||
q = min(rate, output_len-out_ptr)
|
||||
for i in range(q):
|
||||
output[out_ptr+i] = state[nblocks+n][i]
|
||||
out_ptr += rate
|
||||
|
||||
return output
|
||||
|
||||
R = RealField(500) #Real numbers with precision 500 bits
|
||||
|
||||
if len(sys.argv) != Integer(4):
|
||||
print("Usage: <script> <epoch_nonce> <slot_number> <total_stake>")
|
||||
exit()
|
||||
|
||||
epoch_nonce = 10 #int(sys.argv[Integer(1)])
|
||||
slot_number = 1683167 #int(sys.argv[Integer(2)])
|
||||
total_stake = 50000 #int(sys.argv[Integer(3)])
|
||||
|
||||
if epoch_nonce >= p:
|
||||
print("epoch nonce must be less than p")
|
||||
exit()
|
||||
if total_stake >= p:
|
||||
print("total stake must be less than p")
|
||||
exit()
|
||||
|
||||
t0 = F(int(-((R(p) * ln(R(1) - 0.05))) / R(total_stake)))
|
||||
t1 = F(int(-((R(p) * ln(R(1) - 0.05))**2) / R(total_stake)**2))
|
||||
|
||||
|
||||
value = F(50)
|
||||
unit = F(787779)
|
||||
state = F(randrange(0,p,1))
|
||||
note_nonce = F(0)
|
||||
threshold = (t0 + t1 * value) * value
|
||||
starting_slot = randrange(max(0,slot_number-2**25+1),slot_number,1)
|
||||
|
||||
slot_secret = F(randrange(0,p,1))
|
||||
slot_secret_indexes = format(slot_number - starting_slot,'025b')
|
||||
|
||||
slot_secret_path = [F(randrange(0,p,1)) for i in range(25)]
|
||||
secret_root = slot_secret
|
||||
for i in range(25):
|
||||
if int(slot_secret_indexes[24-i]) == 0:
|
||||
secret_root = poseidon2_hash([secret_root,slot_secret_path[i]])
|
||||
else:
|
||||
secret_root = poseidon2_hash([slot_secret_path[i],secret_root])
|
||||
sk = poseidon2_hash([F(78797779839583696782698495756989),starting_slot,secret_root])
|
||||
pk = poseidon2_hash([F(787977798395756870),sk])
|
||||
|
||||
note_cm = poseidon2_hash([F(78797779839578798469956777),state,value,unit,note_nonce,pk,F(80658977697884)])
|
||||
ticket = poseidon2_hash([F(76696568),F(epoch_nonce),F(slot_number),note_cm,sk])
|
||||
while(ticket > threshold):
|
||||
note_nonce += 1
|
||||
note_cm = poseidon2_hash([F(78797779839578798469956777),state,value,unit,note_nonce,pk,F(80658977697884)])
|
||||
ticket = poseidon2_hash([F(76696568),F(epoch_nonce),F(slot_number),note_cm,sk])
|
||||
|
||||
cm_nodes = [F(randrange(0,p,1)) for i in range(32)]
|
||||
cm_selectors = randrange(0,2**32,1)
|
||||
cm_selectors = format(cm_selectors,'032b')
|
||||
cm_root = note_cm
|
||||
for i in range(32):
|
||||
if int(cm_selectors[31-i]) == 0:
|
||||
cm_root = poseidon2_hash([cm_root,cm_nodes[i]])
|
||||
else:
|
||||
cm_root = poseidon2_hash([cm_nodes[i],cm_root])
|
||||
|
||||
note_nf = poseidon2_hash([F(78797779839578798469957870),note_cm,sk])
|
||||
nf_previous = F(randrange(0,note_nf,1))
|
||||
nf_next = F(randrange(note_nf+1,p,1))
|
||||
nf_nodes = [F(randrange(0,p,1)) for i in range(32)]
|
||||
nf_selectors = randrange(0,2**32,1)
|
||||
nf_selectors = format(nf_selectors,'032b')
|
||||
|
||||
nf_root = poseidon2_hash([nf_previous, nf_next])
|
||||
for i in range(32):
|
||||
if int(nf_selectors[31-i]) == 0:
|
||||
nf_root = poseidon2_hash([nf_root,nf_nodes[i]])
|
||||
else:
|
||||
nf_root = poseidon2_hash([nf_nodes[i],nf_root])
|
||||
|
||||
with open("input.json", "w") as file:
|
||||
file.write('{\n\t"slot":\t\t\t\t\t\t"'+str(slot_number)+'",')
|
||||
file.write('\n\t"epoch_nonce":\t\t\t\t\t\t"'+str(epoch_nonce)+'",')
|
||||
file.write('\n\t"t0" :\t\t\t\t\t\t"'+str(t0)+'",')
|
||||
file.write('\n\t"t1" :\t\t\t\t\t\t"'+str(t1)+'",')
|
||||
file.write('\n\t"slot_secret" :\t\t\t\t\t\t"'+str(slot_secret)+'",')
|
||||
file.write('\n\t"slot_secret_path" :\t\t\t\t\t[')
|
||||
for i in range(25):
|
||||
file.write('"')
|
||||
file.write(str(slot_secret_path[i]))
|
||||
file.write('"')
|
||||
if i == 24:
|
||||
file.write('],')
|
||||
else:
|
||||
file.write(',')
|
||||
file.write('\n\t"cm_nodes" :\t\t\t\t\t[')
|
||||
for i in range(32):
|
||||
file.write('"')
|
||||
file.write(str(cm_nodes[i]))
|
||||
file.write('"')
|
||||
if i == 31:
|
||||
file.write('],')
|
||||
else:
|
||||
file.write(',')
|
||||
file.write('\n\t"cm_selectors" :\t\t\t\t\t[')
|
||||
for i in range(32):
|
||||
file.write('"')
|
||||
file.write(str(cm_selectors[i]))
|
||||
file.write('"')
|
||||
if i == 31:
|
||||
file.write('],')
|
||||
else:
|
||||
file.write(',')
|
||||
file.write('\n\t"commitments_root" :\t\t\t\t"'+str(cm_root)+'",')
|
||||
file.write('\n\t"nf_previous" :\t\t\t\t"'+str(nf_previous)+'",')
|
||||
file.write('\n\t"nf_next" :\t\t\t\t"'+str(nf_next)+'",')
|
||||
file.write('\n\t"nf_nodes" :\t\t\t\t\t[')
|
||||
for i in range(32):
|
||||
file.write('"')
|
||||
file.write(str(nf_nodes[i]))
|
||||
file.write('"')
|
||||
if i == 31:
|
||||
file.write('],')
|
||||
else:
|
||||
file.write(',')
|
||||
file.write('\n\t"nf_selectors" :\t\t\t\t\t[')
|
||||
for i in range(32):
|
||||
file.write('"')
|
||||
file.write(str(nf_selectors[i]))
|
||||
file.write('"')
|
||||
if i == 31:
|
||||
file.write('],')
|
||||
else:
|
||||
file.write(',')
|
||||
file.write('\n\t"nullifiers_root" :\t\t\t\t"'+str(nf_root)+'",')
|
||||
file.write('\n\t"starting_slot" :\t\t\t\t"'+str(starting_slot)+'",')
|
||||
file.write('\n\t"secrets_root" :\t\t\t\t"'+str(secret_root)+'",')
|
||||
file.write('\n\t"state" :\t\t\t\t"'+str(state)+'",')
|
||||
file.write('\n\t"value" :\t\t\t\t"'+str(value)+'",')
|
||||
file.write('\n\t"nonce" :\t\t\t\t"'+str(note_nonce)+'"}')
|
||||
@ -1,103 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_16_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_4_to_1_Jubjub.circom";
|
||||
|
||||
template coefficient_hash(){
|
||||
signal input coefficients[2048];
|
||||
|
||||
signal output hash;
|
||||
|
||||
component hasher[136];
|
||||
hasher[0] = permutation_16_to_1();
|
||||
hasher[0].in[0] <== 0;
|
||||
for(var i = 1; i<16; i++){
|
||||
hasher[0].in[i] <== coefficients[i-1];
|
||||
}
|
||||
|
||||
|
||||
for(var i = 1; i<136; i++){
|
||||
hasher[i] = permutation_16_to_1();
|
||||
hasher[i].in[0] <== hasher[i-1].out[0];
|
||||
for(var j = 1; j<16; j++){
|
||||
hasher[i].in[j] <== hasher[i-1].out[j] + coefficients[15*i+j-1];
|
||||
}
|
||||
}
|
||||
|
||||
component final_hasher = hash_16_to_1();
|
||||
for (var i =0; i<8; i++){
|
||||
final_hasher.in[i] <== hasher[135].out[i] + coefficients[2040+i];
|
||||
}
|
||||
for (var i =8; i<16; i++){
|
||||
final_hasher.in[i] <== hasher[135].out[i];
|
||||
}
|
||||
|
||||
hash <== final_hasher.out;
|
||||
}
|
||||
|
||||
template drawn_random_point(){
|
||||
signal input da_commitment[2];
|
||||
signal input hash_of_data;
|
||||
|
||||
signal output x_0;
|
||||
|
||||
component final_hasher = hash_4_to_1();
|
||||
|
||||
final_hasher.in[0] <== 0;
|
||||
final_hasher.in[1] <== da_commitment[0];
|
||||
final_hasher.in[2] <== da_commitment[1];
|
||||
final_hasher.in[3] <== hash_of_data;
|
||||
|
||||
x_0 <== final_hasher.out;
|
||||
}
|
||||
|
||||
template evaluate_polynomial(){
|
||||
signal input coefficients[2048];
|
||||
signal input evaluation_point;
|
||||
|
||||
signal output result;
|
||||
|
||||
signal intermediate_values[2046];
|
||||
intermediate_values[2045] <== coefficients[2047] * evaluation_point + coefficients[2046];
|
||||
for(var i = 2044; i >= 0; i--){
|
||||
intermediate_values[i] <== coefficients[i + 1] + intermediate_values[i+1] * evaluation_point;
|
||||
}
|
||||
|
||||
result <== coefficients[0] + intermediate_values[0] * evaluation_point;
|
||||
}
|
||||
|
||||
template proof_of_equivalence(){
|
||||
signal input coefficients[2048];
|
||||
signal input da_commitment[2];
|
||||
|
||||
signal output x_0;
|
||||
signal output y_0;
|
||||
signal output coefficients_hash;
|
||||
|
||||
//Hash of the coefficients
|
||||
component coefficient_hasher = coefficient_hash();
|
||||
for(var i = 0; i<2048; i++){
|
||||
coefficient_hasher.coefficients[i] <== coefficients[i];
|
||||
}
|
||||
coefficients_hash <== coefficient_hasher.hash;
|
||||
|
||||
//Hash the coefficient hash with the da_commitment
|
||||
component point_drawer = drawn_random_point();
|
||||
point_drawer.da_commitment[0] <== da_commitment[0];
|
||||
point_drawer.da_commitment[1] <== da_commitment[1];
|
||||
point_drawer.hash_of_data <== coefficients_hash;
|
||||
x_0 <== point_drawer.x_0;
|
||||
|
||||
//Evaluate the polynomial at x_0
|
||||
component evaluator = evaluate_polynomial();
|
||||
evaluator.evaluation_point <== x_0;
|
||||
for(var i =0; i<2048; i++){
|
||||
evaluator.coefficients[i] <== coefficients[i];
|
||||
}
|
||||
|
||||
y_0 <== evaluator.result;
|
||||
}
|
||||
|
||||
|
||||
component main {public [da_commitment]} = proof_of_equivalence();
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,301 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_2_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_4_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_16_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/bitify.circom";
|
||||
|
||||
template BLSLessThan(n) {
|
||||
assert(n <= 253);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2Bits(n+1);
|
||||
|
||||
n2b.in <== in[0]+ (1<<n) - in[1];
|
||||
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
template BLSNum2Bits_strict() {
|
||||
signal input in;
|
||||
signal output out[255];
|
||||
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528); // -1 - 2**254 (p-1 without its first bit)
|
||||
component n2b = Num2Bits(255);
|
||||
in ==> n2b.in;
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
n2b.out[i] ==> out[i];
|
||||
if(i != 0){
|
||||
n2b.out[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * (n2b.out[0]) === 0; //must be zero exept if the first bit is 0 => then in is on 254 bits and p-1 on 255
|
||||
}
|
||||
|
||||
template BLSBits2Num_strict() {
|
||||
signal input in[255];
|
||||
signal output out;
|
||||
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528);
|
||||
component b2n = Bits2Num(255);
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
in[i] ==> b2n.in[i];
|
||||
if(i != 0){
|
||||
in[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * in[0] === 0;
|
||||
|
||||
b2n.out ==> out;
|
||||
}
|
||||
|
||||
template check_bits(n){
|
||||
signal input bits[n];
|
||||
for(var i=0; i<n; i++){
|
||||
bits[i] * (1-bits[i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
template check_lottery(){
|
||||
signal input epoch_nonce;
|
||||
signal input slot_number;
|
||||
signal input t0;
|
||||
signal input t1; // The precomputed threshold values
|
||||
|
||||
signal input constraints;
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input randomness;
|
||||
|
||||
component hash = hash_16_to_1();
|
||||
|
||||
//The b"lead" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 1818583396;
|
||||
|
||||
hash.in[1] <== epoch_nonce;
|
||||
hash.in[2] <== slot_number;
|
||||
hash.in[3] <== constraints;
|
||||
hash.in[4] <== value;
|
||||
hash.in[5] <== unit;
|
||||
hash.in[6] <== state;
|
||||
hash.in[7] <== note_nonce;
|
||||
hash.in[8] <== nullifier_secret_key;
|
||||
hash.in[9] <== randomness;
|
||||
for(var i=10; i<16; i++){
|
||||
hash.in[i] <== 0;
|
||||
}
|
||||
|
||||
// As p-1 is divisible by 4, if X follows a uniform distribution between 0 and p-1
|
||||
// Y = X % (p-1)/4 nearly follows a uniform distribution between 0 and ((p-1)/4)-1 (exept that 0 has 20% more chance to appear than another element which is negligible)
|
||||
// So we transform the hash of a maximum of 255 to a hash of a maximum of 253 bits by taking its modulo
|
||||
// (p-1)/4 = 13108968793781547619861935127046491459422638125131909455650914674984645296128
|
||||
// TODO: check this part to ensure it is secure
|
||||
signal quotient;
|
||||
signal ticket;
|
||||
quotient <-- hash.out \ 13108968793781547619861935127046491459422638125131909455650914674984645296128;
|
||||
ticket <-- hash.out % 13108968793781547619861935127046491459422638125131909455650914674984645296128;
|
||||
|
||||
//check that quotient is 0,1,2 or 3
|
||||
signal check_quotient[2];
|
||||
check_quotient[0] <== quotient * (1-quotient);
|
||||
check_quotient[1] <== (2-quotient)*(3-quotient);
|
||||
check_quotient[0] * check_quotient[1] === 0;
|
||||
|
||||
//check the correctness of the division
|
||||
ticket + quotient * 13108968793781547619861935127046491459422638125131909455650914674984645296128 === hash.out;
|
||||
|
||||
//check that the ticket is less than the divisor
|
||||
component isLess = CompConstant(13108968793781547619861935127046491459422638125131909455650914674984645296128);
|
||||
component bitifier = BLSNum2Bits_strict();
|
||||
|
||||
bitifier.in <== ticket;
|
||||
bitifier.out[254] === 0;
|
||||
for(var i=0; i<254; i++){
|
||||
isLess.in[i] <== bitifier.out[i];
|
||||
}
|
||||
isLess.out === 0;
|
||||
|
||||
|
||||
|
||||
// Compute the threshold
|
||||
signal intermediate_value;
|
||||
signal threshold;
|
||||
intermediate_value <== t0 + t1 * value;
|
||||
threshold <== intermediate_value * value;
|
||||
|
||||
// Ensure that the ticket is winning
|
||||
component isLess2 = BLSLessThan(253);
|
||||
|
||||
isLess2.in[0] <== ticket;
|
||||
isLess2.in[1] <== threshold;
|
||||
isLess2.out === 1;
|
||||
}
|
||||
|
||||
|
||||
template nullifier_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input value;
|
||||
signal output nullifier;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-nullifier" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 2016785505923014207119328528655730;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
nullifier <== hash.out;
|
||||
}
|
||||
|
||||
template commitment_computer(){ // TODO: ensure all field are hash
|
||||
signal input note_nonce;
|
||||
signal input nullifier_public_key;
|
||||
signal input value;
|
||||
signal output commitment;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-commitment" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 516297089516239580383111224192495220;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_public_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
commitment <== hash.out;
|
||||
}
|
||||
|
||||
template nonce_updater(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal output updated_nonce;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-evolve" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 120209783668687835891529317;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== 0;
|
||||
|
||||
updated_nonce <== hash.out;
|
||||
}
|
||||
|
||||
template membership_checker(){
|
||||
signal input leaf; //The note commitment
|
||||
signal input root; //The root of the Merkle Tree (of depth 32)
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input node[32]; //Complementary hashes
|
||||
|
||||
component hash[32];
|
||||
|
||||
for(var i=0; i<32; i++){
|
||||
hash[i] = hash_2_to_1();
|
||||
}
|
||||
|
||||
hash[0].in[0] <== leaf - index[31] * (leaf - node[0]);
|
||||
hash[0].in[1] <== node[0] - index[31] * (node[0] - leaf);
|
||||
|
||||
for(var i=1; i<32; i++){
|
||||
hash[i].in[0] <== hash[i-1].out - index[31-i] * (hash[i-1].out - node[i]);
|
||||
hash[i].in[1] <== node[i] - index[31-i] * (node[i] - hash[i-1].out);
|
||||
}
|
||||
|
||||
root === hash[31].out;
|
||||
|
||||
}
|
||||
|
||||
template anemoi_proof_of_leadership(){
|
||||
signal input epoch_nonce; //F_p (BLS12-381 scalar field)
|
||||
signal input slot_number; //F_p (BLS12-381 scalar field)
|
||||
signal input t0; // Precomputed threshold elements in F_p
|
||||
signal input t1;
|
||||
signal input commitments_root;
|
||||
|
||||
// Note variables
|
||||
signal input constraints; // Every note field represented as F_p elements for now (constraints are represented by their Merkle root)
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input randomness;
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input nodes[32]; //Merkle proof of the commitment
|
||||
|
||||
signal output nullifier;
|
||||
signal output updated_commiment;
|
||||
|
||||
|
||||
// Check that index inputs are indeed bits
|
||||
component bit_checker = check_bits(32);
|
||||
for(var i=0; i<32; i++){
|
||||
bit_checker.bits[i] <== index[i];
|
||||
}
|
||||
|
||||
// Check that r < threshold
|
||||
component lottery_checker = check_lottery();
|
||||
lottery_checker.epoch_nonce <== epoch_nonce;
|
||||
lottery_checker.slot_number <== slot_number;
|
||||
lottery_checker.t0 <== t0;
|
||||
lottery_checker.t1 <== t1;
|
||||
lottery_checker.constraints <== constraints;
|
||||
lottery_checker.value <== value;
|
||||
lottery_checker.unit <== unit;
|
||||
lottery_checker.state <== state;
|
||||
lottery_checker.note_nonce <== note_nonce;
|
||||
lottery_checker.nullifier_secret_key <== nullifier_secret_key;
|
||||
lottery_checker.randomness <== randomness;
|
||||
|
||||
|
||||
// Compute the note commitment
|
||||
component note_committer = commitment_computer();
|
||||
note_committer.note_nonce <== note_nonce;
|
||||
note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
note_committer.value <== value;
|
||||
|
||||
// Check the commitment membership
|
||||
component membership_checker = membership_checker();
|
||||
membership_checker.leaf <== note_committer.commitment;
|
||||
membership_checker.root <== commitments_root;
|
||||
for(var i =0; i<32; i++){
|
||||
membership_checker.index[i] <== index[i];
|
||||
membership_checker.node[i] <== nodes[i];
|
||||
}
|
||||
|
||||
|
||||
// Compute the note nullifier
|
||||
component nullifier_computer = nullifier_computer();
|
||||
nullifier_computer.note_nonce <== note_nonce;
|
||||
nullifier_computer.nullifier_secret_key <== nullifier_secret_key;
|
||||
nullifier_computer.value <== value;
|
||||
nullifier <== nullifier_computer.nullifier;
|
||||
|
||||
|
||||
// Compute the evolved nonce
|
||||
component nonce_updater = nonce_updater();
|
||||
nonce_updater.note_nonce <== note_nonce;
|
||||
nonce_updater.nullifier_secret_key <== nullifier_secret_key;
|
||||
|
||||
|
||||
// Compute the new note commitment
|
||||
component updated_note_committer = commitment_computer();
|
||||
updated_note_committer.note_nonce <== nonce_updater.updated_nonce;
|
||||
updated_note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
updated_note_committer.value <== value;
|
||||
updated_commiment <== updated_note_committer.commitment;
|
||||
|
||||
}
|
||||
|
||||
|
||||
component main {public [epoch_nonce, slot_number, t0, t1, commitments_root]} = anemoi_proof_of_leadership();
|
||||
@ -1,317 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_2_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_4_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_16_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/bitify.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/sha256/sha256.circom";
|
||||
|
||||
template BLSLessThan(n) {
|
||||
assert(n <= 253);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2Bits(n+1);
|
||||
|
||||
n2b.in <== in[0]+ (1<<n) - in[1];
|
||||
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
template BLSNum2Bits_strict() {
|
||||
signal input in;
|
||||
signal output out[255];
|
||||
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528); // -1 - 2**254 (p-1 without its first bit)
|
||||
component n2b = Num2Bits(255);
|
||||
in ==> n2b.in;
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
n2b.out[i] ==> out[i];
|
||||
if(i != 0){
|
||||
n2b.out[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * (n2b.out[0]) === 0; //must be zero exept if the first bit is 0 => then in is on 254 bits and p-1 on 255
|
||||
}
|
||||
|
||||
template BLSBits2Num_strict() {
|
||||
signal input in[255];
|
||||
signal output out;
|
||||
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528);
|
||||
component b2n = Bits2Num(255);
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
in[i] ==> b2n.in[i];
|
||||
if(i != 0){
|
||||
in[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * in[0] === 0;
|
||||
|
||||
b2n.out ==> out;
|
||||
}
|
||||
|
||||
template check_bits(n){
|
||||
signal input bits[n];
|
||||
for(var i=0; i<n; i++){
|
||||
bits[i] * (1-bits[i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
template check_lottery(){
|
||||
signal input epoch_nonce;
|
||||
signal input slot_number;
|
||||
signal input t0;
|
||||
signal input t1; // The precomputed threshold values
|
||||
|
||||
signal input constraints;
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input randomness;
|
||||
|
||||
component hash = Sha256(2336);
|
||||
component bitifier[9];
|
||||
for(var i=0; i<9; i++){
|
||||
bitifier[i] = BLSNum2Bits_strict();
|
||||
}
|
||||
bitifier[0].in <== epoch_nonce;
|
||||
bitifier[1].in <== slot_number;
|
||||
bitifier[2].in <== constraints;
|
||||
bitifier[3].in <== value;
|
||||
bitifier[4].in <== unit;
|
||||
bitifier[5].in <== state;
|
||||
bitifier[6].in <== note_nonce;
|
||||
bitifier[7].in <== nullifier_secret_key;
|
||||
bitifier[8].in <== randomness;
|
||||
|
||||
//The b"lead" Tag in bits with big endian order
|
||||
hash.in[0] <== 0;
|
||||
hash.in[1] <== 1;
|
||||
hash.in[2] <== 1;
|
||||
hash.in[3] <== 0;
|
||||
hash.in[4] <== 1;
|
||||
hash.in[5] <== 1;
|
||||
hash.in[6] <== 0;
|
||||
hash.in[7] <== 0;
|
||||
hash.in[8] <== 0;
|
||||
hash.in[9] <== 1;
|
||||
hash.in[10] <== 1;
|
||||
hash.in[11] <== 0;
|
||||
hash.in[12] <== 0;
|
||||
hash.in[13] <== 1;
|
||||
hash.in[14] <== 0;
|
||||
hash.in[15] <== 1;
|
||||
hash.in[16] <== 0;
|
||||
hash.in[17] <== 1;
|
||||
hash.in[18] <== 1;
|
||||
hash.in[19] <== 0;
|
||||
hash.in[20] <== 0;
|
||||
hash.in[21] <== 0;
|
||||
hash.in[22] <== 0;
|
||||
hash.in[23] <== 1;
|
||||
hash.in[24] <== 0;
|
||||
hash.in[25] <== 1;
|
||||
hash.in[26] <== 1;
|
||||
hash.in[27] <== 0;
|
||||
hash.in[28] <== 0;
|
||||
hash.in[29] <== 1;
|
||||
hash.in[30] <== 0;
|
||||
hash.in[31] <== 0;
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
for(var j=0; j<9; j++){
|
||||
if(i != 0){
|
||||
hash.in[32+256*j+i] <== bitifier[j].out[255-i];
|
||||
} else {
|
||||
hash.in[32+256*j] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component intifier = Bits2Num(253); //Because if the scalar field is 255 bits, we support every number of 254 bits (not all of 255) and we can only compare numbers of 253 bits since we need 1 bit for sign.
|
||||
|
||||
for(var i=0; i<253; i++){
|
||||
intifier.in[i] <== hash.out[253-i];
|
||||
}
|
||||
|
||||
// Compute the threshold
|
||||
signal intermediate_value;
|
||||
signal threshold;
|
||||
intermediate_value <== t0 + t1 * value;
|
||||
threshold <== intermediate_value * value;
|
||||
|
||||
// Ensure that the ticket is winning
|
||||
component isLess2 = BLSLessThan(253);
|
||||
|
||||
isLess2.in[0] <== intifier.out;
|
||||
isLess2.in[1] <== threshold;
|
||||
isLess2.out === 1;
|
||||
}
|
||||
|
||||
|
||||
template nullifier_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input value;
|
||||
signal output nullifier;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-nullifier" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 2016785505923014207119328528655730;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
nullifier <== hash.out;
|
||||
}
|
||||
|
||||
template commitment_computer(){ // TODO: ensure all field are hash
|
||||
signal input note_nonce;
|
||||
signal input nullifier_public_key;
|
||||
signal input value;
|
||||
signal output commitment;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-commitment" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 516297089516239580383111224192495220;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_public_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
commitment <== hash.out;
|
||||
}
|
||||
|
||||
template nonce_updater(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal output updated_nonce;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-evolve" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 120209783668687835891529317;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== 0;
|
||||
|
||||
updated_nonce <== hash.out;
|
||||
}
|
||||
|
||||
template membership_checker(){
|
||||
signal input leaf; //The note commitment
|
||||
signal input root; //The root of the Merkle Tree (of depth 32)
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input node[32]; //Complementary hashes
|
||||
|
||||
component hash[32];
|
||||
|
||||
for(var i=0; i<32; i++){
|
||||
hash[i] = hash_2_to_1();
|
||||
}
|
||||
|
||||
hash[0].in[0] <== leaf - index[31] * (leaf - node[0]);
|
||||
hash[0].in[1] <== node[0] - index[31] * (node[0] - leaf);
|
||||
|
||||
for(var i=1; i<32; i++){
|
||||
hash[i].in[0] <== hash[i-1].out - index[31-i] * (hash[i-1].out - node[i]);
|
||||
hash[i].in[1] <== node[i] - index[31-i] * (node[i] - hash[i-1].out);
|
||||
}
|
||||
|
||||
root === hash[31].out;
|
||||
|
||||
}
|
||||
|
||||
template anemoi_sha_proof_of_leadership(){
|
||||
signal input epoch_nonce; //F_p (BLS12-381 scalar field)
|
||||
signal input slot_number; //F_p (BLS12-381 scalar field)
|
||||
signal input t0; // Precomputed threshold elements in F_p
|
||||
signal input t1;
|
||||
signal input commitments_root;
|
||||
|
||||
// Note variables
|
||||
signal input constraints; // Every note field represented as F_p elements for now (constraints are represented by their Merkle root)
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input randomness;
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input nodes[32]; //Merkle proof of the commitment
|
||||
|
||||
signal output nullifier;
|
||||
signal output updated_commiment;
|
||||
|
||||
|
||||
// Check that index inputs are indeed bits
|
||||
component bit_checker = check_bits(32);
|
||||
for(var i=0; i<32; i++){
|
||||
bit_checker.bits[i] <== index[i];
|
||||
}
|
||||
|
||||
// Check that r < threshold
|
||||
component lottery_checker = check_lottery();
|
||||
lottery_checker.epoch_nonce <== epoch_nonce;
|
||||
lottery_checker.slot_number <== slot_number;
|
||||
lottery_checker.t0 <== t0;
|
||||
lottery_checker.t1 <== t1;
|
||||
lottery_checker.constraints <== constraints;
|
||||
lottery_checker.value <== value;
|
||||
lottery_checker.unit <== unit;
|
||||
lottery_checker.state <== state;
|
||||
lottery_checker.note_nonce <== note_nonce;
|
||||
lottery_checker.nullifier_secret_key <== nullifier_secret_key;
|
||||
lottery_checker.randomness <== randomness;
|
||||
|
||||
|
||||
// Compute the note commitment
|
||||
component note_committer = commitment_computer();
|
||||
note_committer.note_nonce <== note_nonce;
|
||||
note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
note_committer.value <== value;
|
||||
|
||||
// Check the commitment membership
|
||||
component membership_checker = membership_checker();
|
||||
membership_checker.leaf <== note_committer.commitment;
|
||||
membership_checker.root <== commitments_root;
|
||||
for(var i =0; i<32; i++){
|
||||
membership_checker.index[i] <== index[i];
|
||||
membership_checker.node[i] <== nodes[i];
|
||||
}
|
||||
|
||||
|
||||
// Compute the note nullifier
|
||||
component nullifier_computer = nullifier_computer();
|
||||
nullifier_computer.note_nonce <== note_nonce;
|
||||
nullifier_computer.nullifier_secret_key <== nullifier_secret_key;
|
||||
nullifier_computer.value <== value;
|
||||
nullifier <== nullifier_computer.nullifier;
|
||||
|
||||
|
||||
// Compute the evolved nonce
|
||||
component nonce_updater = nonce_updater();
|
||||
nonce_updater.note_nonce <== note_nonce;
|
||||
nonce_updater.nullifier_secret_key <== nullifier_secret_key;
|
||||
|
||||
|
||||
// Compute the new note commitment
|
||||
component updated_note_committer = commitment_computer();
|
||||
updated_note_committer.note_nonce <== nonce_updater.updated_nonce;
|
||||
updated_note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
updated_note_committer.value <== value;
|
||||
updated_commiment <== updated_note_committer.commitment;
|
||||
|
||||
}
|
||||
|
||||
|
||||
component main {public [epoch_nonce, slot_number, t0, t1, commitments_root]} = anemoi_sha_proof_of_leadership();
|
||||
@ -1,301 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_2_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_4_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_16_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/bitify.circom";
|
||||
|
||||
template BLSLessThan(n) {
|
||||
assert(n <= 253);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2Bits(n+1);
|
||||
|
||||
n2b.in <== in[0]+ (1<<n) - in[1];
|
||||
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
template BLSNum2Bits_strict() {
|
||||
signal input in;
|
||||
signal output out[255];
|
||||
|
||||
// Ensure that out is lower than p
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528); // -1 - 2**254 (p-1 without its first bit)
|
||||
component n2b = Num2Bits(255);
|
||||
in ==> n2b.in;
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
n2b.out[i] ==> out[i];
|
||||
if(i != 0){
|
||||
n2b.out[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * (n2b.out[0]) === 0; //must be zero exept if the first bit is 0 => then in is on 254 bits and p-1 on 255
|
||||
}
|
||||
|
||||
template BLSBits2Num_strict() {
|
||||
signal input in[255];
|
||||
signal output out;
|
||||
//ensure that in is not greater than p
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528);
|
||||
component b2n = Bits2Num(255);
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
in[i] ==> b2n.in[i];
|
||||
if(i != 0){
|
||||
in[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * in[0] === 0;
|
||||
|
||||
b2n.out ==> out;
|
||||
}
|
||||
|
||||
template check_bits(n){
|
||||
signal input bits[n];
|
||||
for(var i=0; i<n; i++){
|
||||
bits[i] * (1-bits[i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
template check_lottery(){
|
||||
signal input epoch_nonce;
|
||||
signal input slot_number;
|
||||
signal input t0;
|
||||
signal input t1; // The precomputed threshold values
|
||||
|
||||
signal input constraints;
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input randomness;
|
||||
|
||||
component hash = hash_16_to_1();
|
||||
|
||||
//The b"lead" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 1818583396;
|
||||
|
||||
hash.in[1] <== epoch_nonce;
|
||||
hash.in[2] <== slot_number;
|
||||
hash.in[3] <== constraints;
|
||||
hash.in[4] <== value;
|
||||
hash.in[5] <== unit;
|
||||
hash.in[6] <== state;
|
||||
hash.in[7] <== note_nonce;
|
||||
hash.in[8] <== nullifier_secret_key;
|
||||
hash.in[9] <== randomness;
|
||||
for(var i=10; i<16; i++){
|
||||
hash.in[i] <== 0;
|
||||
}
|
||||
|
||||
// As p-1 is divisible by 4, if X follows a uniform distribution between 0 and p-1
|
||||
// Y = X % (p-1)/4 nearly follows a uniform distribution between 0 and ((p-1)/4)-1 (exept that 0 has 20% more chance to appear than another element which is negligible)
|
||||
// So we transform the hash of a maximum of 255 to a hash of a maximum of 253 bits by taking its modulo
|
||||
// (p-1)/4 = 13108968793781547619861935127046491459422638125131909455650914674984645296128
|
||||
// TODO: check this part to ensure it is secure
|
||||
signal quotient;
|
||||
signal ticket;
|
||||
quotient <-- hash.out \ 13108968793781547619861935127046491459422638125131909455650914674984645296128;
|
||||
ticket <-- hash.out % 13108968793781547619861935127046491459422638125131909455650914674984645296128;
|
||||
|
||||
//check that quotient is 0,1,2 or 3
|
||||
signal check_quotient[2];
|
||||
check_quotient[0] <== quotient * (1-quotient);
|
||||
check_quotient[1] <== (2-quotient)*(3-quotient);
|
||||
check_quotient[0] * check_quotient[1] === 0;
|
||||
|
||||
//check the correctness of the division
|
||||
ticket + quotient * 13108968793781547619861935127046491459422638125131909455650914674984645296128 === hash.out;
|
||||
|
||||
//check that the ticket is less than the divisor
|
||||
component isLess = CompConstant(13108968793781547619861935127046491459422638125131909455650914674984645296128);
|
||||
component bitifier = BLSNum2Bits_strict();
|
||||
|
||||
bitifier.in <== ticket;
|
||||
bitifier.out[254] === 0;
|
||||
for(var i=0; i<254; i++){
|
||||
isLess.in[i] <== bitifier.out[i];
|
||||
}
|
||||
isLess.out === 0;
|
||||
|
||||
|
||||
|
||||
// Compute the threshold
|
||||
signal intermediate_value;
|
||||
signal threshold;
|
||||
intermediate_value <== t0 + t1 * value;
|
||||
threshold <== intermediate_value * value;
|
||||
|
||||
// Ensure that the ticket is indeed 253 bits and that the ticket is winning
|
||||
component isLess2 = BLSLessThan(253);
|
||||
isLess2.in[0] <== ticket;
|
||||
isLess2.in[1] <== threshold;
|
||||
isLess2.out === 1;
|
||||
}
|
||||
|
||||
|
||||
template nullifier_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input value;
|
||||
signal output nullifier;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-nullifier" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 2016785505923014207119328528655730;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
nullifier <== hash.out;
|
||||
}
|
||||
|
||||
template commitment_computer(){ // TODO: ensure all field are hash
|
||||
signal input note_nonce;
|
||||
signal input nullifier_public_key;
|
||||
signal input value;
|
||||
signal output commitment;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-commitment" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 516297089516239580383111224192495220;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_public_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
commitment <== hash.out;
|
||||
}
|
||||
|
||||
template nonce_updater(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal output updated_nonce;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-evolve" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 120209783668687835891529317;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== 0;
|
||||
|
||||
updated_nonce <== hash.out;
|
||||
}
|
||||
|
||||
template membership_checker(){
|
||||
signal input leaf; //The note commitment
|
||||
signal input root; //The root of the Merkle Tree (of depth 32)
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input node[32]; //Complementary hashes
|
||||
|
||||
component hash[32];
|
||||
|
||||
for(var i=0; i<32; i++){
|
||||
hash[i] = hash_2_to_1();
|
||||
}
|
||||
|
||||
hash[0].in[0] <== leaf - index[31] * (leaf - node[0]);
|
||||
hash[0].in[1] <== node[0] - index[31] * (node[0] - leaf);
|
||||
|
||||
for(var i=1; i<32; i++){
|
||||
hash[i].in[0] <== hash[i-1].out - index[31-i] * (hash[i-1].out - node[i]);
|
||||
hash[i].in[1] <== node[i] - index[31-i] * (node[i] - hash[i-1].out);
|
||||
}
|
||||
|
||||
root === hash[31].out;
|
||||
|
||||
}
|
||||
|
||||
template poseidon_proof_of_leadership(){
|
||||
signal input epoch_nonce; //F_p (BLS12-381 scalar field)
|
||||
signal input slot_number; //F_p (BLS12-381 scalar field)
|
||||
signal input t0; // Precomputed threshold elements in F_p
|
||||
signal input t1;
|
||||
signal input commitments_root;
|
||||
|
||||
// Note variables
|
||||
signal input constraints; // Every note field represented as F_p elements for now (constraints are represented by their Merkle root)
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input randomness;
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input nodes[32]; //Merkle proof of the commitment
|
||||
|
||||
signal output nullifier;
|
||||
signal output updated_commiment;
|
||||
|
||||
|
||||
// Check that index inputs are indeed bits
|
||||
component bit_checker = check_bits(32);
|
||||
for(var i=0; i<32; i++){
|
||||
bit_checker.bits[i] <== index[i];
|
||||
}
|
||||
|
||||
// Check that r < threshold
|
||||
component lottery_checker = check_lottery();
|
||||
lottery_checker.epoch_nonce <== epoch_nonce;
|
||||
lottery_checker.slot_number <== slot_number;
|
||||
lottery_checker.t0 <== t0;
|
||||
lottery_checker.t1 <== t1;
|
||||
lottery_checker.constraints <== constraints;
|
||||
lottery_checker.value <== value;
|
||||
lottery_checker.unit <== unit;
|
||||
lottery_checker.state <== state;
|
||||
lottery_checker.note_nonce <== note_nonce;
|
||||
lottery_checker.nullifier_secret_key <== nullifier_secret_key;
|
||||
lottery_checker.randomness <== randomness;
|
||||
|
||||
|
||||
// Compute the note commitment
|
||||
component note_committer = commitment_computer();
|
||||
note_committer.note_nonce <== note_nonce;
|
||||
note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
note_committer.value <== value;
|
||||
|
||||
// Check the commitment membership
|
||||
component membership_checker = membership_checker();
|
||||
membership_checker.leaf <== note_committer.commitment;
|
||||
membership_checker.root <== commitments_root;
|
||||
for(var i =0; i<32; i++){
|
||||
membership_checker.index[i] <== index[i];
|
||||
membership_checker.node[i] <== nodes[i];
|
||||
}
|
||||
|
||||
|
||||
// Compute the note nullifier
|
||||
component nullifier_computer = nullifier_computer();
|
||||
nullifier_computer.note_nonce <== note_nonce;
|
||||
nullifier_computer.nullifier_secret_key <== nullifier_secret_key;
|
||||
nullifier_computer.value <== value;
|
||||
nullifier <== nullifier_computer.nullifier;
|
||||
|
||||
|
||||
// Compute the evolved nonce
|
||||
component nonce_updater = nonce_updater();
|
||||
nonce_updater.note_nonce <== note_nonce;
|
||||
nonce_updater.nullifier_secret_key <== nullifier_secret_key;
|
||||
|
||||
|
||||
// Compute the new note commitment
|
||||
component updated_note_committer = commitment_computer();
|
||||
updated_note_committer.note_nonce <== nonce_updater.updated_nonce;
|
||||
updated_note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
updated_note_committer.value <== value;
|
||||
updated_commiment <== updated_note_committer.commitment;
|
||||
|
||||
}
|
||||
|
||||
|
||||
component main {public [epoch_nonce, slot_number, t0, t1, commitments_root]} = poseidon_proof_of_leadership();
|
||||
@ -1,318 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_2_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_4_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_16_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/bitify.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/sha256/sha256.circom";
|
||||
|
||||
template BLSLessThan(n) {
|
||||
assert(n <= 253);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2Bits(n+1);
|
||||
|
||||
n2b.in <== in[0]+ (1<<n) - in[1];
|
||||
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
template BLSNum2Bits_strict() {
|
||||
signal input in;
|
||||
signal output out[255];
|
||||
|
||||
// Ensure that out is lower than p
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528); // -1 - 2**254 (p-1 without its first bit)
|
||||
component n2b = Num2Bits(255);
|
||||
in ==> n2b.in;
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
n2b.out[i] ==> out[i];
|
||||
if(i != 0){
|
||||
n2b.out[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * (n2b.out[0]) === 0; //must be zero exept if the first bit is 0 => then in is on 254 bits and p-1 on 255
|
||||
}
|
||||
|
||||
template BLSBits2Num_strict() {
|
||||
signal input in[255];
|
||||
signal output out;
|
||||
//ensure that in is not greater than p
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528);
|
||||
component b2n = Bits2Num(255);
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
in[i] ==> b2n.in[i];
|
||||
if(i != 0){
|
||||
in[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * in[0] === 0;
|
||||
|
||||
b2n.out ==> out;
|
||||
}
|
||||
|
||||
template check_bits(n){
|
||||
signal input bits[n];
|
||||
for(var i=0; i<n; i++){
|
||||
bits[i] * (1-bits[i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
template check_lottery(){
|
||||
signal input epoch_nonce;
|
||||
signal input slot_number;
|
||||
signal input t0;
|
||||
signal input t1; // The precomputed threshold values
|
||||
|
||||
signal input constraints;
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input randomness;
|
||||
|
||||
component hash = Sha256(2336);
|
||||
component bitifier[9];
|
||||
for(var i=0; i<9; i++){
|
||||
bitifier[i] = BLSNum2Bits_strict();
|
||||
}
|
||||
bitifier[0].in <== epoch_nonce;
|
||||
bitifier[1].in <== slot_number;
|
||||
bitifier[2].in <== constraints;
|
||||
bitifier[3].in <== value;
|
||||
bitifier[4].in <== unit;
|
||||
bitifier[5].in <== state;
|
||||
bitifier[6].in <== note_nonce;
|
||||
bitifier[7].in <== nullifier_secret_key;
|
||||
bitifier[8].in <== randomness;
|
||||
|
||||
//The b"lead" Tag in bits with big endian order
|
||||
hash.in[0] <== 0;
|
||||
hash.in[1] <== 1;
|
||||
hash.in[2] <== 1;
|
||||
hash.in[3] <== 0;
|
||||
hash.in[4] <== 1;
|
||||
hash.in[5] <== 1;
|
||||
hash.in[6] <== 0;
|
||||
hash.in[7] <== 0;
|
||||
hash.in[8] <== 0;
|
||||
hash.in[9] <== 1;
|
||||
hash.in[10] <== 1;
|
||||
hash.in[11] <== 0;
|
||||
hash.in[12] <== 0;
|
||||
hash.in[13] <== 1;
|
||||
hash.in[14] <== 0;
|
||||
hash.in[15] <== 1;
|
||||
hash.in[16] <== 0;
|
||||
hash.in[17] <== 1;
|
||||
hash.in[18] <== 1;
|
||||
hash.in[19] <== 0;
|
||||
hash.in[20] <== 0;
|
||||
hash.in[21] <== 0;
|
||||
hash.in[22] <== 0;
|
||||
hash.in[23] <== 1;
|
||||
hash.in[24] <== 0;
|
||||
hash.in[25] <== 1;
|
||||
hash.in[26] <== 1;
|
||||
hash.in[27] <== 0;
|
||||
hash.in[28] <== 0;
|
||||
hash.in[29] <== 1;
|
||||
hash.in[30] <== 0;
|
||||
hash.in[31] <== 0;
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
for(var j=0; j<9; j++){
|
||||
if(i != 0){
|
||||
hash.in[32+256*j+i] <== bitifier[j].out[255-i];
|
||||
} else {
|
||||
hash.in[32+256*j] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component intifier = Bits2Num(253); //Because if the scalar field is 255 bits, we support every number of 254 bits (not all of 255) and we can only compare numbers of 253 bits since we need 1 bit for sign.
|
||||
|
||||
for(var i=0; i<253; i++){
|
||||
intifier.in[i] <== hash.out[253-i];
|
||||
}
|
||||
|
||||
// Compute the threshold
|
||||
signal intermediate_value;
|
||||
signal threshold;
|
||||
intermediate_value <== t0 + t1 * value;
|
||||
threshold <== intermediate_value * value;
|
||||
|
||||
// Ensure that the ticket is winning
|
||||
component isLess2 = BLSLessThan(253);
|
||||
|
||||
isLess2.in[0] <== intifier.out;
|
||||
isLess2.in[1] <== threshold;
|
||||
//isLess2.out === 1;
|
||||
}
|
||||
|
||||
|
||||
template nullifier_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input value;
|
||||
signal output nullifier;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-nullifier" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 2016785505923014207119328528655730;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
nullifier <== hash.out;
|
||||
}
|
||||
|
||||
template commitment_computer(){ // TODO: ensure all field are hash
|
||||
signal input note_nonce;
|
||||
signal input nullifier_public_key;
|
||||
signal input value;
|
||||
signal output commitment;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-commitment" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 516297089516239580383111224192495220;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_public_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
commitment <== hash.out;
|
||||
}
|
||||
|
||||
template nonce_updater(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal output updated_nonce;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-evolve" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 120209783668687835891529317;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== 0;
|
||||
|
||||
updated_nonce <== hash.out;
|
||||
}
|
||||
|
||||
template membership_checker(){
|
||||
signal input leaf; //The note commitment
|
||||
signal input root; //The root of the Merkle Tree (of depth 32)
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input node[32]; //Complementary hashes
|
||||
|
||||
component hash[32];
|
||||
|
||||
for(var i=0; i<32; i++){
|
||||
hash[i] = hash_2_to_1();
|
||||
}
|
||||
|
||||
hash[0].in[0] <== leaf - index[31] * (leaf - node[0]);
|
||||
hash[0].in[1] <== node[0] - index[31] * (node[0] - leaf);
|
||||
|
||||
for(var i=1; i<32; i++){
|
||||
hash[i].in[0] <== hash[i-1].out - index[31-i] * (hash[i-1].out - node[i]);
|
||||
hash[i].in[1] <== node[i] - index[31-i] * (node[i] - hash[i-1].out);
|
||||
}
|
||||
|
||||
//root === hash[31].out;
|
||||
|
||||
}
|
||||
|
||||
template poseidon_sha_proof_of_leadership(){
|
||||
signal input epoch_nonce; //F_p (BLS12-381 scalar field)
|
||||
signal input slot_number; //F_p (BLS12-381 scalar field)
|
||||
signal input t0; // Precomputed threshold elements in F_p
|
||||
signal input t1;
|
||||
signal input commitments_root;
|
||||
|
||||
// Note variables
|
||||
signal input constraints; // Every note field represented as F_p elements for now (constraints are represented by their Merkle root)
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input randomness;
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input nodes[32]; //Merkle proof of the commitment
|
||||
|
||||
signal output nullifier;
|
||||
signal output updated_commiment;
|
||||
|
||||
|
||||
// Check that index inputs are indeed bits
|
||||
component bit_checker = check_bits(32);
|
||||
for(var i=0; i<32; i++){
|
||||
bit_checker.bits[i] <== index[i];
|
||||
}
|
||||
|
||||
// Check that r < threshold
|
||||
component lottery_checker = check_lottery();
|
||||
lottery_checker.epoch_nonce <== epoch_nonce;
|
||||
lottery_checker.slot_number <== slot_number;
|
||||
lottery_checker.t0 <== t0;
|
||||
lottery_checker.t1 <== t1;
|
||||
lottery_checker.constraints <== constraints;
|
||||
lottery_checker.value <== value;
|
||||
lottery_checker.unit <== unit;
|
||||
lottery_checker.state <== state;
|
||||
lottery_checker.note_nonce <== note_nonce;
|
||||
lottery_checker.nullifier_secret_key <== nullifier_secret_key;
|
||||
lottery_checker.randomness <== randomness;
|
||||
|
||||
|
||||
// Compute the note commitment
|
||||
component note_committer = commitment_computer();
|
||||
note_committer.note_nonce <== note_nonce;
|
||||
note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
note_committer.value <== value;
|
||||
|
||||
// Check the commitment membership
|
||||
component membership_checker = membership_checker();
|
||||
membership_checker.leaf <== note_committer.commitment;
|
||||
membership_checker.root <== commitments_root;
|
||||
for(var i =0; i<32; i++){
|
||||
membership_checker.index[i] <== index[i];
|
||||
membership_checker.node[i] <== nodes[i];
|
||||
}
|
||||
|
||||
|
||||
// Compute the note nullifier
|
||||
component nullifier_computer = nullifier_computer();
|
||||
nullifier_computer.note_nonce <== note_nonce;
|
||||
nullifier_computer.nullifier_secret_key <== nullifier_secret_key;
|
||||
nullifier_computer.value <== value;
|
||||
nullifier <== nullifier_computer.nullifier;
|
||||
|
||||
|
||||
// Compute the evolved nonce
|
||||
component nonce_updater = nonce_updater();
|
||||
nonce_updater.note_nonce <== note_nonce;
|
||||
nonce_updater.nullifier_secret_key <== nullifier_secret_key;
|
||||
|
||||
|
||||
// Compute the new note commitment
|
||||
component updated_note_committer = commitment_computer();
|
||||
updated_note_committer.note_nonce <== nonce_updater.updated_nonce;
|
||||
updated_note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
updated_note_committer.value <== value;
|
||||
updated_commiment <== updated_note_committer.commitment;
|
||||
|
||||
}
|
||||
|
||||
|
||||
component main {public [epoch_nonce, slot_number, t0, t1, commitments_root]} = poseidon_sha_proof_of_leadership();
|
||||
@ -1,683 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/circomlib/circuits/sha256/sha256.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/bitify.circom";
|
||||
|
||||
template BLSLessThan(n) {
|
||||
assert(n <= 253);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2Bits(n+1);
|
||||
|
||||
n2b.in <== in[0]+ (1<<n) - in[1];
|
||||
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
template lottery_ticket() {
|
||||
signal input epoch_nonce[256]; //F_p (BLS12-381 scalar field)
|
||||
signal input slot_number[64]; //F_p (BLS12-381 scalar field)
|
||||
signal input note_nonce[256];
|
||||
signal input nullifier_secret_key[256];
|
||||
|
||||
signal output r; //The lottery ticket in F_p
|
||||
|
||||
component hash = Sha256(864);
|
||||
|
||||
//The b"lead" Tag in bits with big endian order
|
||||
hash.in[0] <== 0;
|
||||
hash.in[1] <== 1;
|
||||
hash.in[2] <== 1;
|
||||
hash.in[3] <== 0;
|
||||
hash.in[4] <== 1;
|
||||
hash.in[5] <== 1;
|
||||
hash.in[6] <== 0;
|
||||
hash.in[7] <== 0;
|
||||
hash.in[8] <== 0;
|
||||
hash.in[9] <== 1;
|
||||
hash.in[10] <== 1;
|
||||
hash.in[11] <== 0;
|
||||
hash.in[12] <== 0;
|
||||
hash.in[13] <== 1;
|
||||
hash.in[14] <== 0;
|
||||
hash.in[15] <== 1;
|
||||
hash.in[16] <== 0;
|
||||
hash.in[17] <== 1;
|
||||
hash.in[18] <== 1;
|
||||
hash.in[19] <== 0;
|
||||
hash.in[20] <== 0;
|
||||
hash.in[21] <== 0;
|
||||
hash.in[22] <== 0;
|
||||
hash.in[23] <== 1;
|
||||
hash.in[24] <== 0;
|
||||
hash.in[25] <== 1;
|
||||
hash.in[26] <== 1;
|
||||
hash.in[27] <== 0;
|
||||
hash.in[28] <== 0;
|
||||
hash.in[29] <== 1;
|
||||
hash.in[30] <== 0;
|
||||
hash.in[31] <== 0;
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
hash.in[32+i] <== epoch_nonce[i];
|
||||
if(i<64){
|
||||
hash.in[288+i] <== slot_number[i];
|
||||
}
|
||||
hash.in[352+i] <== nullifier_secret_key[i];
|
||||
hash.in[608+i] <== note_nonce[i];
|
||||
}
|
||||
|
||||
component intifier = Bits2Num(253); //Because if the scalar field is 255 bits, we support every number of 254 bits (not all of 255) and we can only compare numbers of 253 bits since we need 1 bit for sign.
|
||||
|
||||
for(var i=0; i<253; i++){
|
||||
intifier.in[i] <== hash.out[253-i];
|
||||
}
|
||||
|
||||
r <== intifier.out;
|
||||
}
|
||||
|
||||
template check_lottery(){
|
||||
signal input pre_computed_threshold; //253 bits
|
||||
signal input v; //253 bits max
|
||||
signal input epoch_nonce[256];
|
||||
signal input slot_number[64];
|
||||
signal input nullifier_secret_key[256];
|
||||
signal input note_nonce[256];
|
||||
|
||||
component ticket = lottery_ticket();
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
ticket.epoch_nonce[i] <== epoch_nonce[i];
|
||||
if(i<64){
|
||||
ticket.slot_number[i] <== slot_number[i];
|
||||
}
|
||||
ticket.nullifier_secret_key[i] <== nullifier_secret_key[i];
|
||||
ticket.note_nonce[i] <== note_nonce[i];
|
||||
}
|
||||
|
||||
component isLess = BLSLessThan(253);
|
||||
|
||||
isLess.in[0] <== ticket.r;
|
||||
isLess.in[1] <== pre_computed_threshold * v;
|
||||
//isLess.out === 1;
|
||||
}
|
||||
|
||||
template nullifier_computer(){
|
||||
signal input note_nonce[256];
|
||||
signal input nullifier_public_key[256];
|
||||
signal input v[256];
|
||||
signal output nullifier[256];
|
||||
|
||||
component hash = Sha256(880);
|
||||
|
||||
//The b"coin-nullifier" Tag in bits with big endian order
|
||||
hash.in[0] <== 0;
|
||||
hash.in[1] <== 1;
|
||||
hash.in[2] <== 1;
|
||||
hash.in[3] <== 0;
|
||||
hash.in[4] <== 0;
|
||||
hash.in[5] <== 0;
|
||||
hash.in[6] <== 1;
|
||||
hash.in[7] <== 1;
|
||||
hash.in[8] <== 0;
|
||||
hash.in[9] <== 1;
|
||||
hash.in[10] <== 1;
|
||||
hash.in[11] <== 0;
|
||||
hash.in[12] <== 1;
|
||||
hash.in[13] <== 1;
|
||||
hash.in[14] <== 1;
|
||||
hash.in[15] <== 1;
|
||||
hash.in[16] <== 0;
|
||||
hash.in[17] <== 1;
|
||||
hash.in[18] <== 1;
|
||||
hash.in[19] <== 0;
|
||||
hash.in[20] <== 1;
|
||||
hash.in[21] <== 0;
|
||||
hash.in[22] <== 0;
|
||||
hash.in[23] <== 1;
|
||||
hash.in[24] <== 0;
|
||||
hash.in[25] <== 1;
|
||||
hash.in[26] <== 1;
|
||||
hash.in[27] <== 0;
|
||||
hash.in[28] <== 1;
|
||||
hash.in[29] <== 1;
|
||||
hash.in[30] <== 1;
|
||||
hash.in[31] <== 0;
|
||||
hash.in[32] <== 0;
|
||||
hash.in[33] <== 0;
|
||||
hash.in[34] <== 1;
|
||||
hash.in[35] <== 0;
|
||||
hash.in[36] <== 1;
|
||||
hash.in[37] <== 1;
|
||||
hash.in[38] <== 0;
|
||||
hash.in[39] <== 1;
|
||||
hash.in[40] <== 0;
|
||||
hash.in[41] <== 1;
|
||||
hash.in[42] <== 1;
|
||||
hash.in[43] <== 0;
|
||||
hash.in[44] <== 1;
|
||||
hash.in[45] <== 1;
|
||||
hash.in[46] <== 1;
|
||||
hash.in[47] <== 0;
|
||||
hash.in[48] <== 0;
|
||||
hash.in[49] <== 1;
|
||||
hash.in[50] <== 1;
|
||||
hash.in[51] <== 1;
|
||||
hash.in[52] <== 0;
|
||||
hash.in[53] <== 1;
|
||||
hash.in[54] <== 0;
|
||||
hash.in[55] <== 1;
|
||||
hash.in[56] <== 0;
|
||||
hash.in[57] <== 1;
|
||||
hash.in[58] <== 1;
|
||||
hash.in[59] <== 0;
|
||||
hash.in[60] <== 1;
|
||||
hash.in[61] <== 1;
|
||||
hash.in[62] <== 0;
|
||||
hash.in[63] <== 0;
|
||||
hash.in[64] <== 0;
|
||||
hash.in[65] <== 1;
|
||||
hash.in[66] <== 1;
|
||||
hash.in[67] <== 0;
|
||||
hash.in[68] <== 1;
|
||||
hash.in[69] <== 1;
|
||||
hash.in[70] <== 0;
|
||||
hash.in[71] <== 0;
|
||||
hash.in[72] <== 0;
|
||||
hash.in[73] <== 1;
|
||||
hash.in[74] <== 1;
|
||||
hash.in[75] <== 0;
|
||||
hash.in[76] <== 1;
|
||||
hash.in[77] <== 0;
|
||||
hash.in[78] <== 0;
|
||||
hash.in[79] <== 1;
|
||||
hash.in[80] <== 0;
|
||||
hash.in[81] <== 1;
|
||||
hash.in[82] <== 1;
|
||||
hash.in[83] <== 0;
|
||||
hash.in[84] <== 0;
|
||||
hash.in[85] <== 1;
|
||||
hash.in[86] <== 1;
|
||||
hash.in[87] <== 0;
|
||||
hash.in[88] <== 0;
|
||||
hash.in[89] <== 1;
|
||||
hash.in[90] <== 1;
|
||||
hash.in[91] <== 0;
|
||||
hash.in[92] <== 1;
|
||||
hash.in[93] <== 0;
|
||||
hash.in[94] <== 0;
|
||||
hash.in[95] <== 1;
|
||||
hash.in[96] <== 0;
|
||||
hash.in[97] <== 1;
|
||||
hash.in[98] <== 1;
|
||||
hash.in[99] <== 0;
|
||||
hash.in[100] <== 0;
|
||||
hash.in[101] <== 1;
|
||||
hash.in[102] <== 0;
|
||||
hash.in[103] <== 1;
|
||||
hash.in[104] <== 0;
|
||||
hash.in[105] <== 1;
|
||||
hash.in[106] <== 1;
|
||||
hash.in[107] <== 1;
|
||||
hash.in[108] <== 0;
|
||||
hash.in[109] <== 0;
|
||||
hash.in[110] <== 1;
|
||||
hash.in[111] <== 0;
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
hash.in[112+i] <== note_nonce[i];
|
||||
hash.in[368+i] <== nullifier_public_key[i];
|
||||
hash.in[624+i] <== v[i];
|
||||
}
|
||||
for(var i=0; i<256; i++){
|
||||
nullifier[i] <== hash.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
template commitment_computer(){ // TODO: ensure all field are hash
|
||||
signal input note_nonce[256];
|
||||
signal input nullifier_public_key[256];
|
||||
signal input v[256];
|
||||
signal output commitment[256];
|
||||
|
||||
component hash = Sha256(888);
|
||||
|
||||
//The b"coin-commitment" Tag in bits with big endian order
|
||||
hash.in[0] <== 0;
|
||||
hash.in[1] <== 1;
|
||||
hash.in[2] <== 1;
|
||||
hash.in[3] <== 0;
|
||||
hash.in[4] <== 0;
|
||||
hash.in[5] <== 0;
|
||||
hash.in[6] <== 1;
|
||||
hash.in[7] <== 1;
|
||||
hash.in[8] <== 0;
|
||||
hash.in[9] <== 1;
|
||||
hash.in[10] <== 1;
|
||||
hash.in[11] <== 0;
|
||||
hash.in[12] <== 1;
|
||||
hash.in[13] <== 1;
|
||||
hash.in[14] <== 1;
|
||||
hash.in[15] <== 1;
|
||||
hash.in[16] <== 0;
|
||||
hash.in[17] <== 1;
|
||||
hash.in[18] <== 1;
|
||||
hash.in[19] <== 0;
|
||||
hash.in[20] <== 1;
|
||||
hash.in[21] <== 0;
|
||||
hash.in[22] <== 0;
|
||||
hash.in[23] <== 1;
|
||||
hash.in[24] <== 0;
|
||||
hash.in[25] <== 1;
|
||||
hash.in[26] <== 1;
|
||||
hash.in[27] <== 0;
|
||||
hash.in[28] <== 1;
|
||||
hash.in[29] <== 1;
|
||||
hash.in[30] <== 1;
|
||||
hash.in[31] <== 0;
|
||||
hash.in[32] <== 0;
|
||||
hash.in[33] <== 0;
|
||||
hash.in[34] <== 1;
|
||||
hash.in[35] <== 0;
|
||||
hash.in[36] <== 1;
|
||||
hash.in[37] <== 1;
|
||||
hash.in[38] <== 0;
|
||||
hash.in[39] <== 1;
|
||||
hash.in[40] <== 0;
|
||||
hash.in[41] <== 1;
|
||||
hash.in[42] <== 1;
|
||||
hash.in[43] <== 0;
|
||||
hash.in[44] <== 0;
|
||||
hash.in[45] <== 0;
|
||||
hash.in[46] <== 1;
|
||||
hash.in[47] <== 1;
|
||||
hash.in[48] <== 0;
|
||||
hash.in[49] <== 1;
|
||||
hash.in[50] <== 1;
|
||||
hash.in[51] <== 0;
|
||||
hash.in[52] <== 1;
|
||||
hash.in[53] <== 1;
|
||||
hash.in[54] <== 1;
|
||||
hash.in[55] <== 1;
|
||||
hash.in[56] <== 0;
|
||||
hash.in[57] <== 1;
|
||||
hash.in[58] <== 1;
|
||||
hash.in[59] <== 0;
|
||||
hash.in[60] <== 1;
|
||||
hash.in[61] <== 1;
|
||||
hash.in[62] <== 0;
|
||||
hash.in[63] <== 1;
|
||||
hash.in[64] <== 0;
|
||||
hash.in[65] <== 1;
|
||||
hash.in[66] <== 1;
|
||||
hash.in[67] <== 0;
|
||||
hash.in[68] <== 1;
|
||||
hash.in[69] <== 1;
|
||||
hash.in[70] <== 0;
|
||||
hash.in[71] <== 1;
|
||||
hash.in[72] <== 0;
|
||||
hash.in[73] <== 1;
|
||||
hash.in[74] <== 1;
|
||||
hash.in[75] <== 0;
|
||||
hash.in[76] <== 1;
|
||||
hash.in[77] <== 0;
|
||||
hash.in[78] <== 0;
|
||||
hash.in[79] <== 1;
|
||||
hash.in[80] <== 0;
|
||||
hash.in[81] <== 1;
|
||||
hash.in[82] <== 1;
|
||||
hash.in[83] <== 1;
|
||||
hash.in[84] <== 0;
|
||||
hash.in[85] <== 1;
|
||||
hash.in[86] <== 0;
|
||||
hash.in[87] <== 0;
|
||||
hash.in[88] <== 0;
|
||||
hash.in[89] <== 1;
|
||||
hash.in[90] <== 1;
|
||||
hash.in[91] <== 0;
|
||||
hash.in[92] <== 1;
|
||||
hash.in[93] <== 1;
|
||||
hash.in[94] <== 0;
|
||||
hash.in[95] <== 1;
|
||||
hash.in[96] <== 0;
|
||||
hash.in[97] <== 1;
|
||||
hash.in[98] <== 1;
|
||||
hash.in[99] <== 0;
|
||||
hash.in[100] <== 0;
|
||||
hash.in[101] <== 1;
|
||||
hash.in[102] <== 0;
|
||||
hash.in[103] <== 1;
|
||||
hash.in[104] <== 0;
|
||||
hash.in[105] <== 1;
|
||||
hash.in[106] <== 1;
|
||||
hash.in[107] <== 0;
|
||||
hash.in[108] <== 1;
|
||||
hash.in[109] <== 1;
|
||||
hash.in[110] <== 1;
|
||||
hash.in[111] <== 0;
|
||||
hash.in[112] <== 0;
|
||||
hash.in[113] <== 1;
|
||||
hash.in[114] <== 1;
|
||||
hash.in[115] <== 1;
|
||||
hash.in[116] <== 0;
|
||||
hash.in[117] <== 1;
|
||||
hash.in[118] <== 0;
|
||||
hash.in[119] <== 0;
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
hash.in[120+i] <== note_nonce[i];
|
||||
hash.in[376+i] <== nullifier_public_key[i];
|
||||
hash.in[632+i] <== v[i];
|
||||
}
|
||||
for(var i=0; i<256; i++){
|
||||
commitment[i] <== hash.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
template nonce_updater(){
|
||||
signal input note_nonce[256];
|
||||
signal input nullifier_secret_key[256];
|
||||
signal output updated_nonce[256];
|
||||
|
||||
component hash = Sha256(600);
|
||||
|
||||
//The b"coin-evolve" Tag in bits with big endian order
|
||||
hash.in[0] <== 0;
|
||||
hash.in[1] <== 1;
|
||||
hash.in[2] <== 1;
|
||||
hash.in[3] <== 0;
|
||||
hash.in[4] <== 0;
|
||||
hash.in[5] <== 0;
|
||||
hash.in[6] <== 1;
|
||||
hash.in[7] <== 1;
|
||||
hash.in[8] <== 0;
|
||||
hash.in[9] <== 1;
|
||||
hash.in[10] <== 1;
|
||||
hash.in[11] <== 0;
|
||||
hash.in[12] <== 1;
|
||||
hash.in[13] <== 1;
|
||||
hash.in[14] <== 1;
|
||||
hash.in[15] <== 1;
|
||||
hash.in[16] <== 0;
|
||||
hash.in[17] <== 1;
|
||||
hash.in[18] <== 1;
|
||||
hash.in[19] <== 0;
|
||||
hash.in[20] <== 1;
|
||||
hash.in[21] <== 0;
|
||||
hash.in[22] <== 0;
|
||||
hash.in[23] <== 1;
|
||||
hash.in[24] <== 0;
|
||||
hash.in[25] <== 1;
|
||||
hash.in[26] <== 1;
|
||||
hash.in[27] <== 0;
|
||||
hash.in[28] <== 1;
|
||||
hash.in[29] <== 1;
|
||||
hash.in[30] <== 1;
|
||||
hash.in[31] <== 0;
|
||||
hash.in[32] <== 0;
|
||||
hash.in[33] <== 0;
|
||||
hash.in[34] <== 1;
|
||||
hash.in[35] <== 0;
|
||||
hash.in[36] <== 1;
|
||||
hash.in[37] <== 1;
|
||||
hash.in[38] <== 0;
|
||||
hash.in[39] <== 1;
|
||||
hash.in[40] <== 0;
|
||||
hash.in[41] <== 1;
|
||||
hash.in[42] <== 1;
|
||||
hash.in[43] <== 0;
|
||||
hash.in[44] <== 0;
|
||||
hash.in[45] <== 1;
|
||||
hash.in[46] <== 0;
|
||||
hash.in[47] <== 1;
|
||||
hash.in[48] <== 0;
|
||||
hash.in[49] <== 1;
|
||||
hash.in[50] <== 1;
|
||||
hash.in[51] <== 1;
|
||||
hash.in[52] <== 0;
|
||||
hash.in[53] <== 1;
|
||||
hash.in[54] <== 1;
|
||||
hash.in[55] <== 0;
|
||||
hash.in[56] <== 0;
|
||||
hash.in[57] <== 1;
|
||||
hash.in[58] <== 1;
|
||||
hash.in[59] <== 0;
|
||||
hash.in[60] <== 1;
|
||||
hash.in[61] <== 1;
|
||||
hash.in[62] <== 1;
|
||||
hash.in[63] <== 1;
|
||||
hash.in[64] <== 0;
|
||||
hash.in[65] <== 1;
|
||||
hash.in[66] <== 1;
|
||||
hash.in[67] <== 0;
|
||||
hash.in[68] <== 1;
|
||||
hash.in[69] <== 1;
|
||||
hash.in[70] <== 0;
|
||||
hash.in[71] <== 0;
|
||||
hash.in[72] <== 0;
|
||||
hash.in[73] <== 1;
|
||||
hash.in[74] <== 1;
|
||||
hash.in[75] <== 1;
|
||||
hash.in[76] <== 0;
|
||||
hash.in[77] <== 1;
|
||||
hash.in[78] <== 1;
|
||||
hash.in[79] <== 0;
|
||||
hash.in[80] <== 0;
|
||||
hash.in[81] <== 1;
|
||||
hash.in[82] <== 1;
|
||||
hash.in[83] <== 0;
|
||||
hash.in[84] <== 0;
|
||||
hash.in[85] <== 1;
|
||||
hash.in[86] <== 0;
|
||||
hash.in[87] <== 1;
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
hash.in[88+i] <== note_nonce[i];
|
||||
hash.in[344+i] <== nullifier_secret_key[i];
|
||||
}
|
||||
for(var i=0; i<256; i++){
|
||||
updated_nonce[i] <== hash.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
template sha_2_to_1(){
|
||||
signal input a[256];
|
||||
signal input b[256];
|
||||
signal output hash_ab[256];
|
||||
|
||||
component hash = Sha256(512);
|
||||
for(var i=0; i<256; i++){
|
||||
hash.in[i] <== a[i];
|
||||
hash.in[i+256] <== b[i];
|
||||
}
|
||||
|
||||
for(var i=0;i<256;i++){
|
||||
hash_ab[i] <== hash.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
template membership_checker(){
|
||||
signal input commitment[256]; //The note commitment
|
||||
signal input commitments_root[256]; //The root of the Merkle Tree containing every commitments (of depth 32)
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input node[32][256]; //Complementary hashes
|
||||
|
||||
component hash[32];
|
||||
|
||||
for(var i=0; i<32; i++){
|
||||
hash[i] = sha_2_to_1();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
hash[0].a[i] <== commitment[i] - index[31] * (commitment[i] - node[0][i]);
|
||||
hash[0].b[i] <== node[0][i] - index[31] * (node[0][i] - commitment[i]);
|
||||
}
|
||||
|
||||
for(var i=1; i<32; i++){
|
||||
for(var j=0; j<256; j++){
|
||||
hash[i].a[j] <== hash[i-1].hash_ab[j] - index[31] * (hash[i-1].hash_ab[j] - node[i][j]);
|
||||
hash[i].b[j] <== node[i][j] - index[31] * (node[i][j] - hash[i-1].hash_ab[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for(var i=0; i<256; i++){
|
||||
//commitments_root[i] === hash[31].hash_ab[i];
|
||||
}
|
||||
}
|
||||
|
||||
template bits2num_253(){
|
||||
signal input bits[253];
|
||||
signal output value;
|
||||
|
||||
signal intermediate_value[252];
|
||||
intermediate_value[0] <== bits[0] * 2;
|
||||
for(var i=1; i<252; i++){
|
||||
intermediate_value[i] <== intermediate_value[i-1] * 2 + bits[i];
|
||||
}
|
||||
value <== intermediate_value[251] * 2 + bits[252];
|
||||
}
|
||||
|
||||
template check_bits(n){
|
||||
signal input bits[n];
|
||||
for(var i=0; i<n; i++){
|
||||
bits[i] * (1-bits[i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
template sha_proof_of_leadership(){
|
||||
signal input epoch_nonce[256]; //256 bits
|
||||
signal input slot_number[64]; //64 bits
|
||||
signal input pre_computed_threshold; //253 bits
|
||||
signal input commitments_root[256]; //The root of the Merkle Tree containing every commitments (of depth 32) 256 bits
|
||||
|
||||
signal input note_nonce[256];
|
||||
signal input nullifier_secret_key[256];
|
||||
signal input v[253]; //253 bits
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input node[32][256]; //Complementary hashes
|
||||
|
||||
signal output nullifier[256];
|
||||
signal output updated_commiment[256];
|
||||
|
||||
|
||||
// Check that private inputs are indeed bits
|
||||
component bit_checker[36];
|
||||
for(var i=0; i<34; i++){
|
||||
bit_checker[i] = check_bits(256);
|
||||
if(i<32){
|
||||
for(var j=0; j<256; j++){
|
||||
bit_checker[i].bits[j] <== node[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
bit_checker[34] = check_bits(253);
|
||||
bit_checker[35] = check_bits(32);
|
||||
for(var i=0; i<256; i++){
|
||||
bit_checker[32].bits[i] <== note_nonce[i];
|
||||
bit_checker[33].bits[i] <== nullifier_secret_key[i];
|
||||
if(i<253){
|
||||
bit_checker[34].bits[i] <== v[i];
|
||||
}
|
||||
if(i<32){
|
||||
bit_checker[35].bits[i] <== index[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the value of v
|
||||
component bits2num = bits2num_253();
|
||||
for(var i=0; i<253; i++){
|
||||
bits2num.bits[i] <== v[i];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Check that r < threshold
|
||||
component lottery_checker = check_lottery();
|
||||
lottery_checker.pre_computed_threshold <== pre_computed_threshold;
|
||||
lottery_checker.v <== bits2num.value;
|
||||
for(var i=0; i<256; i++){
|
||||
lottery_checker.epoch_nonce[i] <== epoch_nonce[i];
|
||||
if(i<64){
|
||||
lottery_checker.slot_number[i] <== slot_number[i];
|
||||
}
|
||||
lottery_checker.nullifier_secret_key[i] <== nullifier_secret_key[i];
|
||||
lottery_checker.note_nonce[i] <== note_nonce[i];
|
||||
}
|
||||
|
||||
|
||||
// Compute the note commitment
|
||||
component note_committer = commitment_computer();
|
||||
for(var i=0; i<256; i++){
|
||||
note_committer.note_nonce[i] <== note_nonce[i];
|
||||
note_committer.nullifier_public_key[i] <== nullifier_secret_key[i]; // TODO: reflect the nullifier public key computation later when defined
|
||||
}
|
||||
note_committer.v[0] <== 0;
|
||||
note_committer.v[1] <== 0;
|
||||
note_committer.v[2] <== 0;
|
||||
for(var i=0; i<253; i++){
|
||||
note_committer.v[i+3] <== v[i];
|
||||
}
|
||||
|
||||
// Check the commitment membership
|
||||
component membership_checker = membership_checker();
|
||||
for(var i=0; i<256; i++){
|
||||
membership_checker.commitment[i] <== note_committer.commitment[i];
|
||||
membership_checker.commitments_root[i] <== commitments_root[i];
|
||||
for(var j=0; j<32; j++){
|
||||
if(i==0){
|
||||
membership_checker.index[j] <== index[j];
|
||||
}
|
||||
membership_checker.node[j][i] <== node[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the note nullifier
|
||||
component nullifier_computer = nullifier_computer();
|
||||
for(var i=0; i<256; i++){
|
||||
nullifier_computer.note_nonce[i] <== note_nonce[i];
|
||||
nullifier_computer.nullifier_public_key[i] <== nullifier_secret_key[i]; // TODO: reflect the nullifier public key computation later when defined
|
||||
}
|
||||
nullifier_computer.v[0] <== 0;
|
||||
nullifier_computer.v[1] <== 0;
|
||||
nullifier_computer.v[2] <== 0;
|
||||
for(var i=0; i<253; i++){
|
||||
nullifier_computer.v[i+3] <== v[i];
|
||||
}
|
||||
for(var i=0; i<256; i++){
|
||||
nullifier[i] <== nullifier_computer.nullifier[i];
|
||||
}
|
||||
|
||||
|
||||
// Compute the evolved nonce
|
||||
component nonce_updater = nonce_updater();
|
||||
for(var i=0; i<256; i++){
|
||||
nonce_updater.note_nonce[i] <== note_nonce[i];
|
||||
nonce_updater.nullifier_secret_key[i] <== nullifier_secret_key[i];
|
||||
}
|
||||
|
||||
// Compute the new note commitment
|
||||
component updated_note_committer = commitment_computer();
|
||||
for(var i=0; i<256; i++){
|
||||
updated_note_committer.note_nonce[i] <== nonce_updater.updated_nonce[i];
|
||||
updated_note_committer.nullifier_public_key[i] <== nullifier_secret_key[i]; // TODO: reflect the nullifier public key computation later when defined
|
||||
}
|
||||
updated_note_committer.v[0] <== 0;
|
||||
updated_note_committer.v[1] <== 0;
|
||||
updated_note_committer.v[2] <== 0;
|
||||
for(var i=0; i<253; i++){
|
||||
updated_note_committer.v[i+3] <== v[i];
|
||||
}
|
||||
for(var i =0; i<256; i++){
|
||||
updated_commiment[i] <== updated_note_committer.commitment[i];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
component main {public [epoch_nonce, slot_number, pre_computed_threshold, commitments_root]} = sha_proof_of_leadership();
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"cells": [],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,209 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_2_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_4_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_16_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/bitify.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/comparators.circom";
|
||||
include "../../circom_circuits/Jubjub/escalarmulanyJubjub.circom";
|
||||
include "../../circom_circuits/Jubjub/jubjub.circom";
|
||||
|
||||
template BLSLessThan(n) {
|
||||
assert(n <= 253);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2Bits(n+1);
|
||||
|
||||
n2b.in <== in[0]+ (1<<n) - in[1];
|
||||
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
template BLSNum2Bits_strict() {
|
||||
signal input in;
|
||||
signal output out[255];
|
||||
|
||||
component check_range = CompConstant(23487852865797141623554994256013988874373056334117496812739262697960298774528); // -1 - 2**254 (p-1 without its first bit)
|
||||
component n2b = Num2Bits(255);
|
||||
in ==> n2b.in;
|
||||
|
||||
for (var i=0; i<255; i++) {
|
||||
n2b.out[i] ==> out[i];
|
||||
if(i != 0){
|
||||
n2b.out[i] ==> check_range.in[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
check_range.out * (n2b.out[0]) === 0; //must be zero exept if the first bit is 0 => then in is on 254 bits and p-1 on 255
|
||||
}
|
||||
|
||||
template check_bits(n){
|
||||
signal input bits[n];
|
||||
for(var i=0; i<n; i++){
|
||||
bits[i] * (1-bits[i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
template commitment_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_public_key;
|
||||
signal input value;
|
||||
signal input constraints;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal output commitment;
|
||||
|
||||
component hash = hash_16_to_1();
|
||||
|
||||
//The b"coin-commitment" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 516297089516239580383111224192495220;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_public_key;
|
||||
hash.in[3] <== value;
|
||||
hash.in[4] <== constraints;
|
||||
hash.in[5] <== unit;
|
||||
hash.in[6] <== state;
|
||||
for(var i=7; i<16; i++){
|
||||
hash.in[i] <== 0;
|
||||
}
|
||||
|
||||
commitment <== hash.out;
|
||||
}
|
||||
|
||||
template nonce_updater(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal output updated_nonce;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-evolve" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 120209783668687835891529317;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== 0;
|
||||
|
||||
updated_nonce <== hash.out;
|
||||
}
|
||||
|
||||
template nullifier_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input value;
|
||||
signal output nullifier;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-nullifier" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 2016785505923014207119328528655730;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
nullifier <== hash.out;
|
||||
}
|
||||
|
||||
template membership_checker(){
|
||||
signal input note_commitment;
|
||||
signal input pedersen_randomness;
|
||||
signal input pedersen_commitment[2];
|
||||
signal input h_curve_point[2];
|
||||
|
||||
component note_commitment_bitifier = Num2Bits(255);
|
||||
component pedersen_randomness_bitifier = BLSNum2Bits_strict();
|
||||
note_commitment_bitifier.in <== note_commitment;
|
||||
pedersen_randomness_bitifier.in <== pedersen_randomness;
|
||||
|
||||
// A is note_cm * G and B is r * H
|
||||
component A = EscalarMulAny(255);
|
||||
component B = EscalarMulAny(255);
|
||||
|
||||
A.p[0] <== 0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b;
|
||||
A.p[1] <== 0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa;
|
||||
B.p[0] <== h_curve_point[0];
|
||||
B.p[1] <== h_curve_point[1];
|
||||
for(var i =0; i<255; i++){
|
||||
A.e[i] <== note_commitment_bitifier.out[i];
|
||||
B.e[i] <== pedersen_randomness_bitifier.out[i];
|
||||
}
|
||||
|
||||
component pedersen = JubjubAdd();
|
||||
pedersen.x1 <== A.out[0];
|
||||
pedersen.y1 <== A.out[1];
|
||||
pedersen.x2 <== B.out[0];
|
||||
pedersen.y2 <== B.out[1];
|
||||
|
||||
pedersen.xout === pedersen_commitment[0];
|
||||
pedersen.yout === pedersen_commitment[1];
|
||||
|
||||
}
|
||||
|
||||
template caulk_proof_of_validator(minimum_stake){ //TODO: put minimum_stake in the input to change it dynamically
|
||||
signal input pedersen_commitment[2];
|
||||
signal input h_curve_point[2];
|
||||
|
||||
// Note variables
|
||||
signal input constraints; // Every note field represented as F_p elements for now (constraints are represented by their Merkle root)
|
||||
signal input value; // 0 if no more notes needed
|
||||
signal input unit;
|
||||
signal input state; // This field hold the identity of the owner (its public key or ID) and is revealed
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
|
||||
signal input pedersen_randomness;
|
||||
|
||||
signal output nullifiers;
|
||||
signal output updated_commiments;
|
||||
|
||||
// Compute the note commitments
|
||||
component note_committer = commitment_computer();
|
||||
note_committer.note_nonce <== note_nonce;
|
||||
note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
note_committer.value <== value;
|
||||
note_committer.constraints <== constraints;
|
||||
note_committer.unit <== unit;
|
||||
note_committer.state <== state;
|
||||
|
||||
// Check the commitments membership
|
||||
component membership_checker = membership_checker();
|
||||
membership_checker.note_commitment <== note_committer.commitment;
|
||||
membership_checker.pedersen_randomness <== pedersen_randomness;
|
||||
membership_checker.pedersen_commitment[0] <== pedersen_commitment[0];
|
||||
membership_checker.pedersen_commitment[1] <== pedersen_commitment[1];
|
||||
membership_checker.h_curve_point[0] <== h_curve_point[0];
|
||||
membership_checker.h_curve_point[1] <== h_curve_point[1];
|
||||
|
||||
// Check that the value exceed the minimum stake
|
||||
component isLess = BLSLessThan(253);
|
||||
isLess.in[0] <== minimum_stake;
|
||||
isLess.in[1] <== value;
|
||||
isLess.out === 1;
|
||||
|
||||
// Compute the note nullifiers
|
||||
component nullifier_computer = nullifier_computer();
|
||||
nullifier_computer.note_nonce <== note_nonce;
|
||||
nullifier_computer.nullifier_secret_key <== nullifier_secret_key;
|
||||
nullifier_computer.value <== value;
|
||||
nullifiers <== nullifier_computer.nullifier;
|
||||
|
||||
// Compute the evolved nonces
|
||||
component nonce_updater = nonce_updater();
|
||||
nonce_updater.note_nonce <== note_nonce;
|
||||
nonce_updater.nullifier_secret_key <== nullifier_secret_key;
|
||||
|
||||
// Compute the new note commitments
|
||||
component updated_note_committer = commitment_computer();
|
||||
updated_note_committer.note_nonce <== nonce_updater.updated_nonce;
|
||||
updated_note_committer.nullifier_public_key <== nullifier_secret_key; // TODO: reflect the nullifier public key computation later when defined
|
||||
updated_note_committer.value <== value;
|
||||
updated_note_committer.constraints <== constraints;
|
||||
updated_note_committer.unit <== unit;
|
||||
updated_note_committer.state <== state;
|
||||
updated_commiments <== updated_note_committer.commitment;
|
||||
|
||||
}
|
||||
|
||||
|
||||
component main {public [state,pedersen_commitment,h_curve_point]} = caulk_proof_of_validator(10000);
|
||||
@ -1,226 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_2_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_4_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/anemoi/anemoi_16_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/bitify.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/comparators.circom";
|
||||
|
||||
template BLSLessThan(n) {
|
||||
assert(n <= 253);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2Bits(n+1);
|
||||
|
||||
n2b.in <== in[0]+ (1<<n) - in[1];
|
||||
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
template check_bits(n){
|
||||
signal input bits[n];
|
||||
for(var i=0; i<n; i++){
|
||||
bits[i] * (1-bits[i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
template commitment_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_public_key;
|
||||
signal input value;
|
||||
signal input constraints;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal output commitment;
|
||||
|
||||
component hash = hash_16_to_1();
|
||||
|
||||
//The b"coin-commitment" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 516297089516239580383111224192495220;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_public_key;
|
||||
hash.in[3] <== value;
|
||||
hash.in[4] <== constraints;
|
||||
hash.in[5] <== unit;
|
||||
hash.in[6] <== state;
|
||||
for(var i=7; i<16; i++){
|
||||
hash.in[i] <== 0;
|
||||
}
|
||||
|
||||
commitment <== hash.out;
|
||||
}
|
||||
|
||||
template nonce_updater(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal output updated_nonce;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-evolve" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 120209783668687835891529317;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== 0;
|
||||
|
||||
updated_nonce <== hash.out;
|
||||
}
|
||||
|
||||
template nullifier_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input value;
|
||||
signal output nullifier;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-nullifier" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 2016785505923014207119328528655730;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
nullifier <== hash.out;
|
||||
}
|
||||
|
||||
template membership_checker(){
|
||||
signal input leaf; //The note commitment
|
||||
signal input root; //The root of the Merkle Tree (of depth 32)
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input node[32]; //Complementary hashes
|
||||
signal input is_null; //If is_null is 1 we don't check the membership (any value of node and index will be correct)
|
||||
|
||||
component hash[32];
|
||||
|
||||
for(var i=0; i<32; i++){
|
||||
hash[i] = hash_2_to_1();
|
||||
}
|
||||
|
||||
hash[0].in[0] <== leaf - index[31] * (leaf - node[0]);
|
||||
hash[0].in[1] <== node[0] - index[31] * (node[0] - leaf);
|
||||
|
||||
for(var i=1; i<32; i++){
|
||||
hash[i].in[0] <== hash[i-1].out - index[31-i] * (hash[i-1].out - node[i]);
|
||||
hash[i].in[1] <== node[i] - index[31-i] * (node[i] - hash[i-1].out);
|
||||
}
|
||||
|
||||
root === hash[31].out * (1 - is_null);
|
||||
|
||||
}
|
||||
|
||||
template anemoi_proof_of_validator(max_notes){
|
||||
signal input commitments_root;
|
||||
signal input minimum_stake;
|
||||
|
||||
// Note variables
|
||||
signal input constraints[max_notes]; // Every note field represented as F_p elements for now (constraints are represented by their Merkle root)
|
||||
signal input value[max_notes]; // 0 if no more notes needed
|
||||
signal input unit[max_notes];
|
||||
signal input state[max_notes]; // This field hold the identity of the owner (its public key or ID)
|
||||
signal input note_nonce[max_notes];
|
||||
signal input nullifier_secret_key[max_notes];
|
||||
signal input index[max_notes][32]; //Position of the note commitment in bits in big endian
|
||||
signal input nodes[max_notes][32]; //Merkle proof of the commitment
|
||||
|
||||
signal output identity;
|
||||
signal output nullifiers[max_notes];
|
||||
signal output updated_commiments[max_notes];
|
||||
|
||||
|
||||
// Check that index inputs are indeed bits
|
||||
component bit_checker[max_notes];
|
||||
for(var i=0; i<max_notes; i++){
|
||||
bit_checker[i] = check_bits(32);
|
||||
for(var j=0; j<32; j++){
|
||||
bit_checker[i].bits[j] <== index[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the note commitments
|
||||
component note_committer[max_notes];
|
||||
for(var i=0; i<max_notes; i++){
|
||||
note_committer[i] = commitment_computer();
|
||||
note_committer[i].note_nonce <== note_nonce[i];
|
||||
note_committer[i].nullifier_public_key <== nullifier_secret_key[i]; // TODO: reflect the nullifier public key computation later when defined
|
||||
note_committer[i].value <== value[i];
|
||||
note_committer[i].constraints <== constraints[i];
|
||||
note_committer[i].unit <== unit[i];
|
||||
note_committer[i].state <== state[i];
|
||||
}
|
||||
|
||||
//check the identity between the notes
|
||||
identity <== state[0]; // The first note must not be null
|
||||
component is_null[max_notes];
|
||||
is_null[0] = IsZero();
|
||||
is_null[0].in <== value[0];
|
||||
is_null[0].out === 0;
|
||||
signal intermediate[max_notes-1];
|
||||
for(var i=1; i<max_notes; i++){
|
||||
is_null[i] = IsZero();
|
||||
is_null[i].in <== value[i];
|
||||
intermediate[i-1] <== identity * (1 - is_null[i].out);
|
||||
intermediate[i-1] === state[i] * (1 - is_null[i].out);
|
||||
}
|
||||
|
||||
// Check the commitments membership
|
||||
component membership_checker[max_notes];
|
||||
for(var i=0; i<max_notes; i++){
|
||||
membership_checker[i] = membership_checker();
|
||||
membership_checker[i].leaf <== note_committer[i].commitment;
|
||||
membership_checker[i].root <== commitments_root * (1- is_null[i].out); // Set the root at 0 is note is null
|
||||
membership_checker[i].is_null <== is_null[i].out;
|
||||
for(var j =0; j<32; j++){
|
||||
membership_checker[i].index[j] <== index[i][j];
|
||||
membership_checker[i].node[j] <== nodes[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the value exceed the minimum stake
|
||||
signal sum[max_notes-1];
|
||||
sum[0] <== value[0] + value[1];
|
||||
for(var i = 1; i<max_notes-1; i++){
|
||||
sum[i] <== sum[i-1] + value[i+1];
|
||||
}
|
||||
component isLess = BLSLessThan(253);
|
||||
isLess.in[0] <== minimum_stake;
|
||||
isLess.in[1] <== sum[max_notes-2];
|
||||
isLess.out === 1;
|
||||
|
||||
|
||||
// Compute the note nullifiers
|
||||
component nullifier_computer[max_notes];
|
||||
for(var i=0; i<max_notes; i++){
|
||||
nullifier_computer[i] = nullifier_computer();
|
||||
nullifier_computer[i].note_nonce <== note_nonce[i];
|
||||
nullifier_computer[i].nullifier_secret_key <== nullifier_secret_key[i];
|
||||
nullifier_computer[i].value <== value[i];
|
||||
nullifiers[i] <== nullifier_computer[i].nullifier;
|
||||
}
|
||||
|
||||
// Compute the evolved nonces
|
||||
component nonce_updater[max_notes];
|
||||
for(var i=0; i<max_notes; i++) {
|
||||
nonce_updater[i] = nonce_updater();
|
||||
nonce_updater[i].note_nonce <== note_nonce[i];
|
||||
nonce_updater[i].nullifier_secret_key <== nullifier_secret_key[i];
|
||||
}
|
||||
|
||||
|
||||
// Compute the new note commitments
|
||||
component updated_note_committer[max_notes];
|
||||
for(var i=0; i<max_notes; i++) {
|
||||
updated_note_committer[i] = commitment_computer();
|
||||
updated_note_committer[i].note_nonce <== nonce_updater[i].updated_nonce;
|
||||
updated_note_committer[i].nullifier_public_key <== nullifier_secret_key[i]; // TODO: reflect the nullifier public key computation later when defined
|
||||
updated_note_committer[i].value <== value[i];
|
||||
updated_note_committer[i].constraints <== constraints[i];
|
||||
updated_note_committer[i].unit <== unit[i];
|
||||
updated_note_committer[i].state <== state[i];
|
||||
updated_commiments[i] <== updated_note_committer[i].commitment;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
component main {public [commitments_root, minimum_stake]} = anemoi_proof_of_validator(50);
|
||||
@ -1,227 +0,0 @@
|
||||
//test
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_2_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_4_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/hash/poseidon/poseidon_16_to_1_Jubjub.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/bitify.circom";
|
||||
include "../../circom_circuits/circomlib/circuits/comparators.circom";
|
||||
|
||||
template BLSLessThan(n) {
|
||||
assert(n <= 253);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2Bits(n+1);
|
||||
|
||||
n2b.in <== in[0]+ (1<<n) - in[1];
|
||||
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
template check_bits(n){
|
||||
signal input bits[n];
|
||||
for(var i=0; i<n; i++){
|
||||
bits[i] * (1-bits[i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
template commitment_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_public_key;
|
||||
signal input value;
|
||||
signal input constraints;
|
||||
signal input unit;
|
||||
signal input state;
|
||||
signal output commitment;
|
||||
|
||||
component hash = hash_16_to_1();
|
||||
|
||||
//The b"coin-commitment" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 516297089516239580383111224192495220;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_public_key;
|
||||
hash.in[3] <== value;
|
||||
hash.in[4] <== constraints;
|
||||
hash.in[5] <== unit;
|
||||
hash.in[6] <== state;
|
||||
for(var i=7; i<16; i++){
|
||||
hash.in[i] <== 0;
|
||||
}
|
||||
|
||||
commitment <== hash.out;
|
||||
}
|
||||
|
||||
template nullifier_computer(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal input value;
|
||||
signal output nullifier;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-nullifier" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 2016785505923014207119328528655730;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== value;
|
||||
|
||||
nullifier <== hash.out;
|
||||
}
|
||||
|
||||
template nonce_updater(){
|
||||
signal input note_nonce;
|
||||
signal input nullifier_secret_key;
|
||||
signal output updated_nonce;
|
||||
|
||||
component hash = hash_4_to_1();
|
||||
|
||||
//The b"coin-evolve" Tag converted in F_p element (from bits with big endian order)
|
||||
hash.in[0] <== 120209783668687835891529317;
|
||||
hash.in[1] <== note_nonce;
|
||||
hash.in[2] <== nullifier_secret_key;
|
||||
hash.in[3] <== 0;
|
||||
|
||||
updated_nonce <== hash.out;
|
||||
}
|
||||
|
||||
template membership_checker(){
|
||||
signal input leaf; //The note commitment
|
||||
signal input root; //The root of the Merkle Tree (of depth 32)
|
||||
signal input index[32]; //Position of the note commitment in bits in big endian
|
||||
signal input node[32]; //Complementary hashes
|
||||
signal input is_null; //If is_null is 1 we don't check the membership (any value of node and index will be correct)
|
||||
|
||||
component hash[32];
|
||||
|
||||
for(var i=0; i<32; i++){
|
||||
hash[i] = hash_2_to_1();
|
||||
}
|
||||
|
||||
hash[0].in[0] <== leaf - index[31] * (leaf - node[0]);
|
||||
hash[0].in[1] <== node[0] - index[31] * (node[0] - leaf);
|
||||
|
||||
for(var i=1; i<32; i++){
|
||||
hash[i].in[0] <== hash[i-1].out - index[31-i] * (hash[i-1].out - node[i]);
|
||||
hash[i].in[1] <== node[i] - index[31-i] * (node[i] - hash[i-1].out);
|
||||
}
|
||||
|
||||
root === hash[31].out * (1 - is_null);
|
||||
|
||||
}
|
||||
|
||||
template poseidon_proof_of_validator(max_notes){
|
||||
signal input commitments_root;
|
||||
signal input minimum_stake;
|
||||
|
||||
// Note variables
|
||||
signal input constraints[max_notes]; // Every note field represented as F_p elements for now (constraints are represented by their Merkle root)
|
||||
signal input value[max_notes]; // 0 if no more notes needed
|
||||
signal input unit[max_notes];
|
||||
signal input state[max_notes]; // This field hold the identity of the owner (its public key or ID)
|
||||
signal input note_nonce[max_notes];
|
||||
signal input nullifier_secret_key[max_notes];
|
||||
signal input index[max_notes][32]; //Position of the note commitment in bits in big endian
|
||||
signal input nodes[max_notes][32]; //Merkle proof of the commitment
|
||||
|
||||
signal output identity;
|
||||
signal output nullifiers[max_notes];
|
||||
signal output updated_commiments[max_notes];
|
||||
|
||||
|
||||
// Check that index inputs are indeed bits
|
||||
component bit_checker[max_notes];
|
||||
for(var i=0; i<max_notes; i++){
|
||||
bit_checker[i] = check_bits(32);
|
||||
for(var j=0; j<32; j++){
|
||||
bit_checker[i].bits[j] <== index[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the note commitments
|
||||
component note_committer[max_notes];
|
||||
for(var i=0; i<max_notes; i++){
|
||||
note_committer[i] = commitment_computer();
|
||||
note_committer[i].note_nonce <== note_nonce[i];
|
||||
note_committer[i].nullifier_public_key <== nullifier_secret_key[i]; // TODO: reflect the nullifier public key computation later when defined
|
||||
note_committer[i].value <== value[i];
|
||||
note_committer[i].constraints <== constraints[i];
|
||||
note_committer[i].unit <== unit[i];
|
||||
note_committer[i].state <== state[i];
|
||||
}
|
||||
|
||||
//check the identity between the notes
|
||||
identity <== state[0]; // The first note must not be null
|
||||
component is_null[max_notes];
|
||||
is_null[0] = IsZero();
|
||||
is_null[0].in <== value[0];
|
||||
is_null[0].out === 0;
|
||||
signal intermediate[max_notes-1];
|
||||
for(var i=1; i<max_notes; i++){
|
||||
is_null[i] = IsZero();
|
||||
is_null[i].in <== value[i];
|
||||
intermediate[i-1] <== identity * (1 - is_null[i].out);
|
||||
intermediate[i-1] === state[i] * (1 - is_null[i].out);
|
||||
}
|
||||
|
||||
// Check the commitments membership
|
||||
component membership_checker[max_notes];
|
||||
for(var i=0; i<max_notes; i++){
|
||||
membership_checker[i] = membership_checker();
|
||||
membership_checker[i].leaf <== note_committer[i].commitment;
|
||||
membership_checker[i].root <== commitments_root * (1- is_null[i].out); // Set the root at 0 is note is null
|
||||
membership_checker[i].is_null <== is_null[i].out;
|
||||
for(var j =0; j<32; j++){
|
||||
membership_checker[i].index[j] <== index[i][j];
|
||||
membership_checker[i].node[j] <== nodes[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the value exceed the minimum stake
|
||||
signal sum[max_notes-1];
|
||||
sum[0] <== value[0] + value[1];
|
||||
for(var i = 1; i<max_notes-1; i++){
|
||||
sum[i] <== sum[i-1] + value[i+1];
|
||||
}
|
||||
component isLess = BLSLessThan(253);
|
||||
isLess.in[0] <== minimum_stake;
|
||||
isLess.in[1] <== sum[max_notes-2];
|
||||
isLess.out === 1;
|
||||
|
||||
|
||||
// Compute the note nullifiers
|
||||
component nullifier_computer[max_notes];
|
||||
for(var i=0; i<max_notes; i++){
|
||||
nullifier_computer[i] = nullifier_computer();
|
||||
nullifier_computer[i].note_nonce <== note_nonce[i];
|
||||
nullifier_computer[i].nullifier_secret_key <== nullifier_secret_key[i];
|
||||
nullifier_computer[i].value <== value[i];
|
||||
nullifiers[i] <== nullifier_computer[i].nullifier;
|
||||
}
|
||||
|
||||
// Compute the evolved nonces
|
||||
component nonce_updater[max_notes];
|
||||
for(var i=0; i<max_notes; i++) {
|
||||
nonce_updater[i] = nonce_updater();
|
||||
nonce_updater[i].note_nonce <== note_nonce[i];
|
||||
nonce_updater[i].nullifier_secret_key <== nullifier_secret_key[i];
|
||||
}
|
||||
|
||||
|
||||
// Compute the new note commitments
|
||||
component updated_note_committer[max_notes];
|
||||
for(var i=0; i<max_notes; i++) {
|
||||
updated_note_committer[i] = commitment_computer();
|
||||
updated_note_committer[i].note_nonce <== nonce_updater[i].updated_nonce;
|
||||
updated_note_committer[i].nullifier_public_key <== nullifier_secret_key[i]; // TODO: reflect the nullifier public key computation later when defined
|
||||
updated_note_committer[i].value <== value[i];
|
||||
updated_note_committer[i].constraints <== constraints[i];
|
||||
updated_note_committer[i].unit <== unit[i];
|
||||
updated_note_committer[i].state <== state[i];
|
||||
updated_commiments[i] <== updated_note_committer[i].commitment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
component main {public [commitments_root, minimum_stake]} = poseidon_proof_of_validator(50);
|
||||
Loading…
x
Reference in New Issue
Block a user