From 5712cf05a1dc6b1321bdb4660bb1b0620f38e70e Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:11:09 +0000 Subject: [PATCH 01/13] test: add masking test --- examples/CRISP/circuits/src/main.nr | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index 5f35542770..62c985921e 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -84,7 +84,11 @@ fn main( p1is, p2is, ); +<<<<<<< HEAD +======= + +>>>>>>> caabae5e (test: add masking test) let ct_add: CiphertextAddition<2048, 1, 54, 54, 54> = CiphertextAddition::new( params.crypto_params(), ct0is, @@ -108,9 +112,11 @@ fn main( // If the voter is eligible to vote, output the ciphertext. // Otherwise, output the sum of the ciphertexts. - if (is_signature_valid == true) - & (merkle_root_calculated == merkle_root) - & (slot_address == address) { + if + (is_signature_valid == true) + & (merkle_root_calculated == merkle_root) + & (slot_address == address) + { // @todo: need to check if vote <= balance. // Verify the correct coefficient values. From 6992d796fb67669f8ba6773382fe4e9915ee9435 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:55:16 +0000 Subject: [PATCH 02/13] chore: debugging --- examples/CRISP/circuits/src/main.nr | 49 ++++++++++++++++++++-------- examples/CRISP/circuits/src/utils.nr | 28 +++++++++------- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index 62c985921e..25674a9f50 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -84,11 +84,7 @@ fn main( p1is, p2is, ); -<<<<<<< HEAD - -======= ->>>>>>> caabae5e (test: add masking test) let ct_add: CiphertextAddition<2048, 1, 54, 54, 54> = CiphertextAddition::new( params.crypto_params(), ct0is, @@ -119,20 +115,45 @@ fn main( { // @todo: need to check if vote <= balance. - // Verify the correct coefficient values. - check_coefficient_values(k1, params.crypto_params().q_mod_t); + // Verify conditions based on voter status + // For voter: coefficients must be binary (or we're not a voter, so we don't care) + let voter_check_passes = are_coefficients_binary | (is_voter == false); + + // For non-voter: coefficients must be zero AND ct_add must be valid (or we ARE a voter, so we don't care) + let non_voter_check_passes = (are_coefficients_zero & is_ct_add_valid) | is_voter; + + // Both checks must pass + // assert(voter_check_passes); + // assert(non_voter_check_passes); - (ct0is, ct1is) - } else { - // check if vote == 0. - check_coefficient_zero(k1); + (ct0is, ct1is) + + // // Overall validity check + // let all_conditions_valid = (is_voter & voter_conditions_met) | (!is_voter & non_voter_conditions_met); + // assert(all_conditions_valid); - // @todo: need to check if slot is empty (no previous ciphertext). - // If so, (ct0is, ct1is) should be returned. + // // Conditional selection using Field arithmetic + // // Convert bool to Field (true = 1, false = 0) + // let selector = is_voter as Field; // as well as the sum assert(is_ct_add_valid); - (sum_ct0is, sum_ct1is) - } + // // @todo: need to check if slot is empty (no previous ciphertext). + // // If so, (ct0is, ct1is) should be returned. + + // // For each coefficient in the polynomials, select based on is_voter + // let mut coefficients_ct0: [Field; 2048] = [0; 2048]; + // let mut coefficients_ct1: [Field; 2048] = [0; 2048]; + + // for i in 0..2048 { + // coefficients_ct0[i] = selector * ct0is[0].coefficients[i] + (1 - selector) * sum_ct0is[0].coefficients[i]; + + // coefficients_ct1[i] = selector * ct1is[0].coefficients[i] + (1 - selector) * sum_ct1is[0].coefficients[i]; + // } + + // let mut output_ct0: [Polynomial<2048>; 1] = [Polynomial::new(coefficients_ct0)]; + // let mut output_ct1: [Polynomial<2048>; 1] = [Polynomial::new(coefficients_ct1)]; + + // (output_ct0, output_ct1) } diff --git a/examples/CRISP/circuits/src/utils.nr b/examples/CRISP/circuits/src/utils.nr index 1300072f87..4ace4cff69 100644 --- a/examples/CRISP/circuits/src/utils.nr +++ b/examples/CRISP/circuits/src/utils.nr @@ -7,7 +7,7 @@ use polynomial::Polynomial; // Check that all valid coefficients are either 0 or 1 -pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) { +pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) -> bool { // This value would allow to fit // 268435456 for yes and 268435456 for no // which would fit the supply of most tokens really (imagining one user just holds all tokens) @@ -17,22 +17,28 @@ pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) { let START_INDEX_Y = HALF_D - HALF_LARGEST_MINIMUM_DEGREE; let START_INDEX_N = D - HALF_LARGEST_MINIMUM_DEGREE; + let mut all_valid = true; + // Loop through all coefficients in the space where we could have a vote // yes part for i in START_INDEX_Y..HALF_D { let coeff = k1.coefficients[i]; - assert(0 == coeff * (q_mod_t - coeff)); + let is_valid = (0 == coeff * (q_mod_t - coeff)); + all_valid = all_valid & is_valid; } // no part for i in START_INDEX_N..D { let coeff = k1.coefficients[i]; - assert(0 == coeff * (q_mod_t - coeff)); + let is_valid = (0 == coeff * (q_mod_t - coeff)); + all_valid = all_valid & is_valid; } + + all_valid } // Check that the polynomial has 0 for all relevant coefficients -pub fn check_coefficient_zero(k1: Polynomial) { +pub fn check_coefficient_zero(k1: Polynomial) -> bool { // This value would allow to fit // 268435456 for yes and 268435456 for no // which would fit the supply of most tokens really (imagining one user just holds all tokens) @@ -55,7 +61,7 @@ pub fn check_coefficient_zero(k1: Polynomial) { sum += k1.coefficients[i]; } - assert(sum == 0); + sum == 0 } #[test] @@ -69,10 +75,10 @@ fn test_check_coefficient_values_pass() { ], }; - check_coefficient_values(pol, 1); + assert(check_coefficient_values(pol, 1) == true); } -#[test(should_fail)] +#[test] fn test_check_coefficient_values_fail() { let pol = Polynomial { coefficients: [ @@ -83,10 +89,10 @@ fn test_check_coefficient_values_fail() { ], }; - check_coefficient_values(pol, 1); + assert(check_coefficient_values(pol, 1) == false); } -#[test(should_fail)] +#[test] fn test_check_coefficient_zero_fail() { let pol = Polynomial { coefficients: [ @@ -97,7 +103,7 @@ fn test_check_coefficient_zero_fail() { ], }; - check_coefficient_zero(pol); + assert(check_coefficient_zero(pol) == false); } #[test] @@ -111,5 +117,5 @@ fn test_check_coefficient_zero() { ], }; - check_coefficient_zero(pol); + assert(check_coefficient_zero(pol) == true); } From 1117c9b1f4a0f248d369da622b75900ed6458645 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:04:32 +0000 Subject: [PATCH 03/13] chore: test fail merkle root --- examples/CRISP/circuits/src/main.nr | 57 +++++++------------ examples/CRISP/packages/crisp-sdk/src/vote.ts | 3 +- .../packages/crisp-sdk/tests/vote.test.ts | 3 + 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index 25674a9f50..a3f76cadcc 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -52,7 +52,8 @@ fn main( slot_address: pub Field, // Balance Section. balance: Field, -) -> pub ([Polynomial<2048>; 1], [Polynomial<2048>; 1]) { +// ) -> pub ([Polynomial<2048>; 1], [Polynomial<2048>; 1]) { +) -> pub bool { // Verify the ECDSA signature. let is_signature_valid = verify_signature(hashed_message, public_key_x, public_key_y, signature); @@ -69,6 +70,8 @@ fn main( merkle_proof_siblings, ); + println(merkle_root_calculated); + let greco: Greco<2048, 1, 54, 54, 5, 5, 20, 20, 54, 16, 54> = Greco::new( params, pk0is, @@ -115,45 +118,27 @@ fn main( { // @todo: need to check if vote <= balance. - // Verify conditions based on voter status - // For voter: coefficients must be binary (or we're not a voter, so we don't care) - let voter_check_passes = are_coefficients_binary | (is_voter == false); - - // For non-voter: coefficients must be zero AND ct_add must be valid (or we ARE a voter, so we don't care) - let non_voter_check_passes = (are_coefficients_zero & is_ct_add_valid) | is_voter; - - // Both checks must pass - // assert(voter_check_passes); - // assert(non_voter_check_passes); + // assert(merkle_root_calculated == merkle_root); + if + // (is_signature_valid == true) + (merkle_root_calculated == merkle_root) + // (slot_address == address) + { + // @todo: need to check if vote <= balance. - (ct0is, ct1is) - - // // Overall validity check - // let all_conditions_valid = (is_voter & voter_conditions_met) | (!is_voter & non_voter_conditions_met); - // assert(all_conditions_valid); + // Verify the correct coefficient values. + check_coefficient_values(k1, params.crypto_params().q_mod_t); - // // Conditional selection using Field arithmetic - // // Convert bool to Field (true = 1, false = 0) - // let selector = is_voter as Field; + // (ct0is, ct1is) + true + } else { + // check if vote == 0. + check_coefficient_zero(k1); // as well as the sum assert(is_ct_add_valid); - // // @todo: need to check if slot is empty (no previous ciphertext). - // // If so, (ct0is, ct1is) should be returned. - - // // For each coefficient in the polynomials, select based on is_voter - // let mut coefficients_ct0: [Field; 2048] = [0; 2048]; - // let mut coefficients_ct1: [Field; 2048] = [0; 2048]; - - // for i in 0..2048 { - // coefficients_ct0[i] = selector * ct0is[0].coefficients[i] + (1 - selector) * sum_ct0is[0].coefficients[i]; - - // coefficients_ct1[i] = selector * ct1is[0].coefficients[i] + (1 - selector) * sum_ct1is[0].coefficients[i]; - // } - - // let mut output_ct0: [Polynomial<2048>; 1] = [Polynomial::new(coefficients_ct0)]; - // let mut output_ct1: [Polynomial<2048>; 1] = [Polynomial::new(coefficients_ct1)]; - - // (output_ct0, output_ct1) + // (sum_ct0is, sum_ct1is) + false + } } diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index ae2f35b2ab..601bb3ae8f 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -253,7 +253,8 @@ export const generateProof = async (crispInputs: CRISPCircuitInputs): Promise { slotAddress: account.address.toLowerCase(), }) + // console.log('Generated circuit inputs, generating proof...', merkleProof); + const proof = await generateProof(inputs) const isValid = await verifyProof(proof) @@ -238,6 +240,7 @@ describe('Vote', () => { let maskVote = await generateMaskVote(publicKey, encryptedVote, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress) + maskVote.k1[0] = '1' const proof = await generateProof(maskVote) const isValid = await verifyProof(proof) From 51f6fa46221a5582745adafce2c8a07d41c372a8 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:36:28 +0000 Subject: [PATCH 04/13] test: ecdsa + merkle root --- .../CRISP/packages/crisp-sdk/src/utils.ts | 1 + .../tests/signature-merkle-circuit.json | 1 + .../packages/crisp-sdk/tests/vote.test.ts | 65 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 examples/CRISP/packages/crisp-sdk/tests/signature-merkle-circuit.json diff --git a/examples/CRISP/packages/crisp-sdk/src/utils.ts b/examples/CRISP/packages/crisp-sdk/src/utils.ts index 804013b60f..2b125a08ce 100644 --- a/examples/CRISP/packages/crisp-sdk/src/utils.ts +++ b/examples/CRISP/packages/crisp-sdk/src/utils.ts @@ -48,6 +48,7 @@ export const generateMerkleProof = ( } const leaf = hashLeaf(address, balance.toString()) + console.log("leaf hash", leaf); const index = leaves.findIndex((l) => l === leaf) diff --git a/examples/CRISP/packages/crisp-sdk/tests/signature-merkle-circuit.json b/examples/CRISP/packages/crisp-sdk/tests/signature-merkle-circuit.json new file mode 100644 index 0000000000..00e5df59de --- /dev/null +++ b/examples/CRISP/packages/crisp-sdk/tests/signature-merkle-circuit.json @@ -0,0 +1 @@ +{"noir_version":"1.0.0-beta.13+6e469c3004209a8b107e7707306e25c80a110fd6","hash":"13412045801339707878","abi":{"parameters":[{"name":"public_key_x","type":{"kind":"array","length":32,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"public_key_y","type":{"kind":"array","length":32,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"signature","type":{"kind":"array","length":64,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"hashed_message","type":{"kind":"array","length":32,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"merkle_root","type":{"kind":"field"},"visibility":"public"},{"name":"merkle_proof_length","type":{"kind":"integer","sign":"unsigned","width":32},"visibility":"private"},{"name":"merkle_proof_indices","type":{"kind":"array","length":20,"type":{"kind":"integer","sign":"unsigned","width":1}},"visibility":"private"},{"name":"merkle_proof_siblings","type":{"kind":"array","length":20,"type":{"kind":"field"}},"visibility":"private"},{"name":"slot_address","type":{"kind":"field"},"visibility":"public"},{"name":"balance","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"field"},"visibility":"public"},"error_types":{"819864067177566446":{"error_kind":"string","string":"Field failed to decompose into specified 8 limbs"}}},"bytecode":"H4sIAAAAAAAA/+2dB3QU1xWG70qA6L1X0Tvs7Epo5QbuvfdOE+694d57792Oe+92epzeYwKBQCAQCAQCgUAgEAjEdu61Zqxh9GvRav6353C075x/R/uNuLw3M+/O/VezMwmpbuUJkYpE9c+2KPaX1ppHWAKwIsCKAWsCWFPAmgFWAlhzwFoA1hKwVoC1BqwNYG0BawdYe8A6ANYRsE6AdQasC2BdAesGWHfAegDWE7BegPUGrA9gfQHrB1gpYP0BGwDYQMAGATYYsCGADQVsGGDDARsB2EjARgE2GrAxgI0FLAmYB1gKsDRgZYCVAzYOsArAMoBVArYLYLsCthtguwO2B2DjAZsA2J6A7QXY3oDtA9i+gO0H2P6AHQDYgYAdBNjBgB0C2KGAHQbY4YAdAdiRgB0F2NGAHQPYsYAdB9jxgJ0A2ImAnQTYyYCdAtipgJ0G2OmATQRsEmCTAZsC2FTAqgCbBtgZgJ0J2FmAnQ3YOYCdC9h5gJ0P2AWAXQjYRYBdDNglgF0K2GWAXQ7YFYBNB+xKwK4C7GrArgHsWsCuA+x6wG4A7EbAbgLsZsBuAexWwG4D7HbA7gDsTsDuAuxuwO4B7F7A7gPsfsAeAOxBwB4C7GHAHgHsUcAeA+xxwJ4A7EnAngLsacCeAexZwJ4D7HnAXlCVRtiL/jLMXgLsZcBeAexVwF4D7HXA3gDsTcDeAuxtwN4B7F3A3gPsfcA+AOxDwD4C7GPAPvGX5uOsFUnt1txfTvCXyXjNo8XykskEJ5ZnL0WMWF71ojh+LC/4oUncWF7Nj03jxfLCb5rFieVt/7aEd3wlBbQGxvaiIDw/PvOXM+wlPKlsxacRNiPUiaC1jNe5Wp2lxSpMrBwj7RwTK50cV1ZWVZGq8tLepGSqcnKmPFlWPnlcxst45ZnyqalMOl2VKctUVE6urEhWJsvSVd608sr0ND/yZw2P5UViJWeIOJnwRZE5FbefzDH/PsuYc40dzi0W1/JNE6k5wUdbwuF2YYwlaDNddngmOEDidn6m8BL4LOJ4XW3DWSBu3G04S7jJYEf9y7F5M/xx7+jY2VFLT/GqkpPGTUlOq27M/f0H4jYMJxeLa5+k5tMhNBdeIdNCeIVMS+EVMq2EV8i0Fl4h00Z4hUxb4RUy7cRNwSDxjt2sDmG2v5xjL+FJZSuiDmGOuHcIhYmVS6TGN7HiOoTZwjuhzRFxMuHZDoE55j+KG4dgcfPtEGaTxxK0uS47PFf4DmGu8BL4POJ4XW3DecJ3CPOEmwzYDmGOP262Q2Du7z8Rt2E4uVjcfDuE9sIrZDoIr5DpKLxCppPwCpnOwitkugivkOkqvEKmm7gpGCTesZvVIcz3lwvsJTypbEXUISwQ9w6hMLFyidT4JlZchzBfeCe0BSJOJjzbITDH/Gdx4xAsbr4dwnzyWIK20GWHFwrfISwUXgJfRByvq224SPgOYZFwkwHbISzwx812CMz9/RfiNgwnF4ubb4fQXXiFTA/hFTI9hVfI9BJeIdNbeIVMH+EVMn2FV8j0EzcFg8Q7drM6hMX+com9hCeVrYg6hCXi3iEUJlYukRrfxIrrEBYL74S2RNxMeLZDYI75r+LGIVjcfDuExeSxBG2pyw4vFb5DWCq8BL6MOF5X23CZ8B3CMuEmA7ZDWOKPm+0QmPv7b8RtGE4uFjffDqFUeIVMf+EVMgOEV8gMFF4hM0h4hcxg4RUyQ4RXyAwVNwWDxDt2szqE5f5yhb2EJ5WtiDqEFeLeIZRKYWLVP1Ljm1hxHcJy4Z3QVog4mfBsh8Ac89/FjUOwuPl2CMvJYwnaSpcdXil8h7BSeAl8FXG8rrbhKuE7hFXCTQZsh7DCHzfbITD39z+I2zCcXCxuvh3CMOEVMsOFV8iMEF4hM1J4hcwo4RUyo4VXyIwRXiEzVtwUDBLv2M3qEFb7yzX2Ep5UtiLqENaIe4dQmFi5RGp8EyuuQ1gtvBPaGhEnE57tEJhj/qe4cQgWN98OYTV5LEFb67LDa4XvENYKL4GvI47X1TZcJ3yHsE64yYDtENb442Y7BOb+/hdxG4aTi8XNt0NICq+Q8YRXyKSEV8ikhVfIlAmvkCkXXiEzTniFTIW4KRgk3rGb1SGs95cb7CU8qWxF1CFsEPcOoTCxconU+CZWXIewXngntA0iTiY82yEwx/xvceMQLG6+HcJ68liCttFlhzcK3yFsFF4C30Qcr6ttuEn4DmGTcJMB2yFs8MfNdgjM/f0f4jYMJxeLm2+HkBFeIVMpvEJmF+EVMrsKr5DZTXiFzO7CK2T2EF4hM17cFAwS79jN6hA2+8st9hKeVLYi6hC2iHuHUJhYuURqfBMrrkPYLLwT2hYRJxOe7RCYY/6vuHEIFjffDmEzeSxB2+qyw1uF7xC2Ci+BbyOO19U23CZ8h7BNuMmA7RC2+ONmOwTm/v4fcRuGk4vFHe//3FK2v3V7+CaN4duxhL94Gb7EOnwxRfhj02iBxNpv5HjJQrxCvGwtQ47H7l8hXuOL97nqC9WXFlATbUJVpCpWNVE1VTVTlaiaq1qoWqpaqVqr2qjaqtqp2qs6qDqqOqk6q7okqgtPOzcWS+3GPhd/Ttw+oLus2Blnsf1HkQTbNvh0rqu+6abqruqh6qnqpeqt6hO4guCkbr8cfRBVN8C6A9YDsJ6A9QKsN2B9EjWDCFoL7s7Y7qCJW2jZtosbq+qr8i/pdSPFsjF2p8Sq3l494sdKBZ8a9Iwbq6zmE4he8WIlw59m9I4TK7X9JyN9EryklM9k+oUUkilKpn31TT9Vqaq/aoBqoGqQanA0mfYFSa0fYKWA9QdsAGADARsE2OA8JNPwQRM3mfYlJtN+xGRaSkym/YnJdAAxmQ4kJtNBxGQ6eCdNpl9KIZmiZDpE3wxVDVMNV41QjVSNUo2OJtMhIKkNBWwYYMMBGwHYSMBGATY6D8k0fNDETaZDiMl0KDGZDiMm0+HEZDqCmExHEpPpKGIyHb2TJlNJFJIpSqZj9M1Yla20P5ylVGlVmao8mkzHgKQ2FrAkYB5gKcDSgJUBVp6HZCoJXjIdQ0ymY4nJNElMph4xmaaIyTRNTKZlxGRa7igpdSHPg99JPcacrNc8qK/jSNYnVr0dR8WOY+XgOKbsKNbgXI6RiuyxcizCUtli5VyEVdQdqwFFWEVdsRpUhGVwrAYWYRkUq6FF2LhptWPFKMJqXTg1mpiDxhBz41hizk4SzyUe8RyXIp5708SaoIwQK6hVmOclq9WCq8F/IzXNeFD823p7vLs9lt0ep251ndVydn2IPWzSHhJpD3e0hzLawxTtETr26Bt7ZI09asYeEWPnPHskiz1KxW6gbTe+thtW242m7QbRdmNnuyGz3Ui5VNVfNUA1UGW3h7Pbutnt2Ow2anbzDLvphd2swm4yYTeHsJs62M0Y7CYKdrK1r7zZV9XsK2b21TD7Spd9Fcu+QmVVvF3waheq2gWmdmGoXdBpF2IGF1DuqdpLtbdqH9W+qv1U+6sOUB2oOkh1sOoQ1aGqw1SHq45QHak6SnW06hjVsarjVMerTlCdqDpJdbLqFNWpqtNUp6smqiapJqumqKaqqlTTVGeozlSdpTpbdY7qXNV5qvNVF6guVF2kulh1iepS1WWqy1VXqKarrlRdpbpadY3qWtV1qutVN6huVN2kull1i+pW1W2q21V3qO5U3aW6W3WP6l7Vfar7VQ+oHlQ9pHpY9YjqUdVjqsdVT6ieVD2lelr1jOpZ1XOq51UvqF5UvaR6WfWK6lXVa6rXVW+o3lS9pXpb9Y7qXdV7qvdVH6g+VH2k+lj1ieqbqm+pvq36juq7qu+pvq/6gVRfUPlD1Y9UP1b9RPVT1c9UP1f9QvVL1a9Uv1b9VmquD/uGVM+dwMBZPRbMKWvd/BVrZ1ZOb/bpxFahVV/9RaCudSOzrCuvY11g1gPvFXiu4OLzwKNN8N8n47VUEL+Vm/jJEqndWoZ+bhVZF+yTJuDfJep4XxRZZvvdKA+zNmBdELOTvwz3NxhHi8iySygucVt6QfzObuLDfdUl9HPnyDjD23sCqQ9BvGAeNJXarSiyrq45k+D3z4v2pRj8X0ELjpnOIRZszw6RvjYNxWHu0yB+Mzfxvz4mS9zET6NjMsyix0B4PyQifYrOU3JfJyVA/4oj/2e0j+HfQTmvKPI++s2/4nr8Ljo2g3XtQf+i/64F6GuYBccWyp/R/RLM0aZ1xCoJrQ//fknkd13tw06gT0Hf/w8YGTB+nJwAAA==","debug_symbols":"pdbdiuJAEAXgd8m1F6mq/qmaV1kWiRqHQIiS0YVFfPdtnVNGFyLiXJ2jmfpIYqcnp2rTro6fy27Y7r6qj1+najV2fd99Lvvdujl0u6F8ezovKv+4PIxtW76q7o6XqX0ztsOh+hiOfb+o/jT98fpHX/tmuOahGcvRelG1w6ZkAbdd317aeTFN1/OjGhTDatN45Id5mp/PIWA+xzTN08vz5ievNb8xryndzj/MzYf5eUrqF0CZpzuQ4+tCtkmgt4T0UyGr3ARLbwicbzeScw7vCMrkgorNCfRkMWa6LaY7gOzVUxDmCEBY5I2LeBACvyXkSbDZH5OeEVr7ehDlON0IfSTiPJE4+a1MfPdg/0c8OwmJ/nOKpNnreLa1CN0eTZrdWvTJqubkmwOV9Tm7OzwlQr4R8edEzg/E7/KpWXfjw65ekVK55kVJRgoyICMyITNSkfadViPhGTyDZ/AMnsEzeAbPvj2uayQhGSnIgIzIhMxIRcIjeASP4BE8gkfwCB7BI3gEj+ExPIbH8Bgew2N4DI/hMTyBJ/AEnsATeAJP4Ak8gSfwArwAL8AL8AK8AC/AC/ACvAAvwovwIrwIL8KL8CK8CC/Ci/ASvAQvwUvwErwEL8FL8BK8VDwqDy/n+vLf6VLIC3sRL8FL9JK8ZC/qxVDUZXVZXVaX1WV1WV1Wl9VlddlcNpfNZXPZXDaXzWVz2Vw2yFLXXsgLexEvwUv0krxkL+rlKp8ve8/YNau+xUvh9jis794RD3/3fsTfIvfjbt1ujmN72Xmux8pe9A8=","file_map":{"18":{"source":"pub mod bn254;\nuse crate::{runtime::is_unconstrained, static_assert};\nuse bn254::lt as bn254_lt;\n\nimpl Field {\n /// Asserts that `self` can be represented in `bit_size` bits.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^{bit_size}`.\n // docs:start:assert_max_bit_size\n pub fn assert_max_bit_size(self) {\n // docs:end:assert_max_bit_size\n static_assert(\n BIT_SIZE < modulus_num_bits() as u32,\n \"BIT_SIZE must be less than modulus_num_bits\",\n );\n __assert_max_bit_size(self, BIT_SIZE);\n }\n\n /// Decomposes `self` into its little endian bit decomposition as a `[u1; N]` array.\n /// This slice will be zero padded should not all bits be necessary to represent `self`.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n /// be able to represent the original `Field`.\n ///\n /// # Safety\n /// The bit decomposition returned is canonical and is guaranteed to not overflow the modulus.\n // docs:start:to_le_bits\n pub fn to_le_bits(self: Self) -> [u1; N] {\n // docs:end:to_le_bits\n let bits = __to_le_bits(self);\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_le_bits();\n assert(bits.len() <= p.len());\n let mut ok = bits.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bits[N - 1 - i] != p[N - 1 - i]) {\n assert(p[N - 1 - i] == 1);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bits\n }\n\n /// Decomposes `self` into its big endian bit decomposition as a `[u1; N]` array.\n /// This array will be zero padded should not all bits be necessary to represent `self`.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n /// be able to represent the original `Field`.\n ///\n /// # Safety\n /// The bit decomposition returned is canonical and is guaranteed to not overflow the modulus.\n // docs:start:to_be_bits\n pub fn to_be_bits(self: Self) -> [u1; N] {\n // docs:end:to_be_bits\n let bits = __to_be_bits(self);\n\n if !is_unconstrained() {\n // Ensure that the decomposition does not overflow the modulus\n let p = modulus_be_bits();\n assert(bits.len() <= p.len());\n let mut ok = bits.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bits[i] != p[i]) {\n assert(p[i] == 1);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bits\n }\n\n /// Decomposes `self` into its little endian byte decomposition as a `[u8;N]` array\n /// This array will be zero padded should not all bytes be necessary to represent `self`.\n ///\n /// # Failures\n /// The length N of the array must be big enough to contain all the bytes of the 'self',\n /// and no more than the number of bytes required to represent the field modulus\n ///\n /// # Safety\n /// The result is ensured to be the canonical decomposition of the field element\n // docs:start:to_le_bytes\n pub fn to_le_bytes(self: Self) -> [u8; N] {\n // docs:end:to_le_bytes\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n // Compute the byte decomposition\n let bytes = self.to_le_radix(256);\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_le_bytes();\n assert(bytes.len() <= p.len());\n let mut ok = bytes.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bytes[N - 1 - i] != p[N - 1 - i]) {\n assert(bytes[N - 1 - i] < p[N - 1 - i]);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bytes\n }\n\n /// Decomposes `self` into its big endian byte decomposition as a `[u8;N]` array of length required to represent the field modulus\n /// This array will be zero padded should not all bytes be necessary to represent `self`.\n ///\n /// # Failures\n /// The length N of the array must be big enough to contain all the bytes of the 'self',\n /// and no more than the number of bytes required to represent the field modulus\n ///\n /// # Safety\n /// The result is ensured to be the canonical decomposition of the field element\n // docs:start:to_be_bytes\n pub fn to_be_bytes(self: Self) -> [u8; N] {\n // docs:end:to_be_bytes\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n // Compute the byte decomposition\n let bytes = self.to_be_radix(256);\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_be_bytes();\n assert(bytes.len() <= p.len());\n let mut ok = bytes.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bytes[i] != p[i]) {\n assert(bytes[i] < p[i]);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bytes\n }\n\n fn to_le_radix(self: Self, radix: u32) -> [u8; N] {\n // Brillig does not need an immediate radix\n if !crate::runtime::is_unconstrained() {\n static_assert(1 < radix, \"radix must be greater than 1\");\n static_assert(radix <= 256, \"radix must be less than or equal to 256\");\n static_assert(radix & (radix - 1) == 0, \"radix must be a power of 2\");\n }\n __to_le_radix(self, radix)\n }\n\n fn to_be_radix(self: Self, radix: u32) -> [u8; N] {\n // Brillig does not need an immediate radix\n if !crate::runtime::is_unconstrained() {\n static_assert(1 < radix, \"radix must be greater than 1\");\n static_assert(radix <= 256, \"radix must be less than or equal to 256\");\n static_assert(radix & (radix - 1) == 0, \"radix must be a power of 2\");\n }\n __to_be_radix(self, radix)\n }\n\n // Returns self to the power of the given exponent value.\n // Caution: we assume the exponent fits into 32 bits\n // using a bigger bit size impacts negatively the performance and should be done only if the exponent does not fit in 32 bits\n pub fn pow_32(self, exponent: Field) -> Field {\n let mut r: Field = 1;\n let b: [u1; 32] = exponent.to_le_bits();\n\n for i in 1..33 {\n r *= r;\n r = (b[32 - i] as Field) * (r * self) + (1 - b[32 - i] as Field) * r;\n }\n r\n }\n\n // Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x `elem` {0, ..., p-1} is even, otherwise sgn0(x mod p) = 1.\n pub fn sgn0(self) -> u1 {\n self as u1\n }\n\n pub fn lt(self, another: Field) -> bool {\n if crate::compat::is_bn254() {\n bn254_lt(self, another)\n } else {\n lt_fallback(self, another)\n }\n }\n\n /// Convert a little endian byte array to a field element.\n /// If the provided byte array overflows the field modulus then the Field will silently wrap around.\n pub fn from_le_bytes(bytes: [u8; N]) -> Field {\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n let mut v = 1;\n let mut result = 0;\n\n for i in 0..N {\n result += (bytes[i] as Field) * v;\n v = v * 256;\n }\n result\n }\n\n /// Convert a big endian byte array to a field element.\n /// If the provided byte array overflows the field modulus then the Field will silently wrap around.\n pub fn from_be_bytes(bytes: [u8; N]) -> Field {\n let mut v = 1;\n let mut result = 0;\n\n for i in 0..N {\n result += (bytes[N - 1 - i] as Field) * v;\n v = v * 256;\n }\n result\n }\n}\n\n#[builtin(apply_range_constraint)]\nfn __assert_max_bit_size(value: Field, bit_size: u32) {}\n\n// `_radix` must be less than 256\n#[builtin(to_le_radix)]\nfn __to_le_radix(value: Field, radix: u32) -> [u8; N] {}\n\n// `_radix` must be less than 256\n#[builtin(to_be_radix)]\nfn __to_be_radix(value: Field, radix: u32) -> [u8; N] {}\n\n/// Decomposes `self` into its little endian bit decomposition as a `[u1; N]` array.\n/// This slice will be zero padded should not all bits be necessary to represent `self`.\n///\n/// # Failures\n/// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n/// be able to represent the original `Field`.\n///\n/// # Safety\n/// Values of `N` equal to or greater than the number of bits necessary to represent the `Field` modulus\n/// (e.g. 254 for the BN254 field) allow for multiple bit decompositions. This is due to how the `Field` will\n/// wrap around due to overflow when verifying the decomposition.\n#[builtin(to_le_bits)]\nfn __to_le_bits(value: Field) -> [u1; N] {}\n\n/// Decomposes `self` into its big endian bit decomposition as a `[u1; N]` array.\n/// This array will be zero padded should not all bits be necessary to represent `self`.\n///\n/// # Failures\n/// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n/// be able to represent the original `Field`.\n///\n/// # Safety\n/// Values of `N` equal to or greater than the number of bits necessary to represent the `Field` modulus\n/// (e.g. 254 for the BN254 field) allow for multiple bit decompositions. This is due to how the `Field` will\n/// wrap around due to overflow when verifying the decomposition.\n#[builtin(to_be_bits)]\nfn __to_be_bits(value: Field) -> [u1; N] {}\n\n#[builtin(modulus_num_bits)]\npub comptime fn modulus_num_bits() -> u64 {}\n\n#[builtin(modulus_be_bits)]\npub comptime fn modulus_be_bits() -> [u1] {}\n\n#[builtin(modulus_le_bits)]\npub comptime fn modulus_le_bits() -> [u1] {}\n\n#[builtin(modulus_be_bytes)]\npub comptime fn modulus_be_bytes() -> [u8] {}\n\n#[builtin(modulus_le_bytes)]\npub comptime fn modulus_le_bytes() -> [u8] {}\n\n/// An unconstrained only built in to efficiently compare fields.\n#[builtin(field_less_than)]\nunconstrained fn __field_less_than(x: Field, y: Field) -> bool {}\n\npub(crate) unconstrained fn field_less_than(x: Field, y: Field) -> bool {\n __field_less_than(x, y)\n}\n\n// Convert a 32 byte array to a field element by modding\npub fn bytes32_to_field(bytes32: [u8; 32]) -> Field {\n // Convert it to a field element\n let mut v = 1;\n let mut high = 0 as Field;\n let mut low = 0 as Field;\n\n for i in 0..16 {\n high = high + (bytes32[15 - i] as Field) * v;\n low = low + (bytes32[16 + 15 - i] as Field) * v;\n v = v * 256;\n }\n // Abuse that a % p + b % p = (a + b) % p and that low < p\n low + high * v\n}\n\nfn lt_fallback(x: Field, y: Field) -> bool {\n if is_unconstrained() {\n // Safety: unconstrained context\n unsafe {\n field_less_than(x, y)\n }\n } else {\n let x_bytes: [u8; 32] = x.to_le_bytes();\n let y_bytes: [u8; 32] = y.to_le_bytes();\n let mut x_is_lt = false;\n let mut done = false;\n for i in 0..32 {\n if (!done) {\n let x_byte = x_bytes[32 - 1 - i] as u8;\n let y_byte = y_bytes[32 - 1 - i] as u8;\n let bytes_match = x_byte == y_byte;\n if !bytes_match {\n x_is_lt = x_byte < y_byte;\n done = true;\n }\n }\n }\n x_is_lt\n }\n}\n\nmod tests {\n use crate::{panic::panic, runtime};\n use super::field_less_than;\n\n #[test]\n // docs:start:to_be_bits_example\n fn test_to_be_bits() {\n let field = 2;\n let bits: [u1; 8] = field.to_be_bits();\n assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]);\n }\n // docs:end:to_be_bits_example\n\n #[test]\n // docs:start:to_le_bits_example\n fn test_to_le_bits() {\n let field = 2;\n let bits: [u1; 8] = field.to_le_bits();\n assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]);\n }\n // docs:end:to_le_bits_example\n\n #[test]\n // docs:start:to_be_bytes_example\n fn test_to_be_bytes() {\n let field = 2;\n let bytes: [u8; 8] = field.to_be_bytes();\n assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]);\n assert_eq(Field::from_be_bytes::<8>(bytes), field);\n }\n // docs:end:to_be_bytes_example\n\n #[test]\n // docs:start:to_le_bytes_example\n fn test_to_le_bytes() {\n let field = 2;\n let bytes: [u8; 8] = field.to_le_bytes();\n assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]);\n assert_eq(Field::from_le_bytes::<8>(bytes), field);\n }\n // docs:end:to_le_bytes_example\n\n #[test]\n // docs:start:to_be_radix_example\n fn test_to_be_radix() {\n // 259, in base 256, big endian, is [1, 3].\n // i.e. 3 * 256^0 + 1 * 256^1\n let field = 259;\n\n // The radix (in this example, 256) must be a power of 2.\n // The length of the returned byte array can be specified to be\n // >= the amount of space needed.\n let bytes: [u8; 8] = field.to_be_radix(256);\n assert_eq(bytes, [0, 0, 0, 0, 0, 0, 1, 3]);\n assert_eq(Field::from_be_bytes::<8>(bytes), field);\n }\n // docs:end:to_be_radix_example\n\n #[test]\n // docs:start:to_le_radix_example\n fn test_to_le_radix() {\n // 259, in base 256, little endian, is [3, 1].\n // i.e. 3 * 256^0 + 1 * 256^1\n let field = 259;\n\n // The radix (in this example, 256) must be a power of 2.\n // The length of the returned byte array can be specified to be\n // >= the amount of space needed.\n let bytes: [u8; 8] = field.to_le_radix(256);\n assert_eq(bytes, [3, 1, 0, 0, 0, 0, 0, 0]);\n assert_eq(Field::from_le_bytes::<8>(bytes), field);\n }\n // docs:end:to_le_radix_example\n\n #[test(should_fail_with = \"radix must be greater than 1\")]\n fn test_to_le_radix_1() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(1);\n } else {\n panic(f\"radix must be greater than 1\");\n }\n }\n\n // Updated test to account for Brillig restriction that radix must be greater than 2\n #[test(should_fail_with = \"radix must be greater than 1\")]\n fn test_to_le_radix_brillig_1() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 1;\n let _: [u8; 8] = field.to_le_radix(1);\n } else {\n panic(f\"radix must be greater than 1\");\n }\n }\n\n #[test(should_fail_with = \"radix must be a power of 2\")]\n fn test_to_le_radix_3() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(3);\n } else {\n panic(f\"radix must be a power of 2\");\n }\n }\n\n #[test]\n fn test_to_le_radix_brillig_3() {\n // this test should only fail in constrained mode\n if runtime::is_unconstrained() {\n let field = 1;\n let out: [u8; 8] = field.to_le_radix(3);\n let mut expected = [0; 8];\n expected[0] = 1;\n assert(out == expected, \"unexpected result\");\n }\n }\n\n #[test(should_fail_with = \"radix must be less than or equal to 256\")]\n fn test_to_le_radix_512() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(512);\n } else {\n panic(f\"radix must be less than or equal to 256\")\n }\n }\n\n #[test(should_fail_with = \"Field failed to decompose into specified 16 limbs\")]\n unconstrained fn not_enough_limbs_brillig() {\n let _: [u8; 16] = 0x100000000000000000000000000000000.to_le_bytes();\n }\n\n #[test(should_fail_with = \"Field failed to decompose into specified 16 limbs\")]\n fn not_enough_limbs() {\n let _: [u8; 16] = 0x100000000000000000000000000000000.to_le_bytes();\n }\n\n #[test]\n unconstrained fn test_field_less_than() {\n assert(field_less_than(0, 1));\n assert(field_less_than(0, 0x100));\n assert(field_less_than(0x100, 0 - 1));\n assert(!field_less_than(0 - 1, 0));\n }\n\n #[test]\n unconstrained fn test_large_field_values_unconstrained() {\n let large_field = 0xffffffffffffffff;\n\n let bits: [u1; 64] = large_field.to_le_bits();\n assert_eq(bits[0], 1);\n\n let bytes: [u8; 8] = large_field.to_le_bytes();\n assert_eq(Field::from_le_bytes::<8>(bytes), large_field);\n\n let radix_bytes: [u8; 8] = large_field.to_le_radix(256);\n assert_eq(Field::from_le_bytes::<8>(radix_bytes), large_field);\n }\n\n #[test]\n fn test_large_field_values() {\n let large_val = 0xffffffffffffffff;\n\n let bits: [u1; 64] = large_val.to_le_bits();\n assert_eq(bits[0], 1);\n\n let bytes: [u8; 8] = large_val.to_le_bytes();\n assert_eq(Field::from_le_bytes::<8>(bytes), large_val);\n\n let radix_bytes: [u8; 8] = large_val.to_le_radix(256);\n assert_eq(Field::from_le_bytes::<8>(radix_bytes), large_val);\n }\n\n #[test]\n fn test_decomposition_edge_cases() {\n let zero_bits: [u1; 8] = 0.to_le_bits();\n assert_eq(zero_bits, [0; 8]);\n\n let zero_bytes: [u8; 8] = 0.to_le_bytes();\n assert_eq(zero_bytes, [0; 8]);\n\n let one_bits: [u1; 8] = 1.to_le_bits();\n let expected: [u1; 8] = [1, 0, 0, 0, 0, 0, 0, 0];\n assert_eq(one_bits, expected);\n\n let pow2_bits: [u1; 8] = 4.to_le_bits();\n let expected: [u1; 8] = [0, 0, 1, 0, 0, 0, 0, 0];\n assert_eq(pow2_bits, expected);\n }\n\n #[test]\n fn test_pow_32() {\n assert_eq(2.pow_32(3), 8);\n assert_eq(3.pow_32(2), 9);\n assert_eq(5.pow_32(0), 1);\n assert_eq(7.pow_32(1), 7);\n\n assert_eq(2.pow_32(10), 1024);\n\n assert_eq(0.pow_32(5), 0);\n assert_eq(0.pow_32(0), 1);\n\n assert_eq(1.pow_32(100), 1);\n }\n\n #[test]\n fn test_sgn0() {\n assert_eq(0.sgn0(), 0);\n assert_eq(2.sgn0(), 0);\n assert_eq(4.sgn0(), 0);\n assert_eq(100.sgn0(), 0);\n\n assert_eq(1.sgn0(), 1);\n assert_eq(3.sgn0(), 1);\n assert_eq(5.sgn0(), 1);\n assert_eq(101.sgn0(), 1);\n }\n\n #[test(should_fail_with = \"Field failed to decompose into specified 8 limbs\")]\n fn test_bit_decomposition_overflow() {\n // 8 bits can't represent large field values\n let large_val = 0x1000000000000000;\n let _: [u1; 8] = large_val.to_le_bits();\n }\n\n #[test(should_fail_with = \"Field failed to decompose into specified 4 limbs\")]\n fn test_byte_decomposition_overflow() {\n // 4 bytes can't represent large field values\n let large_val = 0x1000000000000000;\n let _: [u8; 4] = large_val.to_le_bytes();\n }\n\n}\n","path":"std/field/mod.nr"},"19":{"source":"// Exposed only for usage in `std::meta`\npub(crate) mod poseidon2;\n\nuse crate::default::Default;\nuse crate::embedded_curve_ops::{\n EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul, multi_scalar_mul_array_return,\n};\nuse crate::meta::derive_via;\n\n#[foreign(sha256_compression)]\n// docs:start:sha256_compression\npub fn sha256_compression(input: [u32; 16], state: [u32; 8]) -> [u32; 8] {}\n// docs:end:sha256_compression\n\n#[foreign(keccakf1600)]\n// docs:start:keccakf1600\npub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {}\n// docs:end:keccakf1600\n\npub mod keccak {\n #[deprecated(\"This function has been moved to std::hash::keccakf1600\")]\n pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {\n super::keccakf1600(input)\n }\n}\n\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n// docs:start:blake3\npub fn blake3(input: [u8; N]) -> [u8; 32]\n// docs:end:blake3\n{\n if crate::runtime::is_unconstrained() {\n // Temporary measure while Barretenberg is main proving system.\n // Please open an issue if you're working on another proving system and running into problems due to this.\n crate::static_assert(\n N <= 1024,\n \"Barretenberg cannot prove blake3 hashes with inputs larger than 1024 bytes\",\n );\n }\n __blake3(input)\n}\n\n#[foreign(blake3)]\nfn __blake3(input: [u8; N]) -> [u8; 32] {}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint {\n // docs:end:pedersen_commitment\n pedersen_commitment_with_separator(input, 0)\n}\n\n#[inline_always]\npub fn pedersen_commitment_with_separator(\n input: [Field; N],\n separator: u32,\n) -> EmbeddedCurvePoint {\n let mut points = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N];\n for i in 0..N {\n // we use the unsafe version because the multi_scalar_mul will constrain the scalars.\n points[i] = from_field_unsafe(input[i]);\n }\n let generators = derive_generators(\"DEFAULT_DOMAIN_SEPARATOR\".as_bytes(), separator);\n multi_scalar_mul(generators, points)\n}\n\n// docs:start:pedersen_hash\npub fn pedersen_hash(input: [Field; N]) -> Field\n// docs:end:pedersen_hash\n{\n pedersen_hash_with_separator(input, 0)\n}\n\n#[no_predicates]\npub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {\n let mut scalars: [EmbeddedCurveScalar; N + 1] = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N + 1];\n let mut generators: [EmbeddedCurvePoint; N + 1] =\n [EmbeddedCurvePoint::point_at_infinity(); N + 1];\n let domain_generators: [EmbeddedCurvePoint; N] =\n derive_generators(\"DEFAULT_DOMAIN_SEPARATOR\".as_bytes(), separator);\n\n for i in 0..N {\n scalars[i] = from_field_unsafe(input[i]);\n generators[i] = domain_generators[i];\n }\n scalars[N] = EmbeddedCurveScalar { lo: N as Field, hi: 0 as Field };\n\n let length_generator: [EmbeddedCurvePoint; 1] =\n derive_generators(\"pedersen_hash_length\".as_bytes(), 0);\n generators[N] = length_generator[0];\n multi_scalar_mul_array_return(generators, scalars)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n // TODO(https://github.com/noir-lang/noir/issues/5672): Add back assert_constant on starting_index\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\n#[field(bn254)]\n// Same as from_field but:\n// does not assert the limbs are 128 bits\n// does not assert the decomposition does not overflow the EmbeddedCurveScalar\nfn from_field_unsafe(scalar: Field) -> EmbeddedCurveScalar {\n // Safety: xlo and xhi decomposition is checked below\n let (xlo, xhi) = unsafe { crate::field::bn254::decompose_hint(scalar) };\n // Check that the decomposition is correct\n assert_eq(scalar, xlo + crate::field::bn254::TWO_POW_128 * xhi);\n EmbeddedCurveScalar { lo: xlo, hi: xhi }\n}\n\n#[foreign(poseidon2_permutation)]\npub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {}\n\n// Generic hashing support.\n// Partially ported and impacted by rust.\n\n// Hash trait shall be implemented per type.\n#[derive_via(derive_hash)]\npub trait Hash {\n fn hash(self, state: &mut H)\n where\n H: Hasher;\n}\n\n// docs:start:derive_hash\ncomptime fn derive_hash(s: TypeDefinition) -> Quoted {\n let name = quote { $crate::hash::Hash };\n let signature = quote { fn hash(_self: Self, _state: &mut H) where H: $crate::hash::Hasher };\n let for_each_field = |name| quote { _self.$name.hash(_state); };\n crate::meta::make_trait_impl(\n s,\n name,\n signature,\n for_each_field,\n quote {},\n |fields| fields,\n )\n}\n// docs:end:derive_hash\n\n// Hasher trait shall be implemented by algorithms to provide hash-agnostic means.\n// TODO: consider making the types generic here ([u8], [Field], etc.)\npub trait Hasher {\n fn finish(self) -> Field;\n\n fn write(&mut self, input: Field);\n}\n\n// BuildHasher is a factory trait, responsible for production of specific Hasher.\npub trait BuildHasher {\n type H: Hasher;\n\n fn build_hasher(self) -> H;\n}\n\npub struct BuildHasherDefault;\n\nimpl BuildHasher for BuildHasherDefault\nwhere\n H: Hasher + Default,\n{\n type H = H;\n\n fn build_hasher(_self: Self) -> H {\n H::default()\n }\n}\n\nimpl Default for BuildHasherDefault\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u1 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u8 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u16 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u32 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u64 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u128 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for i8 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as u8 as Field);\n }\n}\n\nimpl Hash for i16 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as u16 as Field);\n }\n}\n\nimpl Hash for i32 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as u32 as Field);\n }\n}\n\nimpl Hash for i64 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as u64 as Field);\n }\n}\n\nimpl Hash for bool {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for () {\n fn hash(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl Hash for [T]\nwhere\n T: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.len().hash(state);\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n }\n}\n\nimpl Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n self.3.hash(state);\n }\n}\n\nimpl Hash for (A, B, C, D, E)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n self.3.hash(state);\n self.4.hash(state);\n }\n}\n\n// Some test vectors for Pedersen hash and Pedersen Commitment.\n// They have been generated using the same functions so the tests are for now useless\n// but they will be useful when we switch to Noir implementation.\n#[test]\nfn assert_pedersen() {\n assert_eq(\n pedersen_hash_with_separator([1], 1),\n 0x1b3f4b1a83092a13d8d1a59f7acb62aba15e7002f4440f2275edb99ebbc2305f,\n );\n assert_eq(\n pedersen_commitment_with_separator([1], 1),\n EmbeddedCurvePoint {\n x: 0x054aa86a73cb8a34525e5bbed6e43ba1198e860f5f3950268f71df4591bde402,\n y: 0x209dcfbf2cfb57f9f6046f44d71ac6faf87254afc7407c04eb621a6287cac126,\n is_infinite: false,\n },\n );\n\n assert_eq(\n pedersen_hash_with_separator([1, 2], 2),\n 0x26691c129448e9ace0c66d11f0a16d9014a9e8498ee78f4d69f0083168188255,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2], 2),\n EmbeddedCurvePoint {\n x: 0x2e2b3b191e49541fe468ec6877721d445dcaffe41728df0a0eafeb15e87b0753,\n y: 0x2ff4482400ad3a6228be17a2af33e2bcdf41be04795f9782bd96efe7e24f8778,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3], 3),\n 0x0bc694b7a1f8d10d2d8987d07433f26bd616a2d351bc79a3c540d85b6206dbe4,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3], 3),\n EmbeddedCurvePoint {\n x: 0x1fee4e8cf8d2f527caa2684236b07c4b1bad7342c01b0f75e9a877a71827dc85,\n y: 0x2f9fedb9a090697ab69bf04c8bc15f7385b3e4b68c849c1536e5ae15ff138fd1,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4], 4),\n 0xdae10fb32a8408521803905981a2b300d6a35e40e798743e9322b223a5eddc,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4], 4),\n EmbeddedCurvePoint {\n x: 0x07ae3e202811e1fca39c2d81eabe6f79183978e6f12be0d3b8eda095b79bdbc9,\n y: 0x0afc6f892593db6fbba60f2da558517e279e0ae04f95758587760ba193145014,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5], 5),\n 0xfc375b062c4f4f0150f7100dfb8d9b72a6d28582dd9512390b0497cdad9c22,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5], 5),\n EmbeddedCurvePoint {\n x: 0x1754b12bd475a6984a1094b5109eeca9838f4f81ac89c5f0a41dbce53189bb29,\n y: 0x2da030e3cfcdc7ddad80eaf2599df6692cae0717d4e9f7bfbee8d073d5d278f7,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6], 6),\n 0x1696ed13dc2730062a98ac9d8f9de0661bb98829c7582f699d0273b18c86a572,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6], 6),\n EmbeddedCurvePoint {\n x: 0x190f6c0e97ad83e1e28da22a98aae156da083c5a4100e929b77e750d3106a697,\n y: 0x1f4b60f34ef91221a0b49756fa0705da93311a61af73d37a0c458877706616fb,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7], 7),\n 0x128c0ff144fc66b6cb60eeac8a38e23da52992fc427b92397a7dffd71c45ede3,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7], 7),\n EmbeddedCurvePoint {\n x: 0x015441e9d29491b06563fac16fc76abf7a9534c715421d0de85d20dbe2965939,\n y: 0x1d2575b0276f4e9087e6e07c2cb75aa1baafad127af4be5918ef8a2ef2fea8fc,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8], 8),\n 0x2f960e117482044dfc99d12fece2ef6862fba9242be4846c7c9a3e854325a55c,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8], 8),\n EmbeddedCurvePoint {\n x: 0x1657737676968887fceb6dd516382ea13b3a2c557f509811cd86d5d1199bc443,\n y: 0x1f39f0cb569040105fa1e2f156521e8b8e08261e635a2b210bdc94e8d6d65f77,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9], 9),\n 0x0c96db0790602dcb166cc4699e2d306c479a76926b81c2cb2aaa92d249ec7be7,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9], 9),\n EmbeddedCurvePoint {\n x: 0x0a3ceae42d14914a432aa60ec7fded4af7dad7dd4acdbf2908452675ec67e06d,\n y: 0xfc19761eaaf621ad4aec9a8b2e84a4eceffdba78f60f8b9391b0bd9345a2f2,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10),\n 0x2cd37505871bc460a62ea1e63c7fe51149df5d0801302cf1cbc48beb8dff7e94,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10),\n EmbeddedCurvePoint {\n x: 0x2fb3f8b3d41ddde007c8c3c62550f9a9380ee546fcc639ffbb3fd30c8d8de30c,\n y: 0x300783be23c446b11a4c0fabf6c91af148937cea15fcf5fb054abf7f752ee245,\n is_infinite: false,\n },\n );\n}\n","path":"std/hash/mod.nr"},"51":{"source":"// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\nuse keccak256::keccak256;\n\n/// Given a public key, signature, and hashed message, verify the signature\npub fn verify_signature(\n hashed_message: [u8; 32],\n pub_key_x: [u8; 32],\n pub_key_y: [u8; 32],\n signature: [u8; 64],\n) -> bool {\n std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message)\n}\n\n/// Given a public key, derive the Ethereum address\npub fn derive_address(pub_key_x: [u8; 32], pub_key_y: [u8; 32]) -> [u8; 20] {\n let mut pub_key_bytes = [0; 64];\n for i in 0..32 {\n pub_key_bytes[i] = pub_key_x[i];\n pub_key_bytes[i + 32] = pub_key_y[i];\n }\n\n // Hash the concatenated public key\n let hash = keccak256(pub_key_bytes, 64);\n\n // Extract last 20 bytes as the Ethereum address\n let mut derived_address = [0; 20];\n for i in 0..20 {\n derived_address[i] = hash[i + 12];\n }\n\n derived_address\n}\n\n// Convert a 20-byte address to a field element.\npub fn address_to_field(addr: [u8; 20]) -> Field {\n let mut acc: Field = 0;\n\n for i in 0..20 {\n acc = acc * 256 + (addr[i] as Field);\n }\n\n acc\n}\n\n#[test]\nfn test_derive_address() {\n let pub_key_x = [\n 131, 24, 83, 91, 84, 16, 93, 74, 122, 174, 96, 192, 143, 196, 95, 150, 135, 24, 27, 79, 223,\n 198, 37, 189, 26, 117, 63, 167, 57, 127, 237, 117,\n ];\n let pub_key_y = [\n 53, 71, 241, 28, 168, 105, 102, 70, 242, 243, 172, 176, 142, 49, 1, 106, 250, 194, 62, 99,\n 12, 93, 17, 245, 159, 97, 254, 245, 123, 13, 42, 165,\n ];\n let address = derive_address(pub_key_x, pub_key_y);\n assert(\n address\n == [\n 243, 159, 214, 229, 26, 173, 136, 246, 244, 206, 106, 184, 130, 114, 121, 207, 255,\n 185, 34, 102,\n ],\n );\n}\n\n#[test]\nfn test_verify_signature() {\n let hashed_message = [\n 67, 126, 157, 164, 162, 165, 56, 242, 155, 214, 113, 196, 83, 198, 228, 36, 174, 104, 152,\n 87, 167, 108, 64, 34, 234, 161, 122, 55, 44, 62, 151, 55,\n ];\n let pub_key_x = [\n 131, 24, 83, 91, 84, 16, 93, 74, 122, 174, 96, 192, 143, 196, 95, 150, 135, 24, 27, 79, 223,\n 198, 37, 189, 26, 117, 63, 167, 57, 127, 237, 117,\n ];\n let pub_key_y = [\n 53, 71, 241, 28, 168, 105, 102, 70, 242, 243, 172, 176, 142, 49, 1, 106, 250, 194, 62, 99,\n 12, 93, 17, 245, 159, 97, 254, 245, 123, 13, 42, 165,\n ];\n let signature = [\n 78, 219, 63, 96, 214, 92, 16, 36, 108, 207, 133, 108, 3, 248, 122, 23, 192, 236, 210, 253,\n 206, 125, 83, 124, 163, 54, 106, 125, 188, 223, 192, 39, 44, 76, 228, 107, 188, 221, 183,\n 124, 118, 143, 228, 126, 216, 173, 160, 231, 62, 52, 188, 154, 110, 230, 183, 71, 36, 161,\n 171, 163, 213, 62, 223, 152,\n ];\n assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == true);\n}\n\n#[test]\nfn test_verify_signature_sdk_input() {\n let hashed_message = [\n 200, 232, 98, 162, 80, 131, 242, 57, 252, 76, 226, 45, 127, 206, 207, 39, 206, 44, 211, 171,\n 113, 67, 121, 68, 78, 253, 202, 79, 29, 128, 130, 76,\n ];\n\n let pub_key_x = [\n 131, 24, 83, 91, 84, 16, 93, 74, 122, 174, 96, 192, 143, 196, 95, 150, 135, 24, 27, 79, 223,\n 198, 37, 189, 26, 117, 63, 167, 57, 127, 237, 117,\n ];\n let pub_key_y = [\n 53, 71, 241, 28, 168, 105, 102, 70, 242, 243, 172, 176, 142, 49, 1, 106, 250, 194, 62, 99,\n 12, 93, 17, 245, 159, 97, 254, 245, 123, 13, 42, 165,\n ];\n let signature = [\n 22, 65, 67, 29, 14, 211, 253, 134, 129, 79, 2, 109, 166, 46, 17, 67, 75, 83, 198, 168, 81,\n 98, 254, 167, 249, 146, 24, 191, 60, 48, 125, 236, 127, 54, 28, 35, 95, 7, 182, 88, 120, 10,\n 253, 145, 165, 201, 214, 141, 106, 75, 20, 213, 235, 5, 17, 246, 104, 141, 62, 145, 20, 14,\n 236, 18,\n ];\n\n assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == true);\n}\n\n#[test]\nfn test_fail_verify_signature() {\n let hashed_message = [\n 67, 126, 157, 164, 162, 165, 56, 242, 155, 214, 113, 196, 83, 198, 228, 36, 174, 104, 152,\n 87, 167, 108, 64, 34, 234, 161, 122, 55, 44, 62, 151, 55,\n ];\n let pub_key_x = [\n 131, 24, 83, 91, 84, 16, 93, 74, 122, 174, 96, 192, 143, 196, 95, 150, 135, 24, 27, 79, 223,\n 198, 37, 189, 26, 117, 63, 167, 57, 127, 237, 117,\n ];\n let pub_key_y = [\n 53, 71, 241, 28, 168, 105, 102, 70, 242, 243, 172, 176, 142, 49, 1, 106, 250, 194, 62, 99,\n 12, 93, 17, 245, 159, 97, 254, 245, 123, 13, 42, 165,\n ];\n let signature = [\n 78, 219, 63, 96, 214, 92, 16, 36, 108, 207, 133, 108, 3, 248, 122, 23, 192, 236, 210, 253,\n 206, 125, 83, 124, 163, 54, 106, 125, 188, 223, 192, 39, 44, 76, 228, 107, 188, 221, 183,\n 124, 118, 143, 228, 126, 216, 173, 160, 231, 62, 52, 188, 154, 110, 230, 183, 71, 36, 161,\n 171, 163, 213, 62, 223, 151,\n ];\n\n assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == false);\n}\n\n#[test]\nfn test_address_to_field() {\n let address: [u8; 20] = [\n 243, 159, 214, 229, 26, 173, 136, 246, 244, 206, 106, 184, 130, 114, 121, 207, 255, 185, 34,\n 102,\n ];\n let field = address_to_field(address);\n\n assert(field == 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266);\n}\n","path":"/Users/ctrlc03/Documents/zk/enclave/examples/CRISP/circuits/src/ecdsa.nr"},"52":{"source":"mod ecdsa;\nuse ecdsa::{address_to_field, derive_address, verify_signature};\nmod merkle_tree;\nuse merkle_tree::get_merkle_root;\n// mod utils;\n// use utils::{check_coefficient_values, check_coefficient_zero};\n\nfn main(\n // ECDSA Section.\n public_key_x: [u8; 32],\n public_key_y: [u8; 32],\n signature: [u8; 64],\n hashed_message: [u8; 32],\n // Merkle Tree Section.\n merkle_root: pub Field,\n merkle_proof_length: u32,\n merkle_proof_indices: [u1; 20],\n merkle_proof_siblings: [Field; 20],\n // Slot Address Section.\n slot_address: pub Field,\n // Balance Section.\n balance: Field,\n) -> pub Field {\n // Verify the ECDSA signature.\n let is_signature_valid =\n verify_signature(hashed_message, public_key_x, public_key_y, signature);\n\n // Derive the Ethereum address.\n let address = address_to_field(derive_address(public_key_x, public_key_y));\n\n // Calculate the Merkle root.\n let merkle_root_calculated = get_merkle_root(\n address,\n balance,\n merkle_proof_length,\n merkle_proof_indices,\n merkle_proof_siblings,\n );\n\n address \n}\n","path":"/Users/ctrlc03/Documents/zk/enclave/examples/CRISP/circuits/src/main.nr"},"75":{"source":"mod tests;\n\nuse std::runtime::is_unconstrained;\nuse std::hash::keccak::keccakf1600;\n\nglobal BLOCK_SIZE_IN_BYTES: u32 = 136; //(1600 - BITS * 2) / WORD_SIZE;\nglobal WORD_SIZE: u32 = 8; // Limbs are made up of u64s so 8 bytes each.\nglobal LIMBS_PER_BLOCK: u32 = BLOCK_SIZE_IN_BYTES / WORD_SIZE;\nglobal NUM_KECCAK_LANES: u32 = 25;\n\n#[no_predicates]\npub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] {\n assert(N >= message_size);\n\n // Copy input to block bytes. For that we'll need at least input bytes (N)\n // but we want it to be padded to a multiple of BLOCK_SIZE_IN_BYTES.\n let mut block_bytes = [0; ((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES];\n if is_unconstrained() {\n for i in 0..message_size {\n block_bytes[i] = input[i];\n }\n } else {\n for i in 0..N {\n if i < message_size {\n block_bytes[i] = input[i];\n }\n }\n }\n\n //1. format_input_lanes\n let max_blocks = (N + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES;\n //maximum number of bytes to hash\n let real_max_blocks = (message_size + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES;\n let real_blocks_bytes = real_max_blocks * BLOCK_SIZE_IN_BYTES;\n\n block_bytes[message_size] = 1;\n block_bytes[real_blocks_bytes - 1] = 0x80;\n\n // populate a vector of 64-bit limbs from our byte array\n let mut sliced_buffer =\n [0; (((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES) / WORD_SIZE];\n for i in 0..sliced_buffer.len() {\n let limb_start = WORD_SIZE * i;\n\n let mut sliced = 0;\n let mut v = 1;\n for k in 0..WORD_SIZE {\n sliced += v * (block_bytes[limb_start + k] as Field);\n v *= 256;\n }\n\n sliced_buffer[i] = sliced as u64;\n }\n\n //2. sponge_absorb\n let mut state: [u64; NUM_KECCAK_LANES] = [0; NUM_KECCAK_LANES];\n // When in an unconstrained runtime we can take advantage of runtime loop bounds,\n // thus allowing us to simplify the loop body.\n if is_unconstrained() {\n for i in 0..real_max_blocks {\n if (i == 0) {\n for j in 0..LIMBS_PER_BLOCK {\n state[j] = sliced_buffer[j];\n }\n } else {\n for j in 0..LIMBS_PER_BLOCK {\n state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j];\n }\n }\n state = keccakf1600(state);\n }\n } else {\n // `real_max_blocks` is guaranteed to at least be `1`\n // We peel out the first block as to avoid a conditional inside of the loop.\n // Otherwise, a dynamic predicate can cause a blowup in a constrained runtime.\n for j in 0..LIMBS_PER_BLOCK {\n state[j] = sliced_buffer[j];\n }\n state = keccakf1600(state);\n for i in 1..max_blocks {\n if i < real_max_blocks {\n for j in 0..LIMBS_PER_BLOCK {\n state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j];\n }\n state = keccakf1600(state);\n }\n }\n }\n\n //3. sponge_squeeze\n let mut result = [0; 32];\n for i in 0..4 {\n let lane = state[i] as Field;\n let lane_le: [u8; 8] = lane.to_le_bytes();\n for j in 0..8 {\n result[8 * i + j] = lane_le[j];\n }\n }\n result\n}\n","path":"/Users/ctrlc03/nargo/github.com/noir-lang/keccak256/v0.1.0/src/keccak256.nr"}},"names":["main"],"brillig_names":["directive_integer_quotient","directive_invert","directive_to_radix"]} \ No newline at end of file diff --git a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts index c126f9a0c2..4527d61403 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -22,6 +22,10 @@ import { DEFAULT_BFV_PARAMS, generateMerkleProof, hashLeaf, MAXIMUM_VOTE_VALUE } import { LEAVES, merkleProof, MESSAGE, SIGNATURE, testAddress, VOTE, votingPowerLeaf } from './constants' import { privateKeyToAccount } from 'viem/accounts' +import merkleTreeCircuit from "./signature-merkle-circuit.json"; +import { CompiledCircuit, Noir } from '@noir-lang/noir_js' +import { UltraHonkBackend } from '@aztec/bb.js' + describe('Vote', () => { const votingPower = 10n @@ -220,6 +224,67 @@ describe('Vote', () => { slotAddress: account.address.toLowerCase(), }) + /** + * // ECDSA Section. + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + hashed_message: [u8; 32], + // Merkle Tree Section. + merkle_root: pub Field, + merkle_proof_length: u32, + merkle_proof_indices: [u1; 20], + merkle_proof_siblings: [Field; 20], + // Slot Address Section. + slot_address: pub Field, + // Balance Section. + balance: Field, + */ + + const noir = new Noir(merkleTreeCircuit as CompiledCircuit) + const backend = new UltraHonkBackend((merkleTreeCircuit as CompiledCircuit).bytecode) + + const { witness, returnValue } = await noir.execute({ + public_key_x: inputs.public_key_x, + public_key_y: inputs.public_key_y, + signature: inputs.signature, + hashed_message: inputs.hashed_message, + merkle_root: inputs.merkle_root, + merkle_proof_length: inputs.merkle_proof_length, + merkle_proof_indices: inputs.merkle_proof_indices, + merkle_proof_siblings: inputs.merkle_proof_siblings, + slot_address: inputs.slot_address, + balance: inputs.balance, + } as any) + console.log("addr", inputs.slot_address); + console.log("Circuit return value:", returnValue) + console.log(inputs.slot_address.toLowerCase() === returnValue.toString()); + const proof = await backend.generateProof(witness) + expect(await backend.verifyProof(proof)).toBe(true) + }); + it.only('should generate a proof for a voter and verify it', { timeout: 100000 }, async () => { + const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) + + // hardhat default private key + const privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + const account = privateKeyToAccount(privateKey) + const signature = await account.signMessage({ message: MESSAGE }) + const leaf = hashLeaf(account.address.toLowerCase(), votingPowerLeaf.toString()) + const leaves = [...LEAVES, leaf] + const merkleProof = generateMerkleProof(0n, votingPowerLeaf, account.address.toLowerCase(), leaves, 20) + console.log('Generated Merkle proof:', merkleProof); + + const inputs = await encryptVoteAndGenerateCRISPInputs({ + encodedVote, + publicKey, + previousCiphertext, + signature, + message: MESSAGE, + merkleData: merkleProof, + balance: votingPowerLeaf, + slotAddress: account.address.toLowerCase() + }) + // console.log('Generated circuit inputs, generating proof...', merkleProof); const proof = await generateProof(inputs) From 53489f2c7d9fd104d78cbcf9fb591ffb1960e143 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:18:14 +0000 Subject: [PATCH 05/13] fix: merkle proof error with index --- examples/CRISP/circuits/src/main.nr | 16 +++------- .../CRISP/packages/crisp-sdk/src/utils.ts | 1 - examples/CRISP/packages/crisp-sdk/src/vote.ts | 3 +- .../tests/signature-merkle-circuit.json | 1 - .../packages/crisp-sdk/tests/vote.test.ts | 30 ++----------------- 5 files changed, 7 insertions(+), 44 deletions(-) delete mode 100644 examples/CRISP/packages/crisp-sdk/tests/signature-merkle-circuit.json diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index a3f76cadcc..b3d0d26e4d 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -52,8 +52,7 @@ fn main( slot_address: pub Field, // Balance Section. balance: Field, -// ) -> pub ([Polynomial<2048>; 1], [Polynomial<2048>; 1]) { -) -> pub bool { +) -> pub ([Polynomial<2048>; 1], [Polynomial<2048>; 1]) { // Verify the ECDSA signature. let is_signature_valid = verify_signature(hashed_message, public_key_x, public_key_y, signature); @@ -87,7 +86,7 @@ fn main( p1is, p2is, ); - + let ct_add: CiphertextAddition<2048, 1, 54, 54, 54> = CiphertextAddition::new( params.crypto_params(), ct0is, @@ -118,19 +117,13 @@ fn main( { // @todo: need to check if vote <= balance. - // assert(merkle_root_calculated == merkle_root); - if - // (is_signature_valid == true) - (merkle_root_calculated == merkle_root) - // (slot_address == address) { // @todo: need to check if vote <= balance. // Verify the correct coefficient values. check_coefficient_values(k1, params.crypto_params().q_mod_t); - // (ct0is, ct1is) - true + (ct0is, ct1is) } else { // check if vote == 0. check_coefficient_zero(k1); @@ -138,7 +131,6 @@ fn main( // as well as the sum assert(is_ct_add_valid); - // (sum_ct0is, sum_ct1is) - false + (sum_ct0is, sum_ct1is) } } diff --git a/examples/CRISP/packages/crisp-sdk/src/utils.ts b/examples/CRISP/packages/crisp-sdk/src/utils.ts index 2b125a08ce..804013b60f 100644 --- a/examples/CRISP/packages/crisp-sdk/src/utils.ts +++ b/examples/CRISP/packages/crisp-sdk/src/utils.ts @@ -48,7 +48,6 @@ export const generateMerkleProof = ( } const leaf = hashLeaf(address, balance.toString()) - console.log("leaf hash", leaf); const index = leaves.findIndex((l) => l === leaf) diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index 601bb3ae8f..c7cca0a8c1 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -253,8 +253,7 @@ export const generateProof = async (crispInputs: CRISPCircuitInputs): Promise(self) {\n // docs:end:assert_max_bit_size\n static_assert(\n BIT_SIZE < modulus_num_bits() as u32,\n \"BIT_SIZE must be less than modulus_num_bits\",\n );\n __assert_max_bit_size(self, BIT_SIZE);\n }\n\n /// Decomposes `self` into its little endian bit decomposition as a `[u1; N]` array.\n /// This slice will be zero padded should not all bits be necessary to represent `self`.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n /// be able to represent the original `Field`.\n ///\n /// # Safety\n /// The bit decomposition returned is canonical and is guaranteed to not overflow the modulus.\n // docs:start:to_le_bits\n pub fn to_le_bits(self: Self) -> [u1; N] {\n // docs:end:to_le_bits\n let bits = __to_le_bits(self);\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_le_bits();\n assert(bits.len() <= p.len());\n let mut ok = bits.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bits[N - 1 - i] != p[N - 1 - i]) {\n assert(p[N - 1 - i] == 1);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bits\n }\n\n /// Decomposes `self` into its big endian bit decomposition as a `[u1; N]` array.\n /// This array will be zero padded should not all bits be necessary to represent `self`.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n /// be able to represent the original `Field`.\n ///\n /// # Safety\n /// The bit decomposition returned is canonical and is guaranteed to not overflow the modulus.\n // docs:start:to_be_bits\n pub fn to_be_bits(self: Self) -> [u1; N] {\n // docs:end:to_be_bits\n let bits = __to_be_bits(self);\n\n if !is_unconstrained() {\n // Ensure that the decomposition does not overflow the modulus\n let p = modulus_be_bits();\n assert(bits.len() <= p.len());\n let mut ok = bits.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bits[i] != p[i]) {\n assert(p[i] == 1);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bits\n }\n\n /// Decomposes `self` into its little endian byte decomposition as a `[u8;N]` array\n /// This array will be zero padded should not all bytes be necessary to represent `self`.\n ///\n /// # Failures\n /// The length N of the array must be big enough to contain all the bytes of the 'self',\n /// and no more than the number of bytes required to represent the field modulus\n ///\n /// # Safety\n /// The result is ensured to be the canonical decomposition of the field element\n // docs:start:to_le_bytes\n pub fn to_le_bytes(self: Self) -> [u8; N] {\n // docs:end:to_le_bytes\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n // Compute the byte decomposition\n let bytes = self.to_le_radix(256);\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_le_bytes();\n assert(bytes.len() <= p.len());\n let mut ok = bytes.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bytes[N - 1 - i] != p[N - 1 - i]) {\n assert(bytes[N - 1 - i] < p[N - 1 - i]);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bytes\n }\n\n /// Decomposes `self` into its big endian byte decomposition as a `[u8;N]` array of length required to represent the field modulus\n /// This array will be zero padded should not all bytes be necessary to represent `self`.\n ///\n /// # Failures\n /// The length N of the array must be big enough to contain all the bytes of the 'self',\n /// and no more than the number of bytes required to represent the field modulus\n ///\n /// # Safety\n /// The result is ensured to be the canonical decomposition of the field element\n // docs:start:to_be_bytes\n pub fn to_be_bytes(self: Self) -> [u8; N] {\n // docs:end:to_be_bytes\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n // Compute the byte decomposition\n let bytes = self.to_be_radix(256);\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_be_bytes();\n assert(bytes.len() <= p.len());\n let mut ok = bytes.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bytes[i] != p[i]) {\n assert(bytes[i] < p[i]);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bytes\n }\n\n fn to_le_radix(self: Self, radix: u32) -> [u8; N] {\n // Brillig does not need an immediate radix\n if !crate::runtime::is_unconstrained() {\n static_assert(1 < radix, \"radix must be greater than 1\");\n static_assert(radix <= 256, \"radix must be less than or equal to 256\");\n static_assert(radix & (radix - 1) == 0, \"radix must be a power of 2\");\n }\n __to_le_radix(self, radix)\n }\n\n fn to_be_radix(self: Self, radix: u32) -> [u8; N] {\n // Brillig does not need an immediate radix\n if !crate::runtime::is_unconstrained() {\n static_assert(1 < radix, \"radix must be greater than 1\");\n static_assert(radix <= 256, \"radix must be less than or equal to 256\");\n static_assert(radix & (radix - 1) == 0, \"radix must be a power of 2\");\n }\n __to_be_radix(self, radix)\n }\n\n // Returns self to the power of the given exponent value.\n // Caution: we assume the exponent fits into 32 bits\n // using a bigger bit size impacts negatively the performance and should be done only if the exponent does not fit in 32 bits\n pub fn pow_32(self, exponent: Field) -> Field {\n let mut r: Field = 1;\n let b: [u1; 32] = exponent.to_le_bits();\n\n for i in 1..33 {\n r *= r;\n r = (b[32 - i] as Field) * (r * self) + (1 - b[32 - i] as Field) * r;\n }\n r\n }\n\n // Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x `elem` {0, ..., p-1} is even, otherwise sgn0(x mod p) = 1.\n pub fn sgn0(self) -> u1 {\n self as u1\n }\n\n pub fn lt(self, another: Field) -> bool {\n if crate::compat::is_bn254() {\n bn254_lt(self, another)\n } else {\n lt_fallback(self, another)\n }\n }\n\n /// Convert a little endian byte array to a field element.\n /// If the provided byte array overflows the field modulus then the Field will silently wrap around.\n pub fn from_le_bytes(bytes: [u8; N]) -> Field {\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n let mut v = 1;\n let mut result = 0;\n\n for i in 0..N {\n result += (bytes[i] as Field) * v;\n v = v * 256;\n }\n result\n }\n\n /// Convert a big endian byte array to a field element.\n /// If the provided byte array overflows the field modulus then the Field will silently wrap around.\n pub fn from_be_bytes(bytes: [u8; N]) -> Field {\n let mut v = 1;\n let mut result = 0;\n\n for i in 0..N {\n result += (bytes[N - 1 - i] as Field) * v;\n v = v * 256;\n }\n result\n }\n}\n\n#[builtin(apply_range_constraint)]\nfn __assert_max_bit_size(value: Field, bit_size: u32) {}\n\n// `_radix` must be less than 256\n#[builtin(to_le_radix)]\nfn __to_le_radix(value: Field, radix: u32) -> [u8; N] {}\n\n// `_radix` must be less than 256\n#[builtin(to_be_radix)]\nfn __to_be_radix(value: Field, radix: u32) -> [u8; N] {}\n\n/// Decomposes `self` into its little endian bit decomposition as a `[u1; N]` array.\n/// This slice will be zero padded should not all bits be necessary to represent `self`.\n///\n/// # Failures\n/// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n/// be able to represent the original `Field`.\n///\n/// # Safety\n/// Values of `N` equal to or greater than the number of bits necessary to represent the `Field` modulus\n/// (e.g. 254 for the BN254 field) allow for multiple bit decompositions. This is due to how the `Field` will\n/// wrap around due to overflow when verifying the decomposition.\n#[builtin(to_le_bits)]\nfn __to_le_bits(value: Field) -> [u1; N] {}\n\n/// Decomposes `self` into its big endian bit decomposition as a `[u1; N]` array.\n/// This array will be zero padded should not all bits be necessary to represent `self`.\n///\n/// # Failures\n/// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n/// be able to represent the original `Field`.\n///\n/// # Safety\n/// Values of `N` equal to or greater than the number of bits necessary to represent the `Field` modulus\n/// (e.g. 254 for the BN254 field) allow for multiple bit decompositions. This is due to how the `Field` will\n/// wrap around due to overflow when verifying the decomposition.\n#[builtin(to_be_bits)]\nfn __to_be_bits(value: Field) -> [u1; N] {}\n\n#[builtin(modulus_num_bits)]\npub comptime fn modulus_num_bits() -> u64 {}\n\n#[builtin(modulus_be_bits)]\npub comptime fn modulus_be_bits() -> [u1] {}\n\n#[builtin(modulus_le_bits)]\npub comptime fn modulus_le_bits() -> [u1] {}\n\n#[builtin(modulus_be_bytes)]\npub comptime fn modulus_be_bytes() -> [u8] {}\n\n#[builtin(modulus_le_bytes)]\npub comptime fn modulus_le_bytes() -> [u8] {}\n\n/// An unconstrained only built in to efficiently compare fields.\n#[builtin(field_less_than)]\nunconstrained fn __field_less_than(x: Field, y: Field) -> bool {}\n\npub(crate) unconstrained fn field_less_than(x: Field, y: Field) -> bool {\n __field_less_than(x, y)\n}\n\n// Convert a 32 byte array to a field element by modding\npub fn bytes32_to_field(bytes32: [u8; 32]) -> Field {\n // Convert it to a field element\n let mut v = 1;\n let mut high = 0 as Field;\n let mut low = 0 as Field;\n\n for i in 0..16 {\n high = high + (bytes32[15 - i] as Field) * v;\n low = low + (bytes32[16 + 15 - i] as Field) * v;\n v = v * 256;\n }\n // Abuse that a % p + b % p = (a + b) % p and that low < p\n low + high * v\n}\n\nfn lt_fallback(x: Field, y: Field) -> bool {\n if is_unconstrained() {\n // Safety: unconstrained context\n unsafe {\n field_less_than(x, y)\n }\n } else {\n let x_bytes: [u8; 32] = x.to_le_bytes();\n let y_bytes: [u8; 32] = y.to_le_bytes();\n let mut x_is_lt = false;\n let mut done = false;\n for i in 0..32 {\n if (!done) {\n let x_byte = x_bytes[32 - 1 - i] as u8;\n let y_byte = y_bytes[32 - 1 - i] as u8;\n let bytes_match = x_byte == y_byte;\n if !bytes_match {\n x_is_lt = x_byte < y_byte;\n done = true;\n }\n }\n }\n x_is_lt\n }\n}\n\nmod tests {\n use crate::{panic::panic, runtime};\n use super::field_less_than;\n\n #[test]\n // docs:start:to_be_bits_example\n fn test_to_be_bits() {\n let field = 2;\n let bits: [u1; 8] = field.to_be_bits();\n assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]);\n }\n // docs:end:to_be_bits_example\n\n #[test]\n // docs:start:to_le_bits_example\n fn test_to_le_bits() {\n let field = 2;\n let bits: [u1; 8] = field.to_le_bits();\n assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]);\n }\n // docs:end:to_le_bits_example\n\n #[test]\n // docs:start:to_be_bytes_example\n fn test_to_be_bytes() {\n let field = 2;\n let bytes: [u8; 8] = field.to_be_bytes();\n assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]);\n assert_eq(Field::from_be_bytes::<8>(bytes), field);\n }\n // docs:end:to_be_bytes_example\n\n #[test]\n // docs:start:to_le_bytes_example\n fn test_to_le_bytes() {\n let field = 2;\n let bytes: [u8; 8] = field.to_le_bytes();\n assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]);\n assert_eq(Field::from_le_bytes::<8>(bytes), field);\n }\n // docs:end:to_le_bytes_example\n\n #[test]\n // docs:start:to_be_radix_example\n fn test_to_be_radix() {\n // 259, in base 256, big endian, is [1, 3].\n // i.e. 3 * 256^0 + 1 * 256^1\n let field = 259;\n\n // The radix (in this example, 256) must be a power of 2.\n // The length of the returned byte array can be specified to be\n // >= the amount of space needed.\n let bytes: [u8; 8] = field.to_be_radix(256);\n assert_eq(bytes, [0, 0, 0, 0, 0, 0, 1, 3]);\n assert_eq(Field::from_be_bytes::<8>(bytes), field);\n }\n // docs:end:to_be_radix_example\n\n #[test]\n // docs:start:to_le_radix_example\n fn test_to_le_radix() {\n // 259, in base 256, little endian, is [3, 1].\n // i.e. 3 * 256^0 + 1 * 256^1\n let field = 259;\n\n // The radix (in this example, 256) must be a power of 2.\n // The length of the returned byte array can be specified to be\n // >= the amount of space needed.\n let bytes: [u8; 8] = field.to_le_radix(256);\n assert_eq(bytes, [3, 1, 0, 0, 0, 0, 0, 0]);\n assert_eq(Field::from_le_bytes::<8>(bytes), field);\n }\n // docs:end:to_le_radix_example\n\n #[test(should_fail_with = \"radix must be greater than 1\")]\n fn test_to_le_radix_1() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(1);\n } else {\n panic(f\"radix must be greater than 1\");\n }\n }\n\n // Updated test to account for Brillig restriction that radix must be greater than 2\n #[test(should_fail_with = \"radix must be greater than 1\")]\n fn test_to_le_radix_brillig_1() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 1;\n let _: [u8; 8] = field.to_le_radix(1);\n } else {\n panic(f\"radix must be greater than 1\");\n }\n }\n\n #[test(should_fail_with = \"radix must be a power of 2\")]\n fn test_to_le_radix_3() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(3);\n } else {\n panic(f\"radix must be a power of 2\");\n }\n }\n\n #[test]\n fn test_to_le_radix_brillig_3() {\n // this test should only fail in constrained mode\n if runtime::is_unconstrained() {\n let field = 1;\n let out: [u8; 8] = field.to_le_radix(3);\n let mut expected = [0; 8];\n expected[0] = 1;\n assert(out == expected, \"unexpected result\");\n }\n }\n\n #[test(should_fail_with = \"radix must be less than or equal to 256\")]\n fn test_to_le_radix_512() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(512);\n } else {\n panic(f\"radix must be less than or equal to 256\")\n }\n }\n\n #[test(should_fail_with = \"Field failed to decompose into specified 16 limbs\")]\n unconstrained fn not_enough_limbs_brillig() {\n let _: [u8; 16] = 0x100000000000000000000000000000000.to_le_bytes();\n }\n\n #[test(should_fail_with = \"Field failed to decompose into specified 16 limbs\")]\n fn not_enough_limbs() {\n let _: [u8; 16] = 0x100000000000000000000000000000000.to_le_bytes();\n }\n\n #[test]\n unconstrained fn test_field_less_than() {\n assert(field_less_than(0, 1));\n assert(field_less_than(0, 0x100));\n assert(field_less_than(0x100, 0 - 1));\n assert(!field_less_than(0 - 1, 0));\n }\n\n #[test]\n unconstrained fn test_large_field_values_unconstrained() {\n let large_field = 0xffffffffffffffff;\n\n let bits: [u1; 64] = large_field.to_le_bits();\n assert_eq(bits[0], 1);\n\n let bytes: [u8; 8] = large_field.to_le_bytes();\n assert_eq(Field::from_le_bytes::<8>(bytes), large_field);\n\n let radix_bytes: [u8; 8] = large_field.to_le_radix(256);\n assert_eq(Field::from_le_bytes::<8>(radix_bytes), large_field);\n }\n\n #[test]\n fn test_large_field_values() {\n let large_val = 0xffffffffffffffff;\n\n let bits: [u1; 64] = large_val.to_le_bits();\n assert_eq(bits[0], 1);\n\n let bytes: [u8; 8] = large_val.to_le_bytes();\n assert_eq(Field::from_le_bytes::<8>(bytes), large_val);\n\n let radix_bytes: [u8; 8] = large_val.to_le_radix(256);\n assert_eq(Field::from_le_bytes::<8>(radix_bytes), large_val);\n }\n\n #[test]\n fn test_decomposition_edge_cases() {\n let zero_bits: [u1; 8] = 0.to_le_bits();\n assert_eq(zero_bits, [0; 8]);\n\n let zero_bytes: [u8; 8] = 0.to_le_bytes();\n assert_eq(zero_bytes, [0; 8]);\n\n let one_bits: [u1; 8] = 1.to_le_bits();\n let expected: [u1; 8] = [1, 0, 0, 0, 0, 0, 0, 0];\n assert_eq(one_bits, expected);\n\n let pow2_bits: [u1; 8] = 4.to_le_bits();\n let expected: [u1; 8] = [0, 0, 1, 0, 0, 0, 0, 0];\n assert_eq(pow2_bits, expected);\n }\n\n #[test]\n fn test_pow_32() {\n assert_eq(2.pow_32(3), 8);\n assert_eq(3.pow_32(2), 9);\n assert_eq(5.pow_32(0), 1);\n assert_eq(7.pow_32(1), 7);\n\n assert_eq(2.pow_32(10), 1024);\n\n assert_eq(0.pow_32(5), 0);\n assert_eq(0.pow_32(0), 1);\n\n assert_eq(1.pow_32(100), 1);\n }\n\n #[test]\n fn test_sgn0() {\n assert_eq(0.sgn0(), 0);\n assert_eq(2.sgn0(), 0);\n assert_eq(4.sgn0(), 0);\n assert_eq(100.sgn0(), 0);\n\n assert_eq(1.sgn0(), 1);\n assert_eq(3.sgn0(), 1);\n assert_eq(5.sgn0(), 1);\n assert_eq(101.sgn0(), 1);\n }\n\n #[test(should_fail_with = \"Field failed to decompose into specified 8 limbs\")]\n fn test_bit_decomposition_overflow() {\n // 8 bits can't represent large field values\n let large_val = 0x1000000000000000;\n let _: [u1; 8] = large_val.to_le_bits();\n }\n\n #[test(should_fail_with = \"Field failed to decompose into specified 4 limbs\")]\n fn test_byte_decomposition_overflow() {\n // 4 bytes can't represent large field values\n let large_val = 0x1000000000000000;\n let _: [u8; 4] = large_val.to_le_bytes();\n }\n\n}\n","path":"std/field/mod.nr"},"19":{"source":"// Exposed only for usage in `std::meta`\npub(crate) mod poseidon2;\n\nuse crate::default::Default;\nuse crate::embedded_curve_ops::{\n EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul, multi_scalar_mul_array_return,\n};\nuse crate::meta::derive_via;\n\n#[foreign(sha256_compression)]\n// docs:start:sha256_compression\npub fn sha256_compression(input: [u32; 16], state: [u32; 8]) -> [u32; 8] {}\n// docs:end:sha256_compression\n\n#[foreign(keccakf1600)]\n// docs:start:keccakf1600\npub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {}\n// docs:end:keccakf1600\n\npub mod keccak {\n #[deprecated(\"This function has been moved to std::hash::keccakf1600\")]\n pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {\n super::keccakf1600(input)\n }\n}\n\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n// docs:start:blake3\npub fn blake3(input: [u8; N]) -> [u8; 32]\n// docs:end:blake3\n{\n if crate::runtime::is_unconstrained() {\n // Temporary measure while Barretenberg is main proving system.\n // Please open an issue if you're working on another proving system and running into problems due to this.\n crate::static_assert(\n N <= 1024,\n \"Barretenberg cannot prove blake3 hashes with inputs larger than 1024 bytes\",\n );\n }\n __blake3(input)\n}\n\n#[foreign(blake3)]\nfn __blake3(input: [u8; N]) -> [u8; 32] {}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint {\n // docs:end:pedersen_commitment\n pedersen_commitment_with_separator(input, 0)\n}\n\n#[inline_always]\npub fn pedersen_commitment_with_separator(\n input: [Field; N],\n separator: u32,\n) -> EmbeddedCurvePoint {\n let mut points = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N];\n for i in 0..N {\n // we use the unsafe version because the multi_scalar_mul will constrain the scalars.\n points[i] = from_field_unsafe(input[i]);\n }\n let generators = derive_generators(\"DEFAULT_DOMAIN_SEPARATOR\".as_bytes(), separator);\n multi_scalar_mul(generators, points)\n}\n\n// docs:start:pedersen_hash\npub fn pedersen_hash(input: [Field; N]) -> Field\n// docs:end:pedersen_hash\n{\n pedersen_hash_with_separator(input, 0)\n}\n\n#[no_predicates]\npub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {\n let mut scalars: [EmbeddedCurveScalar; N + 1] = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N + 1];\n let mut generators: [EmbeddedCurvePoint; N + 1] =\n [EmbeddedCurvePoint::point_at_infinity(); N + 1];\n let domain_generators: [EmbeddedCurvePoint; N] =\n derive_generators(\"DEFAULT_DOMAIN_SEPARATOR\".as_bytes(), separator);\n\n for i in 0..N {\n scalars[i] = from_field_unsafe(input[i]);\n generators[i] = domain_generators[i];\n }\n scalars[N] = EmbeddedCurveScalar { lo: N as Field, hi: 0 as Field };\n\n let length_generator: [EmbeddedCurvePoint; 1] =\n derive_generators(\"pedersen_hash_length\".as_bytes(), 0);\n generators[N] = length_generator[0];\n multi_scalar_mul_array_return(generators, scalars)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n // TODO(https://github.com/noir-lang/noir/issues/5672): Add back assert_constant on starting_index\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\n#[field(bn254)]\n// Same as from_field but:\n// does not assert the limbs are 128 bits\n// does not assert the decomposition does not overflow the EmbeddedCurveScalar\nfn from_field_unsafe(scalar: Field) -> EmbeddedCurveScalar {\n // Safety: xlo and xhi decomposition is checked below\n let (xlo, xhi) = unsafe { crate::field::bn254::decompose_hint(scalar) };\n // Check that the decomposition is correct\n assert_eq(scalar, xlo + crate::field::bn254::TWO_POW_128 * xhi);\n EmbeddedCurveScalar { lo: xlo, hi: xhi }\n}\n\n#[foreign(poseidon2_permutation)]\npub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {}\n\n// Generic hashing support.\n// Partially ported and impacted by rust.\n\n// Hash trait shall be implemented per type.\n#[derive_via(derive_hash)]\npub trait Hash {\n fn hash(self, state: &mut H)\n where\n H: Hasher;\n}\n\n// docs:start:derive_hash\ncomptime fn derive_hash(s: TypeDefinition) -> Quoted {\n let name = quote { $crate::hash::Hash };\n let signature = quote { fn hash(_self: Self, _state: &mut H) where H: $crate::hash::Hasher };\n let for_each_field = |name| quote { _self.$name.hash(_state); };\n crate::meta::make_trait_impl(\n s,\n name,\n signature,\n for_each_field,\n quote {},\n |fields| fields,\n )\n}\n// docs:end:derive_hash\n\n// Hasher trait shall be implemented by algorithms to provide hash-agnostic means.\n// TODO: consider making the types generic here ([u8], [Field], etc.)\npub trait Hasher {\n fn finish(self) -> Field;\n\n fn write(&mut self, input: Field);\n}\n\n// BuildHasher is a factory trait, responsible for production of specific Hasher.\npub trait BuildHasher {\n type H: Hasher;\n\n fn build_hasher(self) -> H;\n}\n\npub struct BuildHasherDefault;\n\nimpl BuildHasher for BuildHasherDefault\nwhere\n H: Hasher + Default,\n{\n type H = H;\n\n fn build_hasher(_self: Self) -> H {\n H::default()\n }\n}\n\nimpl Default for BuildHasherDefault\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u1 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u8 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u16 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u32 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u64 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u128 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for i8 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as u8 as Field);\n }\n}\n\nimpl Hash for i16 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as u16 as Field);\n }\n}\n\nimpl Hash for i32 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as u32 as Field);\n }\n}\n\nimpl Hash for i64 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as u64 as Field);\n }\n}\n\nimpl Hash for bool {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for () {\n fn hash(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl Hash for [T]\nwhere\n T: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.len().hash(state);\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n }\n}\n\nimpl Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n self.3.hash(state);\n }\n}\n\nimpl Hash for (A, B, C, D, E)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n self.3.hash(state);\n self.4.hash(state);\n }\n}\n\n// Some test vectors for Pedersen hash and Pedersen Commitment.\n// They have been generated using the same functions so the tests are for now useless\n// but they will be useful when we switch to Noir implementation.\n#[test]\nfn assert_pedersen() {\n assert_eq(\n pedersen_hash_with_separator([1], 1),\n 0x1b3f4b1a83092a13d8d1a59f7acb62aba15e7002f4440f2275edb99ebbc2305f,\n );\n assert_eq(\n pedersen_commitment_with_separator([1], 1),\n EmbeddedCurvePoint {\n x: 0x054aa86a73cb8a34525e5bbed6e43ba1198e860f5f3950268f71df4591bde402,\n y: 0x209dcfbf2cfb57f9f6046f44d71ac6faf87254afc7407c04eb621a6287cac126,\n is_infinite: false,\n },\n );\n\n assert_eq(\n pedersen_hash_with_separator([1, 2], 2),\n 0x26691c129448e9ace0c66d11f0a16d9014a9e8498ee78f4d69f0083168188255,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2], 2),\n EmbeddedCurvePoint {\n x: 0x2e2b3b191e49541fe468ec6877721d445dcaffe41728df0a0eafeb15e87b0753,\n y: 0x2ff4482400ad3a6228be17a2af33e2bcdf41be04795f9782bd96efe7e24f8778,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3], 3),\n 0x0bc694b7a1f8d10d2d8987d07433f26bd616a2d351bc79a3c540d85b6206dbe4,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3], 3),\n EmbeddedCurvePoint {\n x: 0x1fee4e8cf8d2f527caa2684236b07c4b1bad7342c01b0f75e9a877a71827dc85,\n y: 0x2f9fedb9a090697ab69bf04c8bc15f7385b3e4b68c849c1536e5ae15ff138fd1,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4], 4),\n 0xdae10fb32a8408521803905981a2b300d6a35e40e798743e9322b223a5eddc,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4], 4),\n EmbeddedCurvePoint {\n x: 0x07ae3e202811e1fca39c2d81eabe6f79183978e6f12be0d3b8eda095b79bdbc9,\n y: 0x0afc6f892593db6fbba60f2da558517e279e0ae04f95758587760ba193145014,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5], 5),\n 0xfc375b062c4f4f0150f7100dfb8d9b72a6d28582dd9512390b0497cdad9c22,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5], 5),\n EmbeddedCurvePoint {\n x: 0x1754b12bd475a6984a1094b5109eeca9838f4f81ac89c5f0a41dbce53189bb29,\n y: 0x2da030e3cfcdc7ddad80eaf2599df6692cae0717d4e9f7bfbee8d073d5d278f7,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6], 6),\n 0x1696ed13dc2730062a98ac9d8f9de0661bb98829c7582f699d0273b18c86a572,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6], 6),\n EmbeddedCurvePoint {\n x: 0x190f6c0e97ad83e1e28da22a98aae156da083c5a4100e929b77e750d3106a697,\n y: 0x1f4b60f34ef91221a0b49756fa0705da93311a61af73d37a0c458877706616fb,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7], 7),\n 0x128c0ff144fc66b6cb60eeac8a38e23da52992fc427b92397a7dffd71c45ede3,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7], 7),\n EmbeddedCurvePoint {\n x: 0x015441e9d29491b06563fac16fc76abf7a9534c715421d0de85d20dbe2965939,\n y: 0x1d2575b0276f4e9087e6e07c2cb75aa1baafad127af4be5918ef8a2ef2fea8fc,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8], 8),\n 0x2f960e117482044dfc99d12fece2ef6862fba9242be4846c7c9a3e854325a55c,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8], 8),\n EmbeddedCurvePoint {\n x: 0x1657737676968887fceb6dd516382ea13b3a2c557f509811cd86d5d1199bc443,\n y: 0x1f39f0cb569040105fa1e2f156521e8b8e08261e635a2b210bdc94e8d6d65f77,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9], 9),\n 0x0c96db0790602dcb166cc4699e2d306c479a76926b81c2cb2aaa92d249ec7be7,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9], 9),\n EmbeddedCurvePoint {\n x: 0x0a3ceae42d14914a432aa60ec7fded4af7dad7dd4acdbf2908452675ec67e06d,\n y: 0xfc19761eaaf621ad4aec9a8b2e84a4eceffdba78f60f8b9391b0bd9345a2f2,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10),\n 0x2cd37505871bc460a62ea1e63c7fe51149df5d0801302cf1cbc48beb8dff7e94,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10),\n EmbeddedCurvePoint {\n x: 0x2fb3f8b3d41ddde007c8c3c62550f9a9380ee546fcc639ffbb3fd30c8d8de30c,\n y: 0x300783be23c446b11a4c0fabf6c91af148937cea15fcf5fb054abf7f752ee245,\n is_infinite: false,\n },\n );\n}\n","path":"std/hash/mod.nr"},"51":{"source":"// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\nuse keccak256::keccak256;\n\n/// Given a public key, signature, and hashed message, verify the signature\npub fn verify_signature(\n hashed_message: [u8; 32],\n pub_key_x: [u8; 32],\n pub_key_y: [u8; 32],\n signature: [u8; 64],\n) -> bool {\n std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message)\n}\n\n/// Given a public key, derive the Ethereum address\npub fn derive_address(pub_key_x: [u8; 32], pub_key_y: [u8; 32]) -> [u8; 20] {\n let mut pub_key_bytes = [0; 64];\n for i in 0..32 {\n pub_key_bytes[i] = pub_key_x[i];\n pub_key_bytes[i + 32] = pub_key_y[i];\n }\n\n // Hash the concatenated public key\n let hash = keccak256(pub_key_bytes, 64);\n\n // Extract last 20 bytes as the Ethereum address\n let mut derived_address = [0; 20];\n for i in 0..20 {\n derived_address[i] = hash[i + 12];\n }\n\n derived_address\n}\n\n// Convert a 20-byte address to a field element.\npub fn address_to_field(addr: [u8; 20]) -> Field {\n let mut acc: Field = 0;\n\n for i in 0..20 {\n acc = acc * 256 + (addr[i] as Field);\n }\n\n acc\n}\n\n#[test]\nfn test_derive_address() {\n let pub_key_x = [\n 131, 24, 83, 91, 84, 16, 93, 74, 122, 174, 96, 192, 143, 196, 95, 150, 135, 24, 27, 79, 223,\n 198, 37, 189, 26, 117, 63, 167, 57, 127, 237, 117,\n ];\n let pub_key_y = [\n 53, 71, 241, 28, 168, 105, 102, 70, 242, 243, 172, 176, 142, 49, 1, 106, 250, 194, 62, 99,\n 12, 93, 17, 245, 159, 97, 254, 245, 123, 13, 42, 165,\n ];\n let address = derive_address(pub_key_x, pub_key_y);\n assert(\n address\n == [\n 243, 159, 214, 229, 26, 173, 136, 246, 244, 206, 106, 184, 130, 114, 121, 207, 255,\n 185, 34, 102,\n ],\n );\n}\n\n#[test]\nfn test_verify_signature() {\n let hashed_message = [\n 67, 126, 157, 164, 162, 165, 56, 242, 155, 214, 113, 196, 83, 198, 228, 36, 174, 104, 152,\n 87, 167, 108, 64, 34, 234, 161, 122, 55, 44, 62, 151, 55,\n ];\n let pub_key_x = [\n 131, 24, 83, 91, 84, 16, 93, 74, 122, 174, 96, 192, 143, 196, 95, 150, 135, 24, 27, 79, 223,\n 198, 37, 189, 26, 117, 63, 167, 57, 127, 237, 117,\n ];\n let pub_key_y = [\n 53, 71, 241, 28, 168, 105, 102, 70, 242, 243, 172, 176, 142, 49, 1, 106, 250, 194, 62, 99,\n 12, 93, 17, 245, 159, 97, 254, 245, 123, 13, 42, 165,\n ];\n let signature = [\n 78, 219, 63, 96, 214, 92, 16, 36, 108, 207, 133, 108, 3, 248, 122, 23, 192, 236, 210, 253,\n 206, 125, 83, 124, 163, 54, 106, 125, 188, 223, 192, 39, 44, 76, 228, 107, 188, 221, 183,\n 124, 118, 143, 228, 126, 216, 173, 160, 231, 62, 52, 188, 154, 110, 230, 183, 71, 36, 161,\n 171, 163, 213, 62, 223, 152,\n ];\n assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == true);\n}\n\n#[test]\nfn test_verify_signature_sdk_input() {\n let hashed_message = [\n 200, 232, 98, 162, 80, 131, 242, 57, 252, 76, 226, 45, 127, 206, 207, 39, 206, 44, 211, 171,\n 113, 67, 121, 68, 78, 253, 202, 79, 29, 128, 130, 76,\n ];\n\n let pub_key_x = [\n 131, 24, 83, 91, 84, 16, 93, 74, 122, 174, 96, 192, 143, 196, 95, 150, 135, 24, 27, 79, 223,\n 198, 37, 189, 26, 117, 63, 167, 57, 127, 237, 117,\n ];\n let pub_key_y = [\n 53, 71, 241, 28, 168, 105, 102, 70, 242, 243, 172, 176, 142, 49, 1, 106, 250, 194, 62, 99,\n 12, 93, 17, 245, 159, 97, 254, 245, 123, 13, 42, 165,\n ];\n let signature = [\n 22, 65, 67, 29, 14, 211, 253, 134, 129, 79, 2, 109, 166, 46, 17, 67, 75, 83, 198, 168, 81,\n 98, 254, 167, 249, 146, 24, 191, 60, 48, 125, 236, 127, 54, 28, 35, 95, 7, 182, 88, 120, 10,\n 253, 145, 165, 201, 214, 141, 106, 75, 20, 213, 235, 5, 17, 246, 104, 141, 62, 145, 20, 14,\n 236, 18,\n ];\n\n assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == true);\n}\n\n#[test]\nfn test_fail_verify_signature() {\n let hashed_message = [\n 67, 126, 157, 164, 162, 165, 56, 242, 155, 214, 113, 196, 83, 198, 228, 36, 174, 104, 152,\n 87, 167, 108, 64, 34, 234, 161, 122, 55, 44, 62, 151, 55,\n ];\n let pub_key_x = [\n 131, 24, 83, 91, 84, 16, 93, 74, 122, 174, 96, 192, 143, 196, 95, 150, 135, 24, 27, 79, 223,\n 198, 37, 189, 26, 117, 63, 167, 57, 127, 237, 117,\n ];\n let pub_key_y = [\n 53, 71, 241, 28, 168, 105, 102, 70, 242, 243, 172, 176, 142, 49, 1, 106, 250, 194, 62, 99,\n 12, 93, 17, 245, 159, 97, 254, 245, 123, 13, 42, 165,\n ];\n let signature = [\n 78, 219, 63, 96, 214, 92, 16, 36, 108, 207, 133, 108, 3, 248, 122, 23, 192, 236, 210, 253,\n 206, 125, 83, 124, 163, 54, 106, 125, 188, 223, 192, 39, 44, 76, 228, 107, 188, 221, 183,\n 124, 118, 143, 228, 126, 216, 173, 160, 231, 62, 52, 188, 154, 110, 230, 183, 71, 36, 161,\n 171, 163, 213, 62, 223, 151,\n ];\n\n assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == false);\n}\n\n#[test]\nfn test_address_to_field() {\n let address: [u8; 20] = [\n 243, 159, 214, 229, 26, 173, 136, 246, 244, 206, 106, 184, 130, 114, 121, 207, 255, 185, 34,\n 102,\n ];\n let field = address_to_field(address);\n\n assert(field == 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266);\n}\n","path":"/Users/ctrlc03/Documents/zk/enclave/examples/CRISP/circuits/src/ecdsa.nr"},"52":{"source":"mod ecdsa;\nuse ecdsa::{address_to_field, derive_address, verify_signature};\nmod merkle_tree;\nuse merkle_tree::get_merkle_root;\n// mod utils;\n// use utils::{check_coefficient_values, check_coefficient_zero};\n\nfn main(\n // ECDSA Section.\n public_key_x: [u8; 32],\n public_key_y: [u8; 32],\n signature: [u8; 64],\n hashed_message: [u8; 32],\n // Merkle Tree Section.\n merkle_root: pub Field,\n merkle_proof_length: u32,\n merkle_proof_indices: [u1; 20],\n merkle_proof_siblings: [Field; 20],\n // Slot Address Section.\n slot_address: pub Field,\n // Balance Section.\n balance: Field,\n) -> pub Field {\n // Verify the ECDSA signature.\n let is_signature_valid =\n verify_signature(hashed_message, public_key_x, public_key_y, signature);\n\n // Derive the Ethereum address.\n let address = address_to_field(derive_address(public_key_x, public_key_y));\n\n // Calculate the Merkle root.\n let merkle_root_calculated = get_merkle_root(\n address,\n balance,\n merkle_proof_length,\n merkle_proof_indices,\n merkle_proof_siblings,\n );\n\n address \n}\n","path":"/Users/ctrlc03/Documents/zk/enclave/examples/CRISP/circuits/src/main.nr"},"75":{"source":"mod tests;\n\nuse std::runtime::is_unconstrained;\nuse std::hash::keccak::keccakf1600;\n\nglobal BLOCK_SIZE_IN_BYTES: u32 = 136; //(1600 - BITS * 2) / WORD_SIZE;\nglobal WORD_SIZE: u32 = 8; // Limbs are made up of u64s so 8 bytes each.\nglobal LIMBS_PER_BLOCK: u32 = BLOCK_SIZE_IN_BYTES / WORD_SIZE;\nglobal NUM_KECCAK_LANES: u32 = 25;\n\n#[no_predicates]\npub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] {\n assert(N >= message_size);\n\n // Copy input to block bytes. For that we'll need at least input bytes (N)\n // but we want it to be padded to a multiple of BLOCK_SIZE_IN_BYTES.\n let mut block_bytes = [0; ((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES];\n if is_unconstrained() {\n for i in 0..message_size {\n block_bytes[i] = input[i];\n }\n } else {\n for i in 0..N {\n if i < message_size {\n block_bytes[i] = input[i];\n }\n }\n }\n\n //1. format_input_lanes\n let max_blocks = (N + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES;\n //maximum number of bytes to hash\n let real_max_blocks = (message_size + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES;\n let real_blocks_bytes = real_max_blocks * BLOCK_SIZE_IN_BYTES;\n\n block_bytes[message_size] = 1;\n block_bytes[real_blocks_bytes - 1] = 0x80;\n\n // populate a vector of 64-bit limbs from our byte array\n let mut sliced_buffer =\n [0; (((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES) / WORD_SIZE];\n for i in 0..sliced_buffer.len() {\n let limb_start = WORD_SIZE * i;\n\n let mut sliced = 0;\n let mut v = 1;\n for k in 0..WORD_SIZE {\n sliced += v * (block_bytes[limb_start + k] as Field);\n v *= 256;\n }\n\n sliced_buffer[i] = sliced as u64;\n }\n\n //2. sponge_absorb\n let mut state: [u64; NUM_KECCAK_LANES] = [0; NUM_KECCAK_LANES];\n // When in an unconstrained runtime we can take advantage of runtime loop bounds,\n // thus allowing us to simplify the loop body.\n if is_unconstrained() {\n for i in 0..real_max_blocks {\n if (i == 0) {\n for j in 0..LIMBS_PER_BLOCK {\n state[j] = sliced_buffer[j];\n }\n } else {\n for j in 0..LIMBS_PER_BLOCK {\n state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j];\n }\n }\n state = keccakf1600(state);\n }\n } else {\n // `real_max_blocks` is guaranteed to at least be `1`\n // We peel out the first block as to avoid a conditional inside of the loop.\n // Otherwise, a dynamic predicate can cause a blowup in a constrained runtime.\n for j in 0..LIMBS_PER_BLOCK {\n state[j] = sliced_buffer[j];\n }\n state = keccakf1600(state);\n for i in 1..max_blocks {\n if i < real_max_blocks {\n for j in 0..LIMBS_PER_BLOCK {\n state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j];\n }\n state = keccakf1600(state);\n }\n }\n }\n\n //3. sponge_squeeze\n let mut result = [0; 32];\n for i in 0..4 {\n let lane = state[i] as Field;\n let lane_le: [u8; 8] = lane.to_le_bytes();\n for j in 0..8 {\n result[8 * i + j] = lane_le[j];\n }\n }\n result\n}\n","path":"/Users/ctrlc03/nargo/github.com/noir-lang/keccak256/v0.1.0/src/keccak256.nr"}},"names":["main"],"brillig_names":["directive_integer_quotient","directive_invert","directive_to_radix"]} \ No newline at end of file diff --git a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts index 4527d61403..161a97977e 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -22,10 +22,6 @@ import { DEFAULT_BFV_PARAMS, generateMerkleProof, hashLeaf, MAXIMUM_VOTE_VALUE } import { LEAVES, merkleProof, MESSAGE, SIGNATURE, testAddress, VOTE, votingPowerLeaf } from './constants' import { privateKeyToAccount } from 'viem/accounts' -import merkleTreeCircuit from "./signature-merkle-circuit.json"; -import { CompiledCircuit, Noir } from '@noir-lang/noir_js' -import { UltraHonkBackend } from '@aztec/bb.js' - describe('Vote', () => { const votingPower = 10n @@ -241,26 +237,7 @@ describe('Vote', () => { balance: Field, */ - const noir = new Noir(merkleTreeCircuit as CompiledCircuit) - const backend = new UltraHonkBackend((merkleTreeCircuit as CompiledCircuit).bytecode) - - const { witness, returnValue } = await noir.execute({ - public_key_x: inputs.public_key_x, - public_key_y: inputs.public_key_y, - signature: inputs.signature, - hashed_message: inputs.hashed_message, - merkle_root: inputs.merkle_root, - merkle_proof_length: inputs.merkle_proof_length, - merkle_proof_indices: inputs.merkle_proof_indices, - merkle_proof_siblings: inputs.merkle_proof_siblings, - slot_address: inputs.slot_address, - balance: inputs.balance, - } as any) - console.log("addr", inputs.slot_address); - console.log("Circuit return value:", returnValue) - console.log(inputs.slot_address.toLowerCase() === returnValue.toString()); - const proof = await backend.generateProof(witness) - expect(await backend.verifyProof(proof)).toBe(true) + }); it.only('should generate a proof for a voter and verify it', { timeout: 100000 }, async () => { const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) @@ -272,7 +249,6 @@ describe('Vote', () => { const leaf = hashLeaf(account.address.toLowerCase(), votingPowerLeaf.toString()) const leaves = [...LEAVES, leaf] const merkleProof = generateMerkleProof(0n, votingPowerLeaf, account.address.toLowerCase(), leaves, 20) - console.log('Generated Merkle proof:', merkleProof); const inputs = await encryptVoteAndGenerateCRISPInputs({ encodedVote, @@ -285,8 +261,6 @@ describe('Vote', () => { slotAddress: account.address.toLowerCase() }) - // console.log('Generated circuit inputs, generating proof...', merkleProof); - const proof = await generateProof(inputs) const isValid = await verifyProof(proof) @@ -305,7 +279,7 @@ describe('Vote', () => { let maskVote = await generateMaskVote(publicKey, encryptedVote, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress) - maskVote.k1[0] = '1' + maskVote.k1[2047] = '1' const proof = await generateProof(maskVote) const isValid = await verifyProof(proof) From cf4e86940bedb2cd9f9cb9cb76c58f765cc5c87e Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:50:46 +0000 Subject: [PATCH 06/13] chore: cleanup --- examples/CRISP/circuits/src/main.nr | 2 -- examples/CRISP/circuits/src/utils.nr | 28 ++++++++----------- .../packages/crisp-sdk/tests/vote.test.ts | 1 - 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index b3d0d26e4d..ae335bce75 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -69,8 +69,6 @@ fn main( merkle_proof_siblings, ); - println(merkle_root_calculated); - let greco: Greco<2048, 1, 54, 54, 5, 5, 20, 20, 54, 16, 54> = Greco::new( params, pk0is, diff --git a/examples/CRISP/circuits/src/utils.nr b/examples/CRISP/circuits/src/utils.nr index 4ace4cff69..1300072f87 100644 --- a/examples/CRISP/circuits/src/utils.nr +++ b/examples/CRISP/circuits/src/utils.nr @@ -7,7 +7,7 @@ use polynomial::Polynomial; // Check that all valid coefficients are either 0 or 1 -pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) -> bool { +pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) { // This value would allow to fit // 268435456 for yes and 268435456 for no // which would fit the supply of most tokens really (imagining one user just holds all tokens) @@ -17,28 +17,22 @@ pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) - let START_INDEX_Y = HALF_D - HALF_LARGEST_MINIMUM_DEGREE; let START_INDEX_N = D - HALF_LARGEST_MINIMUM_DEGREE; - let mut all_valid = true; - // Loop through all coefficients in the space where we could have a vote // yes part for i in START_INDEX_Y..HALF_D { let coeff = k1.coefficients[i]; - let is_valid = (0 == coeff * (q_mod_t - coeff)); - all_valid = all_valid & is_valid; + assert(0 == coeff * (q_mod_t - coeff)); } // no part for i in START_INDEX_N..D { let coeff = k1.coefficients[i]; - let is_valid = (0 == coeff * (q_mod_t - coeff)); - all_valid = all_valid & is_valid; + assert(0 == coeff * (q_mod_t - coeff)); } - - all_valid } // Check that the polynomial has 0 for all relevant coefficients -pub fn check_coefficient_zero(k1: Polynomial) -> bool { +pub fn check_coefficient_zero(k1: Polynomial) { // This value would allow to fit // 268435456 for yes and 268435456 for no // which would fit the supply of most tokens really (imagining one user just holds all tokens) @@ -61,7 +55,7 @@ pub fn check_coefficient_zero(k1: Polynomial) -> bool { sum += k1.coefficients[i]; } - sum == 0 + assert(sum == 0); } #[test] @@ -75,10 +69,10 @@ fn test_check_coefficient_values_pass() { ], }; - assert(check_coefficient_values(pol, 1) == true); + check_coefficient_values(pol, 1); } -#[test] +#[test(should_fail)] fn test_check_coefficient_values_fail() { let pol = Polynomial { coefficients: [ @@ -89,10 +83,10 @@ fn test_check_coefficient_values_fail() { ], }; - assert(check_coefficient_values(pol, 1) == false); + check_coefficient_values(pol, 1); } -#[test] +#[test(should_fail)] fn test_check_coefficient_zero_fail() { let pol = Polynomial { coefficients: [ @@ -103,7 +97,7 @@ fn test_check_coefficient_zero_fail() { ], }; - assert(check_coefficient_zero(pol) == false); + check_coefficient_zero(pol); } #[test] @@ -117,5 +111,5 @@ fn test_check_coefficient_zero() { ], }; - assert(check_coefficient_zero(pol) == true); + check_coefficient_zero(pol); } diff --git a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts index 161a97977e..65fd929797 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -279,7 +279,6 @@ describe('Vote', () => { let maskVote = await generateMaskVote(publicKey, encryptedVote, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress) - maskVote.k1[2047] = '1' const proof = await generateProof(maskVote) const isValid = await verifyProof(proof) From d0826cab9f624cd1ef8cdeff1d1c6c07ac37492a Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:54:24 +0000 Subject: [PATCH 07/13] chore: prettier --- examples/CRISP/packages/crisp-sdk/src/vote.ts | 2 +- examples/CRISP/packages/crisp-sdk/tests/vote.test.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index c7cca0a8c1..ae2f35b2ab 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -253,7 +253,7 @@ export const generateProof = async (crispInputs: CRISPCircuitInputs): Promise { it.only('should generate a proof for a voter and verify it', { timeout: 100000 }, async () => { const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) - // hardhat default private key - const privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + // hardhat default private key + const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' const account = privateKeyToAccount(privateKey) - const signature = await account.signMessage({ message: MESSAGE }) + const signature = await account.signMessage({ message: MESSAGE }) const leaf = hashLeaf(account.address.toLowerCase(), votingPowerLeaf.toString()) const leaves = [...LEAVES, leaf] const merkleProof = generateMerkleProof(0n, votingPowerLeaf, account.address.toLowerCase(), leaves, 20) @@ -258,7 +258,7 @@ describe('Vote', () => { message: MESSAGE, merkleData: merkleProof, balance: votingPowerLeaf, - slotAddress: account.address.toLowerCase() + slotAddress: account.address.toLowerCase(), }) const proof = await generateProof(inputs) From 8870af8171e3bc8dc96f7b04a7499d65e92b8c81 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:14:49 +0000 Subject: [PATCH 08/13] feat(circuit): check if this is the first value in the slot --- examples/CRISP/circuits/src/main.nr | 14 +++++- examples/CRISP/circuits/src/utils.nr | 4 +- .../contracts/CRISPInputValidator.sol | 5 ++ .../CRISP/packages/crisp-sdk/src/types.ts | 3 ++ examples/CRISP/packages/crisp-sdk/src/vote.ts | 16 +++++++ .../CRISP/packages/crisp-sdk/tests/utils.ts | 29 ++++++++++++ .../packages/crisp-sdk/tests/vote.test.ts | 46 +++++++++++++++++-- 7 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 examples/CRISP/packages/crisp-sdk/tests/utils.ts diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index ae335bce75..375d3d7e2e 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -52,6 +52,8 @@ fn main( slot_address: pub Field, // Balance Section. balance: Field, + // Whether this is the first vote for this slot. + is_first_vote: pub bool, ) -> pub ([Polynomial<2048>; 1], [Polynomial<2048>; 1]) { // Verify the ECDSA signature. let is_signature_valid = @@ -124,11 +126,19 @@ fn main( (ct0is, ct1is) } else { // check if vote == 0. - check_coefficient_zero(k1); + let is_vote_zero = check_coefficient_zero(k1); + assert(is_vote_zero); // as well as the sum assert(is_ct_add_valid); - (sum_ct0is, sum_ct1is) + // need to check if slot is empty (no previous ciphertext). + // If so, (ct0is, ct1is) should be returned. + + if is_first_vote { + (ct0is, ct1is) + } else { + (sum_ct0is, sum_ct1is) + } } } diff --git a/examples/CRISP/circuits/src/utils.nr b/examples/CRISP/circuits/src/utils.nr index 1300072f87..3e2bfd26bb 100644 --- a/examples/CRISP/circuits/src/utils.nr +++ b/examples/CRISP/circuits/src/utils.nr @@ -32,7 +32,7 @@ pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) { } // Check that the polynomial has 0 for all relevant coefficients -pub fn check_coefficient_zero(k1: Polynomial) { +pub fn check_coefficient_zero(k1: Polynomial) -> bool { // This value would allow to fit // 268435456 for yes and 268435456 for no // which would fit the supply of most tokens really (imagining one user just holds all tokens) @@ -55,7 +55,7 @@ pub fn check_coefficient_zero(k1: Polynomial) { sum += k1.coefficients[i]; } - assert(sum == 0); + sum == 0 } #[test] diff --git a/examples/CRISP/packages/crisp-contracts/contracts/CRISPInputValidator.sol b/examples/CRISP/packages/crisp-contracts/contracts/CRISPInputValidator.sol index b23029bc89..917879e62d 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/CRISPInputValidator.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/CRISPInputValidator.sol @@ -89,6 +89,11 @@ contract CRISPInputValidator is IInputValidator, Clone, Ownable(msg.sender) { address slot ) = abi.decode(data, (bytes, bytes32[], bytes, address)); + /// @notice we need to check whether the slot is empty. + /// if the slot is empty + /// @todo pass it to the verifier + // bool isFirstVote = voteSlots[slot].length == 0; + // Check if the ciphertext was encrypted correctly if (!noirVerifier.verify(noirProof, noirPublicInputs)) revert InvalidNoirProof(); diff --git a/examples/CRISP/packages/crisp-sdk/src/types.ts b/examples/CRISP/packages/crisp-sdk/src/types.ts index 05330732b9..39f04e0bbd 100644 --- a/examples/CRISP/packages/crisp-sdk/src/types.ts +++ b/examples/CRISP/packages/crisp-sdk/src/types.ts @@ -168,6 +168,8 @@ export interface CRISPCircuitInputs { slot_address: string // Balance Section. balance: string + // Whether this is the first vote for this slot. + is_first_vote: boolean } /** @@ -214,4 +216,5 @@ export interface EncryptVoteAndGenerateCRISPInputsParams { balance: bigint bfvParams?: BFVParams slotAddress: string + isFirstVote: boolean } diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index ae2f35b2ab..2bd388e792 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -158,6 +158,7 @@ export const validateVote = (votingMode: VotingMode, vote: IVote, votingPower: b * @param signature The signature of the message * @param balance The voter's balance * @param slotAddress The voter's slot address + * @param isFirstVote Whether this is the first vote for this slot * @returns The CRISP circuit inputs */ export const encryptVoteAndGenerateCRISPInputs = async ({ @@ -170,6 +171,7 @@ export const encryptVoteAndGenerateCRISPInputs = async ({ signature, balance, slotAddress, + isFirstVote, }: EncryptVoteAndGenerateCRISPInputsParams): Promise => { if (encodedVote.length !== bfvParams.degree) { throw new RangeError(`encodedVote length ${encodedVote.length} does not match BFV degree ${bfvParams.degree}`) @@ -195,6 +197,7 @@ export const encryptVoteAndGenerateCRISPInputs = async ({ merkle_root: merkleData.proof.root.toString(), slot_address: slotAddress, balance: balance.toString(), + is_first_vote: isFirstVote, } } @@ -206,6 +209,7 @@ export const encryptVoteAndGenerateCRISPInputs = async ({ * @param bfvParams The BFV parameters * @param merkleRoot The merkle root of the census tree * @param slotAddress The voter's slot address + * @param isFirstVote Whether this is the first vote for this slot * @returns The CRISP circuit inputs for a mask vote */ export const generateMaskVote = async ( @@ -214,6 +218,7 @@ export const generateMaskVote = async ( bfvParams = DEFAULT_BFV_PARAMS, merkleRoot: bigint, slotAddress: string, + isFirstVote: boolean, ): Promise => { const plaintextVote: IVote = { yes: 0n, @@ -246,6 +251,7 @@ export const generateMaskVote = async ( merkle_root: merkleRoot.toString(), slot_address: slotAddress, balance: '0', + is_first_vote: isFirstVote, } } @@ -259,6 +265,16 @@ export const generateProof = async (crispInputs: CRISPCircuitInputs): Promise => { + const noir = new Noir(circuit as CompiledCircuit) + const backend = new UltraHonkBackend((circuit as CompiledCircuit).bytecode) + + const { witness, returnValue } = await noir.execute(crispInputs as any) + // const proof = await backend.generateProof(witness) + + return { returnValue, proof: {} as any } +} + export const verifyProof = async (proof: ProofData): Promise => { const backend = new UltraHonkBackend((circuit as CompiledCircuit).bytecode) diff --git a/examples/CRISP/packages/crisp-sdk/tests/utils.ts b/examples/CRISP/packages/crisp-sdk/tests/utils.ts new file mode 100644 index 0000000000..64711d36a5 --- /dev/null +++ b/examples/CRISP/packages/crisp-sdk/tests/utils.ts @@ -0,0 +1,29 @@ +export function normalizeCoefficient(coeff: string): string { + if (coeff.startsWith('0x')) { + return BigInt(coeff).toString(); + } + return coeff.toString(); +} + +export function compareCoefficientsArrays(arr1: any[], arr2: any[]): boolean { + if (arr1.length !== arr2.length) return false; + + for (let i = 0; i < arr1.length; i++) { + if (!arr1[i] || !arr2[i]) return false; + + const coeff1 = arr1[i].coefficients; + const coeff2 = arr2[i].coefficients; + + if (!coeff1 || !coeff2) return false; + if (coeff1.length !== coeff2.length) return false; + + for (let k = 0; k < coeff1.length; k++) { + const normalized1 = normalizeCoefficient(coeff1[k]); + const normalized2 = normalizeCoefficient(coeff2[k]); + + if (normalized1 !== normalized2) return false; + } + } + + return true; +} diff --git a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts index aaffeb57ae..40ba0fbb04 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -13,6 +13,7 @@ import { encryptVoteAndGenerateCRISPInputs, generateMaskVote, generateProof, + generateProofWithReturnValue, validateVote, verifyProof, } from '../src/vote' @@ -21,6 +22,7 @@ import { DEFAULT_BFV_PARAMS, generateMerkleProof, hashLeaf, MAXIMUM_VOTE_VALUE } import { LEAVES, merkleProof, MESSAGE, SIGNATURE, testAddress, VOTE, votingPowerLeaf } from './constants' import { privateKeyToAccount } from 'viem/accounts' +import { compareCoefficientsArrays } from './utils' describe('Vote', () => { const votingPower = 10n @@ -139,6 +141,7 @@ describe('Vote', () => { merkleData: merkleProof, balance: votingPowerLeaf, slotAddress: testAddress, + isFirstVote: false, }) expect(crispInputs.prev_ct0is).toBeInstanceOf(Array) @@ -169,7 +172,7 @@ describe('Vote', () => { describe('generateMaskVote', () => { it('should generate a mask vote and the right circuit inputs', async () => { - const crispInputs = await generateMaskVote(publicKey, previousCiphertext, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress) + const crispInputs = await generateMaskVote(publicKey, previousCiphertext, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress, false) expect(crispInputs.prev_ct0is).toBeInstanceOf(Array) expect(crispInputs.prev_ct1is).toBeInstanceOf(Array) @@ -199,7 +202,7 @@ describe('Vote', () => { describe('generateProof/verifyProof', () => { it('should generate a proof for a voter and verify it', { timeout: 180000 }, async () => { - const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) + const encodedVote = encodeVote({ yes: 0n, no: 0n }, VotingMode.GOVERNANCE, votingPower) // hardhat default private key const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' @@ -218,6 +221,7 @@ describe('Vote', () => { merkleData: merkleProof, balance: votingPowerLeaf, slotAddress: account.address.toLowerCase(), + isFirstVote: false, }) /** @@ -277,12 +281,48 @@ describe('Vote', () => { const vote = BigInt64Array.from(encodedVote.map(BigInt)) const encryptedVote = zkInputsGenerator.encryptVote(publicKey, vote) - let maskVote = await generateMaskVote(publicKey, encryptedVote, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress) + let maskVote = await generateMaskVote(publicKey, encryptedVote, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress, false) const proof = await generateProof(maskVote) const isValid = await verifyProof(proof) expect(isValid).toBe(true) }) + + it('should return ciphertext if masking a vote and it is the first operation on the slot', { timeout: 180000 }, async () => { + const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) + const zkInputsGenerator: ZKInputsGenerator = new ZKInputsGenerator( + DEFAULT_BFV_PARAMS.degree, + DEFAULT_BFV_PARAMS.plaintextModulus, + DEFAULT_BFV_PARAMS.moduli, + ) + const vote = BigInt64Array.from(encodedVote.map(BigInt)) + const encryptedVote = zkInputsGenerator.encryptVote(publicKey, vote) + + let maskVote = await generateMaskVote(publicKey, encryptedVote, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress, true) + + const { returnValue } = await generateProofWithReturnValue(maskVote) + + expect(compareCoefficientsArrays(maskVote.ct0is, (returnValue as any[])[0])).toBe(true); + expect(compareCoefficientsArrays(maskVote.ct1is, (returnValue as any[])[1])).toBe(true); + }) + + it('should return the sum if masking a vote and it is not the first operation on the slot', { timeout: 180000 }, async () => { + const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) + const zkInputsGenerator: ZKInputsGenerator = new ZKInputsGenerator( + DEFAULT_BFV_PARAMS.degree, + DEFAULT_BFV_PARAMS.plaintextModulus, + DEFAULT_BFV_PARAMS.moduli, + ) + const vote = BigInt64Array.from(encodedVote.map(BigInt)) + const encryptedVote = zkInputsGenerator.encryptVote(publicKey, vote) + + let maskVote = await generateMaskVote(publicKey, encryptedVote, DEFAULT_BFV_PARAMS, merkleProof.proof.root, testAddress, false) + + const { returnValue } = await generateProofWithReturnValue(maskVote) + + expect(compareCoefficientsArrays(maskVote.sum_ct0is, (returnValue as any[])[0])).toBe(true); + expect(compareCoefficientsArrays(maskVote.sum_ct1is, (returnValue as any[])[1])).toBe(true); + }) }) }) From c13456e81e756e10b36657e3de6a9f34138d24ec Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:16:51 +0000 Subject: [PATCH 09/13] chore: add missing license --- examples/CRISP/packages/crisp-sdk/tests/utils.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/CRISP/packages/crisp-sdk/tests/utils.ts b/examples/CRISP/packages/crisp-sdk/tests/utils.ts index 64711d36a5..874e8f164a 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/utils.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/utils.ts @@ -1,3 +1,9 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + export function normalizeCoefficient(coeff: string): string { if (coeff.startsWith('0x')) { return BigInt(coeff).toString(); From 7107288c29a33d396a4a14509e544cb24876d0ee Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:24:55 +0000 Subject: [PATCH 10/13] chore: revert returning bool in check_coefficients_zero --- examples/CRISP/circuits/src/utils.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/CRISP/circuits/src/utils.nr b/examples/CRISP/circuits/src/utils.nr index 3e2bfd26bb..6d761306aa 100644 --- a/examples/CRISP/circuits/src/utils.nr +++ b/examples/CRISP/circuits/src/utils.nr @@ -32,7 +32,7 @@ pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) { } // Check that the polynomial has 0 for all relevant coefficients -pub fn check_coefficient_zero(k1: Polynomial) -> bool { +pub fn check_coefficient_zero(k1: Polynomial) { // This value would allow to fit // 268435456 for yes and 268435456 for no // which would fit the supply of most tokens really (imagining one user just holds all tokens) @@ -55,7 +55,7 @@ pub fn check_coefficient_zero(k1: Polynomial) -> bool { sum += k1.coefficients[i]; } - sum == 0 + assert(sum == 0); } #[test] From e205b1255d840dd087abfd451632f71eeb978cb5 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:41:23 +0000 Subject: [PATCH 11/13] fix: ensure we check each coefficient to not be zero in check_coefficients_zero --- examples/CRISP/circuits/src/utils.nr | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/CRISP/circuits/src/utils.nr b/examples/CRISP/circuits/src/utils.nr index 6d761306aa..30ae1f6b93 100644 --- a/examples/CRISP/circuits/src/utils.nr +++ b/examples/CRISP/circuits/src/utils.nr @@ -32,7 +32,7 @@ pub fn check_coefficient_values(k1: Polynomial, q_mod_t: Field) { } // Check that the polynomial has 0 for all relevant coefficients -pub fn check_coefficient_zero(k1: Polynomial) { +pub fn check_coefficient_zero(k1: Polynomial) -> bool { // This value would allow to fit // 268435456 for yes and 268435456 for no // which would fit the supply of most tokens really (imagining one user just holds all tokens) @@ -42,20 +42,24 @@ pub fn check_coefficient_zero(k1: Polynomial) { let START_INDEX_Y = HALF_D - HALF_LARGEST_MINIMUM_DEGREE; let START_INDEX_N = D - HALF_LARGEST_MINIMUM_DEGREE; - let mut sum = 0; + let mut res = true; // Loop through all coefficients in the space where we could have a vote // yes part for i in START_INDEX_Y..HALF_D { - sum += k1.coefficients[i]; + if k1.coefficients[i] != 0 { + res = false; + } } // no part for i in START_INDEX_N..D { - sum += k1.coefficients[i]; + if k1.coefficients[i] != 0 { + res = false; + } } - assert(sum == 0); + res } #[test] @@ -86,7 +90,7 @@ fn test_check_coefficient_values_fail() { check_coefficient_values(pol, 1); } -#[test(should_fail)] +#[test] fn test_check_coefficient_zero_fail() { let pol = Polynomial { coefficients: [ @@ -97,7 +101,7 @@ fn test_check_coefficient_zero_fail() { ], }; - check_coefficient_zero(pol); + assert(check_coefficient_zero(pol) == false); } #[test] @@ -111,5 +115,5 @@ fn test_check_coefficient_zero() { ], }; - check_coefficient_zero(pol); + assert(check_coefficient_zero(pol) == true); } From 67f9ff47b035ad26c5e6769d1249057d75983c00 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:07:17 +0000 Subject: [PATCH 12/13] chore: cleanup --- examples/CRISP/circuits/src/main.nr | 3 -- examples/CRISP/packages/crisp-sdk/src/vote.ts | 4 +- .../packages/crisp-sdk/tests/vote.test.ts | 45 +------------------ 3 files changed, 4 insertions(+), 48 deletions(-) diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index 375d3d7e2e..cc7f5c4487 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -114,9 +114,6 @@ fn main( (is_signature_valid == true) & (merkle_root_calculated == merkle_root) & (slot_address == address) - { - // @todo: need to check if vote <= balance. - { // @todo: need to check if vote <= balance. diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index 2bd388e792..6bea3fd9f1 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -270,9 +270,9 @@ export const generateProofWithReturnValue = async (crispInputs: CRISPCircuitInpu const backend = new UltraHonkBackend((circuit as CompiledCircuit).bytecode) const { witness, returnValue } = await noir.execute(crispInputs as any) - // const proof = await backend.generateProof(witness) + const proof = await backend.generateProof(witness) - return { returnValue, proof: {} as any } + return { returnValue, proof } } export const verifyProof = async (proof: ProofData): Promise => { diff --git a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts index 40ba0fbb04..c4ee3328ad 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -201,49 +201,7 @@ describe('Vote', () => { }) describe('generateProof/verifyProof', () => { - it('should generate a proof for a voter and verify it', { timeout: 180000 }, async () => { - const encodedVote = encodeVote({ yes: 0n, no: 0n }, VotingMode.GOVERNANCE, votingPower) - - // hardhat default private key - const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' - const account = privateKeyToAccount(privateKey) - const signature = await account.signMessage({ message: MESSAGE }) - const leaf = hashLeaf(account.address.toLowerCase(), votingPowerLeaf.toString()) - const leaves = [...LEAVES, leaf] - const merkleProof = generateMerkleProof(0n, votingPowerLeaf, account.address.toLowerCase(), leaves, 20) - - const inputs = await encryptVoteAndGenerateCRISPInputs({ - encodedVote, - publicKey, - previousCiphertext, - signature, - message: MESSAGE, - merkleData: merkleProof, - balance: votingPowerLeaf, - slotAddress: account.address.toLowerCase(), - isFirstVote: false, - }) - - /** - * // ECDSA Section. - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - hashed_message: [u8; 32], - // Merkle Tree Section. - merkle_root: pub Field, - merkle_proof_length: u32, - merkle_proof_indices: [u1; 20], - merkle_proof_siblings: [Field; 20], - // Slot Address Section. - slot_address: pub Field, - // Balance Section. - balance: Field, - */ - - - }); - it.only('should generate a proof for a voter and verify it', { timeout: 100000 }, async () => { + it('should generate a proof for a voter and verify it', { timeout: 100000 }, async () => { const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) // hardhat default private key @@ -263,6 +221,7 @@ describe('Vote', () => { merkleData: merkleProof, balance: votingPowerLeaf, slotAddress: account.address.toLowerCase(), + isFirstVote: false, }) const proof = await generateProof(inputs) From cf977a2dacc8cff79d2249bfbc13b83ae5673354 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:27:26 +0000 Subject: [PATCH 13/13] chore: check sum only if not first vote --- examples/CRISP/circuits/src/main.nr | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index cc7f5c4487..f6c6b9ccff 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -126,15 +126,14 @@ fn main( let is_vote_zero = check_coefficient_zero(k1); assert(is_vote_zero); - // as well as the sum - assert(is_ct_add_valid); - // need to check if slot is empty (no previous ciphertext). // If so, (ct0is, ct1is) should be returned. if is_first_vote { (ct0is, ct1is) } else { + // check if the sum is valid + assert(is_ct_add_valid); (sum_ct0is, sum_ct1is) } }