@crisp-e3/sdk 0.25.0 → 0.27.0-insecure.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{circuits-BTUteTjK.d.ts → circuits-DAHZRhC4.d.ts} +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/presets/{insecure-512.d.ts → insecure-64.d.ts} +2 -2
- package/dist/presets/insecure-64.js +26 -0
- package/dist/presets/secure-8192.d.ts +1 -8
- package/dist/presets/secure-8192.js +6 -20
- package/dist/workers/generateCircuitInputs.worker.js.map +1 -1
- package/package.json +10 -10
- package/dist/.tsbuildinfo +0 -1
- package/dist/presets/insecure-512.js +0 -26
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var registeredPreset = () => registered?.preset ?? null;
|
|
|
8
8
|
var requireCircuits = () => {
|
|
9
9
|
if (!registered) {
|
|
10
10
|
throw new Error(
|
|
11
|
-
'No circuit preset registered. Import `loadCircuits` from "@crisp-e3/sdk/insecure-
|
|
11
|
+
'No circuit preset registered. Import `loadCircuits` from "@crisp-e3/sdk/insecure-64" or "@crisp-e3/sdk/secure-8192" and pass the result to `setCircuits()` before proving.'
|
|
12
12
|
);
|
|
13
13
|
}
|
|
14
14
|
return registered;
|
|
@@ -611,13 +611,13 @@ import { Noir } from "@noir-lang/noir_js";
|
|
|
611
611
|
import { Barretenberg, BackendType, UltraHonkBackend } from "@aztec/bb.js";
|
|
612
612
|
|
|
613
613
|
// ../../circuits/bin/fold/target/crisp_fold.json
|
|
614
|
-
var crisp_fold_default = { noir_version: "1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905", hash: "2923443388622925633", abi: { parameters: [{ name: "user_data_encryption_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_public_inputs", type: { kind: "array", length: 5, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_key_hash", type: { kind: "field" }, visibility: "private" }, { name: "crisp_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "crisp_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "crisp_key_hash", type: { kind: "field" }, visibility: "private" }, { name: "prev_ct_commitment", type: { kind: "field" }, visibility: "public" }, { name: "digest_hi", type: { kind: "field" }, visibility: "public" }, { name: "digest_lo", type: { kind: "field" }, visibility: "public" }, { name: "slot_address", type: { kind: "field" }, visibility: "public" }, { name: "merkle_root", type: { kind: "field" }, visibility: "public" }, { name: "is_first_vote", type: { kind: "boolean" }, visibility: "public" }, { name: "num_options", type: { kind: "integer", sign: "unsigned", width: 32 }, visibility: "public" }, { name: "final_ct_commitment", type: { kind: "field" }, visibility: "public" }, { name: "ct_commitment", type: { kind: "field" }, visibility: "private" }, { name: "k1_commitment", type: { kind: "field" }, visibility: "private" }], return_type: { abi_type: { kind: "field" }, visibility: "public" }, error_types: {} }, bytecode: "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", debug_symbols: "tZbbboMwDIbfJddcJM7B8V5lmira0gkJ0YrBpKnau890JJRJibZUuzIQ8sn+fUiu4tjsp9dd25/Ob+Lp+Sr2Q9t17euuOx/qsT33/PX6WYnwuhuHpuFP4m6dd13qoelH8dRPXVeJ97qbbj+9Xer+Zsd64FVZiaY/smXgqe2a+emzWnfL9FblDC67lTcmAixsCCpHsBAISHol6A0B0gSt0S8EbZyKBIDfRgEKQhSgDKaiyOngbVARAF2KYB/Wwf2rDkZFHYw1BTqAJr0SbAnBkgsEp3URAWElFEWBOmYTHRYRVh28liUEkjEKAp/sLJMrKUexphzeielxC8nUJSIFLdA7mURkClNJb0JO+dnSCqEtBNMQpyQtDHffo+R/jbA+JsWStEkEpRHEmi4IUkQpBGQSqym2qZFq9ULhH7SIXjiQpkSLDUKltcin1cfhL0mqVFohU6PoTChRdDYZCtjHc+Iezkk2EG917BOClBfZjrcqzG8pXcHM0BJ0JKArmjrxHGNC0eQjbSLBm6IoKOqgfky+F36rD+2wuQcJ4D8roVn5ShiWvBKWc8cQNx/olcBv4+e5XAn6NkrOzrJVi4XF6tkFtmaxdrEM03Ne3+uhrfdds1zBTlN/uLuRjR+XsBLubJfhfGiO09DMXt/WOI4v", file_map: { "17": { 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;\nuse crate::static_assert;\n\n/// The size of the state accepted by the backend in `poseidon2_permutation`.\nglobal POSEIDON2_CONFIG_STATE_SIZE: u32 = poseidon2_config_state_size();\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\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s<let N: u32>(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n// docs:start:blake3\npub fn blake3<let N: u32>(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<let N: u32>(input: [u8; N]) -> [u8; 32] {}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment<let N: u32>(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<let N: u32>(\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 points[i] = EmbeddedCurveScalar::from_field(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<let N: u32>(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<let N: u32>(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 crate::assert_constant(separator);\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] = EmbeddedCurveScalar::from_field(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, true)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n crate::assert_constant(starting_index);\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\npub fn poseidon2_permutation<let N: u32>(input: [Field; N]) -> [Field; N] {\n static_assert(\n N == POSEIDON2_CONFIG_STATE_SIZE,\n f"the input length must equal the state size in the Poseidon2 config; expected {POSEIDON2_CONFIG_STATE_SIZE}, got {N}",\n );\n poseidon2_permutation_internal(input)\n}\n\n#[foreign(poseidon2_permutation)]\nfn poseidon2_permutation_internal<let N: u32>(input: [Field; N]) -> [Field; N] {}\n\n#[foreign(poseidon2_config_state_size)]\ncomptime fn poseidon2_config_state_size() -> u32 {}\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<H>(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<H>(_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 /// Returns the hash value without consuming the hasher.\n /// Override this for more efficient implementations that avoid copying.\n /// TODO: deprecate finish() and replace it\n fn finish_ref(&self) -> Field {\n (*self).finish()\n }\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<H>;\n\nimpl<H> BuildHasher for BuildHasherDefault<H>\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<H> Default for BuildHasherDefault<H>\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u8 {\n fn hash<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl<T, let N: u32> Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl<T> Hash for [T]\nwhere\n T: Hash,\n{\n fn hash<H>(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<A> Hash for (A,)\nwhere\n A: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n }\n}\n\nimpl<A, B> Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl<A, B, C> Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash<H>(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<A, B, C, D> Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash<H>(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<A, B, C, D, E> 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<H>(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\nimpl<A, B, C, D, E, F> Hash for (A, B, C, D, E, F)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G> Hash for (A, B, C, D, E, F, G)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_> Hash for (A, B, C, D, E, F, G, H_)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I> Hash for (A, B, C, D, E, F, G, H_, I)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J> Hash for (A, B, C, D, E, F, G, H_, I, J)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K> Hash for (A, B, C, D, E, F, G, H_, I, J, K)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K, L> Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n L: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n self.11.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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n );\n}\n', path: "std/hash/mod.nr", function_locations: [{ start: 572, name: "sha256_compression" }, { start: 707, name: "keccakf1600" }, { start: 852, name: "blake2s" }, { start: 950, name: "blake3" }, { start: 1437, name: "__blake3" }, { start: 1555, name: "pedersen_commitment" }, { start: 1784, name: "pedersen_commitment_with_separator" }, { start: 2188, name: "pedersen_hash" }, { start: 2345, name: "pedersen_hash_with_separator" }, { start: 3339, name: "derive_generators" }, { start: 3698, name: "__derive_generators" }, { start: 3776, name: "poseidon2_permutation" }, { start: 4132, name: "poseidon2_permutation_internal" }, { start: 4225, name: "poseidon2_config_state_size" }, { start: 4536, name: "derive_hash" }, { start: 5327, name: "Hasher::finish_ref" }, { start: 5761, name: "<impl BuildHasher for BuildHasherDefault<H>>::build_hasher" }, { start: 5893, name: "<impl Default for BuildHasherDefault<H>>::default" }, { start: 6025, name: "<impl Hash for Field>::hash" }, { start: 6155, name: "<impl Hash for u8>::hash" }, { start: 6295, name: "<impl Hash for u16>::hash" }, { start: 6435, name: "<impl Hash for u32>::hash" }, { start: 6575, name: "<impl Hash for u64>::hash" }, { start: 6716, name: "<impl Hash for u128>::hash" }, { start: 6855, name: "<impl Hash for i8>::hash" }, { start: 7001, name: "<impl Hash for i16>::hash" }, { start: 7148, name: "<impl Hash for i32>::hash" }, { start: 7295, name: "<impl Hash for i64>::hash" }, { start: 7443, name: "<impl Hash for bool>::hash" }, { start: 7590, name: "<impl Hash for ()>::hash" }, { start: 7722, name: "<impl Hash for [T; N]>::hash" }, { start: 7911, name: "<impl Hash for [T]>::hash" }, { start: 8133, name: "<impl Hash for (A,)>::hash" }, { start: 8302, name: "<impl Hash for (A, B)>::hash" }, { start: 8518, name: "<impl Hash for (A, B, C)>::hash" }, { start: 8781, name: "<impl Hash for (A, B, C, D)>::hash" }, { start: 9091, name: "<impl Hash for (A, B, C, D, E)>::hash" }, { start: 9448, name: "<impl Hash for (A, B, C, D, E, F)>::hash" }, { start: 9852, name: "<impl Hash for (A, B, C, D, E, F, G)>::hash" }, { start: 10306, name: "<impl Hash for (A, B, C, D, E, F, G, H_)>::hash" }, { start: 10807, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I)>::hash" }, { start: 11355, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J)>::hash" }, { start: 11950, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K)>::hash" }, { start: 12593, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)>::hash" }, { start: 13187, name: "assert_pedersen" }] }, "22": { source: "pub mod hash;\npub mod aes128;\npub mod array;\npub mod vector;\npub mod ecdsa_secp256k1;\npub mod ecdsa_secp256r1;\npub mod embedded_curve_ops;\npub mod field;\npub mod collections;\npub mod compat;\npub mod convert;\npub mod option;\npub mod string;\npub mod test;\npub mod cmp;\npub mod ops;\npub mod default;\npub mod prelude;\npub mod runtime;\npub mod meta;\npub mod append;\npub mod mem;\npub mod panic;\npub mod hint;\n\nmod integer;\nmod primitive_docs;\nmod internal;\n\n// Oracle calls are required to be wrapped in an unconstrained function\n// Thus, the only argument to the `println` oracle is expected to always be an ident\n#[oracle(print)]\nunconstrained fn print_oracle<T>(with_newline: bool, input: T) {}\n\nunconstrained fn print_unconstrained<T>(with_newline: bool, input: T) {\n print_oracle(with_newline, input);\n}\n\n/// Print the given input to stdout followed by a newline\npub fn println<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(true, input);\n }\n}\n\n/// Print the given input to stdout\npub fn print<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(false, input);\n }\n}\n\n/// Asserts the validity of the provided proof and public inputs against the provided verification key and hash.\n///\n/// The ACVM cannot determine whether the provided proof is valid during execution as this requires knowledge of\n/// the backend against which the program is being proven. However if an invalid proof if submitted, the program may\n/// fail to prove or the backend may generate a proof which will subsequently fail to verify.\n///\n/// # Important Note\n///\n/// If you are not developing your own backend such as [Barretenberg](https://github.com/AztecProtocol/barretenberg)\n/// you probably shouldn't need to interact with this function directly. It's easier and safer to use a verification\n/// library which is published by the developers of the backend which will document or enforce any safety requirements.\n///\n/// If you use this directly, you're liable to introduce underconstrainedness bugs and *your circuit will be insecure*.\n///\n/// # Arguments\n/// - verification_key: The verification key of the circuit to be verified.\n/// - proof: The proof to be verified.\n/// - public_inputs: The public inputs associated with `proof`\n/// - key_hash: The hash of `verification_key` of the form expected by the backend.\n/// - proof_type: An identifier for the proving scheme used to generate the proof to be verified. This allows\n/// for a single backend to support verifying multiple proving schemes.\n///\n/// # Constraining `key_hash`\n///\n/// The Noir compiler does not by itself constrain that `key_hash` is a valid hash of `verification_key`.\n/// This is because different backends may differ in how they hash their verification keys.\n/// It is then the responsibility of either the noir developer (by explicitly hashing the verification key\n/// in the correct manner) or by the proving system itself internally asserting the correctness of `key_hash`.\npub fn verify_proof_with_type<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {\n if !crate::runtime::is_unconstrained() {\n crate::assert_constant(proof_type);\n }\n verify_proof_internal(verification_key, proof, public_inputs, key_hash, proof_type);\n}\n\n#[foreign(recursive_aggregation)]\nfn verify_proof_internal<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {}\n\n/// Asserts that the given value is known at compile-time.\n/// Useful for debugging for-loop bounds.\n#[builtin(assert_constant)]\npub fn assert_constant<T>(x: T) {}\n\n/// Asserts that the given value is both true and known at compile-time.\n/// The message can be a string, a format string, or any value, as long as it is known at compile-time\n#[builtin(static_assert)]\npub fn static_assert<T>(predicate: bool, message: T) {}\n\n/// Force a field value to be a witness instead of a constant in the compiled output.\n/// This is often only useful for debugging compiler optimizations.\n///\n/// This has no effect in unconstrained or comptime code.\n#[builtin(as_witness)]\npub fn as_witness(x: Field) {}\n", path: "std/lib.nr", function_locations: [{ start: 689, name: "print_oracle" }, { start: 763, name: "print_unconstrained" }, { start: 893, name: "println" }, { start: 1076, name: "print" }, { start: 3277, name: "verify_proof_with_type" }, { start: 3694, name: "verify_proof_internal" }, { start: 3859, name: "assert_constant" }, { start: 4118, name: "static_assert" }, { start: 4389, name: "as_witness" }] }, "52": { 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 bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};\nuse interfold_lib::math::commitments::compute_vk_hash;\n\n/// Binds the four inner `vk_hash` witnesses. Regenerate with `pnpm compute:vk-hash` in\n/// `examples/CRISP` after changing ct0 / ct1 / user_data_encryption / crisp (or the lib preset).\n/// Insecure: `lib::configs::default` uses `insecure::*`; secure: uses `secure::*`.\npub global CRISP_FOLD_EXPECTED_KEY_HASH_INSECURE: Field =\n 0x0fb783a0f9843410cc8252d269ec225a349870415444cbb2263284af2819e1cc;\npub global CRISP_FOLD_EXPECTED_KEY_HASH_SECURE: Field =\n 0x196e81843c72ecfb887e91c1d4243ba605c244193d8e196458c0f0c173f26f58;\n\nfn main(\n // User Data Encryption Section.\n user_data_encryption_verification_key: UltraHonkVerificationKey,\n user_data_encryption_proof: UltraHonkProof,\n user_data_encryption_public_inputs: [Field; 5], // ct0_key_hash, ct1_key_hash, pk_commitment, ct_commitment, k1_commitment\n user_data_encryption_key_hash: Field,\n // Crisp Section.\n crisp_verification_key: UltraHonkVerificationKey,\n crisp_proof: UltraHonkProof,\n crisp_key_hash: Field,\n prev_ct_commitment: pub Field,\n digest_hi: pub Field,\n digest_lo: pub Field,\n slot_address: pub Field,\n merkle_root: pub Field,\n is_first_vote: pub bool,\n num_options: pub u32,\n final_ct_commitment: pub Field,\n ct_commitment: Field,\n k1_commitment: Field,\n) -> pub Field {\n verify_honk_proof_non_zk(\n user_data_encryption_verification_key,\n user_data_encryption_proof,\n user_data_encryption_public_inputs,\n user_data_encryption_key_hash,\n );\n verify_honk_proof_non_zk(\n crisp_verification_key,\n crisp_proof,\n [\n prev_ct_commitment,\n digest_hi,\n digest_lo,\n slot_address,\n merkle_root,\n if is_first_vote { 1 } else { 0 },\n num_options as Field,\n final_ct_commitment,\n ct_commitment,\n k1_commitment,\n ],\n crisp_key_hash,\n );\n\n // Verify that the ct_commitment from the crisp proof matches the one computed from user data encryption.\n assert(ct_commitment == user_data_encryption_public_inputs[3]);\n\n // Verify that the k1_commitment from the crisp proof matches the one computed from user data encryption.\n assert(k1_commitment == user_data_encryption_public_inputs[4]);\n\n let vk_hashes = [\n user_data_encryption_key_hash,\n crisp_key_hash,\n user_data_encryption_public_inputs[0], // ct0_key_hash\n user_data_encryption_public_inputs[1], // ct1_key_hash\n ]\n .as_vector();\n\n let chain_key_hash = compute_vk_hash(vk_hashes);\n assert(\n (chain_key_hash == CRISP_FOLD_EXPECTED_KEY_HASH_INSECURE)\n | (chain_key_hash == CRISP_FOLD_EXPECTED_KEY_HASH_SECURE),\n );\n\n user_data_encryption_public_inputs[2]\n}\n", path: "/Users/ctrlc03/Documents/zk/interfold/examples/CRISP/circuits/bin/fold/src/main.nr", function_locations: [{ start: 1641, name: "main" }] }, "53": { source: '// Constants for UltraHonk recursive verifier inputs\npub global PROOF_TYPE_HONK: u32 = 0; // identifier for UltraHonk verfier\npub global RECURSIVE_PROOF_LENGTH: u32 = 410;\npub global ULTRA_VK_LENGTH_IN_FIELDS: u32 = 115;\n\npub type UltraHonkProof = [Field; RECURSIVE_PROOF_LENGTH];\npub type UltraHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\n// Constants for Rollup-UltraHonk recursive verifier inputs (N.B. this is equivalent to UH plus IPA claim and proof)\npub global PROOF_TYPE_ROLLUP_HONK: u32 = 4; // identifier for rollup-UltraHonk verfier\npub global PROOF_TYPE_ROOT_ROLLUP_HONK: u32 = 5; // identifier for root-rollup-UltraHonk verfier (closes the IPA accumulator)\npub global IPA_CLAIM_SIZE: u32 = 6;\npub global IPA_PROOF_LENGTH: u32 = 64;\npub global RECURSIVE_ROLLUP_HONK_PROOF_LENGTH: u32 =\n RECURSIVE_PROOF_LENGTH + IPA_CLAIM_SIZE + IPA_PROOF_LENGTH;\n\npub type RollupHonkProof = [Field; RECURSIVE_ROLLUP_HONK_PROOF_LENGTH];\npub type RollupHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\npub global PROOF_TYPE_HONK_ZK: u32 = 6; // identifier for UltraHonk ZK verfier\npub global RECURSIVE_ZK_PROOF_LENGTH: u32 = 450 + 8;\n\npub type UltraHonkZKProof = [Field; RECURSIVE_ZK_PROOF_LENGTH];\n\n// Verifies a non-zero-knowledge UltraHonk proof.\n//\n// Represents standard UltraHonk recursive verification for proofs that do not hide the witness.\n// Use this only in situations where zero-knowledge is not required.\npub fn verify_honk_proof_non_zk<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof with IPA (Inner Product Argument).\n//\n// This variant includes an IPA claim and proof appended to the standard UltraHonk proof,\n// used to amortize IPA recursive verification costs in rollup circuits.\npub fn verify_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROLLUP_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof and closes the IPA accumulator in-circuit.\n//\n// Use this at the root of a rollup aggregation tree. The inner proof has the same RollupHonk shape\n// as `verify_rolluphonk_proof`, but instead of propagating an accumulated IPA claim out as a public\n// input, this variant performs a full native IPA verification inside the circuit. The outer circuit\n// should therefore be proved as a standard (non-rollup) UltraHonk circuit, producing a proof\n// suitable for verification by `verify_honk_proof` / `verify_honk_proof_non_zk`.\n//\n// When two RollupHonk inputs are verified this way in one circuit, their two IPA claims are first\n// accumulated into one, then the accumulated claim is fully verified.\npub fn verify_root_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROOT_ROLLUP_HONK,\n );\n}\n\n// Verifies a zero-knowledge UltraHonk proof.\n//\n// This verifier is for UltraHonk proofs constructed with zero-knowledge, which hide the witness\n// values from the verifier.\n// Note: We intentionally choose the generic name "verify_honk_proof" for this function, as we\n// want ZK to be the default unless the user explicitly opts out.\npub fn verify_honk_proof<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkZKProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK_ZK,\n );\n}\n', path: "/Users/ctrlc03/nargo/github.com/AztecProtocol/aztec-packages/v5.1.0/barretenberg/noir/bb_proof_verification/src/lib.nr", function_locations: [{ start: 1646, name: "verify_honk_proof_non_zk" }, { start: 2262, name: "verify_rolluphonk_proof" }, { start: 3386, name: "verify_root_rolluphonk_proof" }, { start: 4087, name: "verify_honk_proof" }] }, "87": { 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 crate::math::helpers::{compute_safe, flatten, flatten_checked};\nuse crate::math::polynomial::Polynomial;\n\n/// DOMAIN SEPARATORS\n\n// Domain separator - "PK"\npub global DS_PK: [u8; 64] = [\n 0x50, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_GENERATION"\npub global DS_PK_GENERATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_COMPUTATION"\npub global DS_SHARE_COMPUTATION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_ENCRYPTION"\npub global DS_SHARE_ENCRYPTION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_AGGREGATION"\npub global DS_PK_AGGREGATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CIPHERTEXT"\npub global DS_CIPHERTEXT: [u8; 64] = [\n 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "AGGREGATED_SHARES"\npub global DS_AGGREGATED_SHARES: [u8; 64] = [\n 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45,\n 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "VK_HASH"\npub global DS_VK_HASH: [u8; 64] = [\n 0x56, 0x4b, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "RECURSIVE_AGGREGATION"\npub global DS_RECURSIVE_AGGREGATION: [u8; 64] = [\n 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47,\n 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_PK_GENERATION"\npub global DS_CLG_PK_GENERATION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_ENCRYPTION"\npub global DS_CLG_SHARE_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_USER_DATA_ENCRYPTION"\npub global DS_CLG_USER_DATA_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e,\n 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_DECRYPTION"\npub global DS_CLG_SHARE_DECRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "THRESHOLD_DECRYPTION_SHARE"\npub global DS_THRESHOLD_DECRYPTION_SHARE: [u8; 64] = [\n 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "USER_DATA_ENCRYPTION_COMMITMENT"\npub global DS_USER_DATA_ENCRYPTION_COMMITMENT: [u8; 64] = [\n 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n/// WRAPPERS\n\npub fn compute_commitment(inputs: [Field], domain_separator: [u8; 64]) -> Field {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 1])[0]\n}\n\npub fn compute_single_polynomial_commitment<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_single_polynomial_commitment`] with the digit asserts, for a polynomial this circuit\n/// carries no bound for. ct1 commits `u` this way: ct0 range-checks the same `u` to `u_bound` and the\n/// `user_data_encryption` fold asserts the two `u` commitments are equal, so making ct1\'s packing\n/// injective is what forces its `u` to equal ct0\'s \u2014 the bound transfers instead of being repeated.\n/// Produces the same value as the plain version for any input the plain version could legitimately\n/// have committed.\npub fn compute_single_polynomial_commitment_checked<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_multiple_polynomial_commitment<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_challenge<let L: u32>(inputs: [Field], domain_separator: [u8; 64]) -> [Field] {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 2 * L])\n}\n\npub fn single_polynomial_payload<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, inputs)\n}\n\npub fn single_polynomial_payload_checked<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload_checked<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, inputs)\n}\n\n/// COMMITMENTS\n\npub fn compute_dkg_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let mut payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n payload = multiple_polynomial_payload::<N, L, BIT_PK>(payload, pk1);\n\n compute_commitment(payload, DS_PK)\n}\n\npub fn compute_threshold_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\n/// [`compute_multiple_polynomial_commitment`] for a circuit that **opens** this commitment.\npub fn compute_multiple_polynomial_commitment_checked<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_threshold_pk_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C5). Asserts each packed digit fits its slot, which makes the opening unique and so\n/// forces the opened `pk0` to equal the `pk0` C1 committed. Produces the same value as the plain\n/// helper, so C1 needs no change.\npub fn compute_threshold_pk_commitment_checked<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\npub fn compute_share_computation_sk_commitment<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\n/// [`compute_share_computation_sk_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2a). C1 creates it and bounds `sk` to the trinary range, so making the opening\n/// unique forces the witness to equal that `sk`.\npub fn compute_share_computation_sk_commitment_checked<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_computation_e_sm_commitment<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_encryption_commitment_from_message<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\n/// [`compute_share_encryption_commitment_from_message`] for a circuit that **opens** this\n/// commitment rather than creating it (C4). Asserts each packed digit fits its slot, forcing the\n/// opened share to equal the share C2 committed. Same value as the plain helper, so C2 and C3 need\n/// no change.\npub fn compute_share_encryption_commitment_from_message_checked<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\npub fn compute_aggregated_shares_commitment<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// Commitment to a threshold decryption share: all CRT limbs, first `K` coefficients per limb\n/// (same layout as `Polynomial<K>` in decrypted_shares_aggregation).\n///\n/// `NATIVE_BIT_WIDTH` must cover native coefficients in \\([0, q_l)\\) per limb (not centered `d` bounds).\npub fn compute_threshold_decryption_share_commitment<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload =\n multiple_polynomial_payload::<K, L, NATIVE_BIT_WIDTH>([].as_vector(), d_share_limbs);\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_threshold_decryption_share_commitment`] for a circuit that **opens** this commitment\n/// rather than creating it (C7). C6 creates it and pins each coefficient to a native residue in\n/// \\([0, q)\\) via `verify_d_native_trunc_binding`, so asserting the digits here makes the opening\n/// unique and forces the opened share to equal the one C6 committed. Produces the same value as the\n/// plain version for any input the plain version could legitimately have committed.\npub fn compute_threshold_decryption_share_commitment_checked<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<K, L, NATIVE_BIT_WIDTH>(\n [].as_vector(),\n d_share_limbs,\n );\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_share_computation_e_sm_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2b). C1 creates it and bounds each `e_sm` limb to a centered residue.\npub fn compute_share_computation_e_sm_commitment_checked<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_pk_aggregation_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let commit_pk0 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk0, DS_PK_AGGREGATION);\n let commit_pk1 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk1, DS_PK_AGGREGATION);\n\n let inputs = [commit_pk0, commit_pk1].as_vector();\n\n compute_commitment(inputs, DS_PK_AGGREGATION)\n}\n\npub fn compute_recursive_aggregation_commitment(payload: [Field]) -> Field {\n compute_commitment(payload, DS_RECURSIVE_AGGREGATION)\n}\n\npub fn compute_vk_hash(vk_hashes: [Field]) -> Field {\n compute_commitment(vk_hashes, DS_VK_HASH)\n}\n\n/// [`compute_aggregated_shares_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). C4 creates it after `normalize_aggregated` has reduced and centered the\n/// aggregate, so making the opening unique forces the witness to equal that canonical value.\npub fn compute_aggregated_shares_commitment_checked<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// [`compute_ciphertext_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). No upstream circuit bounds the ciphertext limbs, so this buys injectivity\n/// rather than canonicality \u2014 which is the property wanted here: it forces C6 to decrypt exactly\n/// the ciphertext that was committed, whatever its representation.\npub fn compute_ciphertext_commitment_checked<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\npub fn compute_ciphertext_commitment<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\n/// COMMITMENTS FOR CHALLENGES\n\npub fn compute_threshold_pk_challenge(payload: [Field]) -> Field {\n compute_commitment(payload, DS_CLG_PK_GENERATION)\n}\n\npub fn compute_share_encryption_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_SHARE_ENCRYPTION)\n}\n\npub fn compute_threshold_share_decryption_challenge<let L: u32>(payload: [Field]) -> Field {\n compute_challenge::<L>(payload, DS_CLG_SHARE_DECRYPTION)[0]\n}\n\npub fn compute_user_data_encryption_ct0_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n\npub fn compute_user_data_encryption_ct1_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n', path: "/Users/ctrlc03/Documents/zk/interfold/circuits/lib/src/math/commitments.nr", function_locations: [{ start: 7784, name: "compute_commitment" }, { start: 8012, name: "compute_single_polynomial_commitment" }, { start: 8827, name: "compute_single_polynomial_commitment_checked" }, { start: 9138, name: "compute_multiple_polynomial_commitment" }, { start: 9375, name: "compute_challenge" }, { start: 9585, name: "single_polynomial_payload" }, { start: 9784, name: "multiple_polynomial_payload" }, { start: 9970, name: "single_polynomial_payload_checked" }, { start: 10185, name: "multiple_polynomial_payload_checked" }, { start: 10406, name: "compute_dkg_pk_commitment" }, { start: 10733, name: "compute_threshold_pk_commitment" }, { start: 11138, name: "compute_multiple_polynomial_commitment_checked" }, { start: 11750, name: "compute_threshold_pk_commitment_checked" }, { start: 12008, name: "compute_share_computation_sk_commitment" }, { start: 12516, name: "compute_share_computation_sk_commitment_checked" }, { start: 12795, name: "compute_share_computation_e_sm_commitment" }, { start: 13067, name: "compute_share_encryption_commitment_from_message" }, { start: 13647, name: "compute_share_encryption_commitment_from_message_checked" }, { start: 13931, name: "compute_aggregated_shares_commitment" }, { start: 14512, name: "compute_threshold_decryption_share_commitment" }, { start: 15336, name: "compute_threshold_decryption_share_commitment_checked" }, { start: 15876, name: "compute_share_computation_e_sm_commitment_checked" }, { start: 16182, name: "compute_pk_aggregation_commitment" }, { start: 16571, name: "compute_recursive_aggregation_commitment" }, { start: 16686, name: "compute_vk_hash" }, { start: 17169, name: "compute_aggregated_shares_commitment_checked" }, { start: 17843, name: "compute_ciphertext_commitment_checked" }, { start: 18325, name: "compute_ciphertext_commitment" }, { start: 18724, name: "compute_threshold_pk_challenge" }, { start: 18866, name: "compute_share_encryption_challenge" }, { start: 19023, name: "compute_threshold_share_decryption_challenge" }, { start: 19183, name: "compute_user_data_encryption_ct0_challenge" }, { start: 19344, name: "compute_user_data_encryption_ct1_challenge" }] }, "89": { 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\n//! Helper functions for circuit construction and cryptographic operations.\nuse crate::math::polynomial::Polynomial;\nuse crate::math::safe::SafeSponge;\n\n/// Compute hex-aligned packing parameters for a given `BIT`.\n///\n/// # Purpose\n/// Returns `(nibble_bits, group)` for use by pack/flatten so layout stays consistent.\n/// - `nibble_bits`: ceil (`BIT`) to the next multiple of 4 (nibble alignment).\n/// - Examples: `BIT = 7 -> 8`, `BIT = 8 -> 8`, `BIT = 9 -> 12`, `BIT = 10 -> 12`, `BIT = 11 -> 12`,\n/// `BIT=16 -> 16`, `BIT = 17 -> 20`.\n/// - `group`: max number of encoded limbs that fit in one BN254 field element,\n/// when each limb uses an extra 4 bits (see below).\n///\n/// # Rationale\n/// - We align to nibbles so powers of two are hex-friendly and deterministic.\n/// - We reserve one extra nibble (4 bits) per stored value to lift signed\n/// coefficients into the non-negative range (e.g., store `v + 2^nibble_bits`),\n/// which implies a radix of `2^(nibble_bits + 4)`.\n///\n/// # Safety\n/// - Asserts `nibble_bits + 4 <= 254` to avoid mod-p wrap on BN254.\n/// - Ensures at least one limb fits: `group >= 1`.\nfn packing_layout<let BIT: u32>() -> (u32, u32) {\n // Ceil BIT up to the next multiple of 4 (nibble alignment).\n let nibble_bits = ((BIT + 3) / 4) * 4;\n\n // Each stored limb uses an extra nibble because negative coefficients\n // will be shifted to positive, so radix = 2^(nibble_bits+4).\n assert(nibble_bits + 4 <= 254);\n\n // Maximum limbs that fit in one BN254 element without wrap.\n let group = 254 / (nibble_bits + 4);\n // `group == 1` packs one value per carrier: it saves no sponge absorptions at all while still\n // charging the range checks that make packing injective, so it is always a loss. Measured at\n // BIT=143: +82% gates against absorbing directly. Reject it rather than pay for nothing.\n assert(group >= 2);\n (nibble_bits, group)\n}\n\n/// Like [`pack`], but asserts every digit fits its slot.\n///\n/// ## Why\n/// `pack` builds each carrier as a base-`radix` number, `acc = acc * radix + (v + base)`. That\n/// representation is unique only while every digit stays below `radix`. An unbounded value can\n/// overflow its slot and cancel against the next, so one commitment gains a second opening and a\n/// prover can choose which coefficients it opens to.\n///\n/// `digit < radix` is exactly `digit.assert_max_bit_size::<nibble_bits + 4>()`, and because\n/// `assert_max_bit_size` also forces `digit >= 0`, a single assert gives the whole condition.\n///\n/// ## When to use it\n/// Use this where a circuit **opens** a commitment it did not create. The creating circuit's own\n/// range checks already keep its digits in slot, so asserting the same here forces the opened\n/// coefficients to equal the committed ones \u2014 injectivity transfers the upstream bound without\n/// repeating it. One assert costs about half a two-sided `range_check_2bounds`.\n///\n/// It does **not** replace a bound the protocol requires in its own right, and it gives injectivity,\n/// not canonicality: it pins the opening, not a unique representative modulo `q`. A value that no\n/// upstream circuit bounded still needs its own check (see C1 `pk0` and C5 `pk0_agg`).\n///\n/// ## Compatibility\n/// Adding asserts does not change the packed value, so a commitment created with [`pack`] and\n/// opened with `pack_checked` agree. Adoption is per circuit and needs no commitment change.\npub fn pack_checked<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field);\n let radix = 2.pow_32((nibble_bits + 4) as Field);\n\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n for chunk in 0..num_chunks {\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n let digit = v + base;\n // digit < radix = 2^(nibble_bits + 4): the injectivity condition.\n digit.assert_max_bit_size::<((BIT + 3) / 4) * 4 + 4>();\n acc = acc * radix + digit;\n }\n\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Like [`flatten`], but packs with [`pack_checked`]. Use where a circuit opens a commitment it did\n/// not create.\npub fn flatten_checked<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n let packed = pack_checked::<A, BIT>(poly[j].coefficients);\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n inputs\n}\n\n/// Flatten `L` polynomials into a single linear stream of packed `Field` carriers.\n///\n/// ## What this does\n/// - For each CRT limb `j` in `0..L`, it packs the coefficients of `poly[j]`\n/// with `pack::<A, BIT>` and appends all resulting carriers to `inputs`.\n/// - The packing layout (nibble-aligned width and `group` size) is taken from\n/// `packing_layout::<BIT>()` and must match what `pack` uses.\n///\n/// ## Determinism & order\n/// - Preserves a stable order: iterate `j = 0..L`, then for each `j` append\n/// carriers in ascending chunk index `i = 0..num_chunks`.\n/// - This ensures transcripts remain deterministic across runs.\n///\n/// ## Generics\n/// - `A`: polynomial degree (number of coefficients per polynomial).\n/// - `L`: number of CRT bases (polynomials).\n/// - `BIT`: per-coefficient bit bound used by the packing layout (compile-time).\n///\n/// ## Returns\n/// - The same `inputs` vector, extended with all carriers in deterministic order.\npub fn flatten<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n // Pack its A coefficients into `num_chunks` carriers using the same BIT layout.\n let packed = pack::<A, BIT>(poly[j].coefficients);\n\n // Append carriers in-order to `inputs` to keep a stable transcript layout.\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n\n // Return the extended input stream.\n inputs\n}\n\n/// Pack `A` values into a `[Field]` vector of carriers using the shared hex-aligned layout.\n///\n/// ## What this does\n/// - Computes `(nibble_bits, group)` via `packing_layout::<BIT>()`.\n/// - Encodes each value as a limb `digit = v + 2^nibble_bits` and concatenates\n/// limbs in base `radix = 2^(nibble_bits + 4)` (one extra nibble of headroom).\n/// - Packs up to `group` limbs per carrier (fits within BN254 254-bit capacity).\n/// - Pads the last, partial carrier with `digit = 2^nibble_bits` to keep a stable layout.\n///\n/// ## Determinism & order\n/// - Processes values in increasing index order and emits carriers in chunk order\n/// (`chunk = 0..num_chunks`). Padding is deterministic.\n///\n/// ## Generics\n/// - `A`: number of input values.\n/// - `BIT`: per-value bit bound; rounded up to `nibble_bits` by `packing_layout`.\n///\n/// ## Preconditions / Notes\n/// - Call with the raw coefficients whose magnitudes already satisfy the BIT bound\n/// (as enforced by the upstream range checks); `pack` performs the signed -> unsigned\n/// shift internally via `v + base`.\n/// - `group >= 1` is enforced by `packing_layout::<BIT>()`.\n/// - Padding with `digit = 2^nibble_bits` encodes `zero limb` consistently.\n///\n/// ## Returns\n/// - A `[Field]` vector where each element is a concatenation of up to `group` limbs,\n/// suitable for hashing or transcript I/O.\npub fn pack<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n // Layout parameters: nibble-aligned width and limbs-per-carrier group size.\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field); // 2^nibble_bits\n let radix = 2.pow_32((nibble_bits + 4) as Field); // 2^(nibble_bits + 4)\n\n // Number of chunks to emit: ceil(A / group).\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n // Process in fixed-size chunks of `group` limbs.\n for chunk in 0..num_chunks {\n // How many real values go into this chunk.\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n // Build field element accumulator (big-endian concatenation in `radix`).\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n acc = acc * radix + (v + base);\n }\n\n // Pad remaining limb slots with the canonical zero-limb `digit = base`.\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Computes a cryptographic hash using the SAFE (Sponge API for Field Elements) protocol.\n///\n/// This is a convenience wrapper around the SAFE sponge API that handles the full\n/// lifecycle: initialization, absorption, squeezing, and finalization. It's designed\n/// for use in Fiat-Shamir challenge generation and commitment schemes within zero-knowledge circuits.\n///\n/// # Arguments\n/// * `domain_separator` - A 64-byte domain separator used to differentiate between\n/// different protocol instances and prevent cross-protocol attacks.\n/// * `inputs` - Vector of field elements to be absorbed into the sponge.\n/// * `io_pattern` - A 2-element array encoding the I/O pattern:\n/// - `io_pattern[0]`: Encoded ABSORB operation (MSB=1, lower 31 bits = length)\n/// - `io_pattern[1]`: Encoded SQUEEZE operation (MSB=0, lower 31 bits = length)\n///\n/// # Returns\n/// A vector of field elements squeezed from the sponge, with length determined by\n/// the SQUEEZE operation in the IO pattern.\n/// Proves `0 <= v < upper` using two one-sided bit-size checks.\n///\n/// `assert_max_bit_size` is one-sided: it forces a small **non-negative** field element. So bounding\n/// `v` gives `v >= 0`, and bounding `upper - 1 - v` gives `v <= upper - 1`. The pair composes into a\n/// two-sided interval without a comparison operator.\n///\n/// `BIT` must cover `upper`; the caller supplies it from the generated config.\npub fn assert_in_range<let BIT: u32>(v: Field, upper: Field) {\n v.assert_max_bit_size::<BIT>();\n (upper - 1 - v).assert_max_bit_size::<BIT>();\n}\n\npub fn compute_safe(domain_separator: [u8; 64], inputs: [Field], io_pattern: [u32; 2]) -> [Field] {\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(inputs);\n let digests = sponge.squeeze();\n sponge.finish();\n\n digests\n}\n\n#[test]\nfn test_flatten() {\n // Create test polynomials\n let poly1 = Polynomial::new([1, 2, 3]); // degree 2\n let poly2 = Polynomial::new([4, -16, 6]); // degree 2\n let poly3 = Polynomial::new([-7, 8, 9]); // degree 2\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 4>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n assert(result[0] == 0x11121310101010101010101010101010101010101010101010101010101010);\n assert(result[1] == 0x14001610101010101010101010101010101010101010101010101010101010); // -16 became 00 at 0x 14 00 16,\n assert(result[2] == 0x09181910101010101010101010101010101010101010101010101010101010); // -7 became 09 at 0x 09 18 19(16 - 7 = 9)\n}\n\n#[test]\nfn test_flatten_big() {\n // Create test polynomials\n let poly1 = Polynomial::new([\n 1791218451968394,\n 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n 5430119342984413,\n 704811298945172,\n 8901715723925099,\n 21888242871839275222246405745257275088548364400416034343698203098124042812559,\n 21888242871839275222246405745257275088548364400416034343698200215091693880034,\n ]);\n let poly2 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698200314078269634250,\n 21888242871839275222246405745257275088548364400416034343698200967285641915872,\n 2909990636858607,\n 7896103832076587,\n 2078397209533893,\n 21888242871839275222246405745257275088548364400416034343698199792421452734531,\n 614400389245817,\n 8290314119277588,\n ]);\n let poly3 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698201373175279892906,\n 21888242871839275222246405745257275088548364400416034343698201087241869723721,\n 6768789983786188,\n 635797784303388,\n 7610153424227556,\n 4633893206538324,\n 2016269760615332,\n 21888242871839275222246405745257275088548364400416034343698201007080554428142,\n ]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 54>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n\n // For the first index of result operation goes like this,\n\n // First four index of poly1\n // 1791218451968394,\n // 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n // 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n // 5430119342984413,\n\n // base + 1791218451968394 = 0x1065d1a8b8b718a\n // base - 5921327228407753 = 0xeaf69591f3b037 (negative coefficient shifted)\n // base - 3644467483862151 = 0xf30d604a3a9b79 (negative coefficient shifted)\n // base + 5430119342984413 = 0x1134aaa2e86ccdd\n assert(result[0] == 0x1065d1a8b8b718a0eaf69591f3b0370f30d604a3a9b791134aaa2e86ccdd);\n assert(result[1] == 0x1028105ab1b789411fa010339db66b0fc220f1326bc8e0f1e3f4cc1e02e1);\n assert(result[2] == 0x0f23dfbe7cd76c90f4901299312ddf10a569efe35acef11c0d76f005412b);\n assert(result[3] == 0x107624a8f605dc50f0638a368960421022ecb3cf36b7911d73ff2c27ec14);\n assert(result[4] == 0x0f6013a24e1b9a90f4fd2c158a08481180c2dba8af4cc10242413515171c);\n assert(result[5] == 0x11b0964eb898ce411076805680b85410729c962da53a40f4b44412d0f6ed);\n}\n\n#[test]\nfn test_flatten_small() {\n // Create test polynomials\n let poly1 = Polynomial::new([712345, 104857, 999999, 500001, 123, 654321, 77]);\n let poly2 = Polynomial::new([1, 524287, 888888, 23456, 34567, 765432, 0]);\n let poly3 = Polynomial::new([444444, 333333, 222222, 111111, 987654, 246810, 13579]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 20>(inputs, polynomials);\n\n assert(result[0] == 0x1ade991199991f423f17a12110007b19fbf110004d100000100000100000);\n assert(result[1] == 0x10000117ffff1d9038105ba01087071badf8100000100000100000100000);\n assert(result[2] == 0x16c81c15161513640e11b2071f120613c41a10350b100000100000100000);\n}\n\n#[test]\nfn test_safe_hashing_with_safe_helper() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let digests1 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests1.len() == 1);\n assert(digests1[0] != 0);\n\n // Test determinism\n let digests2 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests2.len() == 1);\n assert(digests2[0] != 0);\n assert(digests2[0] == digests1[0]);\n}\n\n#[test]\nfn test_pack() {\n // Test pack function directly with small values\n let values = [1, 2, 3, 4];\n let packed = pack::<4, 4>(values);\n\n // With BIT=4, nibble_bits=4, group should be floor(254/(4+4)) = 31\n // So all 4 values should fit in one carrier\n assert(packed.len() >= 1);\n\n // Test with negative values\n let values_neg = [-1, 2, -3, 4];\n let packed_neg = pack::<4, 4>(values_neg);\n assert(packed_neg.len() >= 1);\n}\n\n#[test]\nfn test_pack_single_value() {\n // Test packing a single value\n let values = [42];\n let packed = pack::<1, 8>(values);\n assert(packed.len() == 1);\n assert(packed[0] != 0);\n}\n\n#[test]\nfn test_pack_determinism() {\n // Test that packing is deterministic\n let values = [10, 20, 30];\n let packed1 = pack::<3, 8>(values);\n let packed2 = pack::<3, 8>(values);\n\n assert(packed1.len() == packed2.len());\n for i in 0..packed1.len() {\n assert(packed1[i] == packed2[i]);\n }\n}\n\n/// `pack_checked` must agree with `pack` for in-slot values. The two carry duplicate layout\n/// arithmetic, so if one is changed without the other, a commitment created by a circuit on `pack`\n/// stops matching one opened by a circuit on `pack_checked`. This pins that.\n#[test]\nfn test_pack_checked_matches_pack() {\n // Mixed signs, and values at the edge of the centred window for BIT = 35 (slot = 2^40).\n let vals: [Field; 7] = [0, 1, -1, 34359701504, -34359701504, 12345, -6789];\n let plain = pack::<7, 35>(vals);\n let checked = pack_checked::<7, 35>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n\n/// Same, at a width whose chunking leaves a partial final carrier (BIT = 57 -> group 3, 7 values).\n#[test]\nfn test_pack_checked_matches_pack_partial_chunk() {\n let vals: [Field; 7] = [5, -5, 72057594049265664, -72057594049265664, 1, 0, -1];\n let plain = pack::<7, 57>(vals);\n let checked = pack_checked::<7, 57>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n", path: "/Users/ctrlc03/Documents/zk/interfold/circuits/lib/src/math/helpers.nr", function_locations: [{ start: 1374, name: "packing_layout" }, { start: 3703, name: "pack_checked" }, { start: 4808, name: "flatten_checked" }, { start: 6096, name: "flatten" }, { start: 7946, name: "pack" }, { start: 10581, name: "assert_in_range" }, { start: 10770, name: "compute_safe" }, { start: 10968, name: "test_flatten" }, { start: 11911, name: "test_flatten_big" }, { start: 14812, name: "test_flatten_small" }, { start: 15639, name: "test_safe_hashing_with_safe_helper" }, { start: 16485, name: "test_pack" }, { start: 16955, name: "test_pack_single_value" }, { start: 17151, name: "test_pack_determinism" }, { start: 17749, name: "test_pack_checked_matches_pack" }, { start: 18287, name: "test_pack_checked_matches_pack_partial_chunk" }] }, "98": { 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;\nuse poseidon::poseidon2_permutation;\n\n/// SAFE (Sponge API for Field Elements)\n///\n/// This module provides a complete implementation of the SAFE API in Noir as defined in:\n/// "SAFE (Sponge API for Field Elements) - A Toolbox for ZK Hash Applications"\n/// see https://hackmd.io/bHgsH6mMStCVibM_wYvb2w#22-Sponge-state for more details.\n///\n/// SAFE provides a unified interface for cryptographic sponge functions that can be\n/// instantiated with various permutations to create hash functions, MACs, authenticated\n/// encryption schemes, and other cryptographic primitives for ZK proof systems.\n///\n/// This implementation follows the SAFE specification exactly, providing:\n/// - Complete API: START, ABSORB, SQUEEZE, FINISH operations.\n/// - Full security: Domain separation, tag computation, IO pattern validation.\n/// - Poseidon2 integration: Field-friendly permutation for ZK systems.\n/// - Specification compliance: All operations follow SAFE spec 2.4 exactly.\n/// - Natural API design: Variable-length inputs, automatic length detection from IO patterns.\n///\n/// # API Design\n///\n/// The API is designed for natural usage while maintaining type safety:\n/// - `absorb(input: [Field])`: Accepts variable-length arrays, no padding required.\n/// - `squeeze()`: Returns a vector with field element(s).\n/// - IO patterns automatically determine operation lengths for validation.\n\n/// Rate parameter for the sponge construction (number of field elements that can be absorbed per permutation call).\nglobal RATE: u32 = 3;\n\n/// Capacity parameter for the sponge construction (security parameter, typically 1-2 field elements).\nglobal CAPACITY: u32 = 1;\n\n/// Total state size (rate + capacity) in field elements.\nglobal STATE_SIZE: u32 = RATE + CAPACITY;\n\n/// IO Pattern encoding constants (from SAFE spec 2.3).\n///\n/// These constants are used for encoding operation types in the 32-bit word format:\n/// - MSB set to 1 for ABSORB operations\n/// - MSB set to 0 for SQUEEZE operations\n\n/// Flag for ABSORB operations (MSB = 1)\nglobal ABSORB_FLAG: u32 = 0x80000000;\n\n/// Flag for SQUEEZE operations (MSB = 0)\nglobal SQUEEZE_FLAG: u32 = 0x00000000;\n\n/// SAFE Sponge State (following spec 2.2)\n///\n/// The sponge state consists of the permutation state, tag, position counters,\n/// and IO pattern tracking as defined in the SAFE specification.\n///\n/// # Generic Parameters\n/// - `L`: The length of the IO pattern array\n///\n/// # Fields\n/// - `state`: Permutation state V in F^n (rate + capacity elements)\n/// - `tag`: Parameter tag T used for instance differentiation\n/// - `absorb_pos`: Current absorb position (<= n-c)\n/// - `squeeze_pos`: Current squeeze position (<= n-c)\n/// - `io_pattern`: Expected IO pattern for validation (encoded 32-bit words)\n/// - `io_count`: Current operation count for pattern tracking\npub struct SafeSponge<let L: u32> {\n /// Permutation state V in F^n (rate + capacity elements).\n state: [Field; STATE_SIZE],\n /// Parameter tag T used for instance differentiation.\n tag: Field,\n /// Current absorb position (<= n-c).\n absorb_pos: u32,\n /// Current squeeze position (<= n-c).\n squeeze_pos: u32,\n /// Expected IO pattern for validation.\n io_pattern: [u32; L],\n /// Current operation count for pattern tracking (spec 2.4: io_count).\n io_count: u32,\n}\n\nimpl<let L: u32> SafeSponge<L> {\n /// Initializes a new SAFE sponge instance with the given IO pattern and domain separator (following spec 2.4).\n ///\n /// # Arguments\n /// - `io_pattern`: Array of 32-bit encoded operations defining the expected sequence of ABSORB/SQUEEZE calls.\n /// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n /// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n ///\n /// # Returns\n /// A new `SafeSponge` instance with initialized state\n pub fn start(io_pattern: [u32; L], domain_separator: [u8; 64]) -> SafeSponge<L> {\n // Compute tag from IO pattern and domain separator (spec 2.3).\n let tag = compute_tag(io_pattern, domain_separator);\n\n let mut state = [0; STATE_SIZE];\n // Initialize capacity with tag (spec 2.4).\n // Add T to the first 128 bits of the state.\n state[0] = tag;\n\n SafeSponge { state, tag, absorb_pos: 0, squeeze_pos: 0, io_pattern, io_count: 0 }\n }\n\n /// Absorbs field elements into the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to absorb is automatically validated against the IO pattern.\n /// This method accepts variable-length arrays, making it natural to use without padding.\n ///\n /// # Arguments\n /// - `input`: Array of field elements to absorb (variable length, must match IO pattern)\n pub fn absorb(&mut self, input: [Field]) {\n let length = input.len() as u32;\n\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_absorb = (expected_encoded_word & ABSORB_FLAG) != 0;\n let expected_length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type and length\n assert(is_expected_absorb, "Expected ABSORB operation");\n assert(expected_length == length, "Length mismatch");\n\n // Process each element naturally (no unnecessary iterations).\n for i in 0..length {\n // If absorb_pos == (n-c) then permute and reset (spec 2.4).\n if self.absorb_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.absorb_pos = 0;\n }\n\n // Add X[i] to state at absorb_pos (spec 2.4).\n // Note: absorb_pos is the rate position, not capacity position.\n self.state[self.absorb_pos + CAPACITY] =\n self.state[self.absorb_pos + CAPACITY] + input[i];\n self.absorb_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = ABSORB_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n\n // Force permute at start of next SQUEEZE (spec 2.4).\n self.squeeze_pos = RATE;\n }\n\n /// Extracts field elements from the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to squeeze is automatically determined from the IO pattern.\n pub fn squeeze(&mut self) -> [Field] {\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_squeeze = (expected_encoded_word & ABSORB_FLAG) == 0;\n let length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type\n assert(is_expected_squeeze, "Expected SQUEEZE operation");\n\n let mut output: [Field] = [].as_vector();\n\n // SQUEEZE implementation following spec 2.4.\n // If length==0, loop won\'t execute (spec 2.4).\n for _ in 0..length {\n // If squeeze_pos==(n-c) then permute and reset (spec 2.4).\n if self.squeeze_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.squeeze_pos = 0;\n self.absorb_pos = 0;\n }\n // Set Y[i] to state element at squeeze_pos (spec 2.4).\n output = output.push_back(self.state[self.squeeze_pos + CAPACITY]);\n self.squeeze_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = SQUEEZE_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n output\n }\n\n /// Finalizes the sponge instance, verifying that all expected operations have been performed and clearing the internal state for security (following spec 2.4).\n ///\n /// This function is used to ensure that the sponge instance has been used correctly and to prevent information leakage.\n pub fn finish(&mut self) {\n // Check that io_count equals the length of the IO pattern expected (spec 2.4).\n assert(self.io_count == L, "IO pattern not completed");\n\n // Erase the state and its variables (spec 2.4).\n self.state = [0; STATE_SIZE];\n self.absorb_pos = 0;\n self.squeeze_pos = 0;\n self.io_count = 0;\n }\n\n /// Permute the state using Poseidon2 (following spec 2.4).\n ///\n /// Applies the Poseidon2 permutation to the current state.\n /// This is the core cryptographic primitive of the sponge construction.\n ///\n /// # Returns\n /// New state after permutation\n fn permute(self) -> [Field; STATE_SIZE] {\n poseidon2_permutation(self.state)\n }\n}\n\n/// Computes a unique tag for a sponge instance based on its IO pattern and domain separator.\n/// The tag is used to ensure that distinct instances behave like distinct functions.\n///\n/// # Arguments\n/// - `io_pattern`: Array of 32-bit encoded operations defining the sponge\'s usage pattern.\n/// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n/// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n///\n/// # Returns\n/// A field element representing the 128-bit tag.\npub fn compute_tag<let L: u32>(io_pattern: [u32; L], domain_separator: [u8; 64]) -> Field {\n // Step 1: Parse and aggregate consecutive operations of the same type\n let mut encoded_words = [0; L]; // Support up to L operations.\n let mut word_count = 0;\n let mut current_absorb_sum = 0;\n let mut current_squeeze_sum = 0;\n let mut last_was_absorb = false;\n\n for i in 0..L {\n if io_pattern[i] > 0 {\n // Parse operation type from MSB and length from lower 31 bits\n let is_absorb = (io_pattern[i] & ABSORB_FLAG) != 0;\n let length = io_pattern[i] & 0x7FFFFFFF; // Clear MSB to get length\n\n if is_absorb {\n if last_was_absorb {\n // Aggregate consecutive ABSORB operations\n current_absorb_sum += length;\n } else {\n // Start new ABSORB sequence\n if current_squeeze_sum > 0 {\n // Flush previous SQUEEZE sequence\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n current_squeeze_sum = 0;\n }\n current_absorb_sum = length;\n }\n last_was_absorb = true;\n } else {\n if !last_was_absorb {\n // Aggregate consecutive SQUEEZE operations\n current_squeeze_sum += length;\n } else {\n // Start new SQUEEZE sequence\n if current_absorb_sum > 0 {\n // Flush previous ABSORB sequence\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n current_absorb_sum = 0;\n }\n current_squeeze_sum = length;\n }\n last_was_absorb = false;\n }\n }\n }\n\n // Flush remaining operations\n if current_absorb_sum > 0 {\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n }\n if current_squeeze_sum > 0 {\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n }\n\n // Step 2: Serialize to byte string and append domain separator (following SAFE spec 2.3).\n // Buffer is 256 bytes: max 192 bytes for IO pattern (48 words) + 64 bytes for domain separator.\n // Note: We must use a fixed-size array because Noir\'s keccak256 requires [u8; N], not [u8].\n let max_io_pattern_bytes: u32 = 192; // 256 - 64 (domain separator)\n let io_pattern_bytes = word_count * 4;\n assert(\n io_pattern_bytes <= max_io_pattern_bytes,\n "IO pattern too large: max 48 aggregated words supported",\n );\n\n let mut input_bytes = [0u8; 256];\n let mut byte_count: u32 = 0;\n\n // Serialize encoded words to bytes (big-endian as per SAFE spec).\n // Note: Noir requires compile-time loop bounds, so we iterate over L (the array size)\n // instead of word_count (runtime value). The condition `i < word_count` ensures we only\n // process valid encoded words. This is safe because word_count <= L always holds\n // (we can have at most L encoded words from L input operations).\n for i in 0..L {\n if i < word_count {\n let word = encoded_words[i];\n input_bytes[byte_count] = (word >> 24) as u8;\n input_bytes[byte_count + 1] = (word >> 16) as u8;\n input_bytes[byte_count + 2] = (word >> 8) as u8;\n input_bytes[byte_count + 3] = word as u8;\n byte_count += 4;\n }\n }\n\n // Append full 64-byte domain separator.\n for i in 0..64 {\n input_bytes[byte_count] = domain_separator[i];\n byte_count += 1;\n }\n\n // Step 3: Hash with Keccak-256 and truncate to 128 bits.\n // Note: The SAFE spec uses SHA3-256, but we use Keccak-256 for Noir compatibility.\n // Keccak-256 differs from SHA3-256 in padding, but both provide equivalent security.\n let hash_bytes = keccak256(input_bytes, byte_count);\n\n // Convert first 128 bits (16 bytes) to field element.\n let mut tag_value: Field = 0;\n for i in 0..16 {\n tag_value = tag_value * 256 + (hash_bytes[i] as Field);\n }\n\n tag_value\n}\n\n#[test]\nfn test_safe_hashing() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_merkle_node() {\n // Verifies SAFE can be used for Merkle tree node hashing with pattern ABSORB(1) + ABSORB(1) + SQUEEZE(1).\n // Tests the ability to absorb multiple inputs before squeezing output.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let left = [123].as_vector();\n let right = [456].as_vector();\n\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(left);\n sponge.absorb(right);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(left);\n sponge2.absorb(right);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_commitment_scheme() {\n // Verifies SAFE can be used for commitment schemes with pattern ABSORB(3) + SQUEEZE(1).\n // Tests the ability to create deterministic commitments from multiple field elements.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let values = [10, 20, 30].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(values);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(values);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_domain_separation() {\n // Verifies that different domain separators produce different outputs for the same input.\n // This is crucial for cross-protocol security and preventing collisions between different applications.\n let elements = [1, 2, 3].as_vector();\n let domain1 = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let domain2 = [\n 0x41, 0x42, 0x43, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n\n let mut sponge1 = SafeSponge::start(io_pattern, domain1);\n sponge1.absorb(elements);\n let output1 = sponge1.squeeze();\n sponge1.finish();\n\n let mut sponge2 = SafeSponge::start(io_pattern, domain2);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output1.len() == 1);\n assert(output2.len() == 1);\n assert(output1[0] != output2[0]); // Different domain separators should produce different outputs\n}\n\n#[test]\nfn test_multiple_squeeze() {\n // Verifies that multiple field elements can be squeezed in a single operation.\n // Tests pattern ABSORB(3) + SQUEEZE(2) to ensure proper state management.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(2)\n let io_pattern = [0x80000003, 0x00000002];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 2);\n assert(output[0] != 0);\n assert(output[1] != 0);\n assert(output[0] != output[1]); // Different squeeze outputs should be different\n}\n\n#[test]\nfn test_zero_length_operations() {\n // Verifies that zero-length ABSORB and SQUEEZE operations are handled correctly.\n // Tests pattern ABSORB(0) + SQUEEZE(1) to ensure proper state transitions.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(0), SQUEEZE(1)\n let io_pattern = [0x80000000, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb([].as_vector());\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n}\n\n#[test]\nfn test_tag_computation() {\n // Verifies the tag computation algorithm using the example from the SAFE specification.\n // Pattern: ABSORB(3), ABSORB(3), SQUEEZE(3)\n // Should aggregate to: ABSORB(6), SQUEEZE(3)\n // Encoded as: [0x80000006, 0x00000003]\n // Tests determinism and pattern differentiation.\n\n let io_pattern = [0x80000003, 0x80000003, 0x00000003];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test determinism\n let tag2 = compute_tag(io_pattern, domain_separator);\n assert(tag == tag2);\n\n // Test that different patterns produce different tags\n let io_pattern2 = [0x80000003, 0x00000003]; // ABSORB(3), SQUEEZE(3) - different pattern\n let tag3 = compute_tag(io_pattern2, domain_separator);\n assert(tag != tag3);\n}\n\n#[test]\nfn test_tag_computation_debug() {\n println("=== SAFE Tag Computation Debug Test ===");\n\n // Test your specific pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))\n let io_pattern = [0x80000002, 0x00000002, 0x80000002];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n println(f"Testing pattern: {io_pattern}");\n println(\n f"Expected to aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(2)",\n );\n println(\n f"Expected encoded words: [0x80000002, 0x00000002, 0x80000002]",\n );\n println("");\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n println(f"=== Expected Rust Output ===");\n println("Pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))");\n println("Domain separator: 0x41424344...");\n println("Tag: 0xce3bb9ee4b2d41c42e9cdda38afe8b6a");\n println("");\n\n println(f"=== Noir Output ===");\n println(f"Tag: {tag}");\n println("");\n\n println("Compare the tag values above with Rust script!");\n}\n\n#[test]\nfn test_consecutive_absorb_aggregation() {\n // Test that consecutive ABSORB operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1) should aggregate to ABSORB(2), SQUEEZE(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(1) = [0x80000002, 0x00000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(2), SQUEEZE(1)\n let aggregated_pattern = [0x80000002, 0x00000001];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Consecutive ABSORB operations should aggregate to the same tag");\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive ABSORB operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive ABSORB Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001] (ABSORB(1), ABSORB(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000002, 0x00000001] (ABSORB(2), SQUEEZE(1))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_consecutive_squeeze_aggregation() {\n // Test that consecutive SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1) should aggregate to ABSORB(1), SQUEEZE(2)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x00000001, 0x00000001];\n\n // This should aggregate to: ABSORB(1), SQUEEZE(2) = [0x80000001, 0x00000002]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(1), SQUEEZE(2)\n let aggregated_pattern = [0x80000001, 0x00000002];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(\n tag == aggregated_tag,\n "Consecutive SQUEEZE operations should aggregate to the same tag",\n );\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive SQUEEZE operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive SQUEEZE Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x00000001, 0x00000001] (ABSORB(1), SQUEEZE(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000001, 0x00000002] (ABSORB(1), SQUEEZE(2))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_mixed_consecutive_aggregation() {\n // Test that both consecutive ABSORB and SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n // Should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1) = [0x80000002, 0x00000002, 0x80000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag\n let aggregated_pattern = [0x80000002, 0x00000002, 0x80000001]; // ABSORB(2), SQUEEZE(2), ABSORB(1)\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Mixed consecutive operations should aggregate to the same tag");\n\n println("=== Mixed Consecutive Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001]",\n );\n println(\n f" (ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1))",\n );\n println(f"Aggregated pattern: [0x80000002, 0x00000002, 0x80000001]");\n println(f" (ABSORB(2), SQUEEZE(2), ABSORB(1))");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n}\n\n#[test]\nfn test_large_io_pattern() {\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Create pattern with 48 alternating ABSORB(1) and SQUEEZE(1) operations\n // This is the maximum supported (48 words * 4 bytes = 192 bytes, leaving 64 for domain separator)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1; // ABSORB(1)\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1; // SQUEEZE(1)\n }\n }\n\n let tag = compute_tag(io_pattern, domain_separator);\n assert(tag != 0);\n}\n\n#[test]\nfn test_domain_separator_not_truncated() {\n // This test verifies that the domain separator is always included in the tag computation,\n // even for large IO patterns. If the domain separator were truncated, different domain\n // separators would produce the same tag for large patterns.\n\n let domain_separator_a = [\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41,\n ]; // All \'A\'s\n\n let domain_separator_b = [\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42,\n ]; // All \'B\'s\n\n // Create pattern with 48 alternating operations (max supported: 192 bytes of IO pattern)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1;\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1;\n }\n }\n\n let tag_a = compute_tag(io_pattern, domain_separator_a);\n let tag_b = compute_tag(io_pattern, domain_separator_b);\n\n // Tags MUST be different because domain separators are different.\n // If they were the same, it would mean the domain separator was truncated/ignored.\n assert(tag_a != tag_b, "Domain separator must affect tag even for large IO patterns");\n}\n', path: "/Users/ctrlc03/Documents/zk/interfold/circuits/lib/src/math/safe.nr", function_locations: [{ start: 4164, name: "SafeSponge<L>::start" }, { start: 5046, name: "SafeSponge<L>::absorb" }, { start: 6826, name: "SafeSponge<L>::squeeze" }, { start: 8494, name: "SafeSponge<L>::finish" }, { start: 9156, name: "SafeSponge<L>::permute" }, { start: 9830, name: "compute_tag" }, { start: 14128, name: "test_safe_hashing" }, { start: 15104, name: "test_merkle_node" }, { start: 16281, name: "test_commitment_scheme" }, { start: 17357, name: "test_domain_separation" }, { start: 18710, name: "test_multiple_squeeze" }, { start: 19639, name: "test_zero_length_operations" }, { start: 20415, name: "test_tag_computation" }, { start: 21477, name: "test_tag_computation_debug" }, { start: 22681, name: "test_consecutive_absorb_aggregation" }, { start: 24750, name: "test_consecutive_squeeze_aggregation" }, { start: 26760, name: "test_mixed_consecutive_aggregation" }, { start: 28520, name: "test_large_io_pattern" }, { start: 29333, name: "test_domain_separator_not_truncated" }] } } };
|
|
614
|
+
var crisp_fold_default = { noir_version: "1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905", hash: "9191902676998226238", abi: { parameters: [{ name: "user_data_encryption_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_public_inputs", type: { kind: "array", length: 5, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_key_hash", type: { kind: "field" }, visibility: "private" }, { name: "crisp_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "crisp_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "crisp_key_hash", type: { kind: "field" }, visibility: "private" }, { name: "prev_ct_commitment", type: { kind: "field" }, visibility: "public" }, { name: "digest_hi", type: { kind: "field" }, visibility: "public" }, { name: "digest_lo", type: { kind: "field" }, visibility: "public" }, { name: "slot_address", type: { kind: "field" }, visibility: "public" }, { name: "merkle_root", type: { kind: "field" }, visibility: "public" }, { name: "is_first_vote", type: { kind: "boolean" }, visibility: "public" }, { name: "num_options", type: { kind: "integer", sign: "unsigned", width: 32 }, visibility: "public" }, { name: "final_ct_commitment", type: { kind: "field" }, visibility: "public" }, { name: "ct_commitment", type: { kind: "field" }, visibility: "private" }, { name: "k1_commitment", type: { kind: "field" }, visibility: "private" }], return_type: { abi_type: { kind: "field" }, visibility: "public" }, error_types: {} }, bytecode: "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", debug_symbols: "tZbbboMwDIbfJddcJM7B8V5lmira0gkJ0YrBpKnau890JJRJibZUuzIQ8sn+fUiu4tjsp9dd25/Ob+Lp+Sr2Q9t17euuOx/qsT33/PX6WYnwuhuHpuFP4m6dd13qoelH8dRPXVeJ97qbbj+9Xer+Zsd64FVZiaY/smXgqe2a+emzWnfL9FblDC67lTcmAixsCCpHsBAISHol6A0B0gSt0S8EbZyKBIDfRgEKQhSgDKaiyOngbVARAF2KYB/Wwf2rDkZFHYw1BTqAJr0SbAnBkgsEp3URAWElFEWBOmYTHRYRVh28liUEkjEKAp/sLJMrKUexphzeielxC8nUJSIFLdA7mURkClNJb0JO+dnSCqEtBNMQpyQtDHffo+R/jbA+JsWStEkEpRHEmi4IUkQpBGQSqym2qZFq9ULhH7SIXjiQpkSLDUKltcin1cfhL0mqVFohU6PoTChRdDYZCtjHc+Iezkk2EG917BOClBfZjrcqzG8pXcHM0BJ0JKArmjrxHGNC0eQjbSLBm6IoKOqgfky+F36rD+2wuQcJ4D8roVn5ShiWvBKWc8cQNx/olcBv4+e5XAn6NkrOzrJVi4XF6tkFtmaxdrEM03Ne3+uhrfdds1zBTlN/uLuRjR+XsBLubJfhfGiO09DMXt/WOI4v", file_map: { "17": { 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;\nuse crate::static_assert;\n\n/// The size of the state accepted by the backend in `poseidon2_permutation`.\nglobal POSEIDON2_CONFIG_STATE_SIZE: u32 = poseidon2_config_state_size();\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\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s<let N: u32>(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n// docs:start:blake3\npub fn blake3<let N: u32>(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<let N: u32>(input: [u8; N]) -> [u8; 32] {}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment<let N: u32>(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<let N: u32>(\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 points[i] = EmbeddedCurveScalar::from_field(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<let N: u32>(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<let N: u32>(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 crate::assert_constant(separator);\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] = EmbeddedCurveScalar::from_field(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, true)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n crate::assert_constant(starting_index);\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\npub fn poseidon2_permutation<let N: u32>(input: [Field; N]) -> [Field; N] {\n static_assert(\n N == POSEIDON2_CONFIG_STATE_SIZE,\n f"the input length must equal the state size in the Poseidon2 config; expected {POSEIDON2_CONFIG_STATE_SIZE}, got {N}",\n );\n poseidon2_permutation_internal(input)\n}\n\n#[foreign(poseidon2_permutation)]\nfn poseidon2_permutation_internal<let N: u32>(input: [Field; N]) -> [Field; N] {}\n\n#[foreign(poseidon2_config_state_size)]\ncomptime fn poseidon2_config_state_size() -> u32 {}\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<H>(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<H>(_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 /// Returns the hash value without consuming the hasher.\n /// Override this for more efficient implementations that avoid copying.\n /// TODO: deprecate finish() and replace it\n fn finish_ref(&self) -> Field {\n (*self).finish()\n }\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<H>;\n\nimpl<H> BuildHasher for BuildHasherDefault<H>\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<H> Default for BuildHasherDefault<H>\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u8 {\n fn hash<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl<T, let N: u32> Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl<T> Hash for [T]\nwhere\n T: Hash,\n{\n fn hash<H>(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<A> Hash for (A,)\nwhere\n A: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n }\n}\n\nimpl<A, B> Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl<A, B, C> Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash<H>(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<A, B, C, D> Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash<H>(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<A, B, C, D, E> 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<H>(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\nimpl<A, B, C, D, E, F> Hash for (A, B, C, D, E, F)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G> Hash for (A, B, C, D, E, F, G)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_> Hash for (A, B, C, D, E, F, G, H_)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I> Hash for (A, B, C, D, E, F, G, H_, I)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J> Hash for (A, B, C, D, E, F, G, H_, I, J)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K> Hash for (A, B, C, D, E, F, G, H_, I, J, K)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K, L> Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n L: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n self.11.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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n );\n}\n', path: "std/hash/mod.nr", function_locations: [{ start: 572, name: "sha256_compression" }, { start: 707, name: "keccakf1600" }, { start: 852, name: "blake2s" }, { start: 950, name: "blake3" }, { start: 1437, name: "__blake3" }, { start: 1555, name: "pedersen_commitment" }, { start: 1784, name: "pedersen_commitment_with_separator" }, { start: 2188, name: "pedersen_hash" }, { start: 2345, name: "pedersen_hash_with_separator" }, { start: 3339, name: "derive_generators" }, { start: 3698, name: "__derive_generators" }, { start: 3776, name: "poseidon2_permutation" }, { start: 4132, name: "poseidon2_permutation_internal" }, { start: 4225, name: "poseidon2_config_state_size" }, { start: 4536, name: "derive_hash" }, { start: 5327, name: "Hasher::finish_ref" }, { start: 5761, name: "<impl BuildHasher for BuildHasherDefault<H>>::build_hasher" }, { start: 5893, name: "<impl Default for BuildHasherDefault<H>>::default" }, { start: 6025, name: "<impl Hash for Field>::hash" }, { start: 6155, name: "<impl Hash for u8>::hash" }, { start: 6295, name: "<impl Hash for u16>::hash" }, { start: 6435, name: "<impl Hash for u32>::hash" }, { start: 6575, name: "<impl Hash for u64>::hash" }, { start: 6716, name: "<impl Hash for u128>::hash" }, { start: 6855, name: "<impl Hash for i8>::hash" }, { start: 7001, name: "<impl Hash for i16>::hash" }, { start: 7148, name: "<impl Hash for i32>::hash" }, { start: 7295, name: "<impl Hash for i64>::hash" }, { start: 7443, name: "<impl Hash for bool>::hash" }, { start: 7590, name: "<impl Hash for ()>::hash" }, { start: 7722, name: "<impl Hash for [T; N]>::hash" }, { start: 7911, name: "<impl Hash for [T]>::hash" }, { start: 8133, name: "<impl Hash for (A,)>::hash" }, { start: 8302, name: "<impl Hash for (A, B)>::hash" }, { start: 8518, name: "<impl Hash for (A, B, C)>::hash" }, { start: 8781, name: "<impl Hash for (A, B, C, D)>::hash" }, { start: 9091, name: "<impl Hash for (A, B, C, D, E)>::hash" }, { start: 9448, name: "<impl Hash for (A, B, C, D, E, F)>::hash" }, { start: 9852, name: "<impl Hash for (A, B, C, D, E, F, G)>::hash" }, { start: 10306, name: "<impl Hash for (A, B, C, D, E, F, G, H_)>::hash" }, { start: 10807, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I)>::hash" }, { start: 11355, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J)>::hash" }, { start: 11950, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K)>::hash" }, { start: 12593, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)>::hash" }, { start: 13187, name: "assert_pedersen" }] }, "22": { source: "pub mod hash;\npub mod aes128;\npub mod array;\npub mod vector;\npub mod ecdsa_secp256k1;\npub mod ecdsa_secp256r1;\npub mod embedded_curve_ops;\npub mod field;\npub mod collections;\npub mod compat;\npub mod convert;\npub mod option;\npub mod string;\npub mod test;\npub mod cmp;\npub mod ops;\npub mod default;\npub mod prelude;\npub mod runtime;\npub mod meta;\npub mod append;\npub mod mem;\npub mod panic;\npub mod hint;\n\nmod integer;\nmod primitive_docs;\nmod internal;\n\n// Oracle calls are required to be wrapped in an unconstrained function\n// Thus, the only argument to the `println` oracle is expected to always be an ident\n#[oracle(print)]\nunconstrained fn print_oracle<T>(with_newline: bool, input: T) {}\n\nunconstrained fn print_unconstrained<T>(with_newline: bool, input: T) {\n print_oracle(with_newline, input);\n}\n\n/// Print the given input to stdout followed by a newline\npub fn println<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(true, input);\n }\n}\n\n/// Print the given input to stdout\npub fn print<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(false, input);\n }\n}\n\n/// Asserts the validity of the provided proof and public inputs against the provided verification key and hash.\n///\n/// The ACVM cannot determine whether the provided proof is valid during execution as this requires knowledge of\n/// the backend against which the program is being proven. However if an invalid proof if submitted, the program may\n/// fail to prove or the backend may generate a proof which will subsequently fail to verify.\n///\n/// # Important Note\n///\n/// If you are not developing your own backend such as [Barretenberg](https://github.com/AztecProtocol/barretenberg)\n/// you probably shouldn't need to interact with this function directly. It's easier and safer to use a verification\n/// library which is published by the developers of the backend which will document or enforce any safety requirements.\n///\n/// If you use this directly, you're liable to introduce underconstrainedness bugs and *your circuit will be insecure*.\n///\n/// # Arguments\n/// - verification_key: The verification key of the circuit to be verified.\n/// - proof: The proof to be verified.\n/// - public_inputs: The public inputs associated with `proof`\n/// - key_hash: The hash of `verification_key` of the form expected by the backend.\n/// - proof_type: An identifier for the proving scheme used to generate the proof to be verified. This allows\n/// for a single backend to support verifying multiple proving schemes.\n///\n/// # Constraining `key_hash`\n///\n/// The Noir compiler does not by itself constrain that `key_hash` is a valid hash of `verification_key`.\n/// This is because different backends may differ in how they hash their verification keys.\n/// It is then the responsibility of either the noir developer (by explicitly hashing the verification key\n/// in the correct manner) or by the proving system itself internally asserting the correctness of `key_hash`.\npub fn verify_proof_with_type<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {\n if !crate::runtime::is_unconstrained() {\n crate::assert_constant(proof_type);\n }\n verify_proof_internal(verification_key, proof, public_inputs, key_hash, proof_type);\n}\n\n#[foreign(recursive_aggregation)]\nfn verify_proof_internal<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {}\n\n/// Asserts that the given value is known at compile-time.\n/// Useful for debugging for-loop bounds.\n#[builtin(assert_constant)]\npub fn assert_constant<T>(x: T) {}\n\n/// Asserts that the given value is both true and known at compile-time.\n/// The message can be a string, a format string, or any value, as long as it is known at compile-time\n#[builtin(static_assert)]\npub fn static_assert<T>(predicate: bool, message: T) {}\n\n/// Force a field value to be a witness instead of a constant in the compiled output.\n/// This is often only useful for debugging compiler optimizations.\n///\n/// This has no effect in unconstrained or comptime code.\n#[builtin(as_witness)]\npub fn as_witness(x: Field) {}\n", path: "std/lib.nr", function_locations: [{ start: 689, name: "print_oracle" }, { start: 763, name: "print_unconstrained" }, { start: 893, name: "println" }, { start: 1076, name: "print" }, { start: 3277, name: "verify_proof_with_type" }, { start: 3694, name: "verify_proof_internal" }, { start: 3859, name: "assert_constant" }, { start: 4118, name: "static_assert" }, { start: 4389, name: "as_witness" }] }, "52": { 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 bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};\nuse interfold_lib::math::commitments::compute_vk_hash;\n\n/// Binds the four inner `vk_hash` witnesses. Regenerate with `pnpm compute:vk-hash` in\n/// `examples/CRISP` after changing ct0 / ct1 / user_data_encryption / crisp (or the lib preset).\n/// Insecure: `lib::configs::default` uses `insecure::*`; secure: uses `secure::*`.\npub global CRISP_FOLD_EXPECTED_KEY_HASH_INSECURE: Field =\n 0x1ea21b298df0f63f37fd013592944209caa3b728e239798b1b77a0455e39a13b;\npub global CRISP_FOLD_EXPECTED_KEY_HASH_SECURE: Field =\n 0x196e81843c72ecfb887e91c1d4243ba605c244193d8e196458c0f0c173f26f58;\n\nfn main(\n // User Data Encryption Section.\n user_data_encryption_verification_key: UltraHonkVerificationKey,\n user_data_encryption_proof: UltraHonkProof,\n user_data_encryption_public_inputs: [Field; 5], // ct0_key_hash, ct1_key_hash, pk_commitment, ct_commitment, k1_commitment\n user_data_encryption_key_hash: Field,\n // Crisp Section.\n crisp_verification_key: UltraHonkVerificationKey,\n crisp_proof: UltraHonkProof,\n crisp_key_hash: Field,\n prev_ct_commitment: pub Field,\n digest_hi: pub Field,\n digest_lo: pub Field,\n slot_address: pub Field,\n merkle_root: pub Field,\n is_first_vote: pub bool,\n num_options: pub u32,\n final_ct_commitment: pub Field,\n ct_commitment: Field,\n k1_commitment: Field,\n) -> pub Field {\n verify_honk_proof_non_zk(\n user_data_encryption_verification_key,\n user_data_encryption_proof,\n user_data_encryption_public_inputs,\n user_data_encryption_key_hash,\n );\n verify_honk_proof_non_zk(\n crisp_verification_key,\n crisp_proof,\n [\n prev_ct_commitment,\n digest_hi,\n digest_lo,\n slot_address,\n merkle_root,\n if is_first_vote { 1 } else { 0 },\n num_options as Field,\n final_ct_commitment,\n ct_commitment,\n k1_commitment,\n ],\n crisp_key_hash,\n );\n\n // Verify that the ct_commitment from the crisp proof matches the one computed from user data encryption.\n assert(ct_commitment == user_data_encryption_public_inputs[3]);\n\n // Verify that the k1_commitment from the crisp proof matches the one computed from user data encryption.\n assert(k1_commitment == user_data_encryption_public_inputs[4]);\n\n let vk_hashes = [\n user_data_encryption_key_hash,\n crisp_key_hash,\n user_data_encryption_public_inputs[0], // ct0_key_hash\n user_data_encryption_public_inputs[1], // ct1_key_hash\n ]\n .as_vector();\n\n let chain_key_hash = compute_vk_hash(vk_hashes);\n assert(\n (chain_key_hash == CRISP_FOLD_EXPECTED_KEY_HASH_INSECURE)\n | (chain_key_hash == CRISP_FOLD_EXPECTED_KEY_HASH_SECURE),\n );\n\n user_data_encryption_public_inputs[2]\n}\n", path: "/Users/ctrlc03/.omp/worktrees/interfold/feat-insecure-64-params/examples/CRISP/circuits/bin/fold/src/main.nr", function_locations: [{ start: 1641, name: "main" }] }, "53": { source: '// Constants for UltraHonk recursive verifier inputs\npub global PROOF_TYPE_HONK: u32 = 0; // identifier for UltraHonk verfier\npub global RECURSIVE_PROOF_LENGTH: u32 = 410;\npub global ULTRA_VK_LENGTH_IN_FIELDS: u32 = 115;\n\npub type UltraHonkProof = [Field; RECURSIVE_PROOF_LENGTH];\npub type UltraHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\n// Constants for Rollup-UltraHonk recursive verifier inputs (N.B. this is equivalent to UH plus IPA claim and proof)\npub global PROOF_TYPE_ROLLUP_HONK: u32 = 4; // identifier for rollup-UltraHonk verfier\npub global PROOF_TYPE_ROOT_ROLLUP_HONK: u32 = 5; // identifier for root-rollup-UltraHonk verfier (closes the IPA accumulator)\npub global IPA_CLAIM_SIZE: u32 = 6;\npub global IPA_PROOF_LENGTH: u32 = 64;\npub global RECURSIVE_ROLLUP_HONK_PROOF_LENGTH: u32 =\n RECURSIVE_PROOF_LENGTH + IPA_CLAIM_SIZE + IPA_PROOF_LENGTH;\n\npub type RollupHonkProof = [Field; RECURSIVE_ROLLUP_HONK_PROOF_LENGTH];\npub type RollupHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\npub global PROOF_TYPE_HONK_ZK: u32 = 6; // identifier for UltraHonk ZK verfier\npub global RECURSIVE_ZK_PROOF_LENGTH: u32 = 450 + 8;\n\npub type UltraHonkZKProof = [Field; RECURSIVE_ZK_PROOF_LENGTH];\n\n// Verifies a non-zero-knowledge UltraHonk proof.\n//\n// Represents standard UltraHonk recursive verification for proofs that do not hide the witness.\n// Use this only in situations where zero-knowledge is not required.\npub fn verify_honk_proof_non_zk<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof with IPA (Inner Product Argument).\n//\n// This variant includes an IPA claim and proof appended to the standard UltraHonk proof,\n// used to amortize IPA recursive verification costs in rollup circuits.\npub fn verify_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROLLUP_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof and closes the IPA accumulator in-circuit.\n//\n// Use this at the root of a rollup aggregation tree. The inner proof has the same RollupHonk shape\n// as `verify_rolluphonk_proof`, but instead of propagating an accumulated IPA claim out as a public\n// input, this variant performs a full native IPA verification inside the circuit. The outer circuit\n// should therefore be proved as a standard (non-rollup) UltraHonk circuit, producing a proof\n// suitable for verification by `verify_honk_proof` / `verify_honk_proof_non_zk`.\n//\n// When two RollupHonk inputs are verified this way in one circuit, their two IPA claims are first\n// accumulated into one, then the accumulated claim is fully verified.\npub fn verify_root_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROOT_ROLLUP_HONK,\n );\n}\n\n// Verifies a zero-knowledge UltraHonk proof.\n//\n// This verifier is for UltraHonk proofs constructed with zero-knowledge, which hide the witness\n// values from the verifier.\n// Note: We intentionally choose the generic name "verify_honk_proof" for this function, as we\n// want ZK to be the default unless the user explicitly opts out.\npub fn verify_honk_proof<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkZKProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK_ZK,\n );\n}\n', path: "/Users/ctrlc03/nargo/github.com/AztecProtocol/aztec-packages/v5.2.0/barretenberg/noir/bb_proof_verification/src/lib.nr", function_locations: [{ start: 1646, name: "verify_honk_proof_non_zk" }, { start: 2262, name: "verify_rolluphonk_proof" }, { start: 3386, name: "verify_root_rolluphonk_proof" }, { start: 4087, name: "verify_honk_proof" }] }, "87": { 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 crate::math::helpers::{compute_safe, flatten, flatten_checked};\nuse crate::math::polynomial::Polynomial;\n\n/// DOMAIN SEPARATORS\n\n// Domain separator - "PK"\npub global DS_PK: [u8; 64] = [\n 0x50, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_GENERATION"\npub global DS_PK_GENERATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_COMPUTATION"\npub global DS_SHARE_COMPUTATION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_ENCRYPTION"\npub global DS_SHARE_ENCRYPTION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_AGGREGATION"\npub global DS_PK_AGGREGATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CIPHERTEXT"\npub global DS_CIPHERTEXT: [u8; 64] = [\n 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "AGGREGATED_SHARES"\npub global DS_AGGREGATED_SHARES: [u8; 64] = [\n 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45,\n 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "VK_HASH"\npub global DS_VK_HASH: [u8; 64] = [\n 0x56, 0x4b, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "RECURSIVE_AGGREGATION"\npub global DS_RECURSIVE_AGGREGATION: [u8; 64] = [\n 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47,\n 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_PK_GENERATION"\npub global DS_CLG_PK_GENERATION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_ENCRYPTION"\npub global DS_CLG_SHARE_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_USER_DATA_ENCRYPTION"\npub global DS_CLG_USER_DATA_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e,\n 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_DECRYPTION"\npub global DS_CLG_SHARE_DECRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "THRESHOLD_DECRYPTION_SHARE"\npub global DS_THRESHOLD_DECRYPTION_SHARE: [u8; 64] = [\n 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "USER_DATA_ENCRYPTION_COMMITMENT"\npub global DS_USER_DATA_ENCRYPTION_COMMITMENT: [u8; 64] = [\n 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n/// WRAPPERS\n\npub fn compute_commitment(inputs: [Field], domain_separator: [u8; 64]) -> Field {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 1])[0]\n}\n\npub fn compute_single_polynomial_commitment<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_single_polynomial_commitment`] with the digit asserts, for a polynomial this circuit\n/// carries no bound for. ct1 commits `u` this way: ct0 range-checks the same `u` to `u_bound` and the\n/// `user_data_encryption` fold asserts the two `u` commitments are equal, so making ct1\'s packing\n/// injective is what forces its `u` to equal ct0\'s \u2014 the bound transfers instead of being repeated.\n/// Produces the same value as the plain version for any input the plain version could legitimately\n/// have committed.\npub fn compute_single_polynomial_commitment_checked<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_multiple_polynomial_commitment<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_challenge<let L: u32>(inputs: [Field], domain_separator: [u8; 64]) -> [Field] {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 2 * L])\n}\n\npub fn single_polynomial_payload<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, inputs)\n}\n\npub fn single_polynomial_payload_checked<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload_checked<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, inputs)\n}\n\n/// COMMITMENTS\n\npub fn compute_dkg_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let mut payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n payload = multiple_polynomial_payload::<N, L, BIT_PK>(payload, pk1);\n\n compute_commitment(payload, DS_PK)\n}\n\npub fn compute_threshold_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\n/// [`compute_multiple_polynomial_commitment`] for a circuit that **opens** this commitment.\npub fn compute_multiple_polynomial_commitment_checked<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_threshold_pk_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C5). Asserts each packed digit fits its slot, which makes the opening unique and so\n/// forces the opened `pk0` to equal the `pk0` C1 committed. Produces the same value as the plain\n/// helper, so C1 needs no change.\npub fn compute_threshold_pk_commitment_checked<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\npub fn compute_share_computation_sk_commitment<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\n/// [`compute_share_computation_sk_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2a). C1 creates it and bounds `sk` to the trinary range, so making the opening\n/// unique forces the witness to equal that `sk`.\npub fn compute_share_computation_sk_commitment_checked<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_computation_e_sm_commitment<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_encryption_commitment_from_message<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\n/// [`compute_share_encryption_commitment_from_message`] for a circuit that **opens** this\n/// commitment rather than creating it (C4). Asserts each packed digit fits its slot, forcing the\n/// opened share to equal the share C2 committed. Same value as the plain helper, so C2 and C3 need\n/// no change.\npub fn compute_share_encryption_commitment_from_message_checked<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\npub fn compute_aggregated_shares_commitment<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// Commitment to a threshold decryption share: all CRT limbs, first `K` coefficients per limb\n/// (same layout as `Polynomial<K>` in decrypted_shares_aggregation).\n///\n/// `NATIVE_BIT_WIDTH` must cover native coefficients in \\([0, q_l)\\) per limb (not centered `d` bounds).\npub fn compute_threshold_decryption_share_commitment<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload =\n multiple_polynomial_payload::<K, L, NATIVE_BIT_WIDTH>([].as_vector(), d_share_limbs);\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_threshold_decryption_share_commitment`] for a circuit that **opens** this commitment\n/// rather than creating it (C7). C6 creates it and pins each coefficient to a native residue in\n/// \\([0, q)\\) via `verify_d_native_trunc_binding`, so asserting the digits here makes the opening\n/// unique and forces the opened share to equal the one C6 committed. Produces the same value as the\n/// plain version for any input the plain version could legitimately have committed.\npub fn compute_threshold_decryption_share_commitment_checked<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<K, L, NATIVE_BIT_WIDTH>(\n [].as_vector(),\n d_share_limbs,\n );\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_share_computation_e_sm_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2b). C1 creates it and bounds each `e_sm` limb to a centered residue.\npub fn compute_share_computation_e_sm_commitment_checked<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_pk_aggregation_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let commit_pk0 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk0, DS_PK_AGGREGATION);\n let commit_pk1 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk1, DS_PK_AGGREGATION);\n\n let inputs = [commit_pk0, commit_pk1].as_vector();\n\n compute_commitment(inputs, DS_PK_AGGREGATION)\n}\n\npub fn compute_recursive_aggregation_commitment(payload: [Field]) -> Field {\n compute_commitment(payload, DS_RECURSIVE_AGGREGATION)\n}\n\npub fn compute_vk_hash(vk_hashes: [Field]) -> Field {\n compute_commitment(vk_hashes, DS_VK_HASH)\n}\n\n/// [`compute_aggregated_shares_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). C4 creates it after `normalize_aggregated` has reduced and centered the\n/// aggregate, so making the opening unique forces the witness to equal that canonical value.\npub fn compute_aggregated_shares_commitment_checked<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// [`compute_ciphertext_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). No upstream circuit bounds the ciphertext limbs, so this buys injectivity\n/// rather than canonicality \u2014 which is the property wanted here: it forces C6 to decrypt exactly\n/// the ciphertext that was committed, whatever its representation.\npub fn compute_ciphertext_commitment_checked<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\npub fn compute_ciphertext_commitment<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\n/// COMMITMENTS FOR CHALLENGES\n\npub fn compute_threshold_pk_challenge(payload: [Field]) -> Field {\n compute_commitment(payload, DS_CLG_PK_GENERATION)\n}\n\npub fn compute_share_encryption_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_SHARE_ENCRYPTION)\n}\n\npub fn compute_threshold_share_decryption_challenge<let L: u32>(payload: [Field]) -> Field {\n compute_challenge::<L>(payload, DS_CLG_SHARE_DECRYPTION)[0]\n}\n\npub fn compute_user_data_encryption_ct0_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n\npub fn compute_user_data_encryption_ct1_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n', path: "/Users/ctrlc03/.omp/worktrees/interfold/feat-insecure-64-params/circuits/lib/src/math/commitments.nr", function_locations: [{ start: 7784, name: "compute_commitment" }, { start: 8012, name: "compute_single_polynomial_commitment" }, { start: 8827, name: "compute_single_polynomial_commitment_checked" }, { start: 9138, name: "compute_multiple_polynomial_commitment" }, { start: 9375, name: "compute_challenge" }, { start: 9585, name: "single_polynomial_payload" }, { start: 9784, name: "multiple_polynomial_payload" }, { start: 9970, name: "single_polynomial_payload_checked" }, { start: 10185, name: "multiple_polynomial_payload_checked" }, { start: 10406, name: "compute_dkg_pk_commitment" }, { start: 10733, name: "compute_threshold_pk_commitment" }, { start: 11138, name: "compute_multiple_polynomial_commitment_checked" }, { start: 11750, name: "compute_threshold_pk_commitment_checked" }, { start: 12008, name: "compute_share_computation_sk_commitment" }, { start: 12516, name: "compute_share_computation_sk_commitment_checked" }, { start: 12795, name: "compute_share_computation_e_sm_commitment" }, { start: 13067, name: "compute_share_encryption_commitment_from_message" }, { start: 13647, name: "compute_share_encryption_commitment_from_message_checked" }, { start: 13931, name: "compute_aggregated_shares_commitment" }, { start: 14512, name: "compute_threshold_decryption_share_commitment" }, { start: 15336, name: "compute_threshold_decryption_share_commitment_checked" }, { start: 15876, name: "compute_share_computation_e_sm_commitment_checked" }, { start: 16182, name: "compute_pk_aggregation_commitment" }, { start: 16571, name: "compute_recursive_aggregation_commitment" }, { start: 16686, name: "compute_vk_hash" }, { start: 17169, name: "compute_aggregated_shares_commitment_checked" }, { start: 17843, name: "compute_ciphertext_commitment_checked" }, { start: 18325, name: "compute_ciphertext_commitment" }, { start: 18724, name: "compute_threshold_pk_challenge" }, { start: 18866, name: "compute_share_encryption_challenge" }, { start: 19023, name: "compute_threshold_share_decryption_challenge" }, { start: 19183, name: "compute_user_data_encryption_ct0_challenge" }, { start: 19344, name: "compute_user_data_encryption_ct1_challenge" }] }, "89": { 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\n//! Helper functions for circuit construction and cryptographic operations.\nuse crate::math::polynomial::Polynomial;\nuse crate::math::safe::SafeSponge;\n\n/// Compute hex-aligned packing parameters for a given `BIT`.\n///\n/// # Purpose\n/// Returns `(nibble_bits, group)` for use by pack/flatten so layout stays consistent.\n/// - `nibble_bits`: ceil (`BIT`) to the next multiple of 4 (nibble alignment).\n/// - Examples: `BIT = 7 -> 8`, `BIT = 8 -> 8`, `BIT = 9 -> 12`, `BIT = 10 -> 12`, `BIT = 11 -> 12`,\n/// `BIT=16 -> 16`, `BIT = 17 -> 20`.\n/// - `group`: max number of encoded limbs that fit in one BN254 field element,\n/// when each limb uses an extra 4 bits (see below).\n///\n/// # Rationale\n/// - We align to nibbles so powers of two are hex-friendly and deterministic.\n/// - We reserve one extra nibble (4 bits) per stored value to lift signed\n/// coefficients into the non-negative range (e.g., store `v + 2^nibble_bits`),\n/// which implies a radix of `2^(nibble_bits + 4)`.\n///\n/// # Safety\n/// - Asserts `nibble_bits + 4 <= 254` to avoid mod-p wrap on BN254.\n/// - Ensures at least one limb fits: `group >= 1`.\nfn packing_layout<let BIT: u32>() -> (u32, u32) {\n // Ceil BIT up to the next multiple of 4 (nibble alignment).\n let nibble_bits = ((BIT + 3) / 4) * 4;\n\n // Each stored limb uses an extra nibble because negative coefficients\n // will be shifted to positive, so radix = 2^(nibble_bits+4).\n assert(nibble_bits + 4 <= 254);\n\n // Maximum limbs that fit in one BN254 element without wrap.\n let group = 254 / (nibble_bits + 4);\n // `group == 1` packs one value per carrier: it saves no sponge absorptions at all while still\n // charging the range checks that make packing injective, so it is always a loss. Measured at\n // BIT=143: +82% gates against absorbing directly. Reject it rather than pay for nothing.\n assert(group >= 2);\n (nibble_bits, group)\n}\n\n/// Like [`pack`], but asserts every digit fits its slot.\n///\n/// ## Why\n/// `pack` builds each carrier as a base-`radix` number, `acc = acc * radix + (v + base)`. That\n/// representation is unique only while every digit stays below `radix`. An unbounded value can\n/// overflow its slot and cancel against the next, so one commitment gains a second opening and a\n/// prover can choose which coefficients it opens to.\n///\n/// `digit < radix` is exactly `digit.assert_max_bit_size::<nibble_bits + 4>()`, and because\n/// `assert_max_bit_size` also forces `digit >= 0`, a single assert gives the whole condition.\n///\n/// ## When to use it\n/// Use this where a circuit **opens** a commitment it did not create. The creating circuit's own\n/// range checks already keep its digits in slot, so asserting the same here forces the opened\n/// coefficients to equal the committed ones \u2014 injectivity transfers the upstream bound without\n/// repeating it. One assert costs about half a two-sided `range_check_2bounds`.\n///\n/// It does **not** replace a bound the protocol requires in its own right, and it gives injectivity,\n/// not canonicality: it pins the opening, not a unique representative modulo `q`. A value that no\n/// upstream circuit bounded still needs its own check (see C1 `pk0` and C5 `pk0_agg`).\n///\n/// ## Compatibility\n/// Adding asserts does not change the packed value, so a commitment created with [`pack`] and\n/// opened with `pack_checked` agree. Adoption is per circuit and needs no commitment change.\npub fn pack_checked<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field);\n let radix = 2.pow_32((nibble_bits + 4) as Field);\n\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n for chunk in 0..num_chunks {\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n let digit = v + base;\n // digit < radix = 2^(nibble_bits + 4): the injectivity condition.\n digit.assert_max_bit_size::<((BIT + 3) / 4) * 4 + 4>();\n acc = acc * radix + digit;\n }\n\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Like [`flatten`], but packs with [`pack_checked`]. Use where a circuit opens a commitment it did\n/// not create.\npub fn flatten_checked<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n let packed = pack_checked::<A, BIT>(poly[j].coefficients);\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n inputs\n}\n\n/// Flatten `L` polynomials into a single linear stream of packed `Field` carriers.\n///\n/// ## What this does\n/// - For each CRT limb `j` in `0..L`, it packs the coefficients of `poly[j]`\n/// with `pack::<A, BIT>` and appends all resulting carriers to `inputs`.\n/// - The packing layout (nibble-aligned width and `group` size) is taken from\n/// `packing_layout::<BIT>()` and must match what `pack` uses.\n///\n/// ## Determinism & order\n/// - Preserves a stable order: iterate `j = 0..L`, then for each `j` append\n/// carriers in ascending chunk index `i = 0..num_chunks`.\n/// - This ensures transcripts remain deterministic across runs.\n///\n/// ## Generics\n/// - `A`: polynomial degree (number of coefficients per polynomial).\n/// - `L`: number of CRT bases (polynomials).\n/// - `BIT`: per-coefficient bit bound used by the packing layout (compile-time).\n///\n/// ## Returns\n/// - The same `inputs` vector, extended with all carriers in deterministic order.\npub fn flatten<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n // Pack its A coefficients into `num_chunks` carriers using the same BIT layout.\n let packed = pack::<A, BIT>(poly[j].coefficients);\n\n // Append carriers in-order to `inputs` to keep a stable transcript layout.\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n\n // Return the extended input stream.\n inputs\n}\n\n/// Pack `A` values into a `[Field]` vector of carriers using the shared hex-aligned layout.\n///\n/// ## What this does\n/// - Computes `(nibble_bits, group)` via `packing_layout::<BIT>()`.\n/// - Encodes each value as a limb `digit = v + 2^nibble_bits` and concatenates\n/// limbs in base `radix = 2^(nibble_bits + 4)` (one extra nibble of headroom).\n/// - Packs up to `group` limbs per carrier (fits within BN254 254-bit capacity).\n/// - Pads the last, partial carrier with `digit = 2^nibble_bits` to keep a stable layout.\n///\n/// ## Determinism & order\n/// - Processes values in increasing index order and emits carriers in chunk order\n/// (`chunk = 0..num_chunks`). Padding is deterministic.\n///\n/// ## Generics\n/// - `A`: number of input values.\n/// - `BIT`: per-value bit bound; rounded up to `nibble_bits` by `packing_layout`.\n///\n/// ## Preconditions / Notes\n/// - Call with the raw coefficients whose magnitudes already satisfy the BIT bound\n/// (as enforced by the upstream range checks); `pack` performs the signed -> unsigned\n/// shift internally via `v + base`.\n/// - `group >= 1` is enforced by `packing_layout::<BIT>()`.\n/// - Padding with `digit = 2^nibble_bits` encodes `zero limb` consistently.\n///\n/// ## Returns\n/// - A `[Field]` vector where each element is a concatenation of up to `group` limbs,\n/// suitable for hashing or transcript I/O.\npub fn pack<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n // Layout parameters: nibble-aligned width and limbs-per-carrier group size.\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field); // 2^nibble_bits\n let radix = 2.pow_32((nibble_bits + 4) as Field); // 2^(nibble_bits + 4)\n\n // Number of chunks to emit: ceil(A / group).\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n // Process in fixed-size chunks of `group` limbs.\n for chunk in 0..num_chunks {\n // How many real values go into this chunk.\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n // Build field element accumulator (big-endian concatenation in `radix`).\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n acc = acc * radix + (v + base);\n }\n\n // Pad remaining limb slots with the canonical zero-limb `digit = base`.\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Computes a cryptographic hash using the SAFE (Sponge API for Field Elements) protocol.\n///\n/// This is a convenience wrapper around the SAFE sponge API that handles the full\n/// lifecycle: initialization, absorption, squeezing, and finalization. It's designed\n/// for use in Fiat-Shamir challenge generation and commitment schemes within zero-knowledge circuits.\n///\n/// # Arguments\n/// * `domain_separator` - A 64-byte domain separator used to differentiate between\n/// different protocol instances and prevent cross-protocol attacks.\n/// * `inputs` - Vector of field elements to be absorbed into the sponge.\n/// * `io_pattern` - A 2-element array encoding the I/O pattern:\n/// - `io_pattern[0]`: Encoded ABSORB operation (MSB=1, lower 31 bits = length)\n/// - `io_pattern[1]`: Encoded SQUEEZE operation (MSB=0, lower 31 bits = length)\n///\n/// # Returns\n/// A vector of field elements squeezed from the sponge, with length determined by\n/// the SQUEEZE operation in the IO pattern.\n/// Proves `0 <= v < upper` using two one-sided bit-size checks.\n///\n/// `assert_max_bit_size` is one-sided: it forces a small **non-negative** field element. So bounding\n/// `v` gives `v >= 0`, and bounding `upper - 1 - v` gives `v <= upper - 1`. The pair composes into a\n/// two-sided interval without a comparison operator.\n///\n/// `BIT` must cover `upper`; the caller supplies it from the generated config.\npub fn assert_in_range<let BIT: u32>(v: Field, upper: Field) {\n v.assert_max_bit_size::<BIT>();\n (upper - 1 - v).assert_max_bit_size::<BIT>();\n}\n\npub fn compute_safe(domain_separator: [u8; 64], inputs: [Field], io_pattern: [u32; 2]) -> [Field] {\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(inputs);\n let digests = sponge.squeeze();\n sponge.finish();\n\n digests\n}\n\n#[test]\nfn test_flatten() {\n // Create test polynomials\n let poly1 = Polynomial::new([1, 2, 3]); // degree 2\n let poly2 = Polynomial::new([4, -16, 6]); // degree 2\n let poly3 = Polynomial::new([-7, 8, 9]); // degree 2\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 4>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n assert(result[0] == 0x11121310101010101010101010101010101010101010101010101010101010);\n assert(result[1] == 0x14001610101010101010101010101010101010101010101010101010101010); // -16 became 00 at 0x 14 00 16,\n assert(result[2] == 0x09181910101010101010101010101010101010101010101010101010101010); // -7 became 09 at 0x 09 18 19(16 - 7 = 9)\n}\n\n#[test]\nfn test_flatten_big() {\n // Create test polynomials\n let poly1 = Polynomial::new([\n 1791218451968394,\n 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n 5430119342984413,\n 704811298945172,\n 8901715723925099,\n 21888242871839275222246405745257275088548364400416034343698203098124042812559,\n 21888242871839275222246405745257275088548364400416034343698200215091693880034,\n ]);\n let poly2 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698200314078269634250,\n 21888242871839275222246405745257275088548364400416034343698200967285641915872,\n 2909990636858607,\n 7896103832076587,\n 2078397209533893,\n 21888242871839275222246405745257275088548364400416034343698199792421452734531,\n 614400389245817,\n 8290314119277588,\n ]);\n let poly3 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698201373175279892906,\n 21888242871839275222246405745257275088548364400416034343698201087241869723721,\n 6768789983786188,\n 635797784303388,\n 7610153424227556,\n 4633893206538324,\n 2016269760615332,\n 21888242871839275222246405745257275088548364400416034343698201007080554428142,\n ]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 54>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n\n // For the first index of result operation goes like this,\n\n // First four index of poly1\n // 1791218451968394,\n // 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n // 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n // 5430119342984413,\n\n // base + 1791218451968394 = 0x1065d1a8b8b718a\n // base - 5921327228407753 = 0xeaf69591f3b037 (negative coefficient shifted)\n // base - 3644467483862151 = 0xf30d604a3a9b79 (negative coefficient shifted)\n // base + 5430119342984413 = 0x1134aaa2e86ccdd\n assert(result[0] == 0x1065d1a8b8b718a0eaf69591f3b0370f30d604a3a9b791134aaa2e86ccdd);\n assert(result[1] == 0x1028105ab1b789411fa010339db66b0fc220f1326bc8e0f1e3f4cc1e02e1);\n assert(result[2] == 0x0f23dfbe7cd76c90f4901299312ddf10a569efe35acef11c0d76f005412b);\n assert(result[3] == 0x107624a8f605dc50f0638a368960421022ecb3cf36b7911d73ff2c27ec14);\n assert(result[4] == 0x0f6013a24e1b9a90f4fd2c158a08481180c2dba8af4cc10242413515171c);\n assert(result[5] == 0x11b0964eb898ce411076805680b85410729c962da53a40f4b44412d0f6ed);\n}\n\n#[test]\nfn test_flatten_small() {\n // Create test polynomials\n let poly1 = Polynomial::new([712345, 104857, 999999, 500001, 123, 654321, 77]);\n let poly2 = Polynomial::new([1, 524287, 888888, 23456, 34567, 765432, 0]);\n let poly3 = Polynomial::new([444444, 333333, 222222, 111111, 987654, 246810, 13579]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 20>(inputs, polynomials);\n\n assert(result[0] == 0x1ade991199991f423f17a12110007b19fbf110004d100000100000100000);\n assert(result[1] == 0x10000117ffff1d9038105ba01087071badf8100000100000100000100000);\n assert(result[2] == 0x16c81c15161513640e11b2071f120613c41a10350b100000100000100000);\n}\n\n#[test]\nfn test_safe_hashing_with_safe_helper() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let digests1 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests1.len() == 1);\n assert(digests1[0] != 0);\n\n // Test determinism\n let digests2 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests2.len() == 1);\n assert(digests2[0] != 0);\n assert(digests2[0] == digests1[0]);\n}\n\n#[test]\nfn test_pack() {\n // Test pack function directly with small values\n let values = [1, 2, 3, 4];\n let packed = pack::<4, 4>(values);\n\n // With BIT=4, nibble_bits=4, group should be floor(254/(4+4)) = 31\n // So all 4 values should fit in one carrier\n assert(packed.len() >= 1);\n\n // Test with negative values\n let values_neg = [-1, 2, -3, 4];\n let packed_neg = pack::<4, 4>(values_neg);\n assert(packed_neg.len() >= 1);\n}\n\n#[test]\nfn test_pack_single_value() {\n // Test packing a single value\n let values = [42];\n let packed = pack::<1, 8>(values);\n assert(packed.len() == 1);\n assert(packed[0] != 0);\n}\n\n#[test]\nfn test_pack_determinism() {\n // Test that packing is deterministic\n let values = [10, 20, 30];\n let packed1 = pack::<3, 8>(values);\n let packed2 = pack::<3, 8>(values);\n\n assert(packed1.len() == packed2.len());\n for i in 0..packed1.len() {\n assert(packed1[i] == packed2[i]);\n }\n}\n\n/// `pack_checked` must agree with `pack` for in-slot values. The two carry duplicate layout\n/// arithmetic, so if one is changed without the other, a commitment created by a circuit on `pack`\n/// stops matching one opened by a circuit on `pack_checked`. This pins that.\n#[test]\nfn test_pack_checked_matches_pack() {\n // Mixed signs, and values at the edge of the centred window for BIT = 35 (slot = 2^40).\n let vals: [Field; 7] = [0, 1, -1, 34359701504, -34359701504, 12345, -6789];\n let plain = pack::<7, 35>(vals);\n let checked = pack_checked::<7, 35>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n\n/// Same, at a width whose chunking leaves a partial final carrier (BIT = 57 -> group 3, 7 values).\n#[test]\nfn test_pack_checked_matches_pack_partial_chunk() {\n let vals: [Field; 7] = [5, -5, 72057594049265664, -72057594049265664, 1, 0, -1];\n let plain = pack::<7, 57>(vals);\n let checked = pack_checked::<7, 57>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n", path: "/Users/ctrlc03/.omp/worktrees/interfold/feat-insecure-64-params/circuits/lib/src/math/helpers.nr", function_locations: [{ start: 1374, name: "packing_layout" }, { start: 3703, name: "pack_checked" }, { start: 4808, name: "flatten_checked" }, { start: 6096, name: "flatten" }, { start: 7946, name: "pack" }, { start: 10581, name: "assert_in_range" }, { start: 10770, name: "compute_safe" }, { start: 10968, name: "test_flatten" }, { start: 11911, name: "test_flatten_big" }, { start: 14812, name: "test_flatten_small" }, { start: 15639, name: "test_safe_hashing_with_safe_helper" }, { start: 16485, name: "test_pack" }, { start: 16955, name: "test_pack_single_value" }, { start: 17151, name: "test_pack_determinism" }, { start: 17749, name: "test_pack_checked_matches_pack" }, { start: 18287, name: "test_pack_checked_matches_pack_partial_chunk" }] }, "98": { 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;\nuse poseidon::poseidon2_permutation;\n\n/// SAFE (Sponge API for Field Elements)\n///\n/// This module provides a complete implementation of the SAFE API in Noir as defined in:\n/// "SAFE (Sponge API for Field Elements) - A Toolbox for ZK Hash Applications"\n/// see https://hackmd.io/bHgsH6mMStCVibM_wYvb2w#22-Sponge-state for more details.\n///\n/// SAFE provides a unified interface for cryptographic sponge functions that can be\n/// instantiated with various permutations to create hash functions, MACs, authenticated\n/// encryption schemes, and other cryptographic primitives for ZK proof systems.\n///\n/// This implementation follows the SAFE specification exactly, providing:\n/// - Complete API: START, ABSORB, SQUEEZE, FINISH operations.\n/// - Full security: Domain separation, tag computation, IO pattern validation.\n/// - Poseidon2 integration: Field-friendly permutation for ZK systems.\n/// - Specification compliance: All operations follow SAFE spec 2.4 exactly.\n/// - Natural API design: Variable-length inputs, automatic length detection from IO patterns.\n///\n/// # API Design\n///\n/// The API is designed for natural usage while maintaining type safety:\n/// - `absorb(input: [Field])`: Accepts variable-length arrays, no padding required.\n/// - `squeeze()`: Returns a vector with field element(s).\n/// - IO patterns automatically determine operation lengths for validation.\n\n/// Rate parameter for the sponge construction (number of field elements that can be absorbed per permutation call).\nglobal RATE: u32 = 3;\n\n/// Capacity parameter for the sponge construction (security parameter, typically 1-2 field elements).\nglobal CAPACITY: u32 = 1;\n\n/// Total state size (rate + capacity) in field elements.\nglobal STATE_SIZE: u32 = RATE + CAPACITY;\n\n/// IO Pattern encoding constants (from SAFE spec 2.3).\n///\n/// These constants are used for encoding operation types in the 32-bit word format:\n/// - MSB set to 1 for ABSORB operations\n/// - MSB set to 0 for SQUEEZE operations\n\n/// Flag for ABSORB operations (MSB = 1)\nglobal ABSORB_FLAG: u32 = 0x80000000;\n\n/// Flag for SQUEEZE operations (MSB = 0)\nglobal SQUEEZE_FLAG: u32 = 0x00000000;\n\n/// SAFE Sponge State (following spec 2.2)\n///\n/// The sponge state consists of the permutation state, tag, position counters,\n/// and IO pattern tracking as defined in the SAFE specification.\n///\n/// # Generic Parameters\n/// - `L`: The length of the IO pattern array\n///\n/// # Fields\n/// - `state`: Permutation state V in F^n (rate + capacity elements)\n/// - `tag`: Parameter tag T used for instance differentiation\n/// - `absorb_pos`: Current absorb position (<= n-c)\n/// - `squeeze_pos`: Current squeeze position (<= n-c)\n/// - `io_pattern`: Expected IO pattern for validation (encoded 32-bit words)\n/// - `io_count`: Current operation count for pattern tracking\npub struct SafeSponge<let L: u32> {\n /// Permutation state V in F^n (rate + capacity elements).\n state: [Field; STATE_SIZE],\n /// Parameter tag T used for instance differentiation.\n tag: Field,\n /// Current absorb position (<= n-c).\n absorb_pos: u32,\n /// Current squeeze position (<= n-c).\n squeeze_pos: u32,\n /// Expected IO pattern for validation.\n io_pattern: [u32; L],\n /// Current operation count for pattern tracking (spec 2.4: io_count).\n io_count: u32,\n}\n\nimpl<let L: u32> SafeSponge<L> {\n /// Initializes a new SAFE sponge instance with the given IO pattern and domain separator (following spec 2.4).\n ///\n /// # Arguments\n /// - `io_pattern`: Array of 32-bit encoded operations defining the expected sequence of ABSORB/SQUEEZE calls.\n /// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n /// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n ///\n /// # Returns\n /// A new `SafeSponge` instance with initialized state\n pub fn start(io_pattern: [u32; L], domain_separator: [u8; 64]) -> SafeSponge<L> {\n // Compute tag from IO pattern and domain separator (spec 2.3).\n let tag = compute_tag(io_pattern, domain_separator);\n\n let mut state = [0; STATE_SIZE];\n // Initialize capacity with tag (spec 2.4).\n // Add T to the first 128 bits of the state.\n state[0] = tag;\n\n SafeSponge { state, tag, absorb_pos: 0, squeeze_pos: 0, io_pattern, io_count: 0 }\n }\n\n /// Absorbs field elements into the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to absorb is automatically validated against the IO pattern.\n /// This method accepts variable-length arrays, making it natural to use without padding.\n ///\n /// # Arguments\n /// - `input`: Array of field elements to absorb (variable length, must match IO pattern)\n pub fn absorb(&mut self, input: [Field]) {\n let length = input.len() as u32;\n\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_absorb = (expected_encoded_word & ABSORB_FLAG) != 0;\n let expected_length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type and length\n assert(is_expected_absorb, "Expected ABSORB operation");\n assert(expected_length == length, "Length mismatch");\n\n // Process each element naturally (no unnecessary iterations).\n for i in 0..length {\n // If absorb_pos == (n-c) then permute and reset (spec 2.4).\n if self.absorb_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.absorb_pos = 0;\n }\n\n // Add X[i] to state at absorb_pos (spec 2.4).\n // Note: absorb_pos is the rate position, not capacity position.\n self.state[self.absorb_pos + CAPACITY] =\n self.state[self.absorb_pos + CAPACITY] + input[i];\n self.absorb_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = ABSORB_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n\n // Force permute at start of next SQUEEZE (spec 2.4).\n self.squeeze_pos = RATE;\n }\n\n /// Extracts field elements from the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to squeeze is automatically determined from the IO pattern.\n pub fn squeeze(&mut self) -> [Field] {\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_squeeze = (expected_encoded_word & ABSORB_FLAG) == 0;\n let length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type\n assert(is_expected_squeeze, "Expected SQUEEZE operation");\n\n let mut output: [Field] = [].as_vector();\n\n // SQUEEZE implementation following spec 2.4.\n // If length==0, loop won\'t execute (spec 2.4).\n for _ in 0..length {\n // If squeeze_pos==(n-c) then permute and reset (spec 2.4).\n if self.squeeze_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.squeeze_pos = 0;\n self.absorb_pos = 0;\n }\n // Set Y[i] to state element at squeeze_pos (spec 2.4).\n output = output.push_back(self.state[self.squeeze_pos + CAPACITY]);\n self.squeeze_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = SQUEEZE_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n output\n }\n\n /// Finalizes the sponge instance, verifying that all expected operations have been performed and clearing the internal state for security (following spec 2.4).\n ///\n /// This function is used to ensure that the sponge instance has been used correctly and to prevent information leakage.\n pub fn finish(&mut self) {\n // Check that io_count equals the length of the IO pattern expected (spec 2.4).\n assert(self.io_count == L, "IO pattern not completed");\n\n // Erase the state and its variables (spec 2.4).\n self.state = [0; STATE_SIZE];\n self.absorb_pos = 0;\n self.squeeze_pos = 0;\n self.io_count = 0;\n }\n\n /// Permute the state using Poseidon2 (following spec 2.4).\n ///\n /// Applies the Poseidon2 permutation to the current state.\n /// This is the core cryptographic primitive of the sponge construction.\n ///\n /// # Returns\n /// New state after permutation\n fn permute(self) -> [Field; STATE_SIZE] {\n poseidon2_permutation(self.state)\n }\n}\n\n/// Computes a unique tag for a sponge instance based on its IO pattern and domain separator.\n/// The tag is used to ensure that distinct instances behave like distinct functions.\n///\n/// # Arguments\n/// - `io_pattern`: Array of 32-bit encoded operations defining the sponge\'s usage pattern.\n/// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n/// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n///\n/// # Returns\n/// A field element representing the 128-bit tag.\npub fn compute_tag<let L: u32>(io_pattern: [u32; L], domain_separator: [u8; 64]) -> Field {\n // Step 1: Parse and aggregate consecutive operations of the same type\n let mut encoded_words = [0; L]; // Support up to L operations.\n let mut word_count = 0;\n let mut current_absorb_sum = 0;\n let mut current_squeeze_sum = 0;\n let mut last_was_absorb = false;\n\n for i in 0..L {\n if io_pattern[i] > 0 {\n // Parse operation type from MSB and length from lower 31 bits\n let is_absorb = (io_pattern[i] & ABSORB_FLAG) != 0;\n let length = io_pattern[i] & 0x7FFFFFFF; // Clear MSB to get length\n\n if is_absorb {\n if last_was_absorb {\n // Aggregate consecutive ABSORB operations\n current_absorb_sum += length;\n } else {\n // Start new ABSORB sequence\n if current_squeeze_sum > 0 {\n // Flush previous SQUEEZE sequence\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n current_squeeze_sum = 0;\n }\n current_absorb_sum = length;\n }\n last_was_absorb = true;\n } else {\n if !last_was_absorb {\n // Aggregate consecutive SQUEEZE operations\n current_squeeze_sum += length;\n } else {\n // Start new SQUEEZE sequence\n if current_absorb_sum > 0 {\n // Flush previous ABSORB sequence\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n current_absorb_sum = 0;\n }\n current_squeeze_sum = length;\n }\n last_was_absorb = false;\n }\n }\n }\n\n // Flush remaining operations\n if current_absorb_sum > 0 {\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n }\n if current_squeeze_sum > 0 {\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n }\n\n // Step 2: Serialize to byte string and append domain separator (following SAFE spec 2.3).\n // Buffer is 256 bytes: max 192 bytes for IO pattern (48 words) + 64 bytes for domain separator.\n // Note: We must use a fixed-size array because Noir\'s keccak256 requires [u8; N], not [u8].\n let max_io_pattern_bytes: u32 = 192; // 256 - 64 (domain separator)\n let io_pattern_bytes = word_count * 4;\n assert(\n io_pattern_bytes <= max_io_pattern_bytes,\n "IO pattern too large: max 48 aggregated words supported",\n );\n\n let mut input_bytes = [0u8; 256];\n let mut byte_count: u32 = 0;\n\n // Serialize encoded words to bytes (big-endian as per SAFE spec).\n // Note: Noir requires compile-time loop bounds, so we iterate over L (the array size)\n // instead of word_count (runtime value). The condition `i < word_count` ensures we only\n // process valid encoded words. This is safe because word_count <= L always holds\n // (we can have at most L encoded words from L input operations).\n for i in 0..L {\n if i < word_count {\n let word = encoded_words[i];\n input_bytes[byte_count] = (word >> 24) as u8;\n input_bytes[byte_count + 1] = (word >> 16) as u8;\n input_bytes[byte_count + 2] = (word >> 8) as u8;\n input_bytes[byte_count + 3] = word as u8;\n byte_count += 4;\n }\n }\n\n // Append full 64-byte domain separator.\n for i in 0..64 {\n input_bytes[byte_count] = domain_separator[i];\n byte_count += 1;\n }\n\n // Step 3: Hash with Keccak-256 and truncate to 128 bits.\n // Note: The SAFE spec uses SHA3-256, but we use Keccak-256 for Noir compatibility.\n // Keccak-256 differs from SHA3-256 in padding, but both provide equivalent security.\n let hash_bytes = keccak256(input_bytes, byte_count);\n\n // Convert first 128 bits (16 bytes) to field element.\n let mut tag_value: Field = 0;\n for i in 0..16 {\n tag_value = tag_value * 256 + (hash_bytes[i] as Field);\n }\n\n tag_value\n}\n\n#[test]\nfn test_safe_hashing() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_merkle_node() {\n // Verifies SAFE can be used for Merkle tree node hashing with pattern ABSORB(1) + ABSORB(1) + SQUEEZE(1).\n // Tests the ability to absorb multiple inputs before squeezing output.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let left = [123].as_vector();\n let right = [456].as_vector();\n\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(left);\n sponge.absorb(right);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(left);\n sponge2.absorb(right);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_commitment_scheme() {\n // Verifies SAFE can be used for commitment schemes with pattern ABSORB(3) + SQUEEZE(1).\n // Tests the ability to create deterministic commitments from multiple field elements.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let values = [10, 20, 30].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(values);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(values);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_domain_separation() {\n // Verifies that different domain separators produce different outputs for the same input.\n // This is crucial for cross-protocol security and preventing collisions between different applications.\n let elements = [1, 2, 3].as_vector();\n let domain1 = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let domain2 = [\n 0x41, 0x42, 0x43, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n\n let mut sponge1 = SafeSponge::start(io_pattern, domain1);\n sponge1.absorb(elements);\n let output1 = sponge1.squeeze();\n sponge1.finish();\n\n let mut sponge2 = SafeSponge::start(io_pattern, domain2);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output1.len() == 1);\n assert(output2.len() == 1);\n assert(output1[0] != output2[0]); // Different domain separators should produce different outputs\n}\n\n#[test]\nfn test_multiple_squeeze() {\n // Verifies that multiple field elements can be squeezed in a single operation.\n // Tests pattern ABSORB(3) + SQUEEZE(2) to ensure proper state management.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(2)\n let io_pattern = [0x80000003, 0x00000002];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 2);\n assert(output[0] != 0);\n assert(output[1] != 0);\n assert(output[0] != output[1]); // Different squeeze outputs should be different\n}\n\n#[test]\nfn test_zero_length_operations() {\n // Verifies that zero-length ABSORB and SQUEEZE operations are handled correctly.\n // Tests pattern ABSORB(0) + SQUEEZE(1) to ensure proper state transitions.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(0), SQUEEZE(1)\n let io_pattern = [0x80000000, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb([].as_vector());\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n}\n\n#[test]\nfn test_tag_computation() {\n // Verifies the tag computation algorithm using the example from the SAFE specification.\n // Pattern: ABSORB(3), ABSORB(3), SQUEEZE(3)\n // Should aggregate to: ABSORB(6), SQUEEZE(3)\n // Encoded as: [0x80000006, 0x00000003]\n // Tests determinism and pattern differentiation.\n\n let io_pattern = [0x80000003, 0x80000003, 0x00000003];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test determinism\n let tag2 = compute_tag(io_pattern, domain_separator);\n assert(tag == tag2);\n\n // Test that different patterns produce different tags\n let io_pattern2 = [0x80000003, 0x00000003]; // ABSORB(3), SQUEEZE(3) - different pattern\n let tag3 = compute_tag(io_pattern2, domain_separator);\n assert(tag != tag3);\n}\n\n#[test]\nfn test_tag_computation_debug() {\n println("=== SAFE Tag Computation Debug Test ===");\n\n // Test your specific pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))\n let io_pattern = [0x80000002, 0x00000002, 0x80000002];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n println(f"Testing pattern: {io_pattern}");\n println(\n f"Expected to aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(2)",\n );\n println(\n f"Expected encoded words: [0x80000002, 0x00000002, 0x80000002]",\n );\n println("");\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n println(f"=== Expected Rust Output ===");\n println("Pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))");\n println("Domain separator: 0x41424344...");\n println("Tag: 0xce3bb9ee4b2d41c42e9cdda38afe8b6a");\n println("");\n\n println(f"=== Noir Output ===");\n println(f"Tag: {tag}");\n println("");\n\n println("Compare the tag values above with Rust script!");\n}\n\n#[test]\nfn test_consecutive_absorb_aggregation() {\n // Test that consecutive ABSORB operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1) should aggregate to ABSORB(2), SQUEEZE(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(1) = [0x80000002, 0x00000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(2), SQUEEZE(1)\n let aggregated_pattern = [0x80000002, 0x00000001];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Consecutive ABSORB operations should aggregate to the same tag");\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive ABSORB operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive ABSORB Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001] (ABSORB(1), ABSORB(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000002, 0x00000001] (ABSORB(2), SQUEEZE(1))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_consecutive_squeeze_aggregation() {\n // Test that consecutive SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1) should aggregate to ABSORB(1), SQUEEZE(2)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x00000001, 0x00000001];\n\n // This should aggregate to: ABSORB(1), SQUEEZE(2) = [0x80000001, 0x00000002]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(1), SQUEEZE(2)\n let aggregated_pattern = [0x80000001, 0x00000002];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(\n tag == aggregated_tag,\n "Consecutive SQUEEZE operations should aggregate to the same tag",\n );\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive SQUEEZE operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive SQUEEZE Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x00000001, 0x00000001] (ABSORB(1), SQUEEZE(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000001, 0x00000002] (ABSORB(1), SQUEEZE(2))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_mixed_consecutive_aggregation() {\n // Test that both consecutive ABSORB and SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n // Should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1) = [0x80000002, 0x00000002, 0x80000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag\n let aggregated_pattern = [0x80000002, 0x00000002, 0x80000001]; // ABSORB(2), SQUEEZE(2), ABSORB(1)\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Mixed consecutive operations should aggregate to the same tag");\n\n println("=== Mixed Consecutive Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001]",\n );\n println(\n f" (ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1))",\n );\n println(f"Aggregated pattern: [0x80000002, 0x00000002, 0x80000001]");\n println(f" (ABSORB(2), SQUEEZE(2), ABSORB(1))");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n}\n\n#[test]\nfn test_large_io_pattern() {\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Create pattern with 48 alternating ABSORB(1) and SQUEEZE(1) operations\n // This is the maximum supported (48 words * 4 bytes = 192 bytes, leaving 64 for domain separator)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1; // ABSORB(1)\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1; // SQUEEZE(1)\n }\n }\n\n let tag = compute_tag(io_pattern, domain_separator);\n assert(tag != 0);\n}\n\n#[test]\nfn test_domain_separator_not_truncated() {\n // This test verifies that the domain separator is always included in the tag computation,\n // even for large IO patterns. If the domain separator were truncated, different domain\n // separators would produce the same tag for large patterns.\n\n let domain_separator_a = [\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41,\n ]; // All \'A\'s\n\n let domain_separator_b = [\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42,\n ]; // All \'B\'s\n\n // Create pattern with 48 alternating operations (max supported: 192 bytes of IO pattern)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1;\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1;\n }\n }\n\n let tag_a = compute_tag(io_pattern, domain_separator_a);\n let tag_b = compute_tag(io_pattern, domain_separator_b);\n\n // Tags MUST be different because domain separators are different.\n // If they were the same, it would mean the domain separator was truncated/ignored.\n assert(tag_a != tag_b, "Domain separator must affect tag even for large IO patterns");\n}\n', path: "/Users/ctrlc03/.omp/worktrees/interfold/feat-insecure-64-params/circuits/lib/src/math/safe.nr", function_locations: [{ start: 4164, name: "SafeSponge<L>::start" }, { start: 5046, name: "SafeSponge<L>::absorb" }, { start: 6826, name: "SafeSponge<L>::squeeze" }, { start: 8494, name: "SafeSponge<L>::finish" }, { start: 9156, name: "SafeSponge<L>::permute" }, { start: 9830, name: "compute_tag" }, { start: 14128, name: "test_safe_hashing" }, { start: 15104, name: "test_merkle_node" }, { start: 16281, name: "test_commitment_scheme" }, { start: 17357, name: "test_domain_separation" }, { start: 18710, name: "test_multiple_squeeze" }, { start: 19639, name: "test_zero_length_operations" }, { start: 20415, name: "test_tag_computation" }, { start: 21477, name: "test_tag_computation_debug" }, { start: 22681, name: "test_consecutive_absorb_aggregation" }, { start: 24750, name: "test_consecutive_squeeze_aggregation" }, { start: 26760, name: "test_mixed_consecutive_aggregation" }, { start: 28520, name: "test_large_io_pattern" }, { start: 29333, name: "test_domain_separator_not_truncated" }] } } };
|
|
615
615
|
|
|
616
616
|
// ../../circuits/bin/fold_onchain/target/crisp_onchain_fold.json
|
|
617
|
-
var crisp_onchain_fold_default = { noir_version: "1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905", hash: "17871915766732779512", abi: { parameters: [{ name: "user_data_encryption_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_public_inputs", type: { kind: "array", length: 5, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_key_hash", type: { kind: "field" }, visibility: "private" }, { name: "crisp_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "crisp_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "crisp_key_hash", type: { kind: "field" }, visibility: "private" }, { name: "prev_ct_commitment", type: { kind: "field" }, visibility: "public" }, { name: "digest_hi", type: { kind: "field" }, visibility: "public" }, { name: "digest_lo", type: { kind: "field" }, visibility: "public" }, { name: "slot_address", type: { kind: "field" }, visibility: "public" }, { name: "voting_power", type: { kind: "field" }, visibility: "public" }, { name: "is_first_vote", type: { kind: "boolean" }, visibility: "public" }, { name: "num_options", type: { kind: "integer", sign: "unsigned", width: 32 }, visibility: "public" }, { name: "final_ct_commitment", type: { kind: "field" }, visibility: "public" }, { name: "ct_commitment", type: { kind: "field" }, visibility: "private" }, { name: "k1_commitment", type: { kind: "field" }, visibility: "private" }], return_type: { abi_type: { kind: "field" }, visibility: "public" }, error_types: {} }, bytecode: "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", debug_symbols: "tZZRjqMwDIbvkmceYiex47nKalXRlo6QEK2YMtKqmrtv6JBQVgrqpponAyGf7N+O45s6Nvvxfdf2p/OHevt1U/uh7br2fdedD/W1Pffh6+2rUvF1dx2aJnxSD+th16Uemv6q3vqx6yr1WXfj/aePS93f7bUewqquVNMfgw3AU9s109NXtezW+a1AjPNu8CQJ4HBFgC2CSwQWsxDMioB5gjHsZ4KxBImA+GwUCM7MBAQ2uSi2dPDsIsFozBHcyzrQj+pgbdLBel2gA1qQhQAlBMKoA5KTEoITnwhUFAU7ioRVLp4n+EUH76iEIJiiEGezJ8tulRRJqiliswTieQ3ZqEtmif2B/YOa/yI2ChO0t1HP8PyQVC9rCOchBDoqSmA5IcQ/jXBeYnk70S6LkDxCgqYzQkAkh8CNxBpJx9RqWLwA/g8tkheE2pZosUJAXovttHpOaRUNubTiRo0y2ViiTC4bCrrXc0Iv52QzEJ/uEPaCOS82TzxHOY22JX3LaJcIAL6o6zAthKLuK6QTAUs6nwFjEoHXOvwOb/WhHVZzkMLwZ6VMUL5SNkheKRdyF4YOmi70SvG38VM7r5R8G9CTs8HCbHG2ZhIyWDtbN9sAM1NeP+uhrfddM49gp7E/PExk1z+XuBJntstwPjTHcWgmr+9rIY6/", file_map: { "17": { 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;\nuse crate::static_assert;\n\n/// The size of the state accepted by the backend in `poseidon2_permutation`.\nglobal POSEIDON2_CONFIG_STATE_SIZE: u32 = poseidon2_config_state_size();\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\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s<let N: u32>(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n// docs:start:blake3\npub fn blake3<let N: u32>(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<let N: u32>(input: [u8; N]) -> [u8; 32] {}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment<let N: u32>(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<let N: u32>(\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 points[i] = EmbeddedCurveScalar::from_field(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<let N: u32>(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<let N: u32>(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 crate::assert_constant(separator);\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] = EmbeddedCurveScalar::from_field(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, true)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n crate::assert_constant(starting_index);\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\npub fn poseidon2_permutation<let N: u32>(input: [Field; N]) -> [Field; N] {\n static_assert(\n N == POSEIDON2_CONFIG_STATE_SIZE,\n f"the input length must equal the state size in the Poseidon2 config; expected {POSEIDON2_CONFIG_STATE_SIZE}, got {N}",\n );\n poseidon2_permutation_internal(input)\n}\n\n#[foreign(poseidon2_permutation)]\nfn poseidon2_permutation_internal<let N: u32>(input: [Field; N]) -> [Field; N] {}\n\n#[foreign(poseidon2_config_state_size)]\ncomptime fn poseidon2_config_state_size() -> u32 {}\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<H>(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<H>(_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 /// Returns the hash value without consuming the hasher.\n /// Override this for more efficient implementations that avoid copying.\n /// TODO: deprecate finish() and replace it\n fn finish_ref(&self) -> Field {\n (*self).finish()\n }\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<H>;\n\nimpl<H> BuildHasher for BuildHasherDefault<H>\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<H> Default for BuildHasherDefault<H>\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u8 {\n fn hash<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl<T, let N: u32> Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl<T> Hash for [T]\nwhere\n T: Hash,\n{\n fn hash<H>(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<A> Hash for (A,)\nwhere\n A: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n }\n}\n\nimpl<A, B> Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl<A, B, C> Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash<H>(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<A, B, C, D> Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash<H>(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<A, B, C, D, E> 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<H>(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\nimpl<A, B, C, D, E, F> Hash for (A, B, C, D, E, F)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G> Hash for (A, B, C, D, E, F, G)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_> Hash for (A, B, C, D, E, F, G, H_)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I> Hash for (A, B, C, D, E, F, G, H_, I)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J> Hash for (A, B, C, D, E, F, G, H_, I, J)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K> Hash for (A, B, C, D, E, F, G, H_, I, J, K)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K, L> Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n L: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n self.11.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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n );\n}\n', path: "std/hash/mod.nr", function_locations: [{ start: 572, name: "sha256_compression" }, { start: 707, name: "keccakf1600" }, { start: 852, name: "blake2s" }, { start: 950, name: "blake3" }, { start: 1437, name: "__blake3" }, { start: 1555, name: "pedersen_commitment" }, { start: 1784, name: "pedersen_commitment_with_separator" }, { start: 2188, name: "pedersen_hash" }, { start: 2345, name: "pedersen_hash_with_separator" }, { start: 3339, name: "derive_generators" }, { start: 3698, name: "__derive_generators" }, { start: 3776, name: "poseidon2_permutation" }, { start: 4132, name: "poseidon2_permutation_internal" }, { start: 4225, name: "poseidon2_config_state_size" }, { start: 4536, name: "derive_hash" }, { start: 5327, name: "Hasher::finish_ref" }, { start: 5761, name: "<impl BuildHasher for BuildHasherDefault<H>>::build_hasher" }, { start: 5893, name: "<impl Default for BuildHasherDefault<H>>::default" }, { start: 6025, name: "<impl Hash for Field>::hash" }, { start: 6155, name: "<impl Hash for u8>::hash" }, { start: 6295, name: "<impl Hash for u16>::hash" }, { start: 6435, name: "<impl Hash for u32>::hash" }, { start: 6575, name: "<impl Hash for u64>::hash" }, { start: 6716, name: "<impl Hash for u128>::hash" }, { start: 6855, name: "<impl Hash for i8>::hash" }, { start: 7001, name: "<impl Hash for i16>::hash" }, { start: 7148, name: "<impl Hash for i32>::hash" }, { start: 7295, name: "<impl Hash for i64>::hash" }, { start: 7443, name: "<impl Hash for bool>::hash" }, { start: 7590, name: "<impl Hash for ()>::hash" }, { start: 7722, name: "<impl Hash for [T; N]>::hash" }, { start: 7911, name: "<impl Hash for [T]>::hash" }, { start: 8133, name: "<impl Hash for (A,)>::hash" }, { start: 8302, name: "<impl Hash for (A, B)>::hash" }, { start: 8518, name: "<impl Hash for (A, B, C)>::hash" }, { start: 8781, name: "<impl Hash for (A, B, C, D)>::hash" }, { start: 9091, name: "<impl Hash for (A, B, C, D, E)>::hash" }, { start: 9448, name: "<impl Hash for (A, B, C, D, E, F)>::hash" }, { start: 9852, name: "<impl Hash for (A, B, C, D, E, F, G)>::hash" }, { start: 10306, name: "<impl Hash for (A, B, C, D, E, F, G, H_)>::hash" }, { start: 10807, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I)>::hash" }, { start: 11355, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J)>::hash" }, { start: 11950, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K)>::hash" }, { start: 12593, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)>::hash" }, { start: 13187, name: "assert_pedersen" }] }, "22": { source: "pub mod hash;\npub mod aes128;\npub mod array;\npub mod vector;\npub mod ecdsa_secp256k1;\npub mod ecdsa_secp256r1;\npub mod embedded_curve_ops;\npub mod field;\npub mod collections;\npub mod compat;\npub mod convert;\npub mod option;\npub mod string;\npub mod test;\npub mod cmp;\npub mod ops;\npub mod default;\npub mod prelude;\npub mod runtime;\npub mod meta;\npub mod append;\npub mod mem;\npub mod panic;\npub mod hint;\n\nmod integer;\nmod primitive_docs;\nmod internal;\n\n// Oracle calls are required to be wrapped in an unconstrained function\n// Thus, the only argument to the `println` oracle is expected to always be an ident\n#[oracle(print)]\nunconstrained fn print_oracle<T>(with_newline: bool, input: T) {}\n\nunconstrained fn print_unconstrained<T>(with_newline: bool, input: T) {\n print_oracle(with_newline, input);\n}\n\n/// Print the given input to stdout followed by a newline\npub fn println<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(true, input);\n }\n}\n\n/// Print the given input to stdout\npub fn print<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(false, input);\n }\n}\n\n/// Asserts the validity of the provided proof and public inputs against the provided verification key and hash.\n///\n/// The ACVM cannot determine whether the provided proof is valid during execution as this requires knowledge of\n/// the backend against which the program is being proven. However if an invalid proof if submitted, the program may\n/// fail to prove or the backend may generate a proof which will subsequently fail to verify.\n///\n/// # Important Note\n///\n/// If you are not developing your own backend such as [Barretenberg](https://github.com/AztecProtocol/barretenberg)\n/// you probably shouldn't need to interact with this function directly. It's easier and safer to use a verification\n/// library which is published by the developers of the backend which will document or enforce any safety requirements.\n///\n/// If you use this directly, you're liable to introduce underconstrainedness bugs and *your circuit will be insecure*.\n///\n/// # Arguments\n/// - verification_key: The verification key of the circuit to be verified.\n/// - proof: The proof to be verified.\n/// - public_inputs: The public inputs associated with `proof`\n/// - key_hash: The hash of `verification_key` of the form expected by the backend.\n/// - proof_type: An identifier for the proving scheme used to generate the proof to be verified. This allows\n/// for a single backend to support verifying multiple proving schemes.\n///\n/// # Constraining `key_hash`\n///\n/// The Noir compiler does not by itself constrain that `key_hash` is a valid hash of `verification_key`.\n/// This is because different backends may differ in how they hash their verification keys.\n/// It is then the responsibility of either the noir developer (by explicitly hashing the verification key\n/// in the correct manner) or by the proving system itself internally asserting the correctness of `key_hash`.\npub fn verify_proof_with_type<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {\n if !crate::runtime::is_unconstrained() {\n crate::assert_constant(proof_type);\n }\n verify_proof_internal(verification_key, proof, public_inputs, key_hash, proof_type);\n}\n\n#[foreign(recursive_aggregation)]\nfn verify_proof_internal<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {}\n\n/// Asserts that the given value is known at compile-time.\n/// Useful for debugging for-loop bounds.\n#[builtin(assert_constant)]\npub fn assert_constant<T>(x: T) {}\n\n/// Asserts that the given value is both true and known at compile-time.\n/// The message can be a string, a format string, or any value, as long as it is known at compile-time\n#[builtin(static_assert)]\npub fn static_assert<T>(predicate: bool, message: T) {}\n\n/// Force a field value to be a witness instead of a constant in the compiled output.\n/// This is often only useful for debugging compiler optimizations.\n///\n/// This has no effect in unconstrained or comptime code.\n#[builtin(as_witness)]\npub fn as_witness(x: Field) {}\n", path: "std/lib.nr", function_locations: [{ start: 689, name: "print_oracle" }, { start: 763, name: "print_unconstrained" }, { start: 893, name: "println" }, { start: 1076, name: "print" }, { start: 3277, name: "verify_proof_with_type" }, { start: 3694, name: "verify_proof_internal" }, { start: 3859, name: "assert_constant" }, { start: 4118, name: "static_assert" }, { start: 4389, name: "as_witness" }] }, "52": { 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 bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};\nuse interfold_lib::math::commitments::compute_vk_hash;\n\n/// Binds the four inner `vk_hash` witnesses. Regenerate with `pnpm compute:vk-hash` in\n/// `examples/CRISP` after changing ct0 / ct1 / user_data_encryption / crisp_onchain (or the lib\n/// preset). Insecure: `lib::configs::default` uses `insecure::*`; secure: uses `secure::*`.\npub global CRISP_ONCHAIN_FOLD_EXPECTED_KEY_HASH_INSECURE: Field =\n 0x090d43b8bbd50e0840fd14a5918216321b0fc5dc5696da80da7cca2b22614696;\npub global CRISP_ONCHAIN_FOLD_EXPECTED_KEY_HASH_SECURE: Field =\n 0x225abc9d92035e910c9bd0deac6060640ad49b30ed6ef4de6d094397010ebdac;\n\nfn main(\n // User Data Encryption Section.\n user_data_encryption_verification_key: UltraHonkVerificationKey,\n user_data_encryption_proof: UltraHonkProof,\n user_data_encryption_public_inputs: [Field; 5], // ct0_key_hash, ct1_key_hash, pk_commitment, ct_commitment, k1_commitment\n user_data_encryption_key_hash: Field,\n // Crisp Section.\n crisp_verification_key: UltraHonkVerificationKey,\n crisp_proof: UltraHonkProof,\n crisp_key_hash: Field,\n prev_ct_commitment: pub Field,\n digest_hi: pub Field,\n digest_lo: pub Field,\n slot_address: pub Field,\n voting_power: pub Field,\n is_first_vote: pub bool,\n num_options: pub u32,\n final_ct_commitment: pub Field,\n ct_commitment: Field,\n k1_commitment: Field,\n) -> pub Field {\n verify_honk_proof_non_zk(\n user_data_encryption_verification_key,\n user_data_encryption_proof,\n user_data_encryption_public_inputs,\n user_data_encryption_key_hash,\n );\n verify_honk_proof_non_zk(\n crisp_verification_key,\n crisp_proof,\n [\n prev_ct_commitment,\n digest_hi,\n digest_lo,\n slot_address,\n voting_power,\n if is_first_vote { 1 } else { 0 },\n num_options as Field,\n final_ct_commitment,\n ct_commitment,\n k1_commitment,\n ],\n crisp_key_hash,\n );\n\n // Verify that the ct_commitment from the crisp proof matches the one computed from user data encryption.\n assert(ct_commitment == user_data_encryption_public_inputs[3]);\n\n // Verify that the k1_commitment from the crisp proof matches the one computed from user data encryption.\n assert(k1_commitment == user_data_encryption_public_inputs[4]);\n\n let vk_hashes = [\n user_data_encryption_key_hash,\n crisp_key_hash,\n user_data_encryption_public_inputs[0], // ct0_key_hash\n user_data_encryption_public_inputs[1], // ct1_key_hash\n ]\n .as_vector();\n\n let chain_key_hash = compute_vk_hash(vk_hashes);\n assert(\n (chain_key_hash == CRISP_ONCHAIN_FOLD_EXPECTED_KEY_HASH_INSECURE)\n | (chain_key_hash == CRISP_ONCHAIN_FOLD_EXPECTED_KEY_HASH_SECURE),\n );\n\n user_data_encryption_public_inputs[2]\n}\n", path: "/Users/ctrlc03/Documents/zk/interfold/examples/CRISP/circuits/bin/fold_onchain/src/main.nr", function_locations: [{ start: 1666, name: "main" }] }, "53": { source: '// Constants for UltraHonk recursive verifier inputs\npub global PROOF_TYPE_HONK: u32 = 0; // identifier for UltraHonk verfier\npub global RECURSIVE_PROOF_LENGTH: u32 = 410;\npub global ULTRA_VK_LENGTH_IN_FIELDS: u32 = 115;\n\npub type UltraHonkProof = [Field; RECURSIVE_PROOF_LENGTH];\npub type UltraHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\n// Constants for Rollup-UltraHonk recursive verifier inputs (N.B. this is equivalent to UH plus IPA claim and proof)\npub global PROOF_TYPE_ROLLUP_HONK: u32 = 4; // identifier for rollup-UltraHonk verfier\npub global PROOF_TYPE_ROOT_ROLLUP_HONK: u32 = 5; // identifier for root-rollup-UltraHonk verfier (closes the IPA accumulator)\npub global IPA_CLAIM_SIZE: u32 = 6;\npub global IPA_PROOF_LENGTH: u32 = 64;\npub global RECURSIVE_ROLLUP_HONK_PROOF_LENGTH: u32 =\n RECURSIVE_PROOF_LENGTH + IPA_CLAIM_SIZE + IPA_PROOF_LENGTH;\n\npub type RollupHonkProof = [Field; RECURSIVE_ROLLUP_HONK_PROOF_LENGTH];\npub type RollupHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\npub global PROOF_TYPE_HONK_ZK: u32 = 6; // identifier for UltraHonk ZK verfier\npub global RECURSIVE_ZK_PROOF_LENGTH: u32 = 450 + 8;\n\npub type UltraHonkZKProof = [Field; RECURSIVE_ZK_PROOF_LENGTH];\n\n// Verifies a non-zero-knowledge UltraHonk proof.\n//\n// Represents standard UltraHonk recursive verification for proofs that do not hide the witness.\n// Use this only in situations where zero-knowledge is not required.\npub fn verify_honk_proof_non_zk<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof with IPA (Inner Product Argument).\n//\n// This variant includes an IPA claim and proof appended to the standard UltraHonk proof,\n// used to amortize IPA recursive verification costs in rollup circuits.\npub fn verify_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROLLUP_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof and closes the IPA accumulator in-circuit.\n//\n// Use this at the root of a rollup aggregation tree. The inner proof has the same RollupHonk shape\n// as `verify_rolluphonk_proof`, but instead of propagating an accumulated IPA claim out as a public\n// input, this variant performs a full native IPA verification inside the circuit. The outer circuit\n// should therefore be proved as a standard (non-rollup) UltraHonk circuit, producing a proof\n// suitable for verification by `verify_honk_proof` / `verify_honk_proof_non_zk`.\n//\n// When two RollupHonk inputs are verified this way in one circuit, their two IPA claims are first\n// accumulated into one, then the accumulated claim is fully verified.\npub fn verify_root_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROOT_ROLLUP_HONK,\n );\n}\n\n// Verifies a zero-knowledge UltraHonk proof.\n//\n// This verifier is for UltraHonk proofs constructed with zero-knowledge, which hide the witness\n// values from the verifier.\n// Note: We intentionally choose the generic name "verify_honk_proof" for this function, as we\n// want ZK to be the default unless the user explicitly opts out.\npub fn verify_honk_proof<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkZKProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK_ZK,\n );\n}\n', path: "/Users/ctrlc03/nargo/github.com/AztecProtocol/aztec-packages/v5.1.0/barretenberg/noir/bb_proof_verification/src/lib.nr", function_locations: [{ start: 1646, name: "verify_honk_proof_non_zk" }, { start: 2262, name: "verify_rolluphonk_proof" }, { start: 3386, name: "verify_root_rolluphonk_proof" }, { start: 4087, name: "verify_honk_proof" }] }, "87": { 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 crate::math::helpers::{compute_safe, flatten, flatten_checked};\nuse crate::math::polynomial::Polynomial;\n\n/// DOMAIN SEPARATORS\n\n// Domain separator - "PK"\npub global DS_PK: [u8; 64] = [\n 0x50, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_GENERATION"\npub global DS_PK_GENERATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_COMPUTATION"\npub global DS_SHARE_COMPUTATION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_ENCRYPTION"\npub global DS_SHARE_ENCRYPTION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_AGGREGATION"\npub global DS_PK_AGGREGATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CIPHERTEXT"\npub global DS_CIPHERTEXT: [u8; 64] = [\n 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "AGGREGATED_SHARES"\npub global DS_AGGREGATED_SHARES: [u8; 64] = [\n 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45,\n 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "VK_HASH"\npub global DS_VK_HASH: [u8; 64] = [\n 0x56, 0x4b, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "RECURSIVE_AGGREGATION"\npub global DS_RECURSIVE_AGGREGATION: [u8; 64] = [\n 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47,\n 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_PK_GENERATION"\npub global DS_CLG_PK_GENERATION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_ENCRYPTION"\npub global DS_CLG_SHARE_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_USER_DATA_ENCRYPTION"\npub global DS_CLG_USER_DATA_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e,\n 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_DECRYPTION"\npub global DS_CLG_SHARE_DECRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "THRESHOLD_DECRYPTION_SHARE"\npub global DS_THRESHOLD_DECRYPTION_SHARE: [u8; 64] = [\n 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "USER_DATA_ENCRYPTION_COMMITMENT"\npub global DS_USER_DATA_ENCRYPTION_COMMITMENT: [u8; 64] = [\n 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n/// WRAPPERS\n\npub fn compute_commitment(inputs: [Field], domain_separator: [u8; 64]) -> Field {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 1])[0]\n}\n\npub fn compute_single_polynomial_commitment<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_single_polynomial_commitment`] with the digit asserts, for a polynomial this circuit\n/// carries no bound for. ct1 commits `u` this way: ct0 range-checks the same `u` to `u_bound` and the\n/// `user_data_encryption` fold asserts the two `u` commitments are equal, so making ct1\'s packing\n/// injective is what forces its `u` to equal ct0\'s \u2014 the bound transfers instead of being repeated.\n/// Produces the same value as the plain version for any input the plain version could legitimately\n/// have committed.\npub fn compute_single_polynomial_commitment_checked<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_multiple_polynomial_commitment<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_challenge<let L: u32>(inputs: [Field], domain_separator: [u8; 64]) -> [Field] {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 2 * L])\n}\n\npub fn single_polynomial_payload<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, inputs)\n}\n\npub fn single_polynomial_payload_checked<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload_checked<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, inputs)\n}\n\n/// COMMITMENTS\n\npub fn compute_dkg_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let mut payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n payload = multiple_polynomial_payload::<N, L, BIT_PK>(payload, pk1);\n\n compute_commitment(payload, DS_PK)\n}\n\npub fn compute_threshold_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\n/// [`compute_multiple_polynomial_commitment`] for a circuit that **opens** this commitment.\npub fn compute_multiple_polynomial_commitment_checked<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_threshold_pk_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C5). Asserts each packed digit fits its slot, which makes the opening unique and so\n/// forces the opened `pk0` to equal the `pk0` C1 committed. Produces the same value as the plain\n/// helper, so C1 needs no change.\npub fn compute_threshold_pk_commitment_checked<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\npub fn compute_share_computation_sk_commitment<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\n/// [`compute_share_computation_sk_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2a). C1 creates it and bounds `sk` to the trinary range, so making the opening\n/// unique forces the witness to equal that `sk`.\npub fn compute_share_computation_sk_commitment_checked<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_computation_e_sm_commitment<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_encryption_commitment_from_message<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\n/// [`compute_share_encryption_commitment_from_message`] for a circuit that **opens** this\n/// commitment rather than creating it (C4). Asserts each packed digit fits its slot, forcing the\n/// opened share to equal the share C2 committed. Same value as the plain helper, so C2 and C3 need\n/// no change.\npub fn compute_share_encryption_commitment_from_message_checked<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\npub fn compute_aggregated_shares_commitment<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// Commitment to a threshold decryption share: all CRT limbs, first `K` coefficients per limb\n/// (same layout as `Polynomial<K>` in decrypted_shares_aggregation).\n///\n/// `NATIVE_BIT_WIDTH` must cover native coefficients in \\([0, q_l)\\) per limb (not centered `d` bounds).\npub fn compute_threshold_decryption_share_commitment<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload =\n multiple_polynomial_payload::<K, L, NATIVE_BIT_WIDTH>([].as_vector(), d_share_limbs);\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_threshold_decryption_share_commitment`] for a circuit that **opens** this commitment\n/// rather than creating it (C7). C6 creates it and pins each coefficient to a native residue in\n/// \\([0, q)\\) via `verify_d_native_trunc_binding`, so asserting the digits here makes the opening\n/// unique and forces the opened share to equal the one C6 committed. Produces the same value as the\n/// plain version for any input the plain version could legitimately have committed.\npub fn compute_threshold_decryption_share_commitment_checked<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<K, L, NATIVE_BIT_WIDTH>(\n [].as_vector(),\n d_share_limbs,\n );\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_share_computation_e_sm_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2b). C1 creates it and bounds each `e_sm` limb to a centered residue.\npub fn compute_share_computation_e_sm_commitment_checked<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_pk_aggregation_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let commit_pk0 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk0, DS_PK_AGGREGATION);\n let commit_pk1 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk1, DS_PK_AGGREGATION);\n\n let inputs = [commit_pk0, commit_pk1].as_vector();\n\n compute_commitment(inputs, DS_PK_AGGREGATION)\n}\n\npub fn compute_recursive_aggregation_commitment(payload: [Field]) -> Field {\n compute_commitment(payload, DS_RECURSIVE_AGGREGATION)\n}\n\npub fn compute_vk_hash(vk_hashes: [Field]) -> Field {\n compute_commitment(vk_hashes, DS_VK_HASH)\n}\n\n/// [`compute_aggregated_shares_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). C4 creates it after `normalize_aggregated` has reduced and centered the\n/// aggregate, so making the opening unique forces the witness to equal that canonical value.\npub fn compute_aggregated_shares_commitment_checked<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// [`compute_ciphertext_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). No upstream circuit bounds the ciphertext limbs, so this buys injectivity\n/// rather than canonicality \u2014 which is the property wanted here: it forces C6 to decrypt exactly\n/// the ciphertext that was committed, whatever its representation.\npub fn compute_ciphertext_commitment_checked<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\npub fn compute_ciphertext_commitment<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\n/// COMMITMENTS FOR CHALLENGES\n\npub fn compute_threshold_pk_challenge(payload: [Field]) -> Field {\n compute_commitment(payload, DS_CLG_PK_GENERATION)\n}\n\npub fn compute_share_encryption_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_SHARE_ENCRYPTION)\n}\n\npub fn compute_threshold_share_decryption_challenge<let L: u32>(payload: [Field]) -> Field {\n compute_challenge::<L>(payload, DS_CLG_SHARE_DECRYPTION)[0]\n}\n\npub fn compute_user_data_encryption_ct0_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n\npub fn compute_user_data_encryption_ct1_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n', path: "/Users/ctrlc03/Documents/zk/interfold/circuits/lib/src/math/commitments.nr", function_locations: [{ start: 7784, name: "compute_commitment" }, { start: 8012, name: "compute_single_polynomial_commitment" }, { start: 8827, name: "compute_single_polynomial_commitment_checked" }, { start: 9138, name: "compute_multiple_polynomial_commitment" }, { start: 9375, name: "compute_challenge" }, { start: 9585, name: "single_polynomial_payload" }, { start: 9784, name: "multiple_polynomial_payload" }, { start: 9970, name: "single_polynomial_payload_checked" }, { start: 10185, name: "multiple_polynomial_payload_checked" }, { start: 10406, name: "compute_dkg_pk_commitment" }, { start: 10733, name: "compute_threshold_pk_commitment" }, { start: 11138, name: "compute_multiple_polynomial_commitment_checked" }, { start: 11750, name: "compute_threshold_pk_commitment_checked" }, { start: 12008, name: "compute_share_computation_sk_commitment" }, { start: 12516, name: "compute_share_computation_sk_commitment_checked" }, { start: 12795, name: "compute_share_computation_e_sm_commitment" }, { start: 13067, name: "compute_share_encryption_commitment_from_message" }, { start: 13647, name: "compute_share_encryption_commitment_from_message_checked" }, { start: 13931, name: "compute_aggregated_shares_commitment" }, { start: 14512, name: "compute_threshold_decryption_share_commitment" }, { start: 15336, name: "compute_threshold_decryption_share_commitment_checked" }, { start: 15876, name: "compute_share_computation_e_sm_commitment_checked" }, { start: 16182, name: "compute_pk_aggregation_commitment" }, { start: 16571, name: "compute_recursive_aggregation_commitment" }, { start: 16686, name: "compute_vk_hash" }, { start: 17169, name: "compute_aggregated_shares_commitment_checked" }, { start: 17843, name: "compute_ciphertext_commitment_checked" }, { start: 18325, name: "compute_ciphertext_commitment" }, { start: 18724, name: "compute_threshold_pk_challenge" }, { start: 18866, name: "compute_share_encryption_challenge" }, { start: 19023, name: "compute_threshold_share_decryption_challenge" }, { start: 19183, name: "compute_user_data_encryption_ct0_challenge" }, { start: 19344, name: "compute_user_data_encryption_ct1_challenge" }] }, "89": { 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\n//! Helper functions for circuit construction and cryptographic operations.\nuse crate::math::polynomial::Polynomial;\nuse crate::math::safe::SafeSponge;\n\n/// Compute hex-aligned packing parameters for a given `BIT`.\n///\n/// # Purpose\n/// Returns `(nibble_bits, group)` for use by pack/flatten so layout stays consistent.\n/// - `nibble_bits`: ceil (`BIT`) to the next multiple of 4 (nibble alignment).\n/// - Examples: `BIT = 7 -> 8`, `BIT = 8 -> 8`, `BIT = 9 -> 12`, `BIT = 10 -> 12`, `BIT = 11 -> 12`,\n/// `BIT=16 -> 16`, `BIT = 17 -> 20`.\n/// - `group`: max number of encoded limbs that fit in one BN254 field element,\n/// when each limb uses an extra 4 bits (see below).\n///\n/// # Rationale\n/// - We align to nibbles so powers of two are hex-friendly and deterministic.\n/// - We reserve one extra nibble (4 bits) per stored value to lift signed\n/// coefficients into the non-negative range (e.g., store `v + 2^nibble_bits`),\n/// which implies a radix of `2^(nibble_bits + 4)`.\n///\n/// # Safety\n/// - Asserts `nibble_bits + 4 <= 254` to avoid mod-p wrap on BN254.\n/// - Ensures at least one limb fits: `group >= 1`.\nfn packing_layout<let BIT: u32>() -> (u32, u32) {\n // Ceil BIT up to the next multiple of 4 (nibble alignment).\n let nibble_bits = ((BIT + 3) / 4) * 4;\n\n // Each stored limb uses an extra nibble because negative coefficients\n // will be shifted to positive, so radix = 2^(nibble_bits+4).\n assert(nibble_bits + 4 <= 254);\n\n // Maximum limbs that fit in one BN254 element without wrap.\n let group = 254 / (nibble_bits + 4);\n // `group == 1` packs one value per carrier: it saves no sponge absorptions at all while still\n // charging the range checks that make packing injective, so it is always a loss. Measured at\n // BIT=143: +82% gates against absorbing directly. Reject it rather than pay for nothing.\n assert(group >= 2);\n (nibble_bits, group)\n}\n\n/// Like [`pack`], but asserts every digit fits its slot.\n///\n/// ## Why\n/// `pack` builds each carrier as a base-`radix` number, `acc = acc * radix + (v + base)`. That\n/// representation is unique only while every digit stays below `radix`. An unbounded value can\n/// overflow its slot and cancel against the next, so one commitment gains a second opening and a\n/// prover can choose which coefficients it opens to.\n///\n/// `digit < radix` is exactly `digit.assert_max_bit_size::<nibble_bits + 4>()`, and because\n/// `assert_max_bit_size` also forces `digit >= 0`, a single assert gives the whole condition.\n///\n/// ## When to use it\n/// Use this where a circuit **opens** a commitment it did not create. The creating circuit's own\n/// range checks already keep its digits in slot, so asserting the same here forces the opened\n/// coefficients to equal the committed ones \u2014 injectivity transfers the upstream bound without\n/// repeating it. One assert costs about half a two-sided `range_check_2bounds`.\n///\n/// It does **not** replace a bound the protocol requires in its own right, and it gives injectivity,\n/// not canonicality: it pins the opening, not a unique representative modulo `q`. A value that no\n/// upstream circuit bounded still needs its own check (see C1 `pk0` and C5 `pk0_agg`).\n///\n/// ## Compatibility\n/// Adding asserts does not change the packed value, so a commitment created with [`pack`] and\n/// opened with `pack_checked` agree. Adoption is per circuit and needs no commitment change.\npub fn pack_checked<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field);\n let radix = 2.pow_32((nibble_bits + 4) as Field);\n\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n for chunk in 0..num_chunks {\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n let digit = v + base;\n // digit < radix = 2^(nibble_bits + 4): the injectivity condition.\n digit.assert_max_bit_size::<((BIT + 3) / 4) * 4 + 4>();\n acc = acc * radix + digit;\n }\n\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Like [`flatten`], but packs with [`pack_checked`]. Use where a circuit opens a commitment it did\n/// not create.\npub fn flatten_checked<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n let packed = pack_checked::<A, BIT>(poly[j].coefficients);\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n inputs\n}\n\n/// Flatten `L` polynomials into a single linear stream of packed `Field` carriers.\n///\n/// ## What this does\n/// - For each CRT limb `j` in `0..L`, it packs the coefficients of `poly[j]`\n/// with `pack::<A, BIT>` and appends all resulting carriers to `inputs`.\n/// - The packing layout (nibble-aligned width and `group` size) is taken from\n/// `packing_layout::<BIT>()` and must match what `pack` uses.\n///\n/// ## Determinism & order\n/// - Preserves a stable order: iterate `j = 0..L`, then for each `j` append\n/// carriers in ascending chunk index `i = 0..num_chunks`.\n/// - This ensures transcripts remain deterministic across runs.\n///\n/// ## Generics\n/// - `A`: polynomial degree (number of coefficients per polynomial).\n/// - `L`: number of CRT bases (polynomials).\n/// - `BIT`: per-coefficient bit bound used by the packing layout (compile-time).\n///\n/// ## Returns\n/// - The same `inputs` vector, extended with all carriers in deterministic order.\npub fn flatten<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n // Pack its A coefficients into `num_chunks` carriers using the same BIT layout.\n let packed = pack::<A, BIT>(poly[j].coefficients);\n\n // Append carriers in-order to `inputs` to keep a stable transcript layout.\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n\n // Return the extended input stream.\n inputs\n}\n\n/// Pack `A` values into a `[Field]` vector of carriers using the shared hex-aligned layout.\n///\n/// ## What this does\n/// - Computes `(nibble_bits, group)` via `packing_layout::<BIT>()`.\n/// - Encodes each value as a limb `digit = v + 2^nibble_bits` and concatenates\n/// limbs in base `radix = 2^(nibble_bits + 4)` (one extra nibble of headroom).\n/// - Packs up to `group` limbs per carrier (fits within BN254 254-bit capacity).\n/// - Pads the last, partial carrier with `digit = 2^nibble_bits` to keep a stable layout.\n///\n/// ## Determinism & order\n/// - Processes values in increasing index order and emits carriers in chunk order\n/// (`chunk = 0..num_chunks`). Padding is deterministic.\n///\n/// ## Generics\n/// - `A`: number of input values.\n/// - `BIT`: per-value bit bound; rounded up to `nibble_bits` by `packing_layout`.\n///\n/// ## Preconditions / Notes\n/// - Call with the raw coefficients whose magnitudes already satisfy the BIT bound\n/// (as enforced by the upstream range checks); `pack` performs the signed -> unsigned\n/// shift internally via `v + base`.\n/// - `group >= 1` is enforced by `packing_layout::<BIT>()`.\n/// - Padding with `digit = 2^nibble_bits` encodes `zero limb` consistently.\n///\n/// ## Returns\n/// - A `[Field]` vector where each element is a concatenation of up to `group` limbs,\n/// suitable for hashing or transcript I/O.\npub fn pack<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n // Layout parameters: nibble-aligned width and limbs-per-carrier group size.\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field); // 2^nibble_bits\n let radix = 2.pow_32((nibble_bits + 4) as Field); // 2^(nibble_bits + 4)\n\n // Number of chunks to emit: ceil(A / group).\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n // Process in fixed-size chunks of `group` limbs.\n for chunk in 0..num_chunks {\n // How many real values go into this chunk.\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n // Build field element accumulator (big-endian concatenation in `radix`).\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n acc = acc * radix + (v + base);\n }\n\n // Pad remaining limb slots with the canonical zero-limb `digit = base`.\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Computes a cryptographic hash using the SAFE (Sponge API for Field Elements) protocol.\n///\n/// This is a convenience wrapper around the SAFE sponge API that handles the full\n/// lifecycle: initialization, absorption, squeezing, and finalization. It's designed\n/// for use in Fiat-Shamir challenge generation and commitment schemes within zero-knowledge circuits.\n///\n/// # Arguments\n/// * `domain_separator` - A 64-byte domain separator used to differentiate between\n/// different protocol instances and prevent cross-protocol attacks.\n/// * `inputs` - Vector of field elements to be absorbed into the sponge.\n/// * `io_pattern` - A 2-element array encoding the I/O pattern:\n/// - `io_pattern[0]`: Encoded ABSORB operation (MSB=1, lower 31 bits = length)\n/// - `io_pattern[1]`: Encoded SQUEEZE operation (MSB=0, lower 31 bits = length)\n///\n/// # Returns\n/// A vector of field elements squeezed from the sponge, with length determined by\n/// the SQUEEZE operation in the IO pattern.\n/// Proves `0 <= v < upper` using two one-sided bit-size checks.\n///\n/// `assert_max_bit_size` is one-sided: it forces a small **non-negative** field element. So bounding\n/// `v` gives `v >= 0`, and bounding `upper - 1 - v` gives `v <= upper - 1`. The pair composes into a\n/// two-sided interval without a comparison operator.\n///\n/// `BIT` must cover `upper`; the caller supplies it from the generated config.\npub fn assert_in_range<let BIT: u32>(v: Field, upper: Field) {\n v.assert_max_bit_size::<BIT>();\n (upper - 1 - v).assert_max_bit_size::<BIT>();\n}\n\npub fn compute_safe(domain_separator: [u8; 64], inputs: [Field], io_pattern: [u32; 2]) -> [Field] {\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(inputs);\n let digests = sponge.squeeze();\n sponge.finish();\n\n digests\n}\n\n#[test]\nfn test_flatten() {\n // Create test polynomials\n let poly1 = Polynomial::new([1, 2, 3]); // degree 2\n let poly2 = Polynomial::new([4, -16, 6]); // degree 2\n let poly3 = Polynomial::new([-7, 8, 9]); // degree 2\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 4>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n assert(result[0] == 0x11121310101010101010101010101010101010101010101010101010101010);\n assert(result[1] == 0x14001610101010101010101010101010101010101010101010101010101010); // -16 became 00 at 0x 14 00 16,\n assert(result[2] == 0x09181910101010101010101010101010101010101010101010101010101010); // -7 became 09 at 0x 09 18 19(16 - 7 = 9)\n}\n\n#[test]\nfn test_flatten_big() {\n // Create test polynomials\n let poly1 = Polynomial::new([\n 1791218451968394,\n 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n 5430119342984413,\n 704811298945172,\n 8901715723925099,\n 21888242871839275222246405745257275088548364400416034343698203098124042812559,\n 21888242871839275222246405745257275088548364400416034343698200215091693880034,\n ]);\n let poly2 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698200314078269634250,\n 21888242871839275222246405745257275088548364400416034343698200967285641915872,\n 2909990636858607,\n 7896103832076587,\n 2078397209533893,\n 21888242871839275222246405745257275088548364400416034343698199792421452734531,\n 614400389245817,\n 8290314119277588,\n ]);\n let poly3 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698201373175279892906,\n 21888242871839275222246405745257275088548364400416034343698201087241869723721,\n 6768789983786188,\n 635797784303388,\n 7610153424227556,\n 4633893206538324,\n 2016269760615332,\n 21888242871839275222246405745257275088548364400416034343698201007080554428142,\n ]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 54>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n\n // For the first index of result operation goes like this,\n\n // First four index of poly1\n // 1791218451968394,\n // 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n // 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n // 5430119342984413,\n\n // base + 1791218451968394 = 0x1065d1a8b8b718a\n // base - 5921327228407753 = 0xeaf69591f3b037 (negative coefficient shifted)\n // base - 3644467483862151 = 0xf30d604a3a9b79 (negative coefficient shifted)\n // base + 5430119342984413 = 0x1134aaa2e86ccdd\n assert(result[0] == 0x1065d1a8b8b718a0eaf69591f3b0370f30d604a3a9b791134aaa2e86ccdd);\n assert(result[1] == 0x1028105ab1b789411fa010339db66b0fc220f1326bc8e0f1e3f4cc1e02e1);\n assert(result[2] == 0x0f23dfbe7cd76c90f4901299312ddf10a569efe35acef11c0d76f005412b);\n assert(result[3] == 0x107624a8f605dc50f0638a368960421022ecb3cf36b7911d73ff2c27ec14);\n assert(result[4] == 0x0f6013a24e1b9a90f4fd2c158a08481180c2dba8af4cc10242413515171c);\n assert(result[5] == 0x11b0964eb898ce411076805680b85410729c962da53a40f4b44412d0f6ed);\n}\n\n#[test]\nfn test_flatten_small() {\n // Create test polynomials\n let poly1 = Polynomial::new([712345, 104857, 999999, 500001, 123, 654321, 77]);\n let poly2 = Polynomial::new([1, 524287, 888888, 23456, 34567, 765432, 0]);\n let poly3 = Polynomial::new([444444, 333333, 222222, 111111, 987654, 246810, 13579]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 20>(inputs, polynomials);\n\n assert(result[0] == 0x1ade991199991f423f17a12110007b19fbf110004d100000100000100000);\n assert(result[1] == 0x10000117ffff1d9038105ba01087071badf8100000100000100000100000);\n assert(result[2] == 0x16c81c15161513640e11b2071f120613c41a10350b100000100000100000);\n}\n\n#[test]\nfn test_safe_hashing_with_safe_helper() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let digests1 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests1.len() == 1);\n assert(digests1[0] != 0);\n\n // Test determinism\n let digests2 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests2.len() == 1);\n assert(digests2[0] != 0);\n assert(digests2[0] == digests1[0]);\n}\n\n#[test]\nfn test_pack() {\n // Test pack function directly with small values\n let values = [1, 2, 3, 4];\n let packed = pack::<4, 4>(values);\n\n // With BIT=4, nibble_bits=4, group should be floor(254/(4+4)) = 31\n // So all 4 values should fit in one carrier\n assert(packed.len() >= 1);\n\n // Test with negative values\n let values_neg = [-1, 2, -3, 4];\n let packed_neg = pack::<4, 4>(values_neg);\n assert(packed_neg.len() >= 1);\n}\n\n#[test]\nfn test_pack_single_value() {\n // Test packing a single value\n let values = [42];\n let packed = pack::<1, 8>(values);\n assert(packed.len() == 1);\n assert(packed[0] != 0);\n}\n\n#[test]\nfn test_pack_determinism() {\n // Test that packing is deterministic\n let values = [10, 20, 30];\n let packed1 = pack::<3, 8>(values);\n let packed2 = pack::<3, 8>(values);\n\n assert(packed1.len() == packed2.len());\n for i in 0..packed1.len() {\n assert(packed1[i] == packed2[i]);\n }\n}\n\n/// `pack_checked` must agree with `pack` for in-slot values. The two carry duplicate layout\n/// arithmetic, so if one is changed without the other, a commitment created by a circuit on `pack`\n/// stops matching one opened by a circuit on `pack_checked`. This pins that.\n#[test]\nfn test_pack_checked_matches_pack() {\n // Mixed signs, and values at the edge of the centred window for BIT = 35 (slot = 2^40).\n let vals: [Field; 7] = [0, 1, -1, 34359701504, -34359701504, 12345, -6789];\n let plain = pack::<7, 35>(vals);\n let checked = pack_checked::<7, 35>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n\n/// Same, at a width whose chunking leaves a partial final carrier (BIT = 57 -> group 3, 7 values).\n#[test]\nfn test_pack_checked_matches_pack_partial_chunk() {\n let vals: [Field; 7] = [5, -5, 72057594049265664, -72057594049265664, 1, 0, -1];\n let plain = pack::<7, 57>(vals);\n let checked = pack_checked::<7, 57>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n", path: "/Users/ctrlc03/Documents/zk/interfold/circuits/lib/src/math/helpers.nr", function_locations: [{ start: 1374, name: "packing_layout" }, { start: 3703, name: "pack_checked" }, { start: 4808, name: "flatten_checked" }, { start: 6096, name: "flatten" }, { start: 7946, name: "pack" }, { start: 10581, name: "assert_in_range" }, { start: 10770, name: "compute_safe" }, { start: 10968, name: "test_flatten" }, { start: 11911, name: "test_flatten_big" }, { start: 14812, name: "test_flatten_small" }, { start: 15639, name: "test_safe_hashing_with_safe_helper" }, { start: 16485, name: "test_pack" }, { start: 16955, name: "test_pack_single_value" }, { start: 17151, name: "test_pack_determinism" }, { start: 17749, name: "test_pack_checked_matches_pack" }, { start: 18287, name: "test_pack_checked_matches_pack_partial_chunk" }] }, "98": { 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;\nuse poseidon::poseidon2_permutation;\n\n/// SAFE (Sponge API for Field Elements)\n///\n/// This module provides a complete implementation of the SAFE API in Noir as defined in:\n/// "SAFE (Sponge API for Field Elements) - A Toolbox for ZK Hash Applications"\n/// see https://hackmd.io/bHgsH6mMStCVibM_wYvb2w#22-Sponge-state for more details.\n///\n/// SAFE provides a unified interface for cryptographic sponge functions that can be\n/// instantiated with various permutations to create hash functions, MACs, authenticated\n/// encryption schemes, and other cryptographic primitives for ZK proof systems.\n///\n/// This implementation follows the SAFE specification exactly, providing:\n/// - Complete API: START, ABSORB, SQUEEZE, FINISH operations.\n/// - Full security: Domain separation, tag computation, IO pattern validation.\n/// - Poseidon2 integration: Field-friendly permutation for ZK systems.\n/// - Specification compliance: All operations follow SAFE spec 2.4 exactly.\n/// - Natural API design: Variable-length inputs, automatic length detection from IO patterns.\n///\n/// # API Design\n///\n/// The API is designed for natural usage while maintaining type safety:\n/// - `absorb(input: [Field])`: Accepts variable-length arrays, no padding required.\n/// - `squeeze()`: Returns a vector with field element(s).\n/// - IO patterns automatically determine operation lengths for validation.\n\n/// Rate parameter for the sponge construction (number of field elements that can be absorbed per permutation call).\nglobal RATE: u32 = 3;\n\n/// Capacity parameter for the sponge construction (security parameter, typically 1-2 field elements).\nglobal CAPACITY: u32 = 1;\n\n/// Total state size (rate + capacity) in field elements.\nglobal STATE_SIZE: u32 = RATE + CAPACITY;\n\n/// IO Pattern encoding constants (from SAFE spec 2.3).\n///\n/// These constants are used for encoding operation types in the 32-bit word format:\n/// - MSB set to 1 for ABSORB operations\n/// - MSB set to 0 for SQUEEZE operations\n\n/// Flag for ABSORB operations (MSB = 1)\nglobal ABSORB_FLAG: u32 = 0x80000000;\n\n/// Flag for SQUEEZE operations (MSB = 0)\nglobal SQUEEZE_FLAG: u32 = 0x00000000;\n\n/// SAFE Sponge State (following spec 2.2)\n///\n/// The sponge state consists of the permutation state, tag, position counters,\n/// and IO pattern tracking as defined in the SAFE specification.\n///\n/// # Generic Parameters\n/// - `L`: The length of the IO pattern array\n///\n/// # Fields\n/// - `state`: Permutation state V in F^n (rate + capacity elements)\n/// - `tag`: Parameter tag T used for instance differentiation\n/// - `absorb_pos`: Current absorb position (<= n-c)\n/// - `squeeze_pos`: Current squeeze position (<= n-c)\n/// - `io_pattern`: Expected IO pattern for validation (encoded 32-bit words)\n/// - `io_count`: Current operation count for pattern tracking\npub struct SafeSponge<let L: u32> {\n /// Permutation state V in F^n (rate + capacity elements).\n state: [Field; STATE_SIZE],\n /// Parameter tag T used for instance differentiation.\n tag: Field,\n /// Current absorb position (<= n-c).\n absorb_pos: u32,\n /// Current squeeze position (<= n-c).\n squeeze_pos: u32,\n /// Expected IO pattern for validation.\n io_pattern: [u32; L],\n /// Current operation count for pattern tracking (spec 2.4: io_count).\n io_count: u32,\n}\n\nimpl<let L: u32> SafeSponge<L> {\n /// Initializes a new SAFE sponge instance with the given IO pattern and domain separator (following spec 2.4).\n ///\n /// # Arguments\n /// - `io_pattern`: Array of 32-bit encoded operations defining the expected sequence of ABSORB/SQUEEZE calls.\n /// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n /// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n ///\n /// # Returns\n /// A new `SafeSponge` instance with initialized state\n pub fn start(io_pattern: [u32; L], domain_separator: [u8; 64]) -> SafeSponge<L> {\n // Compute tag from IO pattern and domain separator (spec 2.3).\n let tag = compute_tag(io_pattern, domain_separator);\n\n let mut state = [0; STATE_SIZE];\n // Initialize capacity with tag (spec 2.4).\n // Add T to the first 128 bits of the state.\n state[0] = tag;\n\n SafeSponge { state, tag, absorb_pos: 0, squeeze_pos: 0, io_pattern, io_count: 0 }\n }\n\n /// Absorbs field elements into the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to absorb is automatically validated against the IO pattern.\n /// This method accepts variable-length arrays, making it natural to use without padding.\n ///\n /// # Arguments\n /// - `input`: Array of field elements to absorb (variable length, must match IO pattern)\n pub fn absorb(&mut self, input: [Field]) {\n let length = input.len() as u32;\n\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_absorb = (expected_encoded_word & ABSORB_FLAG) != 0;\n let expected_length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type and length\n assert(is_expected_absorb, "Expected ABSORB operation");\n assert(expected_length == length, "Length mismatch");\n\n // Process each element naturally (no unnecessary iterations).\n for i in 0..length {\n // If absorb_pos == (n-c) then permute and reset (spec 2.4).\n if self.absorb_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.absorb_pos = 0;\n }\n\n // Add X[i] to state at absorb_pos (spec 2.4).\n // Note: absorb_pos is the rate position, not capacity position.\n self.state[self.absorb_pos + CAPACITY] =\n self.state[self.absorb_pos + CAPACITY] + input[i];\n self.absorb_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = ABSORB_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n\n // Force permute at start of next SQUEEZE (spec 2.4).\n self.squeeze_pos = RATE;\n }\n\n /// Extracts field elements from the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to squeeze is automatically determined from the IO pattern.\n pub fn squeeze(&mut self) -> [Field] {\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_squeeze = (expected_encoded_word & ABSORB_FLAG) == 0;\n let length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type\n assert(is_expected_squeeze, "Expected SQUEEZE operation");\n\n let mut output: [Field] = [].as_vector();\n\n // SQUEEZE implementation following spec 2.4.\n // If length==0, loop won\'t execute (spec 2.4).\n for _ in 0..length {\n // If squeeze_pos==(n-c) then permute and reset (spec 2.4).\n if self.squeeze_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.squeeze_pos = 0;\n self.absorb_pos = 0;\n }\n // Set Y[i] to state element at squeeze_pos (spec 2.4).\n output = output.push_back(self.state[self.squeeze_pos + CAPACITY]);\n self.squeeze_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = SQUEEZE_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n output\n }\n\n /// Finalizes the sponge instance, verifying that all expected operations have been performed and clearing the internal state for security (following spec 2.4).\n ///\n /// This function is used to ensure that the sponge instance has been used correctly and to prevent information leakage.\n pub fn finish(&mut self) {\n // Check that io_count equals the length of the IO pattern expected (spec 2.4).\n assert(self.io_count == L, "IO pattern not completed");\n\n // Erase the state and its variables (spec 2.4).\n self.state = [0; STATE_SIZE];\n self.absorb_pos = 0;\n self.squeeze_pos = 0;\n self.io_count = 0;\n }\n\n /// Permute the state using Poseidon2 (following spec 2.4).\n ///\n /// Applies the Poseidon2 permutation to the current state.\n /// This is the core cryptographic primitive of the sponge construction.\n ///\n /// # Returns\n /// New state after permutation\n fn permute(self) -> [Field; STATE_SIZE] {\n poseidon2_permutation(self.state)\n }\n}\n\n/// Computes a unique tag for a sponge instance based on its IO pattern and domain separator.\n/// The tag is used to ensure that distinct instances behave like distinct functions.\n///\n/// # Arguments\n/// - `io_pattern`: Array of 32-bit encoded operations defining the sponge\'s usage pattern.\n/// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n/// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n///\n/// # Returns\n/// A field element representing the 128-bit tag.\npub fn compute_tag<let L: u32>(io_pattern: [u32; L], domain_separator: [u8; 64]) -> Field {\n // Step 1: Parse and aggregate consecutive operations of the same type\n let mut encoded_words = [0; L]; // Support up to L operations.\n let mut word_count = 0;\n let mut current_absorb_sum = 0;\n let mut current_squeeze_sum = 0;\n let mut last_was_absorb = false;\n\n for i in 0..L {\n if io_pattern[i] > 0 {\n // Parse operation type from MSB and length from lower 31 bits\n let is_absorb = (io_pattern[i] & ABSORB_FLAG) != 0;\n let length = io_pattern[i] & 0x7FFFFFFF; // Clear MSB to get length\n\n if is_absorb {\n if last_was_absorb {\n // Aggregate consecutive ABSORB operations\n current_absorb_sum += length;\n } else {\n // Start new ABSORB sequence\n if current_squeeze_sum > 0 {\n // Flush previous SQUEEZE sequence\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n current_squeeze_sum = 0;\n }\n current_absorb_sum = length;\n }\n last_was_absorb = true;\n } else {\n if !last_was_absorb {\n // Aggregate consecutive SQUEEZE operations\n current_squeeze_sum += length;\n } else {\n // Start new SQUEEZE sequence\n if current_absorb_sum > 0 {\n // Flush previous ABSORB sequence\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n current_absorb_sum = 0;\n }\n current_squeeze_sum = length;\n }\n last_was_absorb = false;\n }\n }\n }\n\n // Flush remaining operations\n if current_absorb_sum > 0 {\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n }\n if current_squeeze_sum > 0 {\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n }\n\n // Step 2: Serialize to byte string and append domain separator (following SAFE spec 2.3).\n // Buffer is 256 bytes: max 192 bytes for IO pattern (48 words) + 64 bytes for domain separator.\n // Note: We must use a fixed-size array because Noir\'s keccak256 requires [u8; N], not [u8].\n let max_io_pattern_bytes: u32 = 192; // 256 - 64 (domain separator)\n let io_pattern_bytes = word_count * 4;\n assert(\n io_pattern_bytes <= max_io_pattern_bytes,\n "IO pattern too large: max 48 aggregated words supported",\n );\n\n let mut input_bytes = [0u8; 256];\n let mut byte_count: u32 = 0;\n\n // Serialize encoded words to bytes (big-endian as per SAFE spec).\n // Note: Noir requires compile-time loop bounds, so we iterate over L (the array size)\n // instead of word_count (runtime value). The condition `i < word_count` ensures we only\n // process valid encoded words. This is safe because word_count <= L always holds\n // (we can have at most L encoded words from L input operations).\n for i in 0..L {\n if i < word_count {\n let word = encoded_words[i];\n input_bytes[byte_count] = (word >> 24) as u8;\n input_bytes[byte_count + 1] = (word >> 16) as u8;\n input_bytes[byte_count + 2] = (word >> 8) as u8;\n input_bytes[byte_count + 3] = word as u8;\n byte_count += 4;\n }\n }\n\n // Append full 64-byte domain separator.\n for i in 0..64 {\n input_bytes[byte_count] = domain_separator[i];\n byte_count += 1;\n }\n\n // Step 3: Hash with Keccak-256 and truncate to 128 bits.\n // Note: The SAFE spec uses SHA3-256, but we use Keccak-256 for Noir compatibility.\n // Keccak-256 differs from SHA3-256 in padding, but both provide equivalent security.\n let hash_bytes = keccak256(input_bytes, byte_count);\n\n // Convert first 128 bits (16 bytes) to field element.\n let mut tag_value: Field = 0;\n for i in 0..16 {\n tag_value = tag_value * 256 + (hash_bytes[i] as Field);\n }\n\n tag_value\n}\n\n#[test]\nfn test_safe_hashing() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_merkle_node() {\n // Verifies SAFE can be used for Merkle tree node hashing with pattern ABSORB(1) + ABSORB(1) + SQUEEZE(1).\n // Tests the ability to absorb multiple inputs before squeezing output.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let left = [123].as_vector();\n let right = [456].as_vector();\n\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(left);\n sponge.absorb(right);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(left);\n sponge2.absorb(right);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_commitment_scheme() {\n // Verifies SAFE can be used for commitment schemes with pattern ABSORB(3) + SQUEEZE(1).\n // Tests the ability to create deterministic commitments from multiple field elements.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let values = [10, 20, 30].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(values);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(values);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_domain_separation() {\n // Verifies that different domain separators produce different outputs for the same input.\n // This is crucial for cross-protocol security and preventing collisions between different applications.\n let elements = [1, 2, 3].as_vector();\n let domain1 = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let domain2 = [\n 0x41, 0x42, 0x43, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n\n let mut sponge1 = SafeSponge::start(io_pattern, domain1);\n sponge1.absorb(elements);\n let output1 = sponge1.squeeze();\n sponge1.finish();\n\n let mut sponge2 = SafeSponge::start(io_pattern, domain2);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output1.len() == 1);\n assert(output2.len() == 1);\n assert(output1[0] != output2[0]); // Different domain separators should produce different outputs\n}\n\n#[test]\nfn test_multiple_squeeze() {\n // Verifies that multiple field elements can be squeezed in a single operation.\n // Tests pattern ABSORB(3) + SQUEEZE(2) to ensure proper state management.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(2)\n let io_pattern = [0x80000003, 0x00000002];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 2);\n assert(output[0] != 0);\n assert(output[1] != 0);\n assert(output[0] != output[1]); // Different squeeze outputs should be different\n}\n\n#[test]\nfn test_zero_length_operations() {\n // Verifies that zero-length ABSORB and SQUEEZE operations are handled correctly.\n // Tests pattern ABSORB(0) + SQUEEZE(1) to ensure proper state transitions.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(0), SQUEEZE(1)\n let io_pattern = [0x80000000, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb([].as_vector());\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n}\n\n#[test]\nfn test_tag_computation() {\n // Verifies the tag computation algorithm using the example from the SAFE specification.\n // Pattern: ABSORB(3), ABSORB(3), SQUEEZE(3)\n // Should aggregate to: ABSORB(6), SQUEEZE(3)\n // Encoded as: [0x80000006, 0x00000003]\n // Tests determinism and pattern differentiation.\n\n let io_pattern = [0x80000003, 0x80000003, 0x00000003];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test determinism\n let tag2 = compute_tag(io_pattern, domain_separator);\n assert(tag == tag2);\n\n // Test that different patterns produce different tags\n let io_pattern2 = [0x80000003, 0x00000003]; // ABSORB(3), SQUEEZE(3) - different pattern\n let tag3 = compute_tag(io_pattern2, domain_separator);\n assert(tag != tag3);\n}\n\n#[test]\nfn test_tag_computation_debug() {\n println("=== SAFE Tag Computation Debug Test ===");\n\n // Test your specific pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))\n let io_pattern = [0x80000002, 0x00000002, 0x80000002];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n println(f"Testing pattern: {io_pattern}");\n println(\n f"Expected to aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(2)",\n );\n println(\n f"Expected encoded words: [0x80000002, 0x00000002, 0x80000002]",\n );\n println("");\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n println(f"=== Expected Rust Output ===");\n println("Pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))");\n println("Domain separator: 0x41424344...");\n println("Tag: 0xce3bb9ee4b2d41c42e9cdda38afe8b6a");\n println("");\n\n println(f"=== Noir Output ===");\n println(f"Tag: {tag}");\n println("");\n\n println("Compare the tag values above with Rust script!");\n}\n\n#[test]\nfn test_consecutive_absorb_aggregation() {\n // Test that consecutive ABSORB operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1) should aggregate to ABSORB(2), SQUEEZE(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(1) = [0x80000002, 0x00000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(2), SQUEEZE(1)\n let aggregated_pattern = [0x80000002, 0x00000001];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Consecutive ABSORB operations should aggregate to the same tag");\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive ABSORB operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive ABSORB Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001] (ABSORB(1), ABSORB(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000002, 0x00000001] (ABSORB(2), SQUEEZE(1))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_consecutive_squeeze_aggregation() {\n // Test that consecutive SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1) should aggregate to ABSORB(1), SQUEEZE(2)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x00000001, 0x00000001];\n\n // This should aggregate to: ABSORB(1), SQUEEZE(2) = [0x80000001, 0x00000002]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(1), SQUEEZE(2)\n let aggregated_pattern = [0x80000001, 0x00000002];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(\n tag == aggregated_tag,\n "Consecutive SQUEEZE operations should aggregate to the same tag",\n );\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive SQUEEZE operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive SQUEEZE Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x00000001, 0x00000001] (ABSORB(1), SQUEEZE(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000001, 0x00000002] (ABSORB(1), SQUEEZE(2))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_mixed_consecutive_aggregation() {\n // Test that both consecutive ABSORB and SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n // Should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1) = [0x80000002, 0x00000002, 0x80000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag\n let aggregated_pattern = [0x80000002, 0x00000002, 0x80000001]; // ABSORB(2), SQUEEZE(2), ABSORB(1)\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Mixed consecutive operations should aggregate to the same tag");\n\n println("=== Mixed Consecutive Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001]",\n );\n println(\n f" (ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1))",\n );\n println(f"Aggregated pattern: [0x80000002, 0x00000002, 0x80000001]");\n println(f" (ABSORB(2), SQUEEZE(2), ABSORB(1))");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n}\n\n#[test]\nfn test_large_io_pattern() {\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Create pattern with 48 alternating ABSORB(1) and SQUEEZE(1) operations\n // This is the maximum supported (48 words * 4 bytes = 192 bytes, leaving 64 for domain separator)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1; // ABSORB(1)\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1; // SQUEEZE(1)\n }\n }\n\n let tag = compute_tag(io_pattern, domain_separator);\n assert(tag != 0);\n}\n\n#[test]\nfn test_domain_separator_not_truncated() {\n // This test verifies that the domain separator is always included in the tag computation,\n // even for large IO patterns. If the domain separator were truncated, different domain\n // separators would produce the same tag for large patterns.\n\n let domain_separator_a = [\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41,\n ]; // All \'A\'s\n\n let domain_separator_b = [\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42,\n ]; // All \'B\'s\n\n // Create pattern with 48 alternating operations (max supported: 192 bytes of IO pattern)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1;\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1;\n }\n }\n\n let tag_a = compute_tag(io_pattern, domain_separator_a);\n let tag_b = compute_tag(io_pattern, domain_separator_b);\n\n // Tags MUST be different because domain separators are different.\n // If they were the same, it would mean the domain separator was truncated/ignored.\n assert(tag_a != tag_b, "Domain separator must affect tag even for large IO patterns");\n}\n', path: "/Users/ctrlc03/Documents/zk/interfold/circuits/lib/src/math/safe.nr", function_locations: [{ start: 4164, name: "SafeSponge<L>::start" }, { start: 5046, name: "SafeSponge<L>::absorb" }, { start: 6826, name: "SafeSponge<L>::squeeze" }, { start: 8494, name: "SafeSponge<L>::finish" }, { start: 9156, name: "SafeSponge<L>::permute" }, { start: 9830, name: "compute_tag" }, { start: 14128, name: "test_safe_hashing" }, { start: 15104, name: "test_merkle_node" }, { start: 16281, name: "test_commitment_scheme" }, { start: 17357, name: "test_domain_separation" }, { start: 18710, name: "test_multiple_squeeze" }, { start: 19639, name: "test_zero_length_operations" }, { start: 20415, name: "test_tag_computation" }, { start: 21477, name: "test_tag_computation_debug" }, { start: 22681, name: "test_consecutive_absorb_aggregation" }, { start: 24750, name: "test_consecutive_squeeze_aggregation" }, { start: 26760, name: "test_mixed_consecutive_aggregation" }, { start: 28520, name: "test_large_io_pattern" }, { start: 29333, name: "test_domain_separator_not_truncated" }] } } };
|
|
617
|
+
var crisp_onchain_fold_default = { noir_version: "1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905", hash: "1975921380779995857", abi: { parameters: [{ name: "user_data_encryption_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_public_inputs", type: { kind: "array", length: 5, type: { kind: "field" } }, visibility: "private" }, { name: "user_data_encryption_key_hash", type: { kind: "field" }, visibility: "private" }, { name: "crisp_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "crisp_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "crisp_key_hash", type: { kind: "field" }, visibility: "private" }, { name: "prev_ct_commitment", type: { kind: "field" }, visibility: "public" }, { name: "digest_hi", type: { kind: "field" }, visibility: "public" }, { name: "digest_lo", type: { kind: "field" }, visibility: "public" }, { name: "slot_address", type: { kind: "field" }, visibility: "public" }, { name: "voting_power", type: { kind: "field" }, visibility: "public" }, { name: "is_first_vote", type: { kind: "boolean" }, visibility: "public" }, { name: "num_options", type: { kind: "integer", sign: "unsigned", width: 32 }, visibility: "public" }, { name: "final_ct_commitment", type: { kind: "field" }, visibility: "public" }, { name: "ct_commitment", type: { kind: "field" }, visibility: "private" }, { name: "k1_commitment", type: { kind: "field" }, visibility: "private" }], return_type: { abi_type: { kind: "field" }, visibility: "public" }, error_types: {} }, bytecode: "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", debug_symbols: "tZZRjqMwDIbvkmceYiex47nKalXRlo6QEK2YMtKqmrtv6JBQVgrqpponAyGf7N+O45s6Nvvxfdf2p/OHevt1U/uh7br2fdedD/W1Pffh6+2rUvF1dx2aJnxSD+th16Uemv6q3vqx6yr1WXfj/aePS93f7bUewqquVNMfgw3AU9s109NXtezW+a1AjPNu8CQJ4HBFgC2CSwQWsxDMioB5gjHsZ4KxBImA+GwUCM7MBAQ2uSi2dPDsIsFozBHcyzrQj+pgbdLBel2gA1qQhQAlBMKoA5KTEoITnwhUFAU7ioRVLp4n+EUH76iEIJiiEGezJ8tulRRJqiliswTieQ3ZqEtmif2B/YOa/yI2ChO0t1HP8PyQVC9rCOchBDoqSmA5IcQ/jXBeYnk70S6LkDxCgqYzQkAkh8CNxBpJx9RqWLwA/g8tkheE2pZosUJAXovttHpOaRUNubTiRo0y2ViiTC4bCrrXc0Iv52QzEJ/uEPaCOS82TzxHOY22JX3LaJcIAL6o6zAthKLuK6QTAUs6nwFjEoHXOvwOb/WhHVZzkMLwZ6VMUL5SNkheKRdyF4YOmi70SvG38VM7r5R8G9CTs8HCbHG2ZhIyWDtbN9sAM1NeP+uhrfddM49gp7E/PExk1z+XuBJntstwPjTHcWgmr+9rIY6/", file_map: { "17": { 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;\nuse crate::static_assert;\n\n/// The size of the state accepted by the backend in `poseidon2_permutation`.\nglobal POSEIDON2_CONFIG_STATE_SIZE: u32 = poseidon2_config_state_size();\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\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s<let N: u32>(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n// docs:start:blake3\npub fn blake3<let N: u32>(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<let N: u32>(input: [u8; N]) -> [u8; 32] {}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment<let N: u32>(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<let N: u32>(\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 points[i] = EmbeddedCurveScalar::from_field(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<let N: u32>(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<let N: u32>(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 crate::assert_constant(separator);\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] = EmbeddedCurveScalar::from_field(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, true)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n crate::assert_constant(starting_index);\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\npub fn poseidon2_permutation<let N: u32>(input: [Field; N]) -> [Field; N] {\n static_assert(\n N == POSEIDON2_CONFIG_STATE_SIZE,\n f"the input length must equal the state size in the Poseidon2 config; expected {POSEIDON2_CONFIG_STATE_SIZE}, got {N}",\n );\n poseidon2_permutation_internal(input)\n}\n\n#[foreign(poseidon2_permutation)]\nfn poseidon2_permutation_internal<let N: u32>(input: [Field; N]) -> [Field; N] {}\n\n#[foreign(poseidon2_config_state_size)]\ncomptime fn poseidon2_config_state_size() -> u32 {}\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<H>(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<H>(_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 /// Returns the hash value without consuming the hasher.\n /// Override this for more efficient implementations that avoid copying.\n /// TODO: deprecate finish() and replace it\n fn finish_ref(&self) -> Field {\n (*self).finish()\n }\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<H>;\n\nimpl<H> BuildHasher for BuildHasherDefault<H>\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<H> Default for BuildHasherDefault<H>\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u8 {\n fn hash<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl<T, let N: u32> Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl<T> Hash for [T]\nwhere\n T: Hash,\n{\n fn hash<H>(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<A> Hash for (A,)\nwhere\n A: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n }\n}\n\nimpl<A, B> Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl<A, B, C> Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash<H>(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<A, B, C, D> Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash<H>(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<A, B, C, D, E> 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<H>(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\nimpl<A, B, C, D, E, F> Hash for (A, B, C, D, E, F)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G> Hash for (A, B, C, D, E, F, G)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_> Hash for (A, B, C, D, E, F, G, H_)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I> Hash for (A, B, C, D, E, F, G, H_, I)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J> Hash for (A, B, C, D, E, F, G, H_, I, J)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K> Hash for (A, B, C, D, E, F, G, H_, I, J, K)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K, L> Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n L: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n self.11.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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n );\n}\n', path: "std/hash/mod.nr", function_locations: [{ start: 572, name: "sha256_compression" }, { start: 707, name: "keccakf1600" }, { start: 852, name: "blake2s" }, { start: 950, name: "blake3" }, { start: 1437, name: "__blake3" }, { start: 1555, name: "pedersen_commitment" }, { start: 1784, name: "pedersen_commitment_with_separator" }, { start: 2188, name: "pedersen_hash" }, { start: 2345, name: "pedersen_hash_with_separator" }, { start: 3339, name: "derive_generators" }, { start: 3698, name: "__derive_generators" }, { start: 3776, name: "poseidon2_permutation" }, { start: 4132, name: "poseidon2_permutation_internal" }, { start: 4225, name: "poseidon2_config_state_size" }, { start: 4536, name: "derive_hash" }, { start: 5327, name: "Hasher::finish_ref" }, { start: 5761, name: "<impl BuildHasher for BuildHasherDefault<H>>::build_hasher" }, { start: 5893, name: "<impl Default for BuildHasherDefault<H>>::default" }, { start: 6025, name: "<impl Hash for Field>::hash" }, { start: 6155, name: "<impl Hash for u8>::hash" }, { start: 6295, name: "<impl Hash for u16>::hash" }, { start: 6435, name: "<impl Hash for u32>::hash" }, { start: 6575, name: "<impl Hash for u64>::hash" }, { start: 6716, name: "<impl Hash for u128>::hash" }, { start: 6855, name: "<impl Hash for i8>::hash" }, { start: 7001, name: "<impl Hash for i16>::hash" }, { start: 7148, name: "<impl Hash for i32>::hash" }, { start: 7295, name: "<impl Hash for i64>::hash" }, { start: 7443, name: "<impl Hash for bool>::hash" }, { start: 7590, name: "<impl Hash for ()>::hash" }, { start: 7722, name: "<impl Hash for [T; N]>::hash" }, { start: 7911, name: "<impl Hash for [T]>::hash" }, { start: 8133, name: "<impl Hash for (A,)>::hash" }, { start: 8302, name: "<impl Hash for (A, B)>::hash" }, { start: 8518, name: "<impl Hash for (A, B, C)>::hash" }, { start: 8781, name: "<impl Hash for (A, B, C, D)>::hash" }, { start: 9091, name: "<impl Hash for (A, B, C, D, E)>::hash" }, { start: 9448, name: "<impl Hash for (A, B, C, D, E, F)>::hash" }, { start: 9852, name: "<impl Hash for (A, B, C, D, E, F, G)>::hash" }, { start: 10306, name: "<impl Hash for (A, B, C, D, E, F, G, H_)>::hash" }, { start: 10807, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I)>::hash" }, { start: 11355, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J)>::hash" }, { start: 11950, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K)>::hash" }, { start: 12593, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)>::hash" }, { start: 13187, name: "assert_pedersen" }] }, "22": { source: "pub mod hash;\npub mod aes128;\npub mod array;\npub mod vector;\npub mod ecdsa_secp256k1;\npub mod ecdsa_secp256r1;\npub mod embedded_curve_ops;\npub mod field;\npub mod collections;\npub mod compat;\npub mod convert;\npub mod option;\npub mod string;\npub mod test;\npub mod cmp;\npub mod ops;\npub mod default;\npub mod prelude;\npub mod runtime;\npub mod meta;\npub mod append;\npub mod mem;\npub mod panic;\npub mod hint;\n\nmod integer;\nmod primitive_docs;\nmod internal;\n\n// Oracle calls are required to be wrapped in an unconstrained function\n// Thus, the only argument to the `println` oracle is expected to always be an ident\n#[oracle(print)]\nunconstrained fn print_oracle<T>(with_newline: bool, input: T) {}\n\nunconstrained fn print_unconstrained<T>(with_newline: bool, input: T) {\n print_oracle(with_newline, input);\n}\n\n/// Print the given input to stdout followed by a newline\npub fn println<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(true, input);\n }\n}\n\n/// Print the given input to stdout\npub fn print<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(false, input);\n }\n}\n\n/// Asserts the validity of the provided proof and public inputs against the provided verification key and hash.\n///\n/// The ACVM cannot determine whether the provided proof is valid during execution as this requires knowledge of\n/// the backend against which the program is being proven. However if an invalid proof if submitted, the program may\n/// fail to prove or the backend may generate a proof which will subsequently fail to verify.\n///\n/// # Important Note\n///\n/// If you are not developing your own backend such as [Barretenberg](https://github.com/AztecProtocol/barretenberg)\n/// you probably shouldn't need to interact with this function directly. It's easier and safer to use a verification\n/// library which is published by the developers of the backend which will document or enforce any safety requirements.\n///\n/// If you use this directly, you're liable to introduce underconstrainedness bugs and *your circuit will be insecure*.\n///\n/// # Arguments\n/// - verification_key: The verification key of the circuit to be verified.\n/// - proof: The proof to be verified.\n/// - public_inputs: The public inputs associated with `proof`\n/// - key_hash: The hash of `verification_key` of the form expected by the backend.\n/// - proof_type: An identifier for the proving scheme used to generate the proof to be verified. This allows\n/// for a single backend to support verifying multiple proving schemes.\n///\n/// # Constraining `key_hash`\n///\n/// The Noir compiler does not by itself constrain that `key_hash` is a valid hash of `verification_key`.\n/// This is because different backends may differ in how they hash their verification keys.\n/// It is then the responsibility of either the noir developer (by explicitly hashing the verification key\n/// in the correct manner) or by the proving system itself internally asserting the correctness of `key_hash`.\npub fn verify_proof_with_type<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {\n if !crate::runtime::is_unconstrained() {\n crate::assert_constant(proof_type);\n }\n verify_proof_internal(verification_key, proof, public_inputs, key_hash, proof_type);\n}\n\n#[foreign(recursive_aggregation)]\nfn verify_proof_internal<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {}\n\n/// Asserts that the given value is known at compile-time.\n/// Useful for debugging for-loop bounds.\n#[builtin(assert_constant)]\npub fn assert_constant<T>(x: T) {}\n\n/// Asserts that the given value is both true and known at compile-time.\n/// The message can be a string, a format string, or any value, as long as it is known at compile-time\n#[builtin(static_assert)]\npub fn static_assert<T>(predicate: bool, message: T) {}\n\n/// Force a field value to be a witness instead of a constant in the compiled output.\n/// This is often only useful for debugging compiler optimizations.\n///\n/// This has no effect in unconstrained or comptime code.\n#[builtin(as_witness)]\npub fn as_witness(x: Field) {}\n", path: "std/lib.nr", function_locations: [{ start: 689, name: "print_oracle" }, { start: 763, name: "print_unconstrained" }, { start: 893, name: "println" }, { start: 1076, name: "print" }, { start: 3277, name: "verify_proof_with_type" }, { start: 3694, name: "verify_proof_internal" }, { start: 3859, name: "assert_constant" }, { start: 4118, name: "static_assert" }, { start: 4389, name: "as_witness" }] }, "52": { 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 bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};\nuse interfold_lib::math::commitments::compute_vk_hash;\n\n/// Binds the four inner `vk_hash` witnesses. Regenerate with `pnpm compute:vk-hash` in\n/// `examples/CRISP` after changing ct0 / ct1 / user_data_encryption / crisp_onchain (or the lib\n/// preset). Insecure: `lib::configs::default` uses `insecure::*`; secure: uses `secure::*`.\npub global CRISP_ONCHAIN_FOLD_EXPECTED_KEY_HASH_INSECURE: Field =\n 0x1ba44ab942a8ace27b50f3609b4912adb384f06561133e31b19caa4b97a70145;\npub global CRISP_ONCHAIN_FOLD_EXPECTED_KEY_HASH_SECURE: Field =\n 0x225abc9d92035e910c9bd0deac6060640ad49b30ed6ef4de6d094397010ebdac;\n\nfn main(\n // User Data Encryption Section.\n user_data_encryption_verification_key: UltraHonkVerificationKey,\n user_data_encryption_proof: UltraHonkProof,\n user_data_encryption_public_inputs: [Field; 5], // ct0_key_hash, ct1_key_hash, pk_commitment, ct_commitment, k1_commitment\n user_data_encryption_key_hash: Field,\n // Crisp Section.\n crisp_verification_key: UltraHonkVerificationKey,\n crisp_proof: UltraHonkProof,\n crisp_key_hash: Field,\n prev_ct_commitment: pub Field,\n digest_hi: pub Field,\n digest_lo: pub Field,\n slot_address: pub Field,\n voting_power: pub Field,\n is_first_vote: pub bool,\n num_options: pub u32,\n final_ct_commitment: pub Field,\n ct_commitment: Field,\n k1_commitment: Field,\n) -> pub Field {\n verify_honk_proof_non_zk(\n user_data_encryption_verification_key,\n user_data_encryption_proof,\n user_data_encryption_public_inputs,\n user_data_encryption_key_hash,\n );\n verify_honk_proof_non_zk(\n crisp_verification_key,\n crisp_proof,\n [\n prev_ct_commitment,\n digest_hi,\n digest_lo,\n slot_address,\n voting_power,\n if is_first_vote { 1 } else { 0 },\n num_options as Field,\n final_ct_commitment,\n ct_commitment,\n k1_commitment,\n ],\n crisp_key_hash,\n );\n\n // Verify that the ct_commitment from the crisp proof matches the one computed from user data encryption.\n assert(ct_commitment == user_data_encryption_public_inputs[3]);\n\n // Verify that the k1_commitment from the crisp proof matches the one computed from user data encryption.\n assert(k1_commitment == user_data_encryption_public_inputs[4]);\n\n let vk_hashes = [\n user_data_encryption_key_hash,\n crisp_key_hash,\n user_data_encryption_public_inputs[0], // ct0_key_hash\n user_data_encryption_public_inputs[1], // ct1_key_hash\n ]\n .as_vector();\n\n let chain_key_hash = compute_vk_hash(vk_hashes);\n assert(\n (chain_key_hash == CRISP_ONCHAIN_FOLD_EXPECTED_KEY_HASH_INSECURE)\n | (chain_key_hash == CRISP_ONCHAIN_FOLD_EXPECTED_KEY_HASH_SECURE),\n );\n\n user_data_encryption_public_inputs[2]\n}\n", path: "/Users/ctrlc03/.omp/worktrees/interfold/feat-insecure-64-params/examples/CRISP/circuits/bin/fold_onchain/src/main.nr", function_locations: [{ start: 1666, name: "main" }] }, "53": { source: '// Constants for UltraHonk recursive verifier inputs\npub global PROOF_TYPE_HONK: u32 = 0; // identifier for UltraHonk verfier\npub global RECURSIVE_PROOF_LENGTH: u32 = 410;\npub global ULTRA_VK_LENGTH_IN_FIELDS: u32 = 115;\n\npub type UltraHonkProof = [Field; RECURSIVE_PROOF_LENGTH];\npub type UltraHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\n// Constants for Rollup-UltraHonk recursive verifier inputs (N.B. this is equivalent to UH plus IPA claim and proof)\npub global PROOF_TYPE_ROLLUP_HONK: u32 = 4; // identifier for rollup-UltraHonk verfier\npub global PROOF_TYPE_ROOT_ROLLUP_HONK: u32 = 5; // identifier for root-rollup-UltraHonk verfier (closes the IPA accumulator)\npub global IPA_CLAIM_SIZE: u32 = 6;\npub global IPA_PROOF_LENGTH: u32 = 64;\npub global RECURSIVE_ROLLUP_HONK_PROOF_LENGTH: u32 =\n RECURSIVE_PROOF_LENGTH + IPA_CLAIM_SIZE + IPA_PROOF_LENGTH;\n\npub type RollupHonkProof = [Field; RECURSIVE_ROLLUP_HONK_PROOF_LENGTH];\npub type RollupHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\npub global PROOF_TYPE_HONK_ZK: u32 = 6; // identifier for UltraHonk ZK verfier\npub global RECURSIVE_ZK_PROOF_LENGTH: u32 = 450 + 8;\n\npub type UltraHonkZKProof = [Field; RECURSIVE_ZK_PROOF_LENGTH];\n\n// Verifies a non-zero-knowledge UltraHonk proof.\n//\n// Represents standard UltraHonk recursive verification for proofs that do not hide the witness.\n// Use this only in situations where zero-knowledge is not required.\npub fn verify_honk_proof_non_zk<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof with IPA (Inner Product Argument).\n//\n// This variant includes an IPA claim and proof appended to the standard UltraHonk proof,\n// used to amortize IPA recursive verification costs in rollup circuits.\npub fn verify_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROLLUP_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof and closes the IPA accumulator in-circuit.\n//\n// Use this at the root of a rollup aggregation tree. The inner proof has the same RollupHonk shape\n// as `verify_rolluphonk_proof`, but instead of propagating an accumulated IPA claim out as a public\n// input, this variant performs a full native IPA verification inside the circuit. The outer circuit\n// should therefore be proved as a standard (non-rollup) UltraHonk circuit, producing a proof\n// suitable for verification by `verify_honk_proof` / `verify_honk_proof_non_zk`.\n//\n// When two RollupHonk inputs are verified this way in one circuit, their two IPA claims are first\n// accumulated into one, then the accumulated claim is fully verified.\npub fn verify_root_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROOT_ROLLUP_HONK,\n );\n}\n\n// Verifies a zero-knowledge UltraHonk proof.\n//\n// This verifier is for UltraHonk proofs constructed with zero-knowledge, which hide the witness\n// values from the verifier.\n// Note: We intentionally choose the generic name "verify_honk_proof" for this function, as we\n// want ZK to be the default unless the user explicitly opts out.\npub fn verify_honk_proof<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkZKProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK_ZK,\n );\n}\n', path: "/Users/ctrlc03/nargo/github.com/AztecProtocol/aztec-packages/v5.2.0/barretenberg/noir/bb_proof_verification/src/lib.nr", function_locations: [{ start: 1646, name: "verify_honk_proof_non_zk" }, { start: 2262, name: "verify_rolluphonk_proof" }, { start: 3386, name: "verify_root_rolluphonk_proof" }, { start: 4087, name: "verify_honk_proof" }] }, "87": { 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 crate::math::helpers::{compute_safe, flatten, flatten_checked};\nuse crate::math::polynomial::Polynomial;\n\n/// DOMAIN SEPARATORS\n\n// Domain separator - "PK"\npub global DS_PK: [u8; 64] = [\n 0x50, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_GENERATION"\npub global DS_PK_GENERATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_COMPUTATION"\npub global DS_SHARE_COMPUTATION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_ENCRYPTION"\npub global DS_SHARE_ENCRYPTION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_AGGREGATION"\npub global DS_PK_AGGREGATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CIPHERTEXT"\npub global DS_CIPHERTEXT: [u8; 64] = [\n 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "AGGREGATED_SHARES"\npub global DS_AGGREGATED_SHARES: [u8; 64] = [\n 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45,\n 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "VK_HASH"\npub global DS_VK_HASH: [u8; 64] = [\n 0x56, 0x4b, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "RECURSIVE_AGGREGATION"\npub global DS_RECURSIVE_AGGREGATION: [u8; 64] = [\n 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47,\n 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_PK_GENERATION"\npub global DS_CLG_PK_GENERATION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_ENCRYPTION"\npub global DS_CLG_SHARE_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_USER_DATA_ENCRYPTION"\npub global DS_CLG_USER_DATA_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e,\n 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_DECRYPTION"\npub global DS_CLG_SHARE_DECRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "THRESHOLD_DECRYPTION_SHARE"\npub global DS_THRESHOLD_DECRYPTION_SHARE: [u8; 64] = [\n 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "USER_DATA_ENCRYPTION_COMMITMENT"\npub global DS_USER_DATA_ENCRYPTION_COMMITMENT: [u8; 64] = [\n 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n/// WRAPPERS\n\npub fn compute_commitment(inputs: [Field], domain_separator: [u8; 64]) -> Field {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 1])[0]\n}\n\npub fn compute_single_polynomial_commitment<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_single_polynomial_commitment`] with the digit asserts, for a polynomial this circuit\n/// carries no bound for. ct1 commits `u` this way: ct0 range-checks the same `u` to `u_bound` and the\n/// `user_data_encryption` fold asserts the two `u` commitments are equal, so making ct1\'s packing\n/// injective is what forces its `u` to equal ct0\'s \u2014 the bound transfers instead of being repeated.\n/// Produces the same value as the plain version for any input the plain version could legitimately\n/// have committed.\npub fn compute_single_polynomial_commitment_checked<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_multiple_polynomial_commitment<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_challenge<let L: u32>(inputs: [Field], domain_separator: [u8; 64]) -> [Field] {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 2 * L])\n}\n\npub fn single_polynomial_payload<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, inputs)\n}\n\npub fn single_polynomial_payload_checked<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload_checked<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, inputs)\n}\n\n/// COMMITMENTS\n\npub fn compute_dkg_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let mut payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n payload = multiple_polynomial_payload::<N, L, BIT_PK>(payload, pk1);\n\n compute_commitment(payload, DS_PK)\n}\n\npub fn compute_threshold_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\n/// [`compute_multiple_polynomial_commitment`] for a circuit that **opens** this commitment.\npub fn compute_multiple_polynomial_commitment_checked<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_threshold_pk_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C5). Asserts each packed digit fits its slot, which makes the opening unique and so\n/// forces the opened `pk0` to equal the `pk0` C1 committed. Produces the same value as the plain\n/// helper, so C1 needs no change.\npub fn compute_threshold_pk_commitment_checked<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\npub fn compute_share_computation_sk_commitment<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\n/// [`compute_share_computation_sk_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2a). C1 creates it and bounds `sk` to the trinary range, so making the opening\n/// unique forces the witness to equal that `sk`.\npub fn compute_share_computation_sk_commitment_checked<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_computation_e_sm_commitment<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_encryption_commitment_from_message<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\n/// [`compute_share_encryption_commitment_from_message`] for a circuit that **opens** this\n/// commitment rather than creating it (C4). Asserts each packed digit fits its slot, forcing the\n/// opened share to equal the share C2 committed. Same value as the plain helper, so C2 and C3 need\n/// no change.\npub fn compute_share_encryption_commitment_from_message_checked<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\npub fn compute_aggregated_shares_commitment<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// Commitment to a threshold decryption share: all CRT limbs, first `K` coefficients per limb\n/// (same layout as `Polynomial<K>` in decrypted_shares_aggregation).\n///\n/// `NATIVE_BIT_WIDTH` must cover native coefficients in \\([0, q_l)\\) per limb (not centered `d` bounds).\npub fn compute_threshold_decryption_share_commitment<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload =\n multiple_polynomial_payload::<K, L, NATIVE_BIT_WIDTH>([].as_vector(), d_share_limbs);\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_threshold_decryption_share_commitment`] for a circuit that **opens** this commitment\n/// rather than creating it (C7). C6 creates it and pins each coefficient to a native residue in\n/// \\([0, q)\\) via `verify_d_native_trunc_binding`, so asserting the digits here makes the opening\n/// unique and forces the opened share to equal the one C6 committed. Produces the same value as the\n/// plain version for any input the plain version could legitimately have committed.\npub fn compute_threshold_decryption_share_commitment_checked<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<K, L, NATIVE_BIT_WIDTH>(\n [].as_vector(),\n d_share_limbs,\n );\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_share_computation_e_sm_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2b). C1 creates it and bounds each `e_sm` limb to a centered residue.\npub fn compute_share_computation_e_sm_commitment_checked<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_pk_aggregation_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let commit_pk0 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk0, DS_PK_AGGREGATION);\n let commit_pk1 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk1, DS_PK_AGGREGATION);\n\n let inputs = [commit_pk0, commit_pk1].as_vector();\n\n compute_commitment(inputs, DS_PK_AGGREGATION)\n}\n\npub fn compute_recursive_aggregation_commitment(payload: [Field]) -> Field {\n compute_commitment(payload, DS_RECURSIVE_AGGREGATION)\n}\n\npub fn compute_vk_hash(vk_hashes: [Field]) -> Field {\n compute_commitment(vk_hashes, DS_VK_HASH)\n}\n\n/// [`compute_aggregated_shares_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). C4 creates it after `normalize_aggregated` has reduced and centered the\n/// aggregate, so making the opening unique forces the witness to equal that canonical value.\npub fn compute_aggregated_shares_commitment_checked<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// [`compute_ciphertext_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). No upstream circuit bounds the ciphertext limbs, so this buys injectivity\n/// rather than canonicality \u2014 which is the property wanted here: it forces C6 to decrypt exactly\n/// the ciphertext that was committed, whatever its representation.\npub fn compute_ciphertext_commitment_checked<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\npub fn compute_ciphertext_commitment<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\n/// COMMITMENTS FOR CHALLENGES\n\npub fn compute_threshold_pk_challenge(payload: [Field]) -> Field {\n compute_commitment(payload, DS_CLG_PK_GENERATION)\n}\n\npub fn compute_share_encryption_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_SHARE_ENCRYPTION)\n}\n\npub fn compute_threshold_share_decryption_challenge<let L: u32>(payload: [Field]) -> Field {\n compute_challenge::<L>(payload, DS_CLG_SHARE_DECRYPTION)[0]\n}\n\npub fn compute_user_data_encryption_ct0_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n\npub fn compute_user_data_encryption_ct1_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n', path: "/Users/ctrlc03/.omp/worktrees/interfold/feat-insecure-64-params/circuits/lib/src/math/commitments.nr", function_locations: [{ start: 7784, name: "compute_commitment" }, { start: 8012, name: "compute_single_polynomial_commitment" }, { start: 8827, name: "compute_single_polynomial_commitment_checked" }, { start: 9138, name: "compute_multiple_polynomial_commitment" }, { start: 9375, name: "compute_challenge" }, { start: 9585, name: "single_polynomial_payload" }, { start: 9784, name: "multiple_polynomial_payload" }, { start: 9970, name: "single_polynomial_payload_checked" }, { start: 10185, name: "multiple_polynomial_payload_checked" }, { start: 10406, name: "compute_dkg_pk_commitment" }, { start: 10733, name: "compute_threshold_pk_commitment" }, { start: 11138, name: "compute_multiple_polynomial_commitment_checked" }, { start: 11750, name: "compute_threshold_pk_commitment_checked" }, { start: 12008, name: "compute_share_computation_sk_commitment" }, { start: 12516, name: "compute_share_computation_sk_commitment_checked" }, { start: 12795, name: "compute_share_computation_e_sm_commitment" }, { start: 13067, name: "compute_share_encryption_commitment_from_message" }, { start: 13647, name: "compute_share_encryption_commitment_from_message_checked" }, { start: 13931, name: "compute_aggregated_shares_commitment" }, { start: 14512, name: "compute_threshold_decryption_share_commitment" }, { start: 15336, name: "compute_threshold_decryption_share_commitment_checked" }, { start: 15876, name: "compute_share_computation_e_sm_commitment_checked" }, { start: 16182, name: "compute_pk_aggregation_commitment" }, { start: 16571, name: "compute_recursive_aggregation_commitment" }, { start: 16686, name: "compute_vk_hash" }, { start: 17169, name: "compute_aggregated_shares_commitment_checked" }, { start: 17843, name: "compute_ciphertext_commitment_checked" }, { start: 18325, name: "compute_ciphertext_commitment" }, { start: 18724, name: "compute_threshold_pk_challenge" }, { start: 18866, name: "compute_share_encryption_challenge" }, { start: 19023, name: "compute_threshold_share_decryption_challenge" }, { start: 19183, name: "compute_user_data_encryption_ct0_challenge" }, { start: 19344, name: "compute_user_data_encryption_ct1_challenge" }] }, "89": { 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\n//! Helper functions for circuit construction and cryptographic operations.\nuse crate::math::polynomial::Polynomial;\nuse crate::math::safe::SafeSponge;\n\n/// Compute hex-aligned packing parameters for a given `BIT`.\n///\n/// # Purpose\n/// Returns `(nibble_bits, group)` for use by pack/flatten so layout stays consistent.\n/// - `nibble_bits`: ceil (`BIT`) to the next multiple of 4 (nibble alignment).\n/// - Examples: `BIT = 7 -> 8`, `BIT = 8 -> 8`, `BIT = 9 -> 12`, `BIT = 10 -> 12`, `BIT = 11 -> 12`,\n/// `BIT=16 -> 16`, `BIT = 17 -> 20`.\n/// - `group`: max number of encoded limbs that fit in one BN254 field element,\n/// when each limb uses an extra 4 bits (see below).\n///\n/// # Rationale\n/// - We align to nibbles so powers of two are hex-friendly and deterministic.\n/// - We reserve one extra nibble (4 bits) per stored value to lift signed\n/// coefficients into the non-negative range (e.g., store `v + 2^nibble_bits`),\n/// which implies a radix of `2^(nibble_bits + 4)`.\n///\n/// # Safety\n/// - Asserts `nibble_bits + 4 <= 254` to avoid mod-p wrap on BN254.\n/// - Ensures at least one limb fits: `group >= 1`.\nfn packing_layout<let BIT: u32>() -> (u32, u32) {\n // Ceil BIT up to the next multiple of 4 (nibble alignment).\n let nibble_bits = ((BIT + 3) / 4) * 4;\n\n // Each stored limb uses an extra nibble because negative coefficients\n // will be shifted to positive, so radix = 2^(nibble_bits+4).\n assert(nibble_bits + 4 <= 254);\n\n // Maximum limbs that fit in one BN254 element without wrap.\n let group = 254 / (nibble_bits + 4);\n // `group == 1` packs one value per carrier: it saves no sponge absorptions at all while still\n // charging the range checks that make packing injective, so it is always a loss. Measured at\n // BIT=143: +82% gates against absorbing directly. Reject it rather than pay for nothing.\n assert(group >= 2);\n (nibble_bits, group)\n}\n\n/// Like [`pack`], but asserts every digit fits its slot.\n///\n/// ## Why\n/// `pack` builds each carrier as a base-`radix` number, `acc = acc * radix + (v + base)`. That\n/// representation is unique only while every digit stays below `radix`. An unbounded value can\n/// overflow its slot and cancel against the next, so one commitment gains a second opening and a\n/// prover can choose which coefficients it opens to.\n///\n/// `digit < radix` is exactly `digit.assert_max_bit_size::<nibble_bits + 4>()`, and because\n/// `assert_max_bit_size` also forces `digit >= 0`, a single assert gives the whole condition.\n///\n/// ## When to use it\n/// Use this where a circuit **opens** a commitment it did not create. The creating circuit's own\n/// range checks already keep its digits in slot, so asserting the same here forces the opened\n/// coefficients to equal the committed ones \u2014 injectivity transfers the upstream bound without\n/// repeating it. One assert costs about half a two-sided `range_check_2bounds`.\n///\n/// It does **not** replace a bound the protocol requires in its own right, and it gives injectivity,\n/// not canonicality: it pins the opening, not a unique representative modulo `q`. A value that no\n/// upstream circuit bounded still needs its own check (see C1 `pk0` and C5 `pk0_agg`).\n///\n/// ## Compatibility\n/// Adding asserts does not change the packed value, so a commitment created with [`pack`] and\n/// opened with `pack_checked` agree. Adoption is per circuit and needs no commitment change.\npub fn pack_checked<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field);\n let radix = 2.pow_32((nibble_bits + 4) as Field);\n\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n for chunk in 0..num_chunks {\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n let digit = v + base;\n // digit < radix = 2^(nibble_bits + 4): the injectivity condition.\n digit.assert_max_bit_size::<((BIT + 3) / 4) * 4 + 4>();\n acc = acc * radix + digit;\n }\n\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Like [`flatten`], but packs with [`pack_checked`]. Use where a circuit opens a commitment it did\n/// not create.\npub fn flatten_checked<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n let packed = pack_checked::<A, BIT>(poly[j].coefficients);\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n inputs\n}\n\n/// Flatten `L` polynomials into a single linear stream of packed `Field` carriers.\n///\n/// ## What this does\n/// - For each CRT limb `j` in `0..L`, it packs the coefficients of `poly[j]`\n/// with `pack::<A, BIT>` and appends all resulting carriers to `inputs`.\n/// - The packing layout (nibble-aligned width and `group` size) is taken from\n/// `packing_layout::<BIT>()` and must match what `pack` uses.\n///\n/// ## Determinism & order\n/// - Preserves a stable order: iterate `j = 0..L`, then for each `j` append\n/// carriers in ascending chunk index `i = 0..num_chunks`.\n/// - This ensures transcripts remain deterministic across runs.\n///\n/// ## Generics\n/// - `A`: polynomial degree (number of coefficients per polynomial).\n/// - `L`: number of CRT bases (polynomials).\n/// - `BIT`: per-coefficient bit bound used by the packing layout (compile-time).\n///\n/// ## Returns\n/// - The same `inputs` vector, extended with all carriers in deterministic order.\npub fn flatten<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n // Pack its A coefficients into `num_chunks` carriers using the same BIT layout.\n let packed = pack::<A, BIT>(poly[j].coefficients);\n\n // Append carriers in-order to `inputs` to keep a stable transcript layout.\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n\n // Return the extended input stream.\n inputs\n}\n\n/// Pack `A` values into a `[Field]` vector of carriers using the shared hex-aligned layout.\n///\n/// ## What this does\n/// - Computes `(nibble_bits, group)` via `packing_layout::<BIT>()`.\n/// - Encodes each value as a limb `digit = v + 2^nibble_bits` and concatenates\n/// limbs in base `radix = 2^(nibble_bits + 4)` (one extra nibble of headroom).\n/// - Packs up to `group` limbs per carrier (fits within BN254 254-bit capacity).\n/// - Pads the last, partial carrier with `digit = 2^nibble_bits` to keep a stable layout.\n///\n/// ## Determinism & order\n/// - Processes values in increasing index order and emits carriers in chunk order\n/// (`chunk = 0..num_chunks`). Padding is deterministic.\n///\n/// ## Generics\n/// - `A`: number of input values.\n/// - `BIT`: per-value bit bound; rounded up to `nibble_bits` by `packing_layout`.\n///\n/// ## Preconditions / Notes\n/// - Call with the raw coefficients whose magnitudes already satisfy the BIT bound\n/// (as enforced by the upstream range checks); `pack` performs the signed -> unsigned\n/// shift internally via `v + base`.\n/// - `group >= 1` is enforced by `packing_layout::<BIT>()`.\n/// - Padding with `digit = 2^nibble_bits` encodes `zero limb` consistently.\n///\n/// ## Returns\n/// - A `[Field]` vector where each element is a concatenation of up to `group` limbs,\n/// suitable for hashing or transcript I/O.\npub fn pack<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n // Layout parameters: nibble-aligned width and limbs-per-carrier group size.\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field); // 2^nibble_bits\n let radix = 2.pow_32((nibble_bits + 4) as Field); // 2^(nibble_bits + 4)\n\n // Number of chunks to emit: ceil(A / group).\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n // Process in fixed-size chunks of `group` limbs.\n for chunk in 0..num_chunks {\n // How many real values go into this chunk.\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n // Build field element accumulator (big-endian concatenation in `radix`).\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n acc = acc * radix + (v + base);\n }\n\n // Pad remaining limb slots with the canonical zero-limb `digit = base`.\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Computes a cryptographic hash using the SAFE (Sponge API for Field Elements) protocol.\n///\n/// This is a convenience wrapper around the SAFE sponge API that handles the full\n/// lifecycle: initialization, absorption, squeezing, and finalization. It's designed\n/// for use in Fiat-Shamir challenge generation and commitment schemes within zero-knowledge circuits.\n///\n/// # Arguments\n/// * `domain_separator` - A 64-byte domain separator used to differentiate between\n/// different protocol instances and prevent cross-protocol attacks.\n/// * `inputs` - Vector of field elements to be absorbed into the sponge.\n/// * `io_pattern` - A 2-element array encoding the I/O pattern:\n/// - `io_pattern[0]`: Encoded ABSORB operation (MSB=1, lower 31 bits = length)\n/// - `io_pattern[1]`: Encoded SQUEEZE operation (MSB=0, lower 31 bits = length)\n///\n/// # Returns\n/// A vector of field elements squeezed from the sponge, with length determined by\n/// the SQUEEZE operation in the IO pattern.\n/// Proves `0 <= v < upper` using two one-sided bit-size checks.\n///\n/// `assert_max_bit_size` is one-sided: it forces a small **non-negative** field element. So bounding\n/// `v` gives `v >= 0`, and bounding `upper - 1 - v` gives `v <= upper - 1`. The pair composes into a\n/// two-sided interval without a comparison operator.\n///\n/// `BIT` must cover `upper`; the caller supplies it from the generated config.\npub fn assert_in_range<let BIT: u32>(v: Field, upper: Field) {\n v.assert_max_bit_size::<BIT>();\n (upper - 1 - v).assert_max_bit_size::<BIT>();\n}\n\npub fn compute_safe(domain_separator: [u8; 64], inputs: [Field], io_pattern: [u32; 2]) -> [Field] {\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(inputs);\n let digests = sponge.squeeze();\n sponge.finish();\n\n digests\n}\n\n#[test]\nfn test_flatten() {\n // Create test polynomials\n let poly1 = Polynomial::new([1, 2, 3]); // degree 2\n let poly2 = Polynomial::new([4, -16, 6]); // degree 2\n let poly3 = Polynomial::new([-7, 8, 9]); // degree 2\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 4>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n assert(result[0] == 0x11121310101010101010101010101010101010101010101010101010101010);\n assert(result[1] == 0x14001610101010101010101010101010101010101010101010101010101010); // -16 became 00 at 0x 14 00 16,\n assert(result[2] == 0x09181910101010101010101010101010101010101010101010101010101010); // -7 became 09 at 0x 09 18 19(16 - 7 = 9)\n}\n\n#[test]\nfn test_flatten_big() {\n // Create test polynomials\n let poly1 = Polynomial::new([\n 1791218451968394,\n 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n 5430119342984413,\n 704811298945172,\n 8901715723925099,\n 21888242871839275222246405745257275088548364400416034343698203098124042812559,\n 21888242871839275222246405745257275088548364400416034343698200215091693880034,\n ]);\n let poly2 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698200314078269634250,\n 21888242871839275222246405745257275088548364400416034343698200967285641915872,\n 2909990636858607,\n 7896103832076587,\n 2078397209533893,\n 21888242871839275222246405745257275088548364400416034343698199792421452734531,\n 614400389245817,\n 8290314119277588,\n ]);\n let poly3 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698201373175279892906,\n 21888242871839275222246405745257275088548364400416034343698201087241869723721,\n 6768789983786188,\n 635797784303388,\n 7610153424227556,\n 4633893206538324,\n 2016269760615332,\n 21888242871839275222246405745257275088548364400416034343698201007080554428142,\n ]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 54>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n\n // For the first index of result operation goes like this,\n\n // First four index of poly1\n // 1791218451968394,\n // 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n // 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n // 5430119342984413,\n\n // base + 1791218451968394 = 0x1065d1a8b8b718a\n // base - 5921327228407753 = 0xeaf69591f3b037 (negative coefficient shifted)\n // base - 3644467483862151 = 0xf30d604a3a9b79 (negative coefficient shifted)\n // base + 5430119342984413 = 0x1134aaa2e86ccdd\n assert(result[0] == 0x1065d1a8b8b718a0eaf69591f3b0370f30d604a3a9b791134aaa2e86ccdd);\n assert(result[1] == 0x1028105ab1b789411fa010339db66b0fc220f1326bc8e0f1e3f4cc1e02e1);\n assert(result[2] == 0x0f23dfbe7cd76c90f4901299312ddf10a569efe35acef11c0d76f005412b);\n assert(result[3] == 0x107624a8f605dc50f0638a368960421022ecb3cf36b7911d73ff2c27ec14);\n assert(result[4] == 0x0f6013a24e1b9a90f4fd2c158a08481180c2dba8af4cc10242413515171c);\n assert(result[5] == 0x11b0964eb898ce411076805680b85410729c962da53a40f4b44412d0f6ed);\n}\n\n#[test]\nfn test_flatten_small() {\n // Create test polynomials\n let poly1 = Polynomial::new([712345, 104857, 999999, 500001, 123, 654321, 77]);\n let poly2 = Polynomial::new([1, 524287, 888888, 23456, 34567, 765432, 0]);\n let poly3 = Polynomial::new([444444, 333333, 222222, 111111, 987654, 246810, 13579]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 20>(inputs, polynomials);\n\n assert(result[0] == 0x1ade991199991f423f17a12110007b19fbf110004d100000100000100000);\n assert(result[1] == 0x10000117ffff1d9038105ba01087071badf8100000100000100000100000);\n assert(result[2] == 0x16c81c15161513640e11b2071f120613c41a10350b100000100000100000);\n}\n\n#[test]\nfn test_safe_hashing_with_safe_helper() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let digests1 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests1.len() == 1);\n assert(digests1[0] != 0);\n\n // Test determinism\n let digests2 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests2.len() == 1);\n assert(digests2[0] != 0);\n assert(digests2[0] == digests1[0]);\n}\n\n#[test]\nfn test_pack() {\n // Test pack function directly with small values\n let values = [1, 2, 3, 4];\n let packed = pack::<4, 4>(values);\n\n // With BIT=4, nibble_bits=4, group should be floor(254/(4+4)) = 31\n // So all 4 values should fit in one carrier\n assert(packed.len() >= 1);\n\n // Test with negative values\n let values_neg = [-1, 2, -3, 4];\n let packed_neg = pack::<4, 4>(values_neg);\n assert(packed_neg.len() >= 1);\n}\n\n#[test]\nfn test_pack_single_value() {\n // Test packing a single value\n let values = [42];\n let packed = pack::<1, 8>(values);\n assert(packed.len() == 1);\n assert(packed[0] != 0);\n}\n\n#[test]\nfn test_pack_determinism() {\n // Test that packing is deterministic\n let values = [10, 20, 30];\n let packed1 = pack::<3, 8>(values);\n let packed2 = pack::<3, 8>(values);\n\n assert(packed1.len() == packed2.len());\n for i in 0..packed1.len() {\n assert(packed1[i] == packed2[i]);\n }\n}\n\n/// `pack_checked` must agree with `pack` for in-slot values. The two carry duplicate layout\n/// arithmetic, so if one is changed without the other, a commitment created by a circuit on `pack`\n/// stops matching one opened by a circuit on `pack_checked`. This pins that.\n#[test]\nfn test_pack_checked_matches_pack() {\n // Mixed signs, and values at the edge of the centred window for BIT = 35 (slot = 2^40).\n let vals: [Field; 7] = [0, 1, -1, 34359701504, -34359701504, 12345, -6789];\n let plain = pack::<7, 35>(vals);\n let checked = pack_checked::<7, 35>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n\n/// Same, at a width whose chunking leaves a partial final carrier (BIT = 57 -> group 3, 7 values).\n#[test]\nfn test_pack_checked_matches_pack_partial_chunk() {\n let vals: [Field; 7] = [5, -5, 72057594049265664, -72057594049265664, 1, 0, -1];\n let plain = pack::<7, 57>(vals);\n let checked = pack_checked::<7, 57>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n", path: "/Users/ctrlc03/.omp/worktrees/interfold/feat-insecure-64-params/circuits/lib/src/math/helpers.nr", function_locations: [{ start: 1374, name: "packing_layout" }, { start: 3703, name: "pack_checked" }, { start: 4808, name: "flatten_checked" }, { start: 6096, name: "flatten" }, { start: 7946, name: "pack" }, { start: 10581, name: "assert_in_range" }, { start: 10770, name: "compute_safe" }, { start: 10968, name: "test_flatten" }, { start: 11911, name: "test_flatten_big" }, { start: 14812, name: "test_flatten_small" }, { start: 15639, name: "test_safe_hashing_with_safe_helper" }, { start: 16485, name: "test_pack" }, { start: 16955, name: "test_pack_single_value" }, { start: 17151, name: "test_pack_determinism" }, { start: 17749, name: "test_pack_checked_matches_pack" }, { start: 18287, name: "test_pack_checked_matches_pack_partial_chunk" }] }, "98": { 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;\nuse poseidon::poseidon2_permutation;\n\n/// SAFE (Sponge API for Field Elements)\n///\n/// This module provides a complete implementation of the SAFE API in Noir as defined in:\n/// "SAFE (Sponge API for Field Elements) - A Toolbox for ZK Hash Applications"\n/// see https://hackmd.io/bHgsH6mMStCVibM_wYvb2w#22-Sponge-state for more details.\n///\n/// SAFE provides a unified interface for cryptographic sponge functions that can be\n/// instantiated with various permutations to create hash functions, MACs, authenticated\n/// encryption schemes, and other cryptographic primitives for ZK proof systems.\n///\n/// This implementation follows the SAFE specification exactly, providing:\n/// - Complete API: START, ABSORB, SQUEEZE, FINISH operations.\n/// - Full security: Domain separation, tag computation, IO pattern validation.\n/// - Poseidon2 integration: Field-friendly permutation for ZK systems.\n/// - Specification compliance: All operations follow SAFE spec 2.4 exactly.\n/// - Natural API design: Variable-length inputs, automatic length detection from IO patterns.\n///\n/// # API Design\n///\n/// The API is designed for natural usage while maintaining type safety:\n/// - `absorb(input: [Field])`: Accepts variable-length arrays, no padding required.\n/// - `squeeze()`: Returns a vector with field element(s).\n/// - IO patterns automatically determine operation lengths for validation.\n\n/// Rate parameter for the sponge construction (number of field elements that can be absorbed per permutation call).\nglobal RATE: u32 = 3;\n\n/// Capacity parameter for the sponge construction (security parameter, typically 1-2 field elements).\nglobal CAPACITY: u32 = 1;\n\n/// Total state size (rate + capacity) in field elements.\nglobal STATE_SIZE: u32 = RATE + CAPACITY;\n\n/// IO Pattern encoding constants (from SAFE spec 2.3).\n///\n/// These constants are used for encoding operation types in the 32-bit word format:\n/// - MSB set to 1 for ABSORB operations\n/// - MSB set to 0 for SQUEEZE operations\n\n/// Flag for ABSORB operations (MSB = 1)\nglobal ABSORB_FLAG: u32 = 0x80000000;\n\n/// Flag for SQUEEZE operations (MSB = 0)\nglobal SQUEEZE_FLAG: u32 = 0x00000000;\n\n/// SAFE Sponge State (following spec 2.2)\n///\n/// The sponge state consists of the permutation state, tag, position counters,\n/// and IO pattern tracking as defined in the SAFE specification.\n///\n/// # Generic Parameters\n/// - `L`: The length of the IO pattern array\n///\n/// # Fields\n/// - `state`: Permutation state V in F^n (rate + capacity elements)\n/// - `tag`: Parameter tag T used for instance differentiation\n/// - `absorb_pos`: Current absorb position (<= n-c)\n/// - `squeeze_pos`: Current squeeze position (<= n-c)\n/// - `io_pattern`: Expected IO pattern for validation (encoded 32-bit words)\n/// - `io_count`: Current operation count for pattern tracking\npub struct SafeSponge<let L: u32> {\n /// Permutation state V in F^n (rate + capacity elements).\n state: [Field; STATE_SIZE],\n /// Parameter tag T used for instance differentiation.\n tag: Field,\n /// Current absorb position (<= n-c).\n absorb_pos: u32,\n /// Current squeeze position (<= n-c).\n squeeze_pos: u32,\n /// Expected IO pattern for validation.\n io_pattern: [u32; L],\n /// Current operation count for pattern tracking (spec 2.4: io_count).\n io_count: u32,\n}\n\nimpl<let L: u32> SafeSponge<L> {\n /// Initializes a new SAFE sponge instance with the given IO pattern and domain separator (following spec 2.4).\n ///\n /// # Arguments\n /// - `io_pattern`: Array of 32-bit encoded operations defining the expected sequence of ABSORB/SQUEEZE calls.\n /// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n /// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n ///\n /// # Returns\n /// A new `SafeSponge` instance with initialized state\n pub fn start(io_pattern: [u32; L], domain_separator: [u8; 64]) -> SafeSponge<L> {\n // Compute tag from IO pattern and domain separator (spec 2.3).\n let tag = compute_tag(io_pattern, domain_separator);\n\n let mut state = [0; STATE_SIZE];\n // Initialize capacity with tag (spec 2.4).\n // Add T to the first 128 bits of the state.\n state[0] = tag;\n\n SafeSponge { state, tag, absorb_pos: 0, squeeze_pos: 0, io_pattern, io_count: 0 }\n }\n\n /// Absorbs field elements into the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to absorb is automatically validated against the IO pattern.\n /// This method accepts variable-length arrays, making it natural to use without padding.\n ///\n /// # Arguments\n /// - `input`: Array of field elements to absorb (variable length, must match IO pattern)\n pub fn absorb(&mut self, input: [Field]) {\n let length = input.len() as u32;\n\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_absorb = (expected_encoded_word & ABSORB_FLAG) != 0;\n let expected_length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type and length\n assert(is_expected_absorb, "Expected ABSORB operation");\n assert(expected_length == length, "Length mismatch");\n\n // Process each element naturally (no unnecessary iterations).\n for i in 0..length {\n // If absorb_pos == (n-c) then permute and reset (spec 2.4).\n if self.absorb_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.absorb_pos = 0;\n }\n\n // Add X[i] to state at absorb_pos (spec 2.4).\n // Note: absorb_pos is the rate position, not capacity position.\n self.state[self.absorb_pos + CAPACITY] =\n self.state[self.absorb_pos + CAPACITY] + input[i];\n self.absorb_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = ABSORB_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n\n // Force permute at start of next SQUEEZE (spec 2.4).\n self.squeeze_pos = RATE;\n }\n\n /// Extracts field elements from the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to squeeze is automatically determined from the IO pattern.\n pub fn squeeze(&mut self) -> [Field] {\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_squeeze = (expected_encoded_word & ABSORB_FLAG) == 0;\n let length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type\n assert(is_expected_squeeze, "Expected SQUEEZE operation");\n\n let mut output: [Field] = [].as_vector();\n\n // SQUEEZE implementation following spec 2.4.\n // If length==0, loop won\'t execute (spec 2.4).\n for _ in 0..length {\n // If squeeze_pos==(n-c) then permute and reset (spec 2.4).\n if self.squeeze_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.squeeze_pos = 0;\n self.absorb_pos = 0;\n }\n // Set Y[i] to state element at squeeze_pos (spec 2.4).\n output = output.push_back(self.state[self.squeeze_pos + CAPACITY]);\n self.squeeze_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = SQUEEZE_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n output\n }\n\n /// Finalizes the sponge instance, verifying that all expected operations have been performed and clearing the internal state for security (following spec 2.4).\n ///\n /// This function is used to ensure that the sponge instance has been used correctly and to prevent information leakage.\n pub fn finish(&mut self) {\n // Check that io_count equals the length of the IO pattern expected (spec 2.4).\n assert(self.io_count == L, "IO pattern not completed");\n\n // Erase the state and its variables (spec 2.4).\n self.state = [0; STATE_SIZE];\n self.absorb_pos = 0;\n self.squeeze_pos = 0;\n self.io_count = 0;\n }\n\n /// Permute the state using Poseidon2 (following spec 2.4).\n ///\n /// Applies the Poseidon2 permutation to the current state.\n /// This is the core cryptographic primitive of the sponge construction.\n ///\n /// # Returns\n /// New state after permutation\n fn permute(self) -> [Field; STATE_SIZE] {\n poseidon2_permutation(self.state)\n }\n}\n\n/// Computes a unique tag for a sponge instance based on its IO pattern and domain separator.\n/// The tag is used to ensure that distinct instances behave like distinct functions.\n///\n/// # Arguments\n/// - `io_pattern`: Array of 32-bit encoded operations defining the sponge\'s usage pattern.\n/// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n/// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n///\n/// # Returns\n/// A field element representing the 128-bit tag.\npub fn compute_tag<let L: u32>(io_pattern: [u32; L], domain_separator: [u8; 64]) -> Field {\n // Step 1: Parse and aggregate consecutive operations of the same type\n let mut encoded_words = [0; L]; // Support up to L operations.\n let mut word_count = 0;\n let mut current_absorb_sum = 0;\n let mut current_squeeze_sum = 0;\n let mut last_was_absorb = false;\n\n for i in 0..L {\n if io_pattern[i] > 0 {\n // Parse operation type from MSB and length from lower 31 bits\n let is_absorb = (io_pattern[i] & ABSORB_FLAG) != 0;\n let length = io_pattern[i] & 0x7FFFFFFF; // Clear MSB to get length\n\n if is_absorb {\n if last_was_absorb {\n // Aggregate consecutive ABSORB operations\n current_absorb_sum += length;\n } else {\n // Start new ABSORB sequence\n if current_squeeze_sum > 0 {\n // Flush previous SQUEEZE sequence\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n current_squeeze_sum = 0;\n }\n current_absorb_sum = length;\n }\n last_was_absorb = true;\n } else {\n if !last_was_absorb {\n // Aggregate consecutive SQUEEZE operations\n current_squeeze_sum += length;\n } else {\n // Start new SQUEEZE sequence\n if current_absorb_sum > 0 {\n // Flush previous ABSORB sequence\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n current_absorb_sum = 0;\n }\n current_squeeze_sum = length;\n }\n last_was_absorb = false;\n }\n }\n }\n\n // Flush remaining operations\n if current_absorb_sum > 0 {\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n }\n if current_squeeze_sum > 0 {\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n }\n\n // Step 2: Serialize to byte string and append domain separator (following SAFE spec 2.3).\n // Buffer is 256 bytes: max 192 bytes for IO pattern (48 words) + 64 bytes for domain separator.\n // Note: We must use a fixed-size array because Noir\'s keccak256 requires [u8; N], not [u8].\n let max_io_pattern_bytes: u32 = 192; // 256 - 64 (domain separator)\n let io_pattern_bytes = word_count * 4;\n assert(\n io_pattern_bytes <= max_io_pattern_bytes,\n "IO pattern too large: max 48 aggregated words supported",\n );\n\n let mut input_bytes = [0u8; 256];\n let mut byte_count: u32 = 0;\n\n // Serialize encoded words to bytes (big-endian as per SAFE spec).\n // Note: Noir requires compile-time loop bounds, so we iterate over L (the array size)\n // instead of word_count (runtime value). The condition `i < word_count` ensures we only\n // process valid encoded words. This is safe because word_count <= L always holds\n // (we can have at most L encoded words from L input operations).\n for i in 0..L {\n if i < word_count {\n let word = encoded_words[i];\n input_bytes[byte_count] = (word >> 24) as u8;\n input_bytes[byte_count + 1] = (word >> 16) as u8;\n input_bytes[byte_count + 2] = (word >> 8) as u8;\n input_bytes[byte_count + 3] = word as u8;\n byte_count += 4;\n }\n }\n\n // Append full 64-byte domain separator.\n for i in 0..64 {\n input_bytes[byte_count] = domain_separator[i];\n byte_count += 1;\n }\n\n // Step 3: Hash with Keccak-256 and truncate to 128 bits.\n // Note: The SAFE spec uses SHA3-256, but we use Keccak-256 for Noir compatibility.\n // Keccak-256 differs from SHA3-256 in padding, but both provide equivalent security.\n let hash_bytes = keccak256(input_bytes, byte_count);\n\n // Convert first 128 bits (16 bytes) to field element.\n let mut tag_value: Field = 0;\n for i in 0..16 {\n tag_value = tag_value * 256 + (hash_bytes[i] as Field);\n }\n\n tag_value\n}\n\n#[test]\nfn test_safe_hashing() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_merkle_node() {\n // Verifies SAFE can be used for Merkle tree node hashing with pattern ABSORB(1) + ABSORB(1) + SQUEEZE(1).\n // Tests the ability to absorb multiple inputs before squeezing output.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let left = [123].as_vector();\n let right = [456].as_vector();\n\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(left);\n sponge.absorb(right);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(left);\n sponge2.absorb(right);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_commitment_scheme() {\n // Verifies SAFE can be used for commitment schemes with pattern ABSORB(3) + SQUEEZE(1).\n // Tests the ability to create deterministic commitments from multiple field elements.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let values = [10, 20, 30].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(values);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(values);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_domain_separation() {\n // Verifies that different domain separators produce different outputs for the same input.\n // This is crucial for cross-protocol security and preventing collisions between different applications.\n let elements = [1, 2, 3].as_vector();\n let domain1 = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let domain2 = [\n 0x41, 0x42, 0x43, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n\n let mut sponge1 = SafeSponge::start(io_pattern, domain1);\n sponge1.absorb(elements);\n let output1 = sponge1.squeeze();\n sponge1.finish();\n\n let mut sponge2 = SafeSponge::start(io_pattern, domain2);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output1.len() == 1);\n assert(output2.len() == 1);\n assert(output1[0] != output2[0]); // Different domain separators should produce different outputs\n}\n\n#[test]\nfn test_multiple_squeeze() {\n // Verifies that multiple field elements can be squeezed in a single operation.\n // Tests pattern ABSORB(3) + SQUEEZE(2) to ensure proper state management.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(2)\n let io_pattern = [0x80000003, 0x00000002];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 2);\n assert(output[0] != 0);\n assert(output[1] != 0);\n assert(output[0] != output[1]); // Different squeeze outputs should be different\n}\n\n#[test]\nfn test_zero_length_operations() {\n // Verifies that zero-length ABSORB and SQUEEZE operations are handled correctly.\n // Tests pattern ABSORB(0) + SQUEEZE(1) to ensure proper state transitions.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(0), SQUEEZE(1)\n let io_pattern = [0x80000000, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb([].as_vector());\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n}\n\n#[test]\nfn test_tag_computation() {\n // Verifies the tag computation algorithm using the example from the SAFE specification.\n // Pattern: ABSORB(3), ABSORB(3), SQUEEZE(3)\n // Should aggregate to: ABSORB(6), SQUEEZE(3)\n // Encoded as: [0x80000006, 0x00000003]\n // Tests determinism and pattern differentiation.\n\n let io_pattern = [0x80000003, 0x80000003, 0x00000003];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test determinism\n let tag2 = compute_tag(io_pattern, domain_separator);\n assert(tag == tag2);\n\n // Test that different patterns produce different tags\n let io_pattern2 = [0x80000003, 0x00000003]; // ABSORB(3), SQUEEZE(3) - different pattern\n let tag3 = compute_tag(io_pattern2, domain_separator);\n assert(tag != tag3);\n}\n\n#[test]\nfn test_tag_computation_debug() {\n println("=== SAFE Tag Computation Debug Test ===");\n\n // Test your specific pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))\n let io_pattern = [0x80000002, 0x00000002, 0x80000002];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n println(f"Testing pattern: {io_pattern}");\n println(\n f"Expected to aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(2)",\n );\n println(\n f"Expected encoded words: [0x80000002, 0x00000002, 0x80000002]",\n );\n println("");\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n println(f"=== Expected Rust Output ===");\n println("Pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))");\n println("Domain separator: 0x41424344...");\n println("Tag: 0xce3bb9ee4b2d41c42e9cdda38afe8b6a");\n println("");\n\n println(f"=== Noir Output ===");\n println(f"Tag: {tag}");\n println("");\n\n println("Compare the tag values above with Rust script!");\n}\n\n#[test]\nfn test_consecutive_absorb_aggregation() {\n // Test that consecutive ABSORB operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1) should aggregate to ABSORB(2), SQUEEZE(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(1) = [0x80000002, 0x00000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(2), SQUEEZE(1)\n let aggregated_pattern = [0x80000002, 0x00000001];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Consecutive ABSORB operations should aggregate to the same tag");\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive ABSORB operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive ABSORB Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001] (ABSORB(1), ABSORB(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000002, 0x00000001] (ABSORB(2), SQUEEZE(1))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_consecutive_squeeze_aggregation() {\n // Test that consecutive SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1) should aggregate to ABSORB(1), SQUEEZE(2)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x00000001, 0x00000001];\n\n // This should aggregate to: ABSORB(1), SQUEEZE(2) = [0x80000001, 0x00000002]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(1), SQUEEZE(2)\n let aggregated_pattern = [0x80000001, 0x00000002];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(\n tag == aggregated_tag,\n "Consecutive SQUEEZE operations should aggregate to the same tag",\n );\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive SQUEEZE operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive SQUEEZE Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x00000001, 0x00000001] (ABSORB(1), SQUEEZE(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000001, 0x00000002] (ABSORB(1), SQUEEZE(2))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_mixed_consecutive_aggregation() {\n // Test that both consecutive ABSORB and SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n // Should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1) = [0x80000002, 0x00000002, 0x80000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag\n let aggregated_pattern = [0x80000002, 0x00000002, 0x80000001]; // ABSORB(2), SQUEEZE(2), ABSORB(1)\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Mixed consecutive operations should aggregate to the same tag");\n\n println("=== Mixed Consecutive Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001]",\n );\n println(\n f" (ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1))",\n );\n println(f"Aggregated pattern: [0x80000002, 0x00000002, 0x80000001]");\n println(f" (ABSORB(2), SQUEEZE(2), ABSORB(1))");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n}\n\n#[test]\nfn test_large_io_pattern() {\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Create pattern with 48 alternating ABSORB(1) and SQUEEZE(1) operations\n // This is the maximum supported (48 words * 4 bytes = 192 bytes, leaving 64 for domain separator)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1; // ABSORB(1)\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1; // SQUEEZE(1)\n }\n }\n\n let tag = compute_tag(io_pattern, domain_separator);\n assert(tag != 0);\n}\n\n#[test]\nfn test_domain_separator_not_truncated() {\n // This test verifies that the domain separator is always included in the tag computation,\n // even for large IO patterns. If the domain separator were truncated, different domain\n // separators would produce the same tag for large patterns.\n\n let domain_separator_a = [\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41,\n ]; // All \'A\'s\n\n let domain_separator_b = [\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42,\n ]; // All \'B\'s\n\n // Create pattern with 48 alternating operations (max supported: 192 bytes of IO pattern)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1;\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1;\n }\n }\n\n let tag_a = compute_tag(io_pattern, domain_separator_a);\n let tag_b = compute_tag(io_pattern, domain_separator_b);\n\n // Tags MUST be different because domain separators are different.\n // If they were the same, it would mean the domain separator was truncated/ignored.\n assert(tag_a != tag_b, "Domain separator must affect tag even for large IO patterns");\n}\n', path: "/Users/ctrlc03/.omp/worktrees/interfold/feat-insecure-64-params/circuits/lib/src/math/safe.nr", function_locations: [{ start: 4164, name: "SafeSponge<L>::start" }, { start: 5046, name: "SafeSponge<L>::absorb" }, { start: 6826, name: "SafeSponge<L>::squeeze" }, { start: 8494, name: "SafeSponge<L>::finish" }, { start: 9156, name: "SafeSponge<L>::permute" }, { start: 9830, name: "compute_tag" }, { start: 14128, name: "test_safe_hashing" }, { start: 15104, name: "test_merkle_node" }, { start: 16281, name: "test_commitment_scheme" }, { start: 17357, name: "test_domain_separation" }, { start: 18710, name: "test_multiple_squeeze" }, { start: 19639, name: "test_zero_length_operations" }, { start: 20415, name: "test_tag_computation" }, { start: 21477, name: "test_tag_computation_debug" }, { start: 22681, name: "test_consecutive_absorb_aggregation" }, { start: 24750, name: "test_consecutive_squeeze_aggregation" }, { start: 26760, name: "test_mixed_consecutive_aggregation" }, { start: 28520, name: "test_large_io_pattern" }, { start: 29333, name: "test_domain_separator_not_truncated" }] } } };
|
|
618
618
|
|
|
619
619
|
// ../../../../circuits/bin/threshold/target/user_data_encryption.json
|
|
620
|
-
var user_data_encryption_default = { noir_version: "1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905", hash: "17981472203776318599", abi: { parameters: [{ name: "ct0_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "ct0_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "ct0_public_inputs", type: { kind: "array", length: 4, type: { kind: "field" } }, visibility: "private" }, { name: "ct0_key_hash", type: { kind: "field" }, visibility: "public" }, { name: "ct1_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "ct1_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "ct1_public_inputs", type: { kind: "array", length: 3, type: { kind: "field" } }, visibility: "private" }, { name: "ct1_key_hash", type: { kind: "field" }, visibility: "public" }], return_type: { abi_type: { kind: "tuple", fields: [{ kind: "field" }, { kind: "field" }, { kind: "field" }] }, visibility: "public" }, error_types: {} }, bytecode: "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", debug_symbols: "tZbdjoIwEIXfpddcdKb/vspmY1CrISFAEDbZGN99i9pWLtpV2L0aoczXM50zphdysLvxtK2aY3smm48L2fVVXVenbd3uy6FqG/f2ci2If9wOvbXuFXlad1ld2dtmIJtmrOuCfJX1ePvo3JXNLQ5l71ZpQWxzcNEBj1Vtp1/XImbTdKpW+pEMlKqQL3AGgDQApEBPUIZFApsRME1gLGhgXEIgIL5aBFBgXgNwTFXBV1ch/rUKlKEK1CxVRZaglSewdDezBCEjQS8hMBkJWi4i6OBJvkwD5+EcuIGkqzOmVMr4qVJaxsHSao5gOVdqHo6CamEixMwhGWNKoObBkMBjS41+R0e0BTUUkjpk5jwk9wOipOBJHSqNMBCcYcCYJEJnhsyEIeMURECAer0QLVhorMGUipy7hPItAfFUxxv+lBQ8QSIsIkga/q/SM4JstcOR/4HDUax2+C86XnM4qtUOR73a4WhWOzxbyHqHKyaCu4SYuevTPZX7qp9dZAh1ygsCroUFQbdVQZjLdYHfg7gH6T5z+6opXKf9+6rc1fZxFzqOzf7pajR8d37FX566vt3bw9jbaffbmtPzAw==", file_map: { "17": { 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;\nuse crate::static_assert;\n\n/// The size of the state accepted by the backend in `poseidon2_permutation`.\nglobal POSEIDON2_CONFIG_STATE_SIZE: u32 = poseidon2_config_state_size();\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\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s<let N: u32>(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n// docs:start:blake3\npub fn blake3<let N: u32>(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<let N: u32>(input: [u8; N]) -> [u8; 32] {}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment<let N: u32>(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<let N: u32>(\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 points[i] = EmbeddedCurveScalar::from_field(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<let N: u32>(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<let N: u32>(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 crate::assert_constant(separator);\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] = EmbeddedCurveScalar::from_field(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, true)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n crate::assert_constant(starting_index);\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\npub fn poseidon2_permutation<let N: u32>(input: [Field; N]) -> [Field; N] {\n static_assert(\n N == POSEIDON2_CONFIG_STATE_SIZE,\n f"the input length must equal the state size in the Poseidon2 config; expected {POSEIDON2_CONFIG_STATE_SIZE}, got {N}",\n );\n poseidon2_permutation_internal(input)\n}\n\n#[foreign(poseidon2_permutation)]\nfn poseidon2_permutation_internal<let N: u32>(input: [Field; N]) -> [Field; N] {}\n\n#[foreign(poseidon2_config_state_size)]\ncomptime fn poseidon2_config_state_size() -> u32 {}\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<H>(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<H>(_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 /// Returns the hash value without consuming the hasher.\n /// Override this for more efficient implementations that avoid copying.\n /// TODO: deprecate finish() and replace it\n fn finish_ref(&self) -> Field {\n (*self).finish()\n }\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<H>;\n\nimpl<H> BuildHasher for BuildHasherDefault<H>\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<H> Default for BuildHasherDefault<H>\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u8 {\n fn hash<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl<T, let N: u32> Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl<T> Hash for [T]\nwhere\n T: Hash,\n{\n fn hash<H>(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<A> Hash for (A,)\nwhere\n A: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n }\n}\n\nimpl<A, B> Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl<A, B, C> Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash<H>(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<A, B, C, D> Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash<H>(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<A, B, C, D, E> 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<H>(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\nimpl<A, B, C, D, E, F> Hash for (A, B, C, D, E, F)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G> Hash for (A, B, C, D, E, F, G)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_> Hash for (A, B, C, D, E, F, G, H_)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I> Hash for (A, B, C, D, E, F, G, H_, I)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J> Hash for (A, B, C, D, E, F, G, H_, I, J)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K> Hash for (A, B, C, D, E, F, G, H_, I, J, K)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K, L> Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n L: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n self.11.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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n );\n}\n', path: "std/hash/mod.nr", function_locations: [{ start: 572, name: "sha256_compression" }, { start: 707, name: "keccakf1600" }, { start: 852, name: "blake2s" }, { start: 950, name: "blake3" }, { start: 1437, name: "__blake3" }, { start: 1555, name: "pedersen_commitment" }, { start: 1784, name: "pedersen_commitment_with_separator" }, { start: 2188, name: "pedersen_hash" }, { start: 2345, name: "pedersen_hash_with_separator" }, { start: 3339, name: "derive_generators" }, { start: 3698, name: "__derive_generators" }, { start: 3776, name: "poseidon2_permutation" }, { start: 4132, name: "poseidon2_permutation_internal" }, { start: 4225, name: "poseidon2_config_state_size" }, { start: 4536, name: "derive_hash" }, { start: 5327, name: "Hasher::finish_ref" }, { start: 5761, name: "<impl BuildHasher for BuildHasherDefault<H>>::build_hasher" }, { start: 5893, name: "<impl Default for BuildHasherDefault<H>>::default" }, { start: 6025, name: "<impl Hash for Field>::hash" }, { start: 6155, name: "<impl Hash for u8>::hash" }, { start: 6295, name: "<impl Hash for u16>::hash" }, { start: 6435, name: "<impl Hash for u32>::hash" }, { start: 6575, name: "<impl Hash for u64>::hash" }, { start: 6716, name: "<impl Hash for u128>::hash" }, { start: 6855, name: "<impl Hash for i8>::hash" }, { start: 7001, name: "<impl Hash for i16>::hash" }, { start: 7148, name: "<impl Hash for i32>::hash" }, { start: 7295, name: "<impl Hash for i64>::hash" }, { start: 7443, name: "<impl Hash for bool>::hash" }, { start: 7590, name: "<impl Hash for ()>::hash" }, { start: 7722, name: "<impl Hash for [T; N]>::hash" }, { start: 7911, name: "<impl Hash for [T]>::hash" }, { start: 8133, name: "<impl Hash for (A,)>::hash" }, { start: 8302, name: "<impl Hash for (A, B)>::hash" }, { start: 8518, name: "<impl Hash for (A, B, C)>::hash" }, { start: 8781, name: "<impl Hash for (A, B, C, D)>::hash" }, { start: 9091, name: "<impl Hash for (A, B, C, D, E)>::hash" }, { start: 9448, name: "<impl Hash for (A, B, C, D, E, F)>::hash" }, { start: 9852, name: "<impl Hash for (A, B, C, D, E, F, G)>::hash" }, { start: 10306, name: "<impl Hash for (A, B, C, D, E, F, G, H_)>::hash" }, { start: 10807, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I)>::hash" }, { start: 11355, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J)>::hash" }, { start: 11950, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K)>::hash" }, { start: 12593, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)>::hash" }, { start: 13187, name: "assert_pedersen" }] }, "22": { source: "pub mod hash;\npub mod aes128;\npub mod array;\npub mod vector;\npub mod ecdsa_secp256k1;\npub mod ecdsa_secp256r1;\npub mod embedded_curve_ops;\npub mod field;\npub mod collections;\npub mod compat;\npub mod convert;\npub mod option;\npub mod string;\npub mod test;\npub mod cmp;\npub mod ops;\npub mod default;\npub mod prelude;\npub mod runtime;\npub mod meta;\npub mod append;\npub mod mem;\npub mod panic;\npub mod hint;\n\nmod integer;\nmod primitive_docs;\nmod internal;\n\n// Oracle calls are required to be wrapped in an unconstrained function\n// Thus, the only argument to the `println` oracle is expected to always be an ident\n#[oracle(print)]\nunconstrained fn print_oracle<T>(with_newline: bool, input: T) {}\n\nunconstrained fn print_unconstrained<T>(with_newline: bool, input: T) {\n print_oracle(with_newline, input);\n}\n\n/// Print the given input to stdout followed by a newline\npub fn println<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(true, input);\n }\n}\n\n/// Print the given input to stdout\npub fn print<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(false, input);\n }\n}\n\n/// Asserts the validity of the provided proof and public inputs against the provided verification key and hash.\n///\n/// The ACVM cannot determine whether the provided proof is valid during execution as this requires knowledge of\n/// the backend against which the program is being proven. However if an invalid proof if submitted, the program may\n/// fail to prove or the backend may generate a proof which will subsequently fail to verify.\n///\n/// # Important Note\n///\n/// If you are not developing your own backend such as [Barretenberg](https://github.com/AztecProtocol/barretenberg)\n/// you probably shouldn't need to interact with this function directly. It's easier and safer to use a verification\n/// library which is published by the developers of the backend which will document or enforce any safety requirements.\n///\n/// If you use this directly, you're liable to introduce underconstrainedness bugs and *your circuit will be insecure*.\n///\n/// # Arguments\n/// - verification_key: The verification key of the circuit to be verified.\n/// - proof: The proof to be verified.\n/// - public_inputs: The public inputs associated with `proof`\n/// - key_hash: The hash of `verification_key` of the form expected by the backend.\n/// - proof_type: An identifier for the proving scheme used to generate the proof to be verified. This allows\n/// for a single backend to support verifying multiple proving schemes.\n///\n/// # Constraining `key_hash`\n///\n/// The Noir compiler does not by itself constrain that `key_hash` is a valid hash of `verification_key`.\n/// This is because different backends may differ in how they hash their verification keys.\n/// It is then the responsibility of either the noir developer (by explicitly hashing the verification key\n/// in the correct manner) or by the proving system itself internally asserting the correctness of `key_hash`.\npub fn verify_proof_with_type<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {\n if !crate::runtime::is_unconstrained() {\n crate::assert_constant(proof_type);\n }\n verify_proof_internal(verification_key, proof, public_inputs, key_hash, proof_type);\n}\n\n#[foreign(recursive_aggregation)]\nfn verify_proof_internal<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {}\n\n/// Asserts that the given value is known at compile-time.\n/// Useful for debugging for-loop bounds.\n#[builtin(assert_constant)]\npub fn assert_constant<T>(x: T) {}\n\n/// Asserts that the given value is both true and known at compile-time.\n/// The message can be a string, a format string, or any value, as long as it is known at compile-time\n#[builtin(static_assert)]\npub fn static_assert<T>(predicate: bool, message: T) {}\n\n/// Force a field value to be a witness instead of a constant in the compiled output.\n/// This is often only useful for debugging compiler optimizations.\n///\n/// This has no effect in unconstrained or comptime code.\n#[builtin(as_witness)]\npub fn as_witness(x: Field) {}\n", path: "std/lib.nr", function_locations: [{ start: 689, name: "print_oracle" }, { start: 763, name: "print_unconstrained" }, { start: 893, name: "println" }, { start: 1076, name: "print" }, { start: 3277, name: "verify_proof_with_type" }, { start: 3694, name: "verify_proof_internal" }, { start: 3859, name: "assert_constant" }, { start: 4118, name: "static_assert" }, { start: 4389, name: "as_witness" }] }, "52": { 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 bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};\nuse lib::math::commitments::{compute_commitment, DS_CIPHERTEXT, DS_PK_AGGREGATION};\n\nfn main(\n // P3: ct0 inner proof.\n ct0_verification_key: UltraHonkVerificationKey,\n ct0_proof: UltraHonkProof,\n ct0_public_inputs: [Field; 4], // pk0_commitment, ct0_commitment, k1_commitment, u_commitment\n ct0_key_hash: pub Field,\n // P3: ct1 inner proof.\n ct1_verification_key: UltraHonkVerificationKey,\n ct1_proof: UltraHonkProof,\n ct1_public_inputs: [Field; 3], // pk1_commitment, ct1_commitment, u_commitment\n ct1_key_hash: pub Field,\n) -> pub (Field, Field, Field) {\n verify_honk_proof_non_zk(\n ct0_verification_key,\n ct0_proof,\n ct0_public_inputs,\n ct0_key_hash,\n );\n verify_honk_proof_non_zk(\n ct1_verification_key,\n ct1_proof,\n ct1_public_inputs,\n ct1_key_hash,\n );\n\n // Verify that the u_commitment from the ct0 proof is the same as the u_commitment from the ct1 proof.\n assert(ct0_public_inputs[3] == ct1_public_inputs[2]);\n\n // Compute the ct commitment.\n let ct_inputs = [ct0_public_inputs[1], ct1_public_inputs[1]].as_vector();\n let ct_commitment = compute_commitment(ct_inputs, DS_CIPHERTEXT);\n\n // Threshold PK aggregation commitment (pk0 and pk1 limbs).\n let pk_inputs = [ct0_public_inputs[0], ct1_public_inputs[0]].as_vector();\n let pk_commitment = compute_commitment(pk_inputs, DS_PK_AGGREGATION);\n\n let k1_commitment = ct0_public_inputs[2];\n\n (pk_commitment, ct_commitment, k1_commitment)\n}\n", path: "interfold/circuits/bin/threshold/user_data_encryption/src/main.nr", function_locations: [{ start: 872, name: "main" }] }, "53": { source: '// Constants for UltraHonk recursive verifier inputs\npub global PROOF_TYPE_HONK: u32 = 0; // identifier for UltraHonk verfier\npub global RECURSIVE_PROOF_LENGTH: u32 = 410;\npub global ULTRA_VK_LENGTH_IN_FIELDS: u32 = 115;\n\npub type UltraHonkProof = [Field; RECURSIVE_PROOF_LENGTH];\npub type UltraHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\n// Constants for Rollup-UltraHonk recursive verifier inputs (N.B. this is equivalent to UH plus IPA claim and proof)\npub global PROOF_TYPE_ROLLUP_HONK: u32 = 4; // identifier for rollup-UltraHonk verfier\npub global PROOF_TYPE_ROOT_ROLLUP_HONK: u32 = 5; // identifier for root-rollup-UltraHonk verfier (closes the IPA accumulator)\npub global IPA_CLAIM_SIZE: u32 = 6;\npub global IPA_PROOF_LENGTH: u32 = 64;\npub global RECURSIVE_ROLLUP_HONK_PROOF_LENGTH: u32 =\n RECURSIVE_PROOF_LENGTH + IPA_CLAIM_SIZE + IPA_PROOF_LENGTH;\n\npub type RollupHonkProof = [Field; RECURSIVE_ROLLUP_HONK_PROOF_LENGTH];\npub type RollupHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\npub global PROOF_TYPE_HONK_ZK: u32 = 6; // identifier for UltraHonk ZK verfier\npub global RECURSIVE_ZK_PROOF_LENGTH: u32 = 450 + 8;\n\npub type UltraHonkZKProof = [Field; RECURSIVE_ZK_PROOF_LENGTH];\n\n// Verifies a non-zero-knowledge UltraHonk proof.\n//\n// Represents standard UltraHonk recursive verification for proofs that do not hide the witness.\n// Use this only in situations where zero-knowledge is not required.\npub fn verify_honk_proof_non_zk<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof with IPA (Inner Product Argument).\n//\n// This variant includes an IPA claim and proof appended to the standard UltraHonk proof,\n// used to amortize IPA recursive verification costs in rollup circuits.\npub fn verify_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROLLUP_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof and closes the IPA accumulator in-circuit.\n//\n// Use this at the root of a rollup aggregation tree. The inner proof has the same RollupHonk shape\n// as `verify_rolluphonk_proof`, but instead of propagating an accumulated IPA claim out as a public\n// input, this variant performs a full native IPA verification inside the circuit. The outer circuit\n// should therefore be proved as a standard (non-rollup) UltraHonk circuit, producing a proof\n// suitable for verification by `verify_honk_proof` / `verify_honk_proof_non_zk`.\n//\n// When two RollupHonk inputs are verified this way in one circuit, their two IPA claims are first\n// accumulated into one, then the accumulated claim is fully verified.\npub fn verify_root_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROOT_ROLLUP_HONK,\n );\n}\n\n// Verifies a zero-knowledge UltraHonk proof.\n//\n// This verifier is for UltraHonk proofs constructed with zero-knowledge, which hide the witness\n// values from the verifier.\n// Note: We intentionally choose the generic name "verify_honk_proof" for this function, as we\n// want ZK to be the default unless the user explicitly opts out.\npub fn verify_honk_proof<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkZKProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK_ZK,\n );\n}\n', path: "/Users/ctrlc03/nargo/github.com/AztecProtocol/aztec-packages/v5.1.0/barretenberg/noir/bb_proof_verification/src/lib.nr", function_locations: [{ start: 1646, name: "verify_honk_proof_non_zk" }, { start: 2262, name: "verify_rolluphonk_proof" }, { start: 3386, name: "verify_root_rolluphonk_proof" }, { start: 4087, name: "verify_honk_proof" }] }, "87": { 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 crate::math::helpers::{compute_safe, flatten, flatten_checked};\nuse crate::math::polynomial::Polynomial;\n\n/// DOMAIN SEPARATORS\n\n// Domain separator - "PK"\npub global DS_PK: [u8; 64] = [\n 0x50, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_GENERATION"\npub global DS_PK_GENERATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_COMPUTATION"\npub global DS_SHARE_COMPUTATION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_ENCRYPTION"\npub global DS_SHARE_ENCRYPTION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_AGGREGATION"\npub global DS_PK_AGGREGATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CIPHERTEXT"\npub global DS_CIPHERTEXT: [u8; 64] = [\n 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "AGGREGATED_SHARES"\npub global DS_AGGREGATED_SHARES: [u8; 64] = [\n 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45,\n 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "VK_HASH"\npub global DS_VK_HASH: [u8; 64] = [\n 0x56, 0x4b, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "RECURSIVE_AGGREGATION"\npub global DS_RECURSIVE_AGGREGATION: [u8; 64] = [\n 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47,\n 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_PK_GENERATION"\npub global DS_CLG_PK_GENERATION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_ENCRYPTION"\npub global DS_CLG_SHARE_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_USER_DATA_ENCRYPTION"\npub global DS_CLG_USER_DATA_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e,\n 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_DECRYPTION"\npub global DS_CLG_SHARE_DECRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "THRESHOLD_DECRYPTION_SHARE"\npub global DS_THRESHOLD_DECRYPTION_SHARE: [u8; 64] = [\n 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "USER_DATA_ENCRYPTION_COMMITMENT"\npub global DS_USER_DATA_ENCRYPTION_COMMITMENT: [u8; 64] = [\n 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n/// WRAPPERS\n\npub fn compute_commitment(inputs: [Field], domain_separator: [u8; 64]) -> Field {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 1])[0]\n}\n\npub fn compute_single_polynomial_commitment<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_single_polynomial_commitment`] with the digit asserts, for a polynomial this circuit\n/// carries no bound for. ct1 commits `u` this way: ct0 range-checks the same `u` to `u_bound` and the\n/// `user_data_encryption` fold asserts the two `u` commitments are equal, so making ct1\'s packing\n/// injective is what forces its `u` to equal ct0\'s \u2014 the bound transfers instead of being repeated.\n/// Produces the same value as the plain version for any input the plain version could legitimately\n/// have committed.\npub fn compute_single_polynomial_commitment_checked<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_multiple_polynomial_commitment<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_challenge<let L: u32>(inputs: [Field], domain_separator: [u8; 64]) -> [Field] {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 2 * L])\n}\n\npub fn single_polynomial_payload<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, inputs)\n}\n\npub fn single_polynomial_payload_checked<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload_checked<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, inputs)\n}\n\n/// COMMITMENTS\n\npub fn compute_dkg_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let mut payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n payload = multiple_polynomial_payload::<N, L, BIT_PK>(payload, pk1);\n\n compute_commitment(payload, DS_PK)\n}\n\npub fn compute_threshold_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\n/// [`compute_multiple_polynomial_commitment`] for a circuit that **opens** this commitment.\npub fn compute_multiple_polynomial_commitment_checked<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_threshold_pk_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C5). Asserts each packed digit fits its slot, which makes the opening unique and so\n/// forces the opened `pk0` to equal the `pk0` C1 committed. Produces the same value as the plain\n/// helper, so C1 needs no change.\npub fn compute_threshold_pk_commitment_checked<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\npub fn compute_share_computation_sk_commitment<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\n/// [`compute_share_computation_sk_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2a). C1 creates it and bounds `sk` to the trinary range, so making the opening\n/// unique forces the witness to equal that `sk`.\npub fn compute_share_computation_sk_commitment_checked<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_computation_e_sm_commitment<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_encryption_commitment_from_message<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\n/// [`compute_share_encryption_commitment_from_message`] for a circuit that **opens** this\n/// commitment rather than creating it (C4). Asserts each packed digit fits its slot, forcing the\n/// opened share to equal the share C2 committed. Same value as the plain helper, so C2 and C3 need\n/// no change.\npub fn compute_share_encryption_commitment_from_message_checked<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\npub fn compute_aggregated_shares_commitment<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// Commitment to a threshold decryption share: all CRT limbs, first `K` coefficients per limb\n/// (same layout as `Polynomial<K>` in decrypted_shares_aggregation).\n///\n/// `NATIVE_BIT_WIDTH` must cover native coefficients in \\([0, q_l)\\) per limb (not centered `d` bounds).\npub fn compute_threshold_decryption_share_commitment<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload =\n multiple_polynomial_payload::<K, L, NATIVE_BIT_WIDTH>([].as_vector(), d_share_limbs);\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_threshold_decryption_share_commitment`] for a circuit that **opens** this commitment\n/// rather than creating it (C7). C6 creates it and pins each coefficient to a native residue in\n/// \\([0, q)\\) via `verify_d_native_trunc_binding`, so asserting the digits here makes the opening\n/// unique and forces the opened share to equal the one C6 committed. Produces the same value as the\n/// plain version for any input the plain version could legitimately have committed.\npub fn compute_threshold_decryption_share_commitment_checked<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<K, L, NATIVE_BIT_WIDTH>(\n [].as_vector(),\n d_share_limbs,\n );\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_share_computation_e_sm_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2b). C1 creates it and bounds each `e_sm` limb to a centered residue.\npub fn compute_share_computation_e_sm_commitment_checked<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_pk_aggregation_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let commit_pk0 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk0, DS_PK_AGGREGATION);\n let commit_pk1 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk1, DS_PK_AGGREGATION);\n\n let inputs = [commit_pk0, commit_pk1].as_vector();\n\n compute_commitment(inputs, DS_PK_AGGREGATION)\n}\n\npub fn compute_recursive_aggregation_commitment(payload: [Field]) -> Field {\n compute_commitment(payload, DS_RECURSIVE_AGGREGATION)\n}\n\npub fn compute_vk_hash(vk_hashes: [Field]) -> Field {\n compute_commitment(vk_hashes, DS_VK_HASH)\n}\n\n/// [`compute_aggregated_shares_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). C4 creates it after `normalize_aggregated` has reduced and centered the\n/// aggregate, so making the opening unique forces the witness to equal that canonical value.\npub fn compute_aggregated_shares_commitment_checked<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// [`compute_ciphertext_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). No upstream circuit bounds the ciphertext limbs, so this buys injectivity\n/// rather than canonicality \u2014 which is the property wanted here: it forces C6 to decrypt exactly\n/// the ciphertext that was committed, whatever its representation.\npub fn compute_ciphertext_commitment_checked<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\npub fn compute_ciphertext_commitment<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\n/// COMMITMENTS FOR CHALLENGES\n\npub fn compute_threshold_pk_challenge(payload: [Field]) -> Field {\n compute_commitment(payload, DS_CLG_PK_GENERATION)\n}\n\npub fn compute_share_encryption_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_SHARE_ENCRYPTION)\n}\n\npub fn compute_threshold_share_decryption_challenge<let L: u32>(payload: [Field]) -> Field {\n compute_challenge::<L>(payload, DS_CLG_SHARE_DECRYPTION)[0]\n}\n\npub fn compute_user_data_encryption_ct0_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n\npub fn compute_user_data_encryption_ct1_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n', path: "interfold/circuits/lib/src/math/commitments.nr", function_locations: [{ start: 7784, name: "compute_commitment" }, { start: 8012, name: "compute_single_polynomial_commitment" }, { start: 8827, name: "compute_single_polynomial_commitment_checked" }, { start: 9138, name: "compute_multiple_polynomial_commitment" }, { start: 9375, name: "compute_challenge" }, { start: 9585, name: "single_polynomial_payload" }, { start: 9784, name: "multiple_polynomial_payload" }, { start: 9970, name: "single_polynomial_payload_checked" }, { start: 10185, name: "multiple_polynomial_payload_checked" }, { start: 10406, name: "compute_dkg_pk_commitment" }, { start: 10733, name: "compute_threshold_pk_commitment" }, { start: 11138, name: "compute_multiple_polynomial_commitment_checked" }, { start: 11750, name: "compute_threshold_pk_commitment_checked" }, { start: 12008, name: "compute_share_computation_sk_commitment" }, { start: 12516, name: "compute_share_computation_sk_commitment_checked" }, { start: 12795, name: "compute_share_computation_e_sm_commitment" }, { start: 13067, name: "compute_share_encryption_commitment_from_message" }, { start: 13647, name: "compute_share_encryption_commitment_from_message_checked" }, { start: 13931, name: "compute_aggregated_shares_commitment" }, { start: 14512, name: "compute_threshold_decryption_share_commitment" }, { start: 15336, name: "compute_threshold_decryption_share_commitment_checked" }, { start: 15876, name: "compute_share_computation_e_sm_commitment_checked" }, { start: 16182, name: "compute_pk_aggregation_commitment" }, { start: 16571, name: "compute_recursive_aggregation_commitment" }, { start: 16686, name: "compute_vk_hash" }, { start: 17169, name: "compute_aggregated_shares_commitment_checked" }, { start: 17843, name: "compute_ciphertext_commitment_checked" }, { start: 18325, name: "compute_ciphertext_commitment" }, { start: 18724, name: "compute_threshold_pk_challenge" }, { start: 18866, name: "compute_share_encryption_challenge" }, { start: 19023, name: "compute_threshold_share_decryption_challenge" }, { start: 19183, name: "compute_user_data_encryption_ct0_challenge" }, { start: 19344, name: "compute_user_data_encryption_ct1_challenge" }] }, "89": { 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\n//! Helper functions for circuit construction and cryptographic operations.\nuse crate::math::polynomial::Polynomial;\nuse crate::math::safe::SafeSponge;\n\n/// Compute hex-aligned packing parameters for a given `BIT`.\n///\n/// # Purpose\n/// Returns `(nibble_bits, group)` for use by pack/flatten so layout stays consistent.\n/// - `nibble_bits`: ceil (`BIT`) to the next multiple of 4 (nibble alignment).\n/// - Examples: `BIT = 7 -> 8`, `BIT = 8 -> 8`, `BIT = 9 -> 12`, `BIT = 10 -> 12`, `BIT = 11 -> 12`,\n/// `BIT=16 -> 16`, `BIT = 17 -> 20`.\n/// - `group`: max number of encoded limbs that fit in one BN254 field element,\n/// when each limb uses an extra 4 bits (see below).\n///\n/// # Rationale\n/// - We align to nibbles so powers of two are hex-friendly and deterministic.\n/// - We reserve one extra nibble (4 bits) per stored value to lift signed\n/// coefficients into the non-negative range (e.g., store `v + 2^nibble_bits`),\n/// which implies a radix of `2^(nibble_bits + 4)`.\n///\n/// # Safety\n/// - Asserts `nibble_bits + 4 <= 254` to avoid mod-p wrap on BN254.\n/// - Ensures at least one limb fits: `group >= 1`.\nfn packing_layout<let BIT: u32>() -> (u32, u32) {\n // Ceil BIT up to the next multiple of 4 (nibble alignment).\n let nibble_bits = ((BIT + 3) / 4) * 4;\n\n // Each stored limb uses an extra nibble because negative coefficients\n // will be shifted to positive, so radix = 2^(nibble_bits+4).\n assert(nibble_bits + 4 <= 254);\n\n // Maximum limbs that fit in one BN254 element without wrap.\n let group = 254 / (nibble_bits + 4);\n // `group == 1` packs one value per carrier: it saves no sponge absorptions at all while still\n // charging the range checks that make packing injective, so it is always a loss. Measured at\n // BIT=143: +82% gates against absorbing directly. Reject it rather than pay for nothing.\n assert(group >= 2);\n (nibble_bits, group)\n}\n\n/// Like [`pack`], but asserts every digit fits its slot.\n///\n/// ## Why\n/// `pack` builds each carrier as a base-`radix` number, `acc = acc * radix + (v + base)`. That\n/// representation is unique only while every digit stays below `radix`. An unbounded value can\n/// overflow its slot and cancel against the next, so one commitment gains a second opening and a\n/// prover can choose which coefficients it opens to.\n///\n/// `digit < radix` is exactly `digit.assert_max_bit_size::<nibble_bits + 4>()`, and because\n/// `assert_max_bit_size` also forces `digit >= 0`, a single assert gives the whole condition.\n///\n/// ## When to use it\n/// Use this where a circuit **opens** a commitment it did not create. The creating circuit's own\n/// range checks already keep its digits in slot, so asserting the same here forces the opened\n/// coefficients to equal the committed ones \u2014 injectivity transfers the upstream bound without\n/// repeating it. One assert costs about half a two-sided `range_check_2bounds`.\n///\n/// It does **not** replace a bound the protocol requires in its own right, and it gives injectivity,\n/// not canonicality: it pins the opening, not a unique representative modulo `q`. A value that no\n/// upstream circuit bounded still needs its own check (see C1 `pk0` and C5 `pk0_agg`).\n///\n/// ## Compatibility\n/// Adding asserts does not change the packed value, so a commitment created with [`pack`] and\n/// opened with `pack_checked` agree. Adoption is per circuit and needs no commitment change.\npub fn pack_checked<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field);\n let radix = 2.pow_32((nibble_bits + 4) as Field);\n\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n for chunk in 0..num_chunks {\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n let digit = v + base;\n // digit < radix = 2^(nibble_bits + 4): the injectivity condition.\n digit.assert_max_bit_size::<((BIT + 3) / 4) * 4 + 4>();\n acc = acc * radix + digit;\n }\n\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Like [`flatten`], but packs with [`pack_checked`]. Use where a circuit opens a commitment it did\n/// not create.\npub fn flatten_checked<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n let packed = pack_checked::<A, BIT>(poly[j].coefficients);\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n inputs\n}\n\n/// Flatten `L` polynomials into a single linear stream of packed `Field` carriers.\n///\n/// ## What this does\n/// - For each CRT limb `j` in `0..L`, it packs the coefficients of `poly[j]`\n/// with `pack::<A, BIT>` and appends all resulting carriers to `inputs`.\n/// - The packing layout (nibble-aligned width and `group` size) is taken from\n/// `packing_layout::<BIT>()` and must match what `pack` uses.\n///\n/// ## Determinism & order\n/// - Preserves a stable order: iterate `j = 0..L`, then for each `j` append\n/// carriers in ascending chunk index `i = 0..num_chunks`.\n/// - This ensures transcripts remain deterministic across runs.\n///\n/// ## Generics\n/// - `A`: polynomial degree (number of coefficients per polynomial).\n/// - `L`: number of CRT bases (polynomials).\n/// - `BIT`: per-coefficient bit bound used by the packing layout (compile-time).\n///\n/// ## Returns\n/// - The same `inputs` vector, extended with all carriers in deterministic order.\npub fn flatten<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n // Pack its A coefficients into `num_chunks` carriers using the same BIT layout.\n let packed = pack::<A, BIT>(poly[j].coefficients);\n\n // Append carriers in-order to `inputs` to keep a stable transcript layout.\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n\n // Return the extended input stream.\n inputs\n}\n\n/// Pack `A` values into a `[Field]` vector of carriers using the shared hex-aligned layout.\n///\n/// ## What this does\n/// - Computes `(nibble_bits, group)` via `packing_layout::<BIT>()`.\n/// - Encodes each value as a limb `digit = v + 2^nibble_bits` and concatenates\n/// limbs in base `radix = 2^(nibble_bits + 4)` (one extra nibble of headroom).\n/// - Packs up to `group` limbs per carrier (fits within BN254 254-bit capacity).\n/// - Pads the last, partial carrier with `digit = 2^nibble_bits` to keep a stable layout.\n///\n/// ## Determinism & order\n/// - Processes values in increasing index order and emits carriers in chunk order\n/// (`chunk = 0..num_chunks`). Padding is deterministic.\n///\n/// ## Generics\n/// - `A`: number of input values.\n/// - `BIT`: per-value bit bound; rounded up to `nibble_bits` by `packing_layout`.\n///\n/// ## Preconditions / Notes\n/// - Call with the raw coefficients whose magnitudes already satisfy the BIT bound\n/// (as enforced by the upstream range checks); `pack` performs the signed -> unsigned\n/// shift internally via `v + base`.\n/// - `group >= 1` is enforced by `packing_layout::<BIT>()`.\n/// - Padding with `digit = 2^nibble_bits` encodes `zero limb` consistently.\n///\n/// ## Returns\n/// - A `[Field]` vector where each element is a concatenation of up to `group` limbs,\n/// suitable for hashing or transcript I/O.\npub fn pack<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n // Layout parameters: nibble-aligned width and limbs-per-carrier group size.\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field); // 2^nibble_bits\n let radix = 2.pow_32((nibble_bits + 4) as Field); // 2^(nibble_bits + 4)\n\n // Number of chunks to emit: ceil(A / group).\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n // Process in fixed-size chunks of `group` limbs.\n for chunk in 0..num_chunks {\n // How many real values go into this chunk.\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n // Build field element accumulator (big-endian concatenation in `radix`).\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n acc = acc * radix + (v + base);\n }\n\n // Pad remaining limb slots with the canonical zero-limb `digit = base`.\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Computes a cryptographic hash using the SAFE (Sponge API for Field Elements) protocol.\n///\n/// This is a convenience wrapper around the SAFE sponge API that handles the full\n/// lifecycle: initialization, absorption, squeezing, and finalization. It's designed\n/// for use in Fiat-Shamir challenge generation and commitment schemes within zero-knowledge circuits.\n///\n/// # Arguments\n/// * `domain_separator` - A 64-byte domain separator used to differentiate between\n/// different protocol instances and prevent cross-protocol attacks.\n/// * `inputs` - Vector of field elements to be absorbed into the sponge.\n/// * `io_pattern` - A 2-element array encoding the I/O pattern:\n/// - `io_pattern[0]`: Encoded ABSORB operation (MSB=1, lower 31 bits = length)\n/// - `io_pattern[1]`: Encoded SQUEEZE operation (MSB=0, lower 31 bits = length)\n///\n/// # Returns\n/// A vector of field elements squeezed from the sponge, with length determined by\n/// the SQUEEZE operation in the IO pattern.\n/// Proves `0 <= v < upper` using two one-sided bit-size checks.\n///\n/// `assert_max_bit_size` is one-sided: it forces a small **non-negative** field element. So bounding\n/// `v` gives `v >= 0`, and bounding `upper - 1 - v` gives `v <= upper - 1`. The pair composes into a\n/// two-sided interval without a comparison operator.\n///\n/// `BIT` must cover `upper`; the caller supplies it from the generated config.\npub fn assert_in_range<let BIT: u32>(v: Field, upper: Field) {\n v.assert_max_bit_size::<BIT>();\n (upper - 1 - v).assert_max_bit_size::<BIT>();\n}\n\npub fn compute_safe(domain_separator: [u8; 64], inputs: [Field], io_pattern: [u32; 2]) -> [Field] {\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(inputs);\n let digests = sponge.squeeze();\n sponge.finish();\n\n digests\n}\n\n#[test]\nfn test_flatten() {\n // Create test polynomials\n let poly1 = Polynomial::new([1, 2, 3]); // degree 2\n let poly2 = Polynomial::new([4, -16, 6]); // degree 2\n let poly3 = Polynomial::new([-7, 8, 9]); // degree 2\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 4>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n assert(result[0] == 0x11121310101010101010101010101010101010101010101010101010101010);\n assert(result[1] == 0x14001610101010101010101010101010101010101010101010101010101010); // -16 became 00 at 0x 14 00 16,\n assert(result[2] == 0x09181910101010101010101010101010101010101010101010101010101010); // -7 became 09 at 0x 09 18 19(16 - 7 = 9)\n}\n\n#[test]\nfn test_flatten_big() {\n // Create test polynomials\n let poly1 = Polynomial::new([\n 1791218451968394,\n 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n 5430119342984413,\n 704811298945172,\n 8901715723925099,\n 21888242871839275222246405745257275088548364400416034343698203098124042812559,\n 21888242871839275222246405745257275088548364400416034343698200215091693880034,\n ]);\n let poly2 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698200314078269634250,\n 21888242871839275222246405745257275088548364400416034343698200967285641915872,\n 2909990636858607,\n 7896103832076587,\n 2078397209533893,\n 21888242871839275222246405745257275088548364400416034343698199792421452734531,\n 614400389245817,\n 8290314119277588,\n ]);\n let poly3 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698201373175279892906,\n 21888242871839275222246405745257275088548364400416034343698201087241869723721,\n 6768789983786188,\n 635797784303388,\n 7610153424227556,\n 4633893206538324,\n 2016269760615332,\n 21888242871839275222246405745257275088548364400416034343698201007080554428142,\n ]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 54>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n\n // For the first index of result operation goes like this,\n\n // First four index of poly1\n // 1791218451968394,\n // 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n // 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n // 5430119342984413,\n\n // base + 1791218451968394 = 0x1065d1a8b8b718a\n // base - 5921327228407753 = 0xeaf69591f3b037 (negative coefficient shifted)\n // base - 3644467483862151 = 0xf30d604a3a9b79 (negative coefficient shifted)\n // base + 5430119342984413 = 0x1134aaa2e86ccdd\n assert(result[0] == 0x1065d1a8b8b718a0eaf69591f3b0370f30d604a3a9b791134aaa2e86ccdd);\n assert(result[1] == 0x1028105ab1b789411fa010339db66b0fc220f1326bc8e0f1e3f4cc1e02e1);\n assert(result[2] == 0x0f23dfbe7cd76c90f4901299312ddf10a569efe35acef11c0d76f005412b);\n assert(result[3] == 0x107624a8f605dc50f0638a368960421022ecb3cf36b7911d73ff2c27ec14);\n assert(result[4] == 0x0f6013a24e1b9a90f4fd2c158a08481180c2dba8af4cc10242413515171c);\n assert(result[5] == 0x11b0964eb898ce411076805680b85410729c962da53a40f4b44412d0f6ed);\n}\n\n#[test]\nfn test_flatten_small() {\n // Create test polynomials\n let poly1 = Polynomial::new([712345, 104857, 999999, 500001, 123, 654321, 77]);\n let poly2 = Polynomial::new([1, 524287, 888888, 23456, 34567, 765432, 0]);\n let poly3 = Polynomial::new([444444, 333333, 222222, 111111, 987654, 246810, 13579]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 20>(inputs, polynomials);\n\n assert(result[0] == 0x1ade991199991f423f17a12110007b19fbf110004d100000100000100000);\n assert(result[1] == 0x10000117ffff1d9038105ba01087071badf8100000100000100000100000);\n assert(result[2] == 0x16c81c15161513640e11b2071f120613c41a10350b100000100000100000);\n}\n\n#[test]\nfn test_safe_hashing_with_safe_helper() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let digests1 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests1.len() == 1);\n assert(digests1[0] != 0);\n\n // Test determinism\n let digests2 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests2.len() == 1);\n assert(digests2[0] != 0);\n assert(digests2[0] == digests1[0]);\n}\n\n#[test]\nfn test_pack() {\n // Test pack function directly with small values\n let values = [1, 2, 3, 4];\n let packed = pack::<4, 4>(values);\n\n // With BIT=4, nibble_bits=4, group should be floor(254/(4+4)) = 31\n // So all 4 values should fit in one carrier\n assert(packed.len() >= 1);\n\n // Test with negative values\n let values_neg = [-1, 2, -3, 4];\n let packed_neg = pack::<4, 4>(values_neg);\n assert(packed_neg.len() >= 1);\n}\n\n#[test]\nfn test_pack_single_value() {\n // Test packing a single value\n let values = [42];\n let packed = pack::<1, 8>(values);\n assert(packed.len() == 1);\n assert(packed[0] != 0);\n}\n\n#[test]\nfn test_pack_determinism() {\n // Test that packing is deterministic\n let values = [10, 20, 30];\n let packed1 = pack::<3, 8>(values);\n let packed2 = pack::<3, 8>(values);\n\n assert(packed1.len() == packed2.len());\n for i in 0..packed1.len() {\n assert(packed1[i] == packed2[i]);\n }\n}\n\n/// `pack_checked` must agree with `pack` for in-slot values. The two carry duplicate layout\n/// arithmetic, so if one is changed without the other, a commitment created by a circuit on `pack`\n/// stops matching one opened by a circuit on `pack_checked`. This pins that.\n#[test]\nfn test_pack_checked_matches_pack() {\n // Mixed signs, and values at the edge of the centred window for BIT = 35 (slot = 2^40).\n let vals: [Field; 7] = [0, 1, -1, 34359701504, -34359701504, 12345, -6789];\n let plain = pack::<7, 35>(vals);\n let checked = pack_checked::<7, 35>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n\n/// Same, at a width whose chunking leaves a partial final carrier (BIT = 57 -> group 3, 7 values).\n#[test]\nfn test_pack_checked_matches_pack_partial_chunk() {\n let vals: [Field; 7] = [5, -5, 72057594049265664, -72057594049265664, 1, 0, -1];\n let plain = pack::<7, 57>(vals);\n let checked = pack_checked::<7, 57>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n", path: "interfold/circuits/lib/src/math/helpers.nr", function_locations: [{ start: 1374, name: "packing_layout" }, { start: 3703, name: "pack_checked" }, { start: 4808, name: "flatten_checked" }, { start: 6096, name: "flatten" }, { start: 7946, name: "pack" }, { start: 10581, name: "assert_in_range" }, { start: 10770, name: "compute_safe" }, { start: 10968, name: "test_flatten" }, { start: 11911, name: "test_flatten_big" }, { start: 14812, name: "test_flatten_small" }, { start: 15639, name: "test_safe_hashing_with_safe_helper" }, { start: 16485, name: "test_pack" }, { start: 16955, name: "test_pack_single_value" }, { start: 17151, name: "test_pack_determinism" }, { start: 17749, name: "test_pack_checked_matches_pack" }, { start: 18287, name: "test_pack_checked_matches_pack_partial_chunk" }] }, "98": { 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;\nuse poseidon::poseidon2_permutation;\n\n/// SAFE (Sponge API for Field Elements)\n///\n/// This module provides a complete implementation of the SAFE API in Noir as defined in:\n/// "SAFE (Sponge API for Field Elements) - A Toolbox for ZK Hash Applications"\n/// see https://hackmd.io/bHgsH6mMStCVibM_wYvb2w#22-Sponge-state for more details.\n///\n/// SAFE provides a unified interface for cryptographic sponge functions that can be\n/// instantiated with various permutations to create hash functions, MACs, authenticated\n/// encryption schemes, and other cryptographic primitives for ZK proof systems.\n///\n/// This implementation follows the SAFE specification exactly, providing:\n/// - Complete API: START, ABSORB, SQUEEZE, FINISH operations.\n/// - Full security: Domain separation, tag computation, IO pattern validation.\n/// - Poseidon2 integration: Field-friendly permutation for ZK systems.\n/// - Specification compliance: All operations follow SAFE spec 2.4 exactly.\n/// - Natural API design: Variable-length inputs, automatic length detection from IO patterns.\n///\n/// # API Design\n///\n/// The API is designed for natural usage while maintaining type safety:\n/// - `absorb(input: [Field])`: Accepts variable-length arrays, no padding required.\n/// - `squeeze()`: Returns a vector with field element(s).\n/// - IO patterns automatically determine operation lengths for validation.\n\n/// Rate parameter for the sponge construction (number of field elements that can be absorbed per permutation call).\nglobal RATE: u32 = 3;\n\n/// Capacity parameter for the sponge construction (security parameter, typically 1-2 field elements).\nglobal CAPACITY: u32 = 1;\n\n/// Total state size (rate + capacity) in field elements.\nglobal STATE_SIZE: u32 = RATE + CAPACITY;\n\n/// IO Pattern encoding constants (from SAFE spec 2.3).\n///\n/// These constants are used for encoding operation types in the 32-bit word format:\n/// - MSB set to 1 for ABSORB operations\n/// - MSB set to 0 for SQUEEZE operations\n\n/// Flag for ABSORB operations (MSB = 1)\nglobal ABSORB_FLAG: u32 = 0x80000000;\n\n/// Flag for SQUEEZE operations (MSB = 0)\nglobal SQUEEZE_FLAG: u32 = 0x00000000;\n\n/// SAFE Sponge State (following spec 2.2)\n///\n/// The sponge state consists of the permutation state, tag, position counters,\n/// and IO pattern tracking as defined in the SAFE specification.\n///\n/// # Generic Parameters\n/// - `L`: The length of the IO pattern array\n///\n/// # Fields\n/// - `state`: Permutation state V in F^n (rate + capacity elements)\n/// - `tag`: Parameter tag T used for instance differentiation\n/// - `absorb_pos`: Current absorb position (<= n-c)\n/// - `squeeze_pos`: Current squeeze position (<= n-c)\n/// - `io_pattern`: Expected IO pattern for validation (encoded 32-bit words)\n/// - `io_count`: Current operation count for pattern tracking\npub struct SafeSponge<let L: u32> {\n /// Permutation state V in F^n (rate + capacity elements).\n state: [Field; STATE_SIZE],\n /// Parameter tag T used for instance differentiation.\n tag: Field,\n /// Current absorb position (<= n-c).\n absorb_pos: u32,\n /// Current squeeze position (<= n-c).\n squeeze_pos: u32,\n /// Expected IO pattern for validation.\n io_pattern: [u32; L],\n /// Current operation count for pattern tracking (spec 2.4: io_count).\n io_count: u32,\n}\n\nimpl<let L: u32> SafeSponge<L> {\n /// Initializes a new SAFE sponge instance with the given IO pattern and domain separator (following spec 2.4).\n ///\n /// # Arguments\n /// - `io_pattern`: Array of 32-bit encoded operations defining the expected sequence of ABSORB/SQUEEZE calls.\n /// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n /// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n ///\n /// # Returns\n /// A new `SafeSponge` instance with initialized state\n pub fn start(io_pattern: [u32; L], domain_separator: [u8; 64]) -> SafeSponge<L> {\n // Compute tag from IO pattern and domain separator (spec 2.3).\n let tag = compute_tag(io_pattern, domain_separator);\n\n let mut state = [0; STATE_SIZE];\n // Initialize capacity with tag (spec 2.4).\n // Add T to the first 128 bits of the state.\n state[0] = tag;\n\n SafeSponge { state, tag, absorb_pos: 0, squeeze_pos: 0, io_pattern, io_count: 0 }\n }\n\n /// Absorbs field elements into the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to absorb is automatically validated against the IO pattern.\n /// This method accepts variable-length arrays, making it natural to use without padding.\n ///\n /// # Arguments\n /// - `input`: Array of field elements to absorb (variable length, must match IO pattern)\n pub fn absorb(&mut self, input: [Field]) {\n let length = input.len() as u32;\n\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_absorb = (expected_encoded_word & ABSORB_FLAG) != 0;\n let expected_length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type and length\n assert(is_expected_absorb, "Expected ABSORB operation");\n assert(expected_length == length, "Length mismatch");\n\n // Process each element naturally (no unnecessary iterations).\n for i in 0..length {\n // If absorb_pos == (n-c) then permute and reset (spec 2.4).\n if self.absorb_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.absorb_pos = 0;\n }\n\n // Add X[i] to state at absorb_pos (spec 2.4).\n // Note: absorb_pos is the rate position, not capacity position.\n self.state[self.absorb_pos + CAPACITY] =\n self.state[self.absorb_pos + CAPACITY] + input[i];\n self.absorb_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = ABSORB_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n\n // Force permute at start of next SQUEEZE (spec 2.4).\n self.squeeze_pos = RATE;\n }\n\n /// Extracts field elements from the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to squeeze is automatically determined from the IO pattern.\n pub fn squeeze(&mut self) -> [Field] {\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_squeeze = (expected_encoded_word & ABSORB_FLAG) == 0;\n let length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type\n assert(is_expected_squeeze, "Expected SQUEEZE operation");\n\n let mut output: [Field] = [].as_vector();\n\n // SQUEEZE implementation following spec 2.4.\n // If length==0, loop won\'t execute (spec 2.4).\n for _ in 0..length {\n // If squeeze_pos==(n-c) then permute and reset (spec 2.4).\n if self.squeeze_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.squeeze_pos = 0;\n self.absorb_pos = 0;\n }\n // Set Y[i] to state element at squeeze_pos (spec 2.4).\n output = output.push_back(self.state[self.squeeze_pos + CAPACITY]);\n self.squeeze_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = SQUEEZE_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n output\n }\n\n /// Finalizes the sponge instance, verifying that all expected operations have been performed and clearing the internal state for security (following spec 2.4).\n ///\n /// This function is used to ensure that the sponge instance has been used correctly and to prevent information leakage.\n pub fn finish(&mut self) {\n // Check that io_count equals the length of the IO pattern expected (spec 2.4).\n assert(self.io_count == L, "IO pattern not completed");\n\n // Erase the state and its variables (spec 2.4).\n self.state = [0; STATE_SIZE];\n self.absorb_pos = 0;\n self.squeeze_pos = 0;\n self.io_count = 0;\n }\n\n /// Permute the state using Poseidon2 (following spec 2.4).\n ///\n /// Applies the Poseidon2 permutation to the current state.\n /// This is the core cryptographic primitive of the sponge construction.\n ///\n /// # Returns\n /// New state after permutation\n fn permute(self) -> [Field; STATE_SIZE] {\n poseidon2_permutation(self.state)\n }\n}\n\n/// Computes a unique tag for a sponge instance based on its IO pattern and domain separator.\n/// The tag is used to ensure that distinct instances behave like distinct functions.\n///\n/// # Arguments\n/// - `io_pattern`: Array of 32-bit encoded operations defining the sponge\'s usage pattern.\n/// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n/// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n///\n/// # Returns\n/// A field element representing the 128-bit tag.\npub fn compute_tag<let L: u32>(io_pattern: [u32; L], domain_separator: [u8; 64]) -> Field {\n // Step 1: Parse and aggregate consecutive operations of the same type\n let mut encoded_words = [0; L]; // Support up to L operations.\n let mut word_count = 0;\n let mut current_absorb_sum = 0;\n let mut current_squeeze_sum = 0;\n let mut last_was_absorb = false;\n\n for i in 0..L {\n if io_pattern[i] > 0 {\n // Parse operation type from MSB and length from lower 31 bits\n let is_absorb = (io_pattern[i] & ABSORB_FLAG) != 0;\n let length = io_pattern[i] & 0x7FFFFFFF; // Clear MSB to get length\n\n if is_absorb {\n if last_was_absorb {\n // Aggregate consecutive ABSORB operations\n current_absorb_sum += length;\n } else {\n // Start new ABSORB sequence\n if current_squeeze_sum > 0 {\n // Flush previous SQUEEZE sequence\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n current_squeeze_sum = 0;\n }\n current_absorb_sum = length;\n }\n last_was_absorb = true;\n } else {\n if !last_was_absorb {\n // Aggregate consecutive SQUEEZE operations\n current_squeeze_sum += length;\n } else {\n // Start new SQUEEZE sequence\n if current_absorb_sum > 0 {\n // Flush previous ABSORB sequence\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n current_absorb_sum = 0;\n }\n current_squeeze_sum = length;\n }\n last_was_absorb = false;\n }\n }\n }\n\n // Flush remaining operations\n if current_absorb_sum > 0 {\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n }\n if current_squeeze_sum > 0 {\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n }\n\n // Step 2: Serialize to byte string and append domain separator (following SAFE spec 2.3).\n // Buffer is 256 bytes: max 192 bytes for IO pattern (48 words) + 64 bytes for domain separator.\n // Note: We must use a fixed-size array because Noir\'s keccak256 requires [u8; N], not [u8].\n let max_io_pattern_bytes: u32 = 192; // 256 - 64 (domain separator)\n let io_pattern_bytes = word_count * 4;\n assert(\n io_pattern_bytes <= max_io_pattern_bytes,\n "IO pattern too large: max 48 aggregated words supported",\n );\n\n let mut input_bytes = [0u8; 256];\n let mut byte_count: u32 = 0;\n\n // Serialize encoded words to bytes (big-endian as per SAFE spec).\n // Note: Noir requires compile-time loop bounds, so we iterate over L (the array size)\n // instead of word_count (runtime value). The condition `i < word_count` ensures we only\n // process valid encoded words. This is safe because word_count <= L always holds\n // (we can have at most L encoded words from L input operations).\n for i in 0..L {\n if i < word_count {\n let word = encoded_words[i];\n input_bytes[byte_count] = (word >> 24) as u8;\n input_bytes[byte_count + 1] = (word >> 16) as u8;\n input_bytes[byte_count + 2] = (word >> 8) as u8;\n input_bytes[byte_count + 3] = word as u8;\n byte_count += 4;\n }\n }\n\n // Append full 64-byte domain separator.\n for i in 0..64 {\n input_bytes[byte_count] = domain_separator[i];\n byte_count += 1;\n }\n\n // Step 3: Hash with Keccak-256 and truncate to 128 bits.\n // Note: The SAFE spec uses SHA3-256, but we use Keccak-256 for Noir compatibility.\n // Keccak-256 differs from SHA3-256 in padding, but both provide equivalent security.\n let hash_bytes = keccak256(input_bytes, byte_count);\n\n // Convert first 128 bits (16 bytes) to field element.\n let mut tag_value: Field = 0;\n for i in 0..16 {\n tag_value = tag_value * 256 + (hash_bytes[i] as Field);\n }\n\n tag_value\n}\n\n#[test]\nfn test_safe_hashing() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_merkle_node() {\n // Verifies SAFE can be used for Merkle tree node hashing with pattern ABSORB(1) + ABSORB(1) + SQUEEZE(1).\n // Tests the ability to absorb multiple inputs before squeezing output.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let left = [123].as_vector();\n let right = [456].as_vector();\n\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(left);\n sponge.absorb(right);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(left);\n sponge2.absorb(right);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_commitment_scheme() {\n // Verifies SAFE can be used for commitment schemes with pattern ABSORB(3) + SQUEEZE(1).\n // Tests the ability to create deterministic commitments from multiple field elements.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let values = [10, 20, 30].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(values);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(values);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_domain_separation() {\n // Verifies that different domain separators produce different outputs for the same input.\n // This is crucial for cross-protocol security and preventing collisions between different applications.\n let elements = [1, 2, 3].as_vector();\n let domain1 = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let domain2 = [\n 0x41, 0x42, 0x43, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n\n let mut sponge1 = SafeSponge::start(io_pattern, domain1);\n sponge1.absorb(elements);\n let output1 = sponge1.squeeze();\n sponge1.finish();\n\n let mut sponge2 = SafeSponge::start(io_pattern, domain2);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output1.len() == 1);\n assert(output2.len() == 1);\n assert(output1[0] != output2[0]); // Different domain separators should produce different outputs\n}\n\n#[test]\nfn test_multiple_squeeze() {\n // Verifies that multiple field elements can be squeezed in a single operation.\n // Tests pattern ABSORB(3) + SQUEEZE(2) to ensure proper state management.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(2)\n let io_pattern = [0x80000003, 0x00000002];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 2);\n assert(output[0] != 0);\n assert(output[1] != 0);\n assert(output[0] != output[1]); // Different squeeze outputs should be different\n}\n\n#[test]\nfn test_zero_length_operations() {\n // Verifies that zero-length ABSORB and SQUEEZE operations are handled correctly.\n // Tests pattern ABSORB(0) + SQUEEZE(1) to ensure proper state transitions.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(0), SQUEEZE(1)\n let io_pattern = [0x80000000, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb([].as_vector());\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n}\n\n#[test]\nfn test_tag_computation() {\n // Verifies the tag computation algorithm using the example from the SAFE specification.\n // Pattern: ABSORB(3), ABSORB(3), SQUEEZE(3)\n // Should aggregate to: ABSORB(6), SQUEEZE(3)\n // Encoded as: [0x80000006, 0x00000003]\n // Tests determinism and pattern differentiation.\n\n let io_pattern = [0x80000003, 0x80000003, 0x00000003];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test determinism\n let tag2 = compute_tag(io_pattern, domain_separator);\n assert(tag == tag2);\n\n // Test that different patterns produce different tags\n let io_pattern2 = [0x80000003, 0x00000003]; // ABSORB(3), SQUEEZE(3) - different pattern\n let tag3 = compute_tag(io_pattern2, domain_separator);\n assert(tag != tag3);\n}\n\n#[test]\nfn test_tag_computation_debug() {\n println("=== SAFE Tag Computation Debug Test ===");\n\n // Test your specific pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))\n let io_pattern = [0x80000002, 0x00000002, 0x80000002];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n println(f"Testing pattern: {io_pattern}");\n println(\n f"Expected to aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(2)",\n );\n println(\n f"Expected encoded words: [0x80000002, 0x00000002, 0x80000002]",\n );\n println("");\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n println(f"=== Expected Rust Output ===");\n println("Pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))");\n println("Domain separator: 0x41424344...");\n println("Tag: 0xce3bb9ee4b2d41c42e9cdda38afe8b6a");\n println("");\n\n println(f"=== Noir Output ===");\n println(f"Tag: {tag}");\n println("");\n\n println("Compare the tag values above with Rust script!");\n}\n\n#[test]\nfn test_consecutive_absorb_aggregation() {\n // Test that consecutive ABSORB operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1) should aggregate to ABSORB(2), SQUEEZE(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(1) = [0x80000002, 0x00000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(2), SQUEEZE(1)\n let aggregated_pattern = [0x80000002, 0x00000001];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Consecutive ABSORB operations should aggregate to the same tag");\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive ABSORB operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive ABSORB Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001] (ABSORB(1), ABSORB(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000002, 0x00000001] (ABSORB(2), SQUEEZE(1))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_consecutive_squeeze_aggregation() {\n // Test that consecutive SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1) should aggregate to ABSORB(1), SQUEEZE(2)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x00000001, 0x00000001];\n\n // This should aggregate to: ABSORB(1), SQUEEZE(2) = [0x80000001, 0x00000002]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(1), SQUEEZE(2)\n let aggregated_pattern = [0x80000001, 0x00000002];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(\n tag == aggregated_tag,\n "Consecutive SQUEEZE operations should aggregate to the same tag",\n );\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive SQUEEZE operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive SQUEEZE Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x00000001, 0x00000001] (ABSORB(1), SQUEEZE(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000001, 0x00000002] (ABSORB(1), SQUEEZE(2))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_mixed_consecutive_aggregation() {\n // Test that both consecutive ABSORB and SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n // Should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1) = [0x80000002, 0x00000002, 0x80000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag\n let aggregated_pattern = [0x80000002, 0x00000002, 0x80000001]; // ABSORB(2), SQUEEZE(2), ABSORB(1)\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Mixed consecutive operations should aggregate to the same tag");\n\n println("=== Mixed Consecutive Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001]",\n );\n println(\n f" (ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1))",\n );\n println(f"Aggregated pattern: [0x80000002, 0x00000002, 0x80000001]");\n println(f" (ABSORB(2), SQUEEZE(2), ABSORB(1))");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n}\n\n#[test]\nfn test_large_io_pattern() {\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Create pattern with 48 alternating ABSORB(1) and SQUEEZE(1) operations\n // This is the maximum supported (48 words * 4 bytes = 192 bytes, leaving 64 for domain separator)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1; // ABSORB(1)\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1; // SQUEEZE(1)\n }\n }\n\n let tag = compute_tag(io_pattern, domain_separator);\n assert(tag != 0);\n}\n\n#[test]\nfn test_domain_separator_not_truncated() {\n // This test verifies that the domain separator is always included in the tag computation,\n // even for large IO patterns. If the domain separator were truncated, different domain\n // separators would produce the same tag for large patterns.\n\n let domain_separator_a = [\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41,\n ]; // All \'A\'s\n\n let domain_separator_b = [\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42,\n ]; // All \'B\'s\n\n // Create pattern with 48 alternating operations (max supported: 192 bytes of IO pattern)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1;\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1;\n }\n }\n\n let tag_a = compute_tag(io_pattern, domain_separator_a);\n let tag_b = compute_tag(io_pattern, domain_separator_b);\n\n // Tags MUST be different because domain separators are different.\n // If they were the same, it would mean the domain separator was truncated/ignored.\n assert(tag_a != tag_b, "Domain separator must affect tag even for large IO patterns");\n}\n', path: "interfold/circuits/lib/src/math/safe.nr", function_locations: [{ start: 4164, name: "SafeSponge<L>::start" }, { start: 5046, name: "SafeSponge<L>::absorb" }, { start: 6826, name: "SafeSponge<L>::squeeze" }, { start: 8494, name: "SafeSponge<L>::finish" }, { start: 9156, name: "SafeSponge<L>::permute" }, { start: 9830, name: "compute_tag" }, { start: 14128, name: "test_safe_hashing" }, { start: 15104, name: "test_merkle_node" }, { start: 16281, name: "test_commitment_scheme" }, { start: 17357, name: "test_domain_separation" }, { start: 18710, name: "test_multiple_squeeze" }, { start: 19639, name: "test_zero_length_operations" }, { start: 20415, name: "test_tag_computation" }, { start: 21477, name: "test_tag_computation_debug" }, { start: 22681, name: "test_consecutive_absorb_aggregation" }, { start: 24750, name: "test_consecutive_squeeze_aggregation" }, { start: 26760, name: "test_mixed_consecutive_aggregation" }, { start: 28520, name: "test_large_io_pattern" }, { start: 29333, name: "test_domain_separator_not_truncated" }] } } };
|
|
620
|
+
var user_data_encryption_default = { noir_version: "1.0.0-beta.26+40d6574f851d926f93e0c3a271bac3e6e82ac905", hash: "17981472203776318599", abi: { parameters: [{ name: "ct0_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "ct0_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "ct0_public_inputs", type: { kind: "array", length: 4, type: { kind: "field" } }, visibility: "private" }, { name: "ct0_key_hash", type: { kind: "field" }, visibility: "public" }, { name: "ct1_verification_key", type: { kind: "array", length: 115, type: { kind: "field" } }, visibility: "private" }, { name: "ct1_proof", type: { kind: "array", length: 410, type: { kind: "field" } }, visibility: "private" }, { name: "ct1_public_inputs", type: { kind: "array", length: 3, type: { kind: "field" } }, visibility: "private" }, { name: "ct1_key_hash", type: { kind: "field" }, visibility: "public" }], return_type: { abi_type: { kind: "tuple", fields: [{ kind: "field" }, { kind: "field" }, { kind: "field" }] }, visibility: "public" }, error_types: {} }, bytecode: "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", debug_symbols: "tZbdjoIwEIXfpddcdKb/vspmY1CrISFAEDbZGN99i9pWLtpV2L0aoczXM50zphdysLvxtK2aY3smm48L2fVVXVenbd3uy6FqG/f2ci2If9wOvbXuFXlad1ld2dtmIJtmrOuCfJX1ePvo3JXNLQ5l71ZpQWxzcNEBj1Vtp1/XImbTdKpW+pEMlKqQL3AGgDQApEBPUIZFApsRME1gLGhgXEIgIL5aBFBgXgNwTFXBV1ch/rUKlKEK1CxVRZaglSewdDezBCEjQS8hMBkJWi4i6OBJvkwD5+EcuIGkqzOmVMr4qVJaxsHSao5gOVdqHo6CamEixMwhGWNKoObBkMBjS41+R0e0BTUUkjpk5jwk9wOipOBJHSqNMBCcYcCYJEJnhsyEIeMURECAer0QLVhorMGUipy7hPItAfFUxxv+lBQ8QSIsIkga/q/SM4JstcOR/4HDUax2+C86XnM4qtUOR73a4WhWOzxbyHqHKyaCu4SYuevTPZX7qp9dZAh1ygsCroUFQbdVQZjLdYHfg7gH6T5z+6opXKf9+6rc1fZxFzqOzf7pajR8d37FX566vt3bw9jbaffbmtPzAw==", file_map: { "17": { 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;\nuse crate::static_assert;\n\n/// The size of the state accepted by the backend in `poseidon2_permutation`.\nglobal POSEIDON2_CONFIG_STATE_SIZE: u32 = poseidon2_config_state_size();\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\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s<let N: u32>(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n// docs:start:blake3\npub fn blake3<let N: u32>(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<let N: u32>(input: [u8; N]) -> [u8; 32] {}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment<let N: u32>(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<let N: u32>(\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 points[i] = EmbeddedCurveScalar::from_field(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<let N: u32>(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<let N: u32>(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 crate::assert_constant(separator);\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] = EmbeddedCurveScalar::from_field(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, true)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n crate::assert_constant(starting_index);\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators<let N: u32, let M: u32>(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\npub fn poseidon2_permutation<let N: u32>(input: [Field; N]) -> [Field; N] {\n static_assert(\n N == POSEIDON2_CONFIG_STATE_SIZE,\n f"the input length must equal the state size in the Poseidon2 config; expected {POSEIDON2_CONFIG_STATE_SIZE}, got {N}",\n );\n poseidon2_permutation_internal(input)\n}\n\n#[foreign(poseidon2_permutation)]\nfn poseidon2_permutation_internal<let N: u32>(input: [Field; N]) -> [Field; N] {}\n\n#[foreign(poseidon2_config_state_size)]\ncomptime fn poseidon2_config_state_size() -> u32 {}\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<H>(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<H>(_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 /// Returns the hash value without consuming the hasher.\n /// Override this for more efficient implementations that avoid copying.\n /// TODO: deprecate finish() and replace it\n fn finish_ref(&self) -> Field {\n (*self).finish()\n }\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<H>;\n\nimpl<H> BuildHasher for BuildHasherDefault<H>\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<H> Default for BuildHasherDefault<H>\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u8 {\n fn hash<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(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<H>(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl<T, let N: u32> Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl<T> Hash for [T]\nwhere\n T: Hash,\n{\n fn hash<H>(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<A> Hash for (A,)\nwhere\n A: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n }\n}\n\nimpl<A, B> Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash<H>(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl<A, B, C> Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash<H>(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<A, B, C, D> Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash<H>(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<A, B, C, D, E> 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<H>(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\nimpl<A, B, C, D, E, F> Hash for (A, B, C, D, E, F)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G> Hash for (A, B, C, D, E, F, G)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_> Hash for (A, B, C, D, E, F, G, H_)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I> Hash for (A, B, C, D, E, F, G, H_, I)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J> Hash for (A, B, C, D, E, F, G, H_, I, J)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K> Hash for (A, B, C, D, E, F, G, H_, I, J, K)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n }\n}\n\nimpl<A, B, C, D, E, F, G, H_, I, J, K, L> Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n F: Hash,\n G: Hash,\n H_: Hash,\n I: Hash,\n J: Hash,\n K: Hash,\n L: Hash,\n{\n fn hash<H>(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 self.5.hash(state);\n self.6.hash(state);\n self.7.hash(state);\n self.8.hash(state);\n self.9.hash(state);\n self.10.hash(state);\n self.11.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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n );\n}\n', path: "std/hash/mod.nr", function_locations: [{ start: 572, name: "sha256_compression" }, { start: 707, name: "keccakf1600" }, { start: 852, name: "blake2s" }, { start: 950, name: "blake3" }, { start: 1437, name: "__blake3" }, { start: 1555, name: "pedersen_commitment" }, { start: 1784, name: "pedersen_commitment_with_separator" }, { start: 2188, name: "pedersen_hash" }, { start: 2345, name: "pedersen_hash_with_separator" }, { start: 3339, name: "derive_generators" }, { start: 3698, name: "__derive_generators" }, { start: 3776, name: "poseidon2_permutation" }, { start: 4132, name: "poseidon2_permutation_internal" }, { start: 4225, name: "poseidon2_config_state_size" }, { start: 4536, name: "derive_hash" }, { start: 5327, name: "Hasher::finish_ref" }, { start: 5761, name: "<impl BuildHasher for BuildHasherDefault<H>>::build_hasher" }, { start: 5893, name: "<impl Default for BuildHasherDefault<H>>::default" }, { start: 6025, name: "<impl Hash for Field>::hash" }, { start: 6155, name: "<impl Hash for u8>::hash" }, { start: 6295, name: "<impl Hash for u16>::hash" }, { start: 6435, name: "<impl Hash for u32>::hash" }, { start: 6575, name: "<impl Hash for u64>::hash" }, { start: 6716, name: "<impl Hash for u128>::hash" }, { start: 6855, name: "<impl Hash for i8>::hash" }, { start: 7001, name: "<impl Hash for i16>::hash" }, { start: 7148, name: "<impl Hash for i32>::hash" }, { start: 7295, name: "<impl Hash for i64>::hash" }, { start: 7443, name: "<impl Hash for bool>::hash" }, { start: 7590, name: "<impl Hash for ()>::hash" }, { start: 7722, name: "<impl Hash for [T; N]>::hash" }, { start: 7911, name: "<impl Hash for [T]>::hash" }, { start: 8133, name: "<impl Hash for (A,)>::hash" }, { start: 8302, name: "<impl Hash for (A, B)>::hash" }, { start: 8518, name: "<impl Hash for (A, B, C)>::hash" }, { start: 8781, name: "<impl Hash for (A, B, C, D)>::hash" }, { start: 9091, name: "<impl Hash for (A, B, C, D, E)>::hash" }, { start: 9448, name: "<impl Hash for (A, B, C, D, E, F)>::hash" }, { start: 9852, name: "<impl Hash for (A, B, C, D, E, F, G)>::hash" }, { start: 10306, name: "<impl Hash for (A, B, C, D, E, F, G, H_)>::hash" }, { start: 10807, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I)>::hash" }, { start: 11355, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J)>::hash" }, { start: 11950, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K)>::hash" }, { start: 12593, name: "<impl Hash for (A, B, C, D, E, F, G, H_, I, J, K, L)>::hash" }, { start: 13187, name: "assert_pedersen" }] }, "22": { source: "pub mod hash;\npub mod aes128;\npub mod array;\npub mod vector;\npub mod ecdsa_secp256k1;\npub mod ecdsa_secp256r1;\npub mod embedded_curve_ops;\npub mod field;\npub mod collections;\npub mod compat;\npub mod convert;\npub mod option;\npub mod string;\npub mod test;\npub mod cmp;\npub mod ops;\npub mod default;\npub mod prelude;\npub mod runtime;\npub mod meta;\npub mod append;\npub mod mem;\npub mod panic;\npub mod hint;\n\nmod integer;\nmod primitive_docs;\nmod internal;\n\n// Oracle calls are required to be wrapped in an unconstrained function\n// Thus, the only argument to the `println` oracle is expected to always be an ident\n#[oracle(print)]\nunconstrained fn print_oracle<T>(with_newline: bool, input: T) {}\n\nunconstrained fn print_unconstrained<T>(with_newline: bool, input: T) {\n print_oracle(with_newline, input);\n}\n\n/// Print the given input to stdout followed by a newline\npub fn println<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(true, input);\n }\n}\n\n/// Print the given input to stdout\npub fn print<T>(input: T) {\n // Safety: a print statement cannot be constrained\n unsafe {\n print_unconstrained(false, input);\n }\n}\n\n/// Asserts the validity of the provided proof and public inputs against the provided verification key and hash.\n///\n/// The ACVM cannot determine whether the provided proof is valid during execution as this requires knowledge of\n/// the backend against which the program is being proven. However if an invalid proof if submitted, the program may\n/// fail to prove or the backend may generate a proof which will subsequently fail to verify.\n///\n/// # Important Note\n///\n/// If you are not developing your own backend such as [Barretenberg](https://github.com/AztecProtocol/barretenberg)\n/// you probably shouldn't need to interact with this function directly. It's easier and safer to use a verification\n/// library which is published by the developers of the backend which will document or enforce any safety requirements.\n///\n/// If you use this directly, you're liable to introduce underconstrainedness bugs and *your circuit will be insecure*.\n///\n/// # Arguments\n/// - verification_key: The verification key of the circuit to be verified.\n/// - proof: The proof to be verified.\n/// - public_inputs: The public inputs associated with `proof`\n/// - key_hash: The hash of `verification_key` of the form expected by the backend.\n/// - proof_type: An identifier for the proving scheme used to generate the proof to be verified. This allows\n/// for a single backend to support verifying multiple proving schemes.\n///\n/// # Constraining `key_hash`\n///\n/// The Noir compiler does not by itself constrain that `key_hash` is a valid hash of `verification_key`.\n/// This is because different backends may differ in how they hash their verification keys.\n/// It is then the responsibility of either the noir developer (by explicitly hashing the verification key\n/// in the correct manner) or by the proving system itself internally asserting the correctness of `key_hash`.\npub fn verify_proof_with_type<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {\n if !crate::runtime::is_unconstrained() {\n crate::assert_constant(proof_type);\n }\n verify_proof_internal(verification_key, proof, public_inputs, key_hash, proof_type);\n}\n\n#[foreign(recursive_aggregation)]\nfn verify_proof_internal<let N: u32, let M: u32, let K: u32>(\n verification_key: [Field; N],\n proof: [Field; M],\n public_inputs: [Field; K],\n key_hash: Field,\n proof_type: u32,\n) {}\n\n/// Asserts that the given value is known at compile-time.\n/// Useful for debugging for-loop bounds.\n#[builtin(assert_constant)]\npub fn assert_constant<T>(x: T) {}\n\n/// Asserts that the given value is both true and known at compile-time.\n/// The message can be a string, a format string, or any value, as long as it is known at compile-time\n#[builtin(static_assert)]\npub fn static_assert<T>(predicate: bool, message: T) {}\n\n/// Force a field value to be a witness instead of a constant in the compiled output.\n/// This is often only useful for debugging compiler optimizations.\n///\n/// This has no effect in unconstrained or comptime code.\n#[builtin(as_witness)]\npub fn as_witness(x: Field) {}\n", path: "std/lib.nr", function_locations: [{ start: 689, name: "print_oracle" }, { start: 763, name: "print_unconstrained" }, { start: 893, name: "println" }, { start: 1076, name: "print" }, { start: 3277, name: "verify_proof_with_type" }, { start: 3694, name: "verify_proof_internal" }, { start: 3859, name: "assert_constant" }, { start: 4118, name: "static_assert" }, { start: 4389, name: "as_witness" }] }, "52": { 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 bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};\nuse lib::math::commitments::{compute_commitment, DS_CIPHERTEXT, DS_PK_AGGREGATION};\n\nfn main(\n // P3: ct0 inner proof.\n ct0_verification_key: UltraHonkVerificationKey,\n ct0_proof: UltraHonkProof,\n ct0_public_inputs: [Field; 4], // pk0_commitment, ct0_commitment, k1_commitment, u_commitment\n ct0_key_hash: pub Field,\n // P3: ct1 inner proof.\n ct1_verification_key: UltraHonkVerificationKey,\n ct1_proof: UltraHonkProof,\n ct1_public_inputs: [Field; 3], // pk1_commitment, ct1_commitment, u_commitment\n ct1_key_hash: pub Field,\n) -> pub (Field, Field, Field) {\n verify_honk_proof_non_zk(\n ct0_verification_key,\n ct0_proof,\n ct0_public_inputs,\n ct0_key_hash,\n );\n verify_honk_proof_non_zk(\n ct1_verification_key,\n ct1_proof,\n ct1_public_inputs,\n ct1_key_hash,\n );\n\n // Verify that the u_commitment from the ct0 proof is the same as the u_commitment from the ct1 proof.\n assert(ct0_public_inputs[3] == ct1_public_inputs[2]);\n\n // Compute the ct commitment.\n let ct_inputs = [ct0_public_inputs[1], ct1_public_inputs[1]].as_vector();\n let ct_commitment = compute_commitment(ct_inputs, DS_CIPHERTEXT);\n\n // Threshold PK aggregation commitment (pk0 and pk1 limbs).\n let pk_inputs = [ct0_public_inputs[0], ct1_public_inputs[0]].as_vector();\n let pk_commitment = compute_commitment(pk_inputs, DS_PK_AGGREGATION);\n\n let k1_commitment = ct0_public_inputs[2];\n\n (pk_commitment, ct_commitment, k1_commitment)\n}\n", path: "interfold/circuits/bin/threshold/user_data_encryption/src/main.nr", function_locations: [{ start: 872, name: "main" }] }, "53": { source: '// Constants for UltraHonk recursive verifier inputs\npub global PROOF_TYPE_HONK: u32 = 0; // identifier for UltraHonk verfier\npub global RECURSIVE_PROOF_LENGTH: u32 = 410;\npub global ULTRA_VK_LENGTH_IN_FIELDS: u32 = 115;\n\npub type UltraHonkProof = [Field; RECURSIVE_PROOF_LENGTH];\npub type UltraHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\n// Constants for Rollup-UltraHonk recursive verifier inputs (N.B. this is equivalent to UH plus IPA claim and proof)\npub global PROOF_TYPE_ROLLUP_HONK: u32 = 4; // identifier for rollup-UltraHonk verfier\npub global PROOF_TYPE_ROOT_ROLLUP_HONK: u32 = 5; // identifier for root-rollup-UltraHonk verfier (closes the IPA accumulator)\npub global IPA_CLAIM_SIZE: u32 = 6;\npub global IPA_PROOF_LENGTH: u32 = 64;\npub global RECURSIVE_ROLLUP_HONK_PROOF_LENGTH: u32 =\n RECURSIVE_PROOF_LENGTH + IPA_CLAIM_SIZE + IPA_PROOF_LENGTH;\n\npub type RollupHonkProof = [Field; RECURSIVE_ROLLUP_HONK_PROOF_LENGTH];\npub type RollupHonkVerificationKey = [Field; ULTRA_VK_LENGTH_IN_FIELDS];\n\npub global PROOF_TYPE_HONK_ZK: u32 = 6; // identifier for UltraHonk ZK verfier\npub global RECURSIVE_ZK_PROOF_LENGTH: u32 = 450 + 8;\n\npub type UltraHonkZKProof = [Field; RECURSIVE_ZK_PROOF_LENGTH];\n\n// Verifies a non-zero-knowledge UltraHonk proof.\n//\n// Represents standard UltraHonk recursive verification for proofs that do not hide the witness.\n// Use this only in situations where zero-knowledge is not required.\npub fn verify_honk_proof_non_zk<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof with IPA (Inner Product Argument).\n//\n// This variant includes an IPA claim and proof appended to the standard UltraHonk proof,\n// used to amortize IPA recursive verification costs in rollup circuits.\npub fn verify_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROLLUP_HONK,\n );\n}\n\n// Verifies a non-zero-knowledge Rollup UltraHonk proof and closes the IPA accumulator in-circuit.\n//\n// Use this at the root of a rollup aggregation tree. The inner proof has the same RollupHonk shape\n// as `verify_rolluphonk_proof`, but instead of propagating an accumulated IPA claim out as a public\n// input, this variant performs a full native IPA verification inside the circuit. The outer circuit\n// should therefore be proved as a standard (non-rollup) UltraHonk circuit, producing a proof\n// suitable for verification by `verify_honk_proof` / `verify_honk_proof_non_zk`.\n//\n// When two RollupHonk inputs are verified this way in one circuit, their two IPA claims are first\n// accumulated into one, then the accumulated claim is fully verified.\npub fn verify_root_rolluphonk_proof<let N: u32>(\n verification_key: RollupHonkVerificationKey,\n proof: RollupHonkProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_ROOT_ROLLUP_HONK,\n );\n}\n\n// Verifies a zero-knowledge UltraHonk proof.\n//\n// This verifier is for UltraHonk proofs constructed with zero-knowledge, which hide the witness\n// values from the verifier.\n// Note: We intentionally choose the generic name "verify_honk_proof" for this function, as we\n// want ZK to be the default unless the user explicitly opts out.\npub fn verify_honk_proof<let N: u32>(\n verification_key: UltraHonkVerificationKey,\n proof: UltraHonkZKProof,\n public_inputs: [Field; N],\n key_hash: Field, // Hash of the verification key\n) {\n std::verify_proof_with_type(\n verification_key,\n proof,\n public_inputs,\n key_hash,\n PROOF_TYPE_HONK_ZK,\n );\n}\n', path: "/Users/ctrlc03/nargo/github.com/AztecProtocol/aztec-packages/v5.2.0/barretenberg/noir/bb_proof_verification/src/lib.nr", function_locations: [{ start: 1646, name: "verify_honk_proof_non_zk" }, { start: 2262, name: "verify_rolluphonk_proof" }, { start: 3386, name: "verify_root_rolluphonk_proof" }, { start: 4087, name: "verify_honk_proof" }] }, "87": { 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 crate::math::helpers::{compute_safe, flatten, flatten_checked};\nuse crate::math::polynomial::Polynomial;\n\n/// DOMAIN SEPARATORS\n\n// Domain separator - "PK"\npub global DS_PK: [u8; 64] = [\n 0x50, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_GENERATION"\npub global DS_PK_GENERATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_COMPUTATION"\npub global DS_SHARE_COMPUTATION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "SHARE_ENCRYPTION"\npub global DS_SHARE_ENCRYPTION: [u8; 64] = [\n 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "PK_AGGREGATION"\npub global DS_PK_AGGREGATION: [u8; 64] = [\n 0x50, 0x4b, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CIPHERTEXT"\npub global DS_CIPHERTEXT: [u8; 64] = [\n 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "AGGREGATED_SHARES"\npub global DS_AGGREGATED_SHARES: [u8; 64] = [\n 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45,\n 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "VK_HASH"\npub global DS_VK_HASH: [u8; 64] = [\n 0x56, 0x4b, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "RECURSIVE_AGGREGATION"\npub global DS_RECURSIVE_AGGREGATION: [u8; 64] = [\n 0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47,\n 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_PK_GENERATION"\npub global DS_CLG_PK_GENERATION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x50, 0x4b, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f,\n 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_ENCRYPTION"\npub global DS_CLG_SHARE_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_USER_DATA_ENCRYPTION"\npub global DS_CLG_USER_DATA_ENCRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e,\n 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n// Domain separator - "CLG_SHARE_DECRYPTION"\npub global DS_CLG_SHARE_DECRYPTION: [u8; 64] = [\n 0x43, 0x4c, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "THRESHOLD_DECRYPTION_SHARE"\npub global DS_THRESHOLD_DECRYPTION_SHARE: [u8; 64] = [\n 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n// Domain separator - "USER_DATA_ENCRYPTION_COMMITMENT"\npub global DS_USER_DATA_ENCRYPTION_COMMITMENT: [u8; 64] = [\n 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50,\n 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n];\n\n/// WRAPPERS\n\npub fn compute_commitment(inputs: [Field], domain_separator: [u8; 64]) -> Field {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 1])[0]\n}\n\npub fn compute_single_polynomial_commitment<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_single_polynomial_commitment`] with the digit asserts, for a polynomial this circuit\n/// carries no bound for. ct1 commits `u` this way: ct0 range-checks the same `u` to `u_bound` and the\n/// `user_data_encryption` fold asserts the two `u` commitments are equal, so making ct1\'s packing\n/// injective is what forces its `u` to equal ct0\'s \u2014 the bound transfers instead of being repeated.\n/// Produces the same value as the plain version for any input the plain version could legitimately\n/// have committed.\npub fn compute_single_polynomial_commitment_checked<let N: u32, let BIT: u32>(\n polynomial: Polynomial<N>,\n domain_separator: [u8; 64],\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT>([].as_vector(), polynomial);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_multiple_polynomial_commitment<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\npub fn compute_challenge<let L: u32>(inputs: [Field], domain_separator: [u8; 64]) -> [Field] {\n compute_safe(domain_separator, inputs, [0x80000000 | inputs.len(), 2 * L])\n}\n\npub fn single_polynomial_payload<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten::<_, _, BIT_POLY>(payload, inputs)\n}\n\npub fn single_polynomial_payload_checked<let N: u32, let BIT_POLY: u32>(\n payload: [Field],\n input: Polynomial<N>,\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, [input])\n}\n\npub fn multiple_polynomial_payload_checked<let N: u32, let L: u32, let BIT_POLY: u32>(\n payload: [Field],\n inputs: [Polynomial<N>; L],\n) -> [Field] {\n flatten_checked::<_, _, BIT_POLY>(payload, inputs)\n}\n\n/// COMMITMENTS\n\npub fn compute_dkg_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let mut payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n payload = multiple_polynomial_payload::<N, L, BIT_PK>(payload, pk1);\n\n compute_commitment(payload, DS_PK)\n}\n\npub fn compute_threshold_pk_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\n/// [`compute_multiple_polynomial_commitment`] for a circuit that **opens** this commitment.\npub fn compute_multiple_polynomial_commitment_checked<let N: u32, let L: u32, let BIT: u32>(\n polynomials: [Polynomial<N>; L],\n domain_separator: [u8; 64],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT>([].as_vector(), polynomials);\n compute_commitment(payload, domain_separator)\n}\n\n/// [`compute_threshold_pk_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C5). Asserts each packed digit fits its slot, which makes the opening unique and so\n/// forces the opened `pk0` to equal the `pk0` C1 committed. Produces the same value as the plain\n/// helper, so C1 needs no change.\npub fn compute_threshold_pk_commitment_checked<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_PK>([].as_vector(), pk0);\n compute_commitment(payload, DS_PK_GENERATION)\n}\n\npub fn compute_share_computation_sk_commitment<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\n/// [`compute_share_computation_sk_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2a). C1 creates it and bounds `sk` to the trinary range, so making the opening\n/// unique forces the witness to equal that `sk`.\npub fn compute_share_computation_sk_commitment_checked<let N: u32, let BIT_SK: u32>(\n sk: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_SK>([].as_vector(), sk);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_computation_e_sm_commitment<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_share_encryption_commitment_from_message<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\n/// [`compute_share_encryption_commitment_from_message`] for a circuit that **opens** this\n/// commitment rather than creating it (C4). Asserts each packed digit fits its slot, forcing the\n/// opened share to equal the share C2 committed. Same value as the plain helper, so C2 and C3 need\n/// no change.\npub fn compute_share_encryption_commitment_from_message_checked<let N: u32, let BIT_MSG: u32>(\n message: Polynomial<N>,\n) -> Field {\n let payload = single_polynomial_payload_checked::<N, BIT_MSG>([].as_vector(), message);\n compute_commitment(payload, DS_SHARE_ENCRYPTION)\n}\n\npub fn compute_aggregated_shares_commitment<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// Commitment to a threshold decryption share: all CRT limbs, first `K` coefficients per limb\n/// (same layout as `Polynomial<K>` in decrypted_shares_aggregation).\n///\n/// `NATIVE_BIT_WIDTH` must cover native coefficients in \\([0, q_l)\\) per limb (not centered `d` bounds).\npub fn compute_threshold_decryption_share_commitment<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload =\n multiple_polynomial_payload::<K, L, NATIVE_BIT_WIDTH>([].as_vector(), d_share_limbs);\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_threshold_decryption_share_commitment`] for a circuit that **opens** this commitment\n/// rather than creating it (C7). C6 creates it and pins each coefficient to a native residue in\n/// \\([0, q)\\) via `verify_d_native_trunc_binding`, so asserting the digits here makes the opening\n/// unique and forces the opened share to equal the one C6 committed. Produces the same value as the\n/// plain version for any input the plain version could legitimately have committed.\npub fn compute_threshold_decryption_share_commitment_checked<let K: u32, let L: u32, let NATIVE_BIT_WIDTH: u32>(\n d_share_limbs: [Polynomial<K>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<K, L, NATIVE_BIT_WIDTH>(\n [].as_vector(),\n d_share_limbs,\n );\n compute_commitment(payload, DS_THRESHOLD_DECRYPTION_SHARE)\n}\n\n/// [`compute_share_computation_e_sm_commitment`] for a circuit that **opens** this commitment rather\n/// than creating it (C2b). C1 creates it and bounds each `e_sm` limb to a centered residue.\npub fn compute_share_computation_e_sm_commitment_checked<let N: u32, let L: u32, let BIT_E_SM: u32>(\n e_sm: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_E_SM>([].as_vector(), e_sm);\n compute_commitment(payload, DS_SHARE_COMPUTATION)\n}\n\npub fn compute_pk_aggregation_commitment<let N: u32, let L: u32, let BIT_PK: u32>(\n pk0: [Polynomial<N>; L],\n pk1: [Polynomial<N>; L],\n) -> Field {\n let commit_pk0 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk0, DS_PK_AGGREGATION);\n let commit_pk1 = compute_multiple_polynomial_commitment::<N, L, BIT_PK>(pk1, DS_PK_AGGREGATION);\n\n let inputs = [commit_pk0, commit_pk1].as_vector();\n\n compute_commitment(inputs, DS_PK_AGGREGATION)\n}\n\npub fn compute_recursive_aggregation_commitment(payload: [Field]) -> Field {\n compute_commitment(payload, DS_RECURSIVE_AGGREGATION)\n}\n\npub fn compute_vk_hash(vk_hashes: [Field]) -> Field {\n compute_commitment(vk_hashes, DS_VK_HASH)\n}\n\n/// [`compute_aggregated_shares_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). C4 creates it after `normalize_aggregated` has reduced and centered the\n/// aggregate, so making the opening unique forces the witness to equal that canonical value.\npub fn compute_aggregated_shares_commitment_checked<let N: u32, let L: u32, let BIT_MSG: u32>(\n agg_shares: [Polynomial<N>; L],\n) -> Field {\n let payload = multiple_polynomial_payload_checked::<N, L, BIT_MSG>([].as_vector(), agg_shares);\n compute_commitment(payload, DS_AGGREGATED_SHARES)\n}\n\n/// [`compute_ciphertext_commitment`] for a circuit that **opens** this commitment rather than\n/// creating it (C6). No upstream circuit bounds the ciphertext limbs, so this buys injectivity\n/// rather than canonicality \u2014 which is the property wanted here: it forces C6 to decrypt exactly\n/// the ciphertext that was committed, whatever its representation.\npub fn compute_ciphertext_commitment_checked<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 =\n compute_multiple_polynomial_commitment_checked::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\npub fn compute_ciphertext_commitment<let N: u32, let L: u32, let BIT_CT: u32>(\n ct0: [Polynomial<N>; L],\n ct1: [Polynomial<N>; L],\n) -> Field {\n let commit_ct0 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct0, DS_CIPHERTEXT);\n let commit_ct1 = compute_multiple_polynomial_commitment::<N, L, BIT_CT>(ct1, DS_CIPHERTEXT);\n\n let inputs = [commit_ct0, commit_ct1].as_vector();\n\n compute_commitment(inputs, DS_CIPHERTEXT)\n}\n\n/// COMMITMENTS FOR CHALLENGES\n\npub fn compute_threshold_pk_challenge(payload: [Field]) -> Field {\n compute_commitment(payload, DS_CLG_PK_GENERATION)\n}\n\npub fn compute_share_encryption_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_SHARE_ENCRYPTION)\n}\n\npub fn compute_threshold_share_decryption_challenge<let L: u32>(payload: [Field]) -> Field {\n compute_challenge::<L>(payload, DS_CLG_SHARE_DECRYPTION)[0]\n}\n\npub fn compute_user_data_encryption_ct0_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n\npub fn compute_user_data_encryption_ct1_challenge<let L: u32>(payload: [Field]) -> [Field] {\n compute_challenge::<L>(payload, DS_CLG_USER_DATA_ENCRYPTION)\n}\n', path: "interfold/circuits/lib/src/math/commitments.nr", function_locations: [{ start: 7784, name: "compute_commitment" }, { start: 8012, name: "compute_single_polynomial_commitment" }, { start: 8827, name: "compute_single_polynomial_commitment_checked" }, { start: 9138, name: "compute_multiple_polynomial_commitment" }, { start: 9375, name: "compute_challenge" }, { start: 9585, name: "single_polynomial_payload" }, { start: 9784, name: "multiple_polynomial_payload" }, { start: 9970, name: "single_polynomial_payload_checked" }, { start: 10185, name: "multiple_polynomial_payload_checked" }, { start: 10406, name: "compute_dkg_pk_commitment" }, { start: 10733, name: "compute_threshold_pk_commitment" }, { start: 11138, name: "compute_multiple_polynomial_commitment_checked" }, { start: 11750, name: "compute_threshold_pk_commitment_checked" }, { start: 12008, name: "compute_share_computation_sk_commitment" }, { start: 12516, name: "compute_share_computation_sk_commitment_checked" }, { start: 12795, name: "compute_share_computation_e_sm_commitment" }, { start: 13067, name: "compute_share_encryption_commitment_from_message" }, { start: 13647, name: "compute_share_encryption_commitment_from_message_checked" }, { start: 13931, name: "compute_aggregated_shares_commitment" }, { start: 14512, name: "compute_threshold_decryption_share_commitment" }, { start: 15336, name: "compute_threshold_decryption_share_commitment_checked" }, { start: 15876, name: "compute_share_computation_e_sm_commitment_checked" }, { start: 16182, name: "compute_pk_aggregation_commitment" }, { start: 16571, name: "compute_recursive_aggregation_commitment" }, { start: 16686, name: "compute_vk_hash" }, { start: 17169, name: "compute_aggregated_shares_commitment_checked" }, { start: 17843, name: "compute_ciphertext_commitment_checked" }, { start: 18325, name: "compute_ciphertext_commitment" }, { start: 18724, name: "compute_threshold_pk_challenge" }, { start: 18866, name: "compute_share_encryption_challenge" }, { start: 19023, name: "compute_threshold_share_decryption_challenge" }, { start: 19183, name: "compute_user_data_encryption_ct0_challenge" }, { start: 19344, name: "compute_user_data_encryption_ct1_challenge" }] }, "89": { 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\n//! Helper functions for circuit construction and cryptographic operations.\nuse crate::math::polynomial::Polynomial;\nuse crate::math::safe::SafeSponge;\n\n/// Compute hex-aligned packing parameters for a given `BIT`.\n///\n/// # Purpose\n/// Returns `(nibble_bits, group)` for use by pack/flatten so layout stays consistent.\n/// - `nibble_bits`: ceil (`BIT`) to the next multiple of 4 (nibble alignment).\n/// - Examples: `BIT = 7 -> 8`, `BIT = 8 -> 8`, `BIT = 9 -> 12`, `BIT = 10 -> 12`, `BIT = 11 -> 12`,\n/// `BIT=16 -> 16`, `BIT = 17 -> 20`.\n/// - `group`: max number of encoded limbs that fit in one BN254 field element,\n/// when each limb uses an extra 4 bits (see below).\n///\n/// # Rationale\n/// - We align to nibbles so powers of two are hex-friendly and deterministic.\n/// - We reserve one extra nibble (4 bits) per stored value to lift signed\n/// coefficients into the non-negative range (e.g., store `v + 2^nibble_bits`),\n/// which implies a radix of `2^(nibble_bits + 4)`.\n///\n/// # Safety\n/// - Asserts `nibble_bits + 4 <= 254` to avoid mod-p wrap on BN254.\n/// - Ensures at least one limb fits: `group >= 1`.\nfn packing_layout<let BIT: u32>() -> (u32, u32) {\n // Ceil BIT up to the next multiple of 4 (nibble alignment).\n let nibble_bits = ((BIT + 3) / 4) * 4;\n\n // Each stored limb uses an extra nibble because negative coefficients\n // will be shifted to positive, so radix = 2^(nibble_bits+4).\n assert(nibble_bits + 4 <= 254);\n\n // Maximum limbs that fit in one BN254 element without wrap.\n let group = 254 / (nibble_bits + 4);\n // `group == 1` packs one value per carrier: it saves no sponge absorptions at all while still\n // charging the range checks that make packing injective, so it is always a loss. Measured at\n // BIT=143: +82% gates against absorbing directly. Reject it rather than pay for nothing.\n assert(group >= 2);\n (nibble_bits, group)\n}\n\n/// Like [`pack`], but asserts every digit fits its slot.\n///\n/// ## Why\n/// `pack` builds each carrier as a base-`radix` number, `acc = acc * radix + (v + base)`. That\n/// representation is unique only while every digit stays below `radix`. An unbounded value can\n/// overflow its slot and cancel against the next, so one commitment gains a second opening and a\n/// prover can choose which coefficients it opens to.\n///\n/// `digit < radix` is exactly `digit.assert_max_bit_size::<nibble_bits + 4>()`, and because\n/// `assert_max_bit_size` also forces `digit >= 0`, a single assert gives the whole condition.\n///\n/// ## When to use it\n/// Use this where a circuit **opens** a commitment it did not create. The creating circuit's own\n/// range checks already keep its digits in slot, so asserting the same here forces the opened\n/// coefficients to equal the committed ones \u2014 injectivity transfers the upstream bound without\n/// repeating it. One assert costs about half a two-sided `range_check_2bounds`.\n///\n/// It does **not** replace a bound the protocol requires in its own right, and it gives injectivity,\n/// not canonicality: it pins the opening, not a unique representative modulo `q`. A value that no\n/// upstream circuit bounded still needs its own check (see C1 `pk0` and C5 `pk0_agg`).\n///\n/// ## Compatibility\n/// Adding asserts does not change the packed value, so a commitment created with [`pack`] and\n/// opened with `pack_checked` agree. Adoption is per circuit and needs no commitment change.\npub fn pack_checked<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field);\n let radix = 2.pow_32((nibble_bits + 4) as Field);\n\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n for chunk in 0..num_chunks {\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n let digit = v + base;\n // digit < radix = 2^(nibble_bits + 4): the injectivity condition.\n digit.assert_max_bit_size::<((BIT + 3) / 4) * 4 + 4>();\n acc = acc * radix + digit;\n }\n\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Like [`flatten`], but packs with [`pack_checked`]. Use where a circuit opens a commitment it did\n/// not create.\npub fn flatten_checked<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n let packed = pack_checked::<A, BIT>(poly[j].coefficients);\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n inputs\n}\n\n/// Flatten `L` polynomials into a single linear stream of packed `Field` carriers.\n///\n/// ## What this does\n/// - For each CRT limb `j` in `0..L`, it packs the coefficients of `poly[j]`\n/// with `pack::<A, BIT>` and appends all resulting carriers to `inputs`.\n/// - The packing layout (nibble-aligned width and `group` size) is taken from\n/// `packing_layout::<BIT>()` and must match what `pack` uses.\n///\n/// ## Determinism & order\n/// - Preserves a stable order: iterate `j = 0..L`, then for each `j` append\n/// carriers in ascending chunk index `i = 0..num_chunks`.\n/// - This ensures transcripts remain deterministic across runs.\n///\n/// ## Generics\n/// - `A`: polynomial degree (number of coefficients per polynomial).\n/// - `L`: number of CRT bases (polynomials).\n/// - `BIT`: per-coefficient bit bound used by the packing layout (compile-time).\n///\n/// ## Returns\n/// - The same `inputs` vector, extended with all carriers in deterministic order.\npub fn flatten<let A: u32, let L: u32, let BIT: u32>(\n mut inputs: [Field],\n poly: [Polynomial<A>; L],\n) -> [Field] {\n for j in 0..L {\n // Pack its A coefficients into `num_chunks` carriers using the same BIT layout.\n let packed = pack::<A, BIT>(poly[j].coefficients);\n\n // Append carriers in-order to `inputs` to keep a stable transcript layout.\n for i in 0..packed.len() {\n inputs = inputs.push_back(packed[i]);\n }\n }\n\n // Return the extended input stream.\n inputs\n}\n\n/// Pack `A` values into a `[Field]` vector of carriers using the shared hex-aligned layout.\n///\n/// ## What this does\n/// - Computes `(nibble_bits, group)` via `packing_layout::<BIT>()`.\n/// - Encodes each value as a limb `digit = v + 2^nibble_bits` and concatenates\n/// limbs in base `radix = 2^(nibble_bits + 4)` (one extra nibble of headroom).\n/// - Packs up to `group` limbs per carrier (fits within BN254 254-bit capacity).\n/// - Pads the last, partial carrier with `digit = 2^nibble_bits` to keep a stable layout.\n///\n/// ## Determinism & order\n/// - Processes values in increasing index order and emits carriers in chunk order\n/// (`chunk = 0..num_chunks`). Padding is deterministic.\n///\n/// ## Generics\n/// - `A`: number of input values.\n/// - `BIT`: per-value bit bound; rounded up to `nibble_bits` by `packing_layout`.\n///\n/// ## Preconditions / Notes\n/// - Call with the raw coefficients whose magnitudes already satisfy the BIT bound\n/// (as enforced by the upstream range checks); `pack` performs the signed -> unsigned\n/// shift internally via `v + base`.\n/// - `group >= 1` is enforced by `packing_layout::<BIT>()`.\n/// - Padding with `digit = 2^nibble_bits` encodes `zero limb` consistently.\n///\n/// ## Returns\n/// - A `[Field]` vector where each element is a concatenation of up to `group` limbs,\n/// suitable for hashing or transcript I/O.\npub fn pack<let A: u32, let BIT: u32>(values: [Field; A]) -> [Field] {\n // Layout parameters: nibble-aligned width and limbs-per-carrier group size.\n let (nibble_bits, group) = packing_layout::<BIT>();\n\n let base = 2.pow_32(nibble_bits as Field); // 2^nibble_bits\n let radix = 2.pow_32((nibble_bits + 4) as Field); // 2^(nibble_bits + 4)\n\n // Number of chunks to emit: ceil(A / group).\n let num_chunks = (A + group - 1) / group;\n let mut out: [Field] = [].as_vector();\n\n // Process in fixed-size chunks of `group` limbs.\n for chunk in 0..num_chunks {\n // How many real values go into this chunk.\n let remain = A - (chunk * group);\n let take = if remain < group { remain } else { group };\n\n // Build field element accumulator (big-endian concatenation in `radix`).\n let mut acc = 0;\n for i in 0..take {\n let v = values[chunk * group + i];\n acc = acc * radix + (v + base);\n }\n\n // Pad remaining limb slots with the canonical zero-limb `digit = base`.\n for _ in 0..(group - take) {\n acc = acc * radix + base;\n }\n\n out = out.push_back(acc);\n }\n out\n}\n\n/// Computes a cryptographic hash using the SAFE (Sponge API for Field Elements) protocol.\n///\n/// This is a convenience wrapper around the SAFE sponge API that handles the full\n/// lifecycle: initialization, absorption, squeezing, and finalization. It's designed\n/// for use in Fiat-Shamir challenge generation and commitment schemes within zero-knowledge circuits.\n///\n/// # Arguments\n/// * `domain_separator` - A 64-byte domain separator used to differentiate between\n/// different protocol instances and prevent cross-protocol attacks.\n/// * `inputs` - Vector of field elements to be absorbed into the sponge.\n/// * `io_pattern` - A 2-element array encoding the I/O pattern:\n/// - `io_pattern[0]`: Encoded ABSORB operation (MSB=1, lower 31 bits = length)\n/// - `io_pattern[1]`: Encoded SQUEEZE operation (MSB=0, lower 31 bits = length)\n///\n/// # Returns\n/// A vector of field elements squeezed from the sponge, with length determined by\n/// the SQUEEZE operation in the IO pattern.\n/// Proves `0 <= v < upper` using two one-sided bit-size checks.\n///\n/// `assert_max_bit_size` is one-sided: it forces a small **non-negative** field element. So bounding\n/// `v` gives `v >= 0`, and bounding `upper - 1 - v` gives `v <= upper - 1`. The pair composes into a\n/// two-sided interval without a comparison operator.\n///\n/// `BIT` must cover `upper`; the caller supplies it from the generated config.\npub fn assert_in_range<let BIT: u32>(v: Field, upper: Field) {\n v.assert_max_bit_size::<BIT>();\n (upper - 1 - v).assert_max_bit_size::<BIT>();\n}\n\npub fn compute_safe(domain_separator: [u8; 64], inputs: [Field], io_pattern: [u32; 2]) -> [Field] {\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(inputs);\n let digests = sponge.squeeze();\n sponge.finish();\n\n digests\n}\n\n#[test]\nfn test_flatten() {\n // Create test polynomials\n let poly1 = Polynomial::new([1, 2, 3]); // degree 2\n let poly2 = Polynomial::new([4, -16, 6]); // degree 2\n let poly3 = Polynomial::new([-7, 8, 9]); // degree 2\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 4>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n assert(result[0] == 0x11121310101010101010101010101010101010101010101010101010101010);\n assert(result[1] == 0x14001610101010101010101010101010101010101010101010101010101010); // -16 became 00 at 0x 14 00 16,\n assert(result[2] == 0x09181910101010101010101010101010101010101010101010101010101010); // -7 became 09 at 0x 09 18 19(16 - 7 = 9)\n}\n\n#[test]\nfn test_flatten_big() {\n // Create test polynomials\n let poly1 = Polynomial::new([\n 1791218451968394,\n 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n 5430119342984413,\n 704811298945172,\n 8901715723925099,\n 21888242871839275222246405745257275088548364400416034343698203098124042812559,\n 21888242871839275222246405745257275088548364400416034343698200215091693880034,\n ]);\n let poly2 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698200314078269634250,\n 21888242871839275222246405745257275088548364400416034343698200967285641915872,\n 2909990636858607,\n 7896103832076587,\n 2078397209533893,\n 21888242871839275222246405745257275088548364400416034343698199792421452734531,\n 614400389245817,\n 8290314119277588,\n ]);\n let poly3 = Polynomial::new([\n 21888242871839275222246405745257275088548364400416034343698201373175279892906,\n 21888242871839275222246405745257275088548364400416034343698201087241869723721,\n 6768789983786188,\n 635797784303388,\n 7610153424227556,\n 4633893206538324,\n 2016269760615332,\n 21888242871839275222246405745257275088548364400416034343698201007080554428142,\n ]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 54>(inputs, polynomials);\n\n // Verify the flattened coefficients are in the correct positions\n // Every value shifted 1 nibble incase of negative integers\n\n // For the first index of result operation goes like this,\n\n // First four index of poly1\n // 1791218451968394,\n // 21888242871839275222246405745257275088548364400416034343698198265248580087864,\n // 21888242871839275222246405745257275088548364400416034343698200542108324633466,\n // 5430119342984413,\n\n // base + 1791218451968394 = 0x1065d1a8b8b718a\n // base - 5921327228407753 = 0xeaf69591f3b037 (negative coefficient shifted)\n // base - 3644467483862151 = 0xf30d604a3a9b79 (negative coefficient shifted)\n // base + 5430119342984413 = 0x1134aaa2e86ccdd\n assert(result[0] == 0x1065d1a8b8b718a0eaf69591f3b0370f30d604a3a9b791134aaa2e86ccdd);\n assert(result[1] == 0x1028105ab1b789411fa010339db66b0fc220f1326bc8e0f1e3f4cc1e02e1);\n assert(result[2] == 0x0f23dfbe7cd76c90f4901299312ddf10a569efe35acef11c0d76f005412b);\n assert(result[3] == 0x107624a8f605dc50f0638a368960421022ecb3cf36b7911d73ff2c27ec14);\n assert(result[4] == 0x0f6013a24e1b9a90f4fd2c158a08481180c2dba8af4cc10242413515171c);\n assert(result[5] == 0x11b0964eb898ce411076805680b85410729c962da53a40f4b44412d0f6ed);\n}\n\n#[test]\nfn test_flatten_small() {\n // Create test polynomials\n let poly1 = Polynomial::new([712345, 104857, 999999, 500001, 123, 654321, 77]);\n let poly2 = Polynomial::new([1, 524287, 888888, 23456, 34567, 765432, 0]);\n let poly3 = Polynomial::new([444444, 333333, 222222, 111111, 987654, 246810, 13579]);\n\n let polynomials = [poly1, poly2, poly3];\n\n // Initialize target array with zeros\n let inputs: [Field] = [].as_vector();\n\n // Flatten the polynomials\n let result = flatten::<_, _, 20>(inputs, polynomials);\n\n assert(result[0] == 0x1ade991199991f423f17a12110007b19fbf110004d100000100000100000);\n assert(result[1] == 0x10000117ffff1d9038105ba01087071badf8100000100000100000100000);\n assert(result[2] == 0x16c81c15161513640e11b2071f120613c41a10350b100000100000100000);\n}\n\n#[test]\nfn test_safe_hashing_with_safe_helper() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let digests1 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests1.len() == 1);\n assert(digests1[0] != 0);\n\n // Test determinism\n let digests2 = compute_safe(domain_separator, elements, io_pattern);\n\n assert(digests2.len() == 1);\n assert(digests2[0] != 0);\n assert(digests2[0] == digests1[0]);\n}\n\n#[test]\nfn test_pack() {\n // Test pack function directly with small values\n let values = [1, 2, 3, 4];\n let packed = pack::<4, 4>(values);\n\n // With BIT=4, nibble_bits=4, group should be floor(254/(4+4)) = 31\n // So all 4 values should fit in one carrier\n assert(packed.len() >= 1);\n\n // Test with negative values\n let values_neg = [-1, 2, -3, 4];\n let packed_neg = pack::<4, 4>(values_neg);\n assert(packed_neg.len() >= 1);\n}\n\n#[test]\nfn test_pack_single_value() {\n // Test packing a single value\n let values = [42];\n let packed = pack::<1, 8>(values);\n assert(packed.len() == 1);\n assert(packed[0] != 0);\n}\n\n#[test]\nfn test_pack_determinism() {\n // Test that packing is deterministic\n let values = [10, 20, 30];\n let packed1 = pack::<3, 8>(values);\n let packed2 = pack::<3, 8>(values);\n\n assert(packed1.len() == packed2.len());\n for i in 0..packed1.len() {\n assert(packed1[i] == packed2[i]);\n }\n}\n\n/// `pack_checked` must agree with `pack` for in-slot values. The two carry duplicate layout\n/// arithmetic, so if one is changed without the other, a commitment created by a circuit on `pack`\n/// stops matching one opened by a circuit on `pack_checked`. This pins that.\n#[test]\nfn test_pack_checked_matches_pack() {\n // Mixed signs, and values at the edge of the centred window for BIT = 35 (slot = 2^40).\n let vals: [Field; 7] = [0, 1, -1, 34359701504, -34359701504, 12345, -6789];\n let plain = pack::<7, 35>(vals);\n let checked = pack_checked::<7, 35>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n\n/// Same, at a width whose chunking leaves a partial final carrier (BIT = 57 -> group 3, 7 values).\n#[test]\nfn test_pack_checked_matches_pack_partial_chunk() {\n let vals: [Field; 7] = [5, -5, 72057594049265664, -72057594049265664, 1, 0, -1];\n let plain = pack::<7, 57>(vals);\n let checked = pack_checked::<7, 57>(vals);\n assert(plain.len() == checked.len());\n for i in 0..plain.len() {\n assert(plain[i] == checked[i]);\n }\n}\n", path: "interfold/circuits/lib/src/math/helpers.nr", function_locations: [{ start: 1374, name: "packing_layout" }, { start: 3703, name: "pack_checked" }, { start: 4808, name: "flatten_checked" }, { start: 6096, name: "flatten" }, { start: 7946, name: "pack" }, { start: 10581, name: "assert_in_range" }, { start: 10770, name: "compute_safe" }, { start: 10968, name: "test_flatten" }, { start: 11911, name: "test_flatten_big" }, { start: 14812, name: "test_flatten_small" }, { start: 15639, name: "test_safe_hashing_with_safe_helper" }, { start: 16485, name: "test_pack" }, { start: 16955, name: "test_pack_single_value" }, { start: 17151, name: "test_pack_determinism" }, { start: 17749, name: "test_pack_checked_matches_pack" }, { start: 18287, name: "test_pack_checked_matches_pack_partial_chunk" }] }, "98": { 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;\nuse poseidon::poseidon2_permutation;\n\n/// SAFE (Sponge API for Field Elements)\n///\n/// This module provides a complete implementation of the SAFE API in Noir as defined in:\n/// "SAFE (Sponge API for Field Elements) - A Toolbox for ZK Hash Applications"\n/// see https://hackmd.io/bHgsH6mMStCVibM_wYvb2w#22-Sponge-state for more details.\n///\n/// SAFE provides a unified interface for cryptographic sponge functions that can be\n/// instantiated with various permutations to create hash functions, MACs, authenticated\n/// encryption schemes, and other cryptographic primitives for ZK proof systems.\n///\n/// This implementation follows the SAFE specification exactly, providing:\n/// - Complete API: START, ABSORB, SQUEEZE, FINISH operations.\n/// - Full security: Domain separation, tag computation, IO pattern validation.\n/// - Poseidon2 integration: Field-friendly permutation for ZK systems.\n/// - Specification compliance: All operations follow SAFE spec 2.4 exactly.\n/// - Natural API design: Variable-length inputs, automatic length detection from IO patterns.\n///\n/// # API Design\n///\n/// The API is designed for natural usage while maintaining type safety:\n/// - `absorb(input: [Field])`: Accepts variable-length arrays, no padding required.\n/// - `squeeze()`: Returns a vector with field element(s).\n/// - IO patterns automatically determine operation lengths for validation.\n\n/// Rate parameter for the sponge construction (number of field elements that can be absorbed per permutation call).\nglobal RATE: u32 = 3;\n\n/// Capacity parameter for the sponge construction (security parameter, typically 1-2 field elements).\nglobal CAPACITY: u32 = 1;\n\n/// Total state size (rate + capacity) in field elements.\nglobal STATE_SIZE: u32 = RATE + CAPACITY;\n\n/// IO Pattern encoding constants (from SAFE spec 2.3).\n///\n/// These constants are used for encoding operation types in the 32-bit word format:\n/// - MSB set to 1 for ABSORB operations\n/// - MSB set to 0 for SQUEEZE operations\n\n/// Flag for ABSORB operations (MSB = 1)\nglobal ABSORB_FLAG: u32 = 0x80000000;\n\n/// Flag for SQUEEZE operations (MSB = 0)\nglobal SQUEEZE_FLAG: u32 = 0x00000000;\n\n/// SAFE Sponge State (following spec 2.2)\n///\n/// The sponge state consists of the permutation state, tag, position counters,\n/// and IO pattern tracking as defined in the SAFE specification.\n///\n/// # Generic Parameters\n/// - `L`: The length of the IO pattern array\n///\n/// # Fields\n/// - `state`: Permutation state V in F^n (rate + capacity elements)\n/// - `tag`: Parameter tag T used for instance differentiation\n/// - `absorb_pos`: Current absorb position (<= n-c)\n/// - `squeeze_pos`: Current squeeze position (<= n-c)\n/// - `io_pattern`: Expected IO pattern for validation (encoded 32-bit words)\n/// - `io_count`: Current operation count for pattern tracking\npub struct SafeSponge<let L: u32> {\n /// Permutation state V in F^n (rate + capacity elements).\n state: [Field; STATE_SIZE],\n /// Parameter tag T used for instance differentiation.\n tag: Field,\n /// Current absorb position (<= n-c).\n absorb_pos: u32,\n /// Current squeeze position (<= n-c).\n squeeze_pos: u32,\n /// Expected IO pattern for validation.\n io_pattern: [u32; L],\n /// Current operation count for pattern tracking (spec 2.4: io_count).\n io_count: u32,\n}\n\nimpl<let L: u32> SafeSponge<L> {\n /// Initializes a new SAFE sponge instance with the given IO pattern and domain separator (following spec 2.4).\n ///\n /// # Arguments\n /// - `io_pattern`: Array of 32-bit encoded operations defining the expected sequence of ABSORB/SQUEEZE calls.\n /// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n /// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n ///\n /// # Returns\n /// A new `SafeSponge` instance with initialized state\n pub fn start(io_pattern: [u32; L], domain_separator: [u8; 64]) -> SafeSponge<L> {\n // Compute tag from IO pattern and domain separator (spec 2.3).\n let tag = compute_tag(io_pattern, domain_separator);\n\n let mut state = [0; STATE_SIZE];\n // Initialize capacity with tag (spec 2.4).\n // Add T to the first 128 bits of the state.\n state[0] = tag;\n\n SafeSponge { state, tag, absorb_pos: 0, squeeze_pos: 0, io_pattern, io_count: 0 }\n }\n\n /// Absorbs field elements into the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to absorb is automatically validated against the IO pattern.\n /// This method accepts variable-length arrays, making it natural to use without padding.\n ///\n /// # Arguments\n /// - `input`: Array of field elements to absorb (variable length, must match IO pattern)\n pub fn absorb(&mut self, input: [Field]) {\n let length = input.len() as u32;\n\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_absorb = (expected_encoded_word & ABSORB_FLAG) != 0;\n let expected_length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type and length\n assert(is_expected_absorb, "Expected ABSORB operation");\n assert(expected_length == length, "Length mismatch");\n\n // Process each element naturally (no unnecessary iterations).\n for i in 0..length {\n // If absorb_pos == (n-c) then permute and reset (spec 2.4).\n if self.absorb_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.absorb_pos = 0;\n }\n\n // Add X[i] to state at absorb_pos (spec 2.4).\n // Note: absorb_pos is the rate position, not capacity position.\n self.state[self.absorb_pos + CAPACITY] =\n self.state[self.absorb_pos + CAPACITY] + input[i];\n self.absorb_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = ABSORB_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n\n // Force permute at start of next SQUEEZE (spec 2.4).\n self.squeeze_pos = RATE;\n }\n\n /// Extracts field elements from the sponge state, interleaving permutation calls as needed (following spec 2.4).\n ///\n /// The number of elements to squeeze is automatically determined from the IO pattern.\n pub fn squeeze(&mut self) -> [Field] {\n // Validate against IO pattern.\n assert(self.io_count < L);\n\n // Parse expected operation from io_pattern (encoded word)\n let expected_encoded_word = self.io_pattern[self.io_count];\n let is_expected_squeeze = (expected_encoded_word & ABSORB_FLAG) == 0;\n let length = expected_encoded_word & 0x7FFFFFFF;\n\n // Validate operation type\n assert(is_expected_squeeze, "Expected SQUEEZE operation");\n\n let mut output: [Field] = [].as_vector();\n\n // SQUEEZE implementation following spec 2.4.\n // If length==0, loop won\'t execute (spec 2.4).\n for _ in 0..length {\n // If squeeze_pos==(n-c) then permute and reset (spec 2.4).\n if self.squeeze_pos == RATE {\n // n-c = RATE.\n self.state = self.permute();\n self.squeeze_pos = 0;\n self.absorb_pos = 0;\n }\n // Set Y[i] to state element at squeeze_pos (spec 2.4).\n output = output.push_back(self.state[self.squeeze_pos + CAPACITY]);\n self.squeeze_pos += 1;\n }\n\n // Verify that the encoded word matches the expected pattern.\n let encoded_word = SQUEEZE_FLAG | length;\n assert(encoded_word == expected_encoded_word);\n\n self.io_count += 1;\n output\n }\n\n /// Finalizes the sponge instance, verifying that all expected operations have been performed and clearing the internal state for security (following spec 2.4).\n ///\n /// This function is used to ensure that the sponge instance has been used correctly and to prevent information leakage.\n pub fn finish(&mut self) {\n // Check that io_count equals the length of the IO pattern expected (spec 2.4).\n assert(self.io_count == L, "IO pattern not completed");\n\n // Erase the state and its variables (spec 2.4).\n self.state = [0; STATE_SIZE];\n self.absorb_pos = 0;\n self.squeeze_pos = 0;\n self.io_count = 0;\n }\n\n /// Permute the state using Poseidon2 (following spec 2.4).\n ///\n /// Applies the Poseidon2 permutation to the current state.\n /// This is the core cryptographic primitive of the sponge construction.\n ///\n /// # Returns\n /// New state after permutation\n fn permute(self) -> [Field; STATE_SIZE] {\n poseidon2_permutation(self.state)\n }\n}\n\n/// Computes a unique tag for a sponge instance based on its IO pattern and domain separator.\n/// The tag is used to ensure that distinct instances behave like distinct functions.\n///\n/// # Arguments\n/// - `io_pattern`: Array of 32-bit encoded operations defining the sponge\'s usage pattern.\n/// Each word has MSB=1 for ABSORB operations, MSB=0 for SQUEEZE operations.\n/// - `domain_separator`: 64-byte domain separator for cross-protocol security.\n///\n/// # Returns\n/// A field element representing the 128-bit tag.\npub fn compute_tag<let L: u32>(io_pattern: [u32; L], domain_separator: [u8; 64]) -> Field {\n // Step 1: Parse and aggregate consecutive operations of the same type\n let mut encoded_words = [0; L]; // Support up to L operations.\n let mut word_count = 0;\n let mut current_absorb_sum = 0;\n let mut current_squeeze_sum = 0;\n let mut last_was_absorb = false;\n\n for i in 0..L {\n if io_pattern[i] > 0 {\n // Parse operation type from MSB and length from lower 31 bits\n let is_absorb = (io_pattern[i] & ABSORB_FLAG) != 0;\n let length = io_pattern[i] & 0x7FFFFFFF; // Clear MSB to get length\n\n if is_absorb {\n if last_was_absorb {\n // Aggregate consecutive ABSORB operations\n current_absorb_sum += length;\n } else {\n // Start new ABSORB sequence\n if current_squeeze_sum > 0 {\n // Flush previous SQUEEZE sequence\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n current_squeeze_sum = 0;\n }\n current_absorb_sum = length;\n }\n last_was_absorb = true;\n } else {\n if !last_was_absorb {\n // Aggregate consecutive SQUEEZE operations\n current_squeeze_sum += length;\n } else {\n // Start new SQUEEZE sequence\n if current_absorb_sum > 0 {\n // Flush previous ABSORB sequence\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n current_absorb_sum = 0;\n }\n current_squeeze_sum = length;\n }\n last_was_absorb = false;\n }\n }\n }\n\n // Flush remaining operations\n if current_absorb_sum > 0 {\n encoded_words[word_count] = ABSORB_FLAG | current_absorb_sum;\n word_count += 1;\n }\n if current_squeeze_sum > 0 {\n encoded_words[word_count] = SQUEEZE_FLAG | current_squeeze_sum;\n word_count += 1;\n }\n\n // Step 2: Serialize to byte string and append domain separator (following SAFE spec 2.3).\n // Buffer is 256 bytes: max 192 bytes for IO pattern (48 words) + 64 bytes for domain separator.\n // Note: We must use a fixed-size array because Noir\'s keccak256 requires [u8; N], not [u8].\n let max_io_pattern_bytes: u32 = 192; // 256 - 64 (domain separator)\n let io_pattern_bytes = word_count * 4;\n assert(\n io_pattern_bytes <= max_io_pattern_bytes,\n "IO pattern too large: max 48 aggregated words supported",\n );\n\n let mut input_bytes = [0u8; 256];\n let mut byte_count: u32 = 0;\n\n // Serialize encoded words to bytes (big-endian as per SAFE spec).\n // Note: Noir requires compile-time loop bounds, so we iterate over L (the array size)\n // instead of word_count (runtime value). The condition `i < word_count` ensures we only\n // process valid encoded words. This is safe because word_count <= L always holds\n // (we can have at most L encoded words from L input operations).\n for i in 0..L {\n if i < word_count {\n let word = encoded_words[i];\n input_bytes[byte_count] = (word >> 24) as u8;\n input_bytes[byte_count + 1] = (word >> 16) as u8;\n input_bytes[byte_count + 2] = (word >> 8) as u8;\n input_bytes[byte_count + 3] = word as u8;\n byte_count += 4;\n }\n }\n\n // Append full 64-byte domain separator.\n for i in 0..64 {\n input_bytes[byte_count] = domain_separator[i];\n byte_count += 1;\n }\n\n // Step 3: Hash with Keccak-256 and truncate to 128 bits.\n // Note: The SAFE spec uses SHA3-256, but we use Keccak-256 for Noir compatibility.\n // Keccak-256 differs from SHA3-256 in padding, but both provide equivalent security.\n let hash_bytes = keccak256(input_bytes, byte_count);\n\n // Convert first 128 bits (16 bytes) to field element.\n let mut tag_value: Field = 0;\n for i in 0..16 {\n tag_value = tag_value * 256 + (hash_bytes[i] as Field);\n }\n\n tag_value\n}\n\n#[test]\nfn test_safe_hashing() {\n // Verifies basic hash functionality with a simple ABSORB(3) + SQUEEZE(1) pattern.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_merkle_node() {\n // Verifies SAFE can be used for Merkle tree node hashing with pattern ABSORB(1) + ABSORB(1) + SQUEEZE(1).\n // Tests the ability to absorb multiple inputs before squeezing output.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let left = [123].as_vector();\n let right = [456].as_vector();\n\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(left);\n sponge.absorb(right);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(left);\n sponge2.absorb(right);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_commitment_scheme() {\n // Verifies SAFE can be used for commitment schemes with pattern ABSORB(3) + SQUEEZE(1).\n // Tests the ability to create deterministic commitments from multiple field elements.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let values = [10, 20, 30].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(values);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n\n // Test determinism\n let mut sponge2 = SafeSponge::start(io_pattern, domain_separator);\n sponge2.absorb(values);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output2.len() == 1);\n assert(output2[0] != 0);\n}\n\n#[test]\nfn test_domain_separation() {\n // Verifies that different domain separators produce different outputs for the same input.\n // This is crucial for cross-protocol security and preventing collisions between different applications.\n let elements = [1, 2, 3].as_vector();\n let domain1 = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let domain2 = [\n 0x41, 0x42, 0x43, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(3), SQUEEZE(1)\n let io_pattern = [0x80000003, 0x00000001];\n\n let mut sponge1 = SafeSponge::start(io_pattern, domain1);\n sponge1.absorb(elements);\n let output1 = sponge1.squeeze();\n sponge1.finish();\n\n let mut sponge2 = SafeSponge::start(io_pattern, domain2);\n sponge2.absorb(elements);\n let output2 = sponge2.squeeze();\n sponge2.finish();\n\n assert(output1.len() == 1);\n assert(output2.len() == 1);\n assert(output1[0] != output2[0]); // Different domain separators should produce different outputs\n}\n\n#[test]\nfn test_multiple_squeeze() {\n // Verifies that multiple field elements can be squeezed in a single operation.\n // Tests pattern ABSORB(3) + SQUEEZE(2) to ensure proper state management.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n let elements = [1, 2, 3].as_vector();\n\n // Pattern: ABSORB(3), SQUEEZE(2)\n let io_pattern = [0x80000003, 0x00000002];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb(elements);\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 2);\n assert(output[0] != 0);\n assert(output[1] != 0);\n assert(output[0] != output[1]); // Different squeeze outputs should be different\n}\n\n#[test]\nfn test_zero_length_operations() {\n // Verifies that zero-length ABSORB and SQUEEZE operations are handled correctly.\n // Tests pattern ABSORB(0) + SQUEEZE(1) to ensure proper state transitions.\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Pattern: ABSORB(0), SQUEEZE(1)\n let io_pattern = [0x80000000, 0x00000001];\n let mut sponge = SafeSponge::start(io_pattern, domain_separator);\n sponge.absorb([].as_vector());\n let output = sponge.squeeze();\n sponge.finish();\n\n assert(output.len() == 1);\n assert(output[0] != 0);\n}\n\n#[test]\nfn test_tag_computation() {\n // Verifies the tag computation algorithm using the example from the SAFE specification.\n // Pattern: ABSORB(3), ABSORB(3), SQUEEZE(3)\n // Should aggregate to: ABSORB(6), SQUEEZE(3)\n // Encoded as: [0x80000006, 0x00000003]\n // Tests determinism and pattern differentiation.\n\n let io_pattern = [0x80000003, 0x80000003, 0x00000003];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test determinism\n let tag2 = compute_tag(io_pattern, domain_separator);\n assert(tag == tag2);\n\n // Test that different patterns produce different tags\n let io_pattern2 = [0x80000003, 0x00000003]; // ABSORB(3), SQUEEZE(3) - different pattern\n let tag3 = compute_tag(io_pattern2, domain_separator);\n assert(tag != tag3);\n}\n\n#[test]\nfn test_tag_computation_debug() {\n println("=== SAFE Tag Computation Debug Test ===");\n\n // Test your specific pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))\n let io_pattern = [0x80000002, 0x00000002, 0x80000002];\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n println(f"Testing pattern: {io_pattern}");\n println(\n f"Expected to aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(2)",\n );\n println(\n f"Expected encoded words: [0x80000002, 0x00000002, 0x80000002]",\n );\n println("");\n\n let tag = compute_tag(io_pattern, domain_separator);\n\n println(f"=== Expected Rust Output ===");\n println("Pattern [2, 2, 2] (ABSORB(2), SQUEEZE(2), ABSORB(2))");\n println("Domain separator: 0x41424344...");\n println("Tag: 0xce3bb9ee4b2d41c42e9cdda38afe8b6a");\n println("");\n\n println(f"=== Noir Output ===");\n println(f"Tag: {tag}");\n println("");\n\n println("Compare the tag values above with Rust script!");\n}\n\n#[test]\nfn test_consecutive_absorb_aggregation() {\n // Test that consecutive ABSORB operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1) should aggregate to ABSORB(2), SQUEEZE(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(1) = [0x80000002, 0x00000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(2), SQUEEZE(1)\n let aggregated_pattern = [0x80000002, 0x00000001];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Consecutive ABSORB operations should aggregate to the same tag");\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive ABSORB operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive ABSORB Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001] (ABSORB(1), ABSORB(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000002, 0x00000001] (ABSORB(2), SQUEEZE(1))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_consecutive_squeeze_aggregation() {\n // Test that consecutive SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1) should aggregate to ABSORB(1), SQUEEZE(2)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), SQUEEZE(1), SQUEEZE(1)\n let io_pattern = [0x80000001, 0x00000001, 0x00000001];\n\n // This should aggregate to: ABSORB(1), SQUEEZE(2) = [0x80000001, 0x00000002]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag ABSORB(1), SQUEEZE(2)\n let aggregated_pattern = [0x80000001, 0x00000002];\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(\n tag == aggregated_tag,\n "Consecutive SQUEEZE operations should aggregate to the same tag",\n );\n\n // Test that a different pattern produces a different tag\n let different_pattern = [0x80000001, 0x00000001, 0x80000001]; // ABSORB(1), SQUEEZE(1), ABSORB(1)\n let different_tag = compute_tag(different_pattern, domain_separator);\n\n // This should be different because it doesn\'t have consecutive SQUEEZE operations\n assert(tag != different_tag, "Different patterns should produce different tags");\n\n println("=== Consecutive SQUEEZE Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x00000001, 0x00000001] (ABSORB(1), SQUEEZE(1), SQUEEZE(1))",\n );\n println(\n f"Aggregated pattern: [0x80000001, 0x00000002] (ABSORB(1), SQUEEZE(2))",\n );\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n println(f"Different pattern tag: {different_tag}");\n}\n\n#[test]\nfn test_mixed_consecutive_aggregation() {\n // Test that both consecutive ABSORB and SQUEEZE operations are properly aggregated\n // Pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n // Should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1)\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Test pattern: ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1)\n let io_pattern = [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001];\n\n // This should aggregate to: ABSORB(2), SQUEEZE(2), ABSORB(1) = [0x80000002, 0x00000002, 0x80000001]\n let tag = compute_tag(io_pattern, domain_separator);\n\n // Test that the aggregated pattern produces the same tag\n let aggregated_pattern = [0x80000002, 0x00000002, 0x80000001]; // ABSORB(2), SQUEEZE(2), ABSORB(1)\n let aggregated_tag = compute_tag(aggregated_pattern, domain_separator);\n\n // The tags should be identical because the patterns are equivalent after aggregation\n assert(tag == aggregated_tag, "Mixed consecutive operations should aggregate to the same tag");\n\n println("=== Mixed Consecutive Aggregation Test ===");\n println(\n f"Original pattern: [0x80000001, 0x80000001, 0x00000001, 0x00000001, 0x80000001]",\n );\n println(\n f" (ABSORB(1), ABSORB(1), SQUEEZE(1), SQUEEZE(1), ABSORB(1))",\n );\n println(f"Aggregated pattern: [0x80000002, 0x00000002, 0x80000001]");\n println(f" (ABSORB(2), SQUEEZE(2), ABSORB(1))");\n println(f"Original tag: {tag}");\n println(f"Aggregated tag: {aggregated_tag}");\n}\n\n#[test]\nfn test_large_io_pattern() {\n let domain_separator = [\n 0x41, 0x42, 0x43, 0x44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0,\n ];\n\n // Create pattern with 48 alternating ABSORB(1) and SQUEEZE(1) operations\n // This is the maximum supported (48 words * 4 bytes = 192 bytes, leaving 64 for domain separator)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1; // ABSORB(1)\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1; // SQUEEZE(1)\n }\n }\n\n let tag = compute_tag(io_pattern, domain_separator);\n assert(tag != 0);\n}\n\n#[test]\nfn test_domain_separator_not_truncated() {\n // This test verifies that the domain separator is always included in the tag computation,\n // even for large IO patterns. If the domain separator were truncated, different domain\n // separators would produce the same tag for large patterns.\n\n let domain_separator_a = [\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,\n 0x41, 0x41, 0x41, 0x41,\n ]; // All \'A\'s\n\n let domain_separator_b = [\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,\n 0x42, 0x42, 0x42, 0x42,\n ]; // All \'B\'s\n\n // Create pattern with 48 alternating operations (max supported: 192 bytes of IO pattern)\n let mut io_pattern = [0u32; 48];\n for i in 0..48 {\n if i % 2 == 0 {\n io_pattern[i] = ABSORB_FLAG | 1;\n } else {\n io_pattern[i] = SQUEEZE_FLAG | 1;\n }\n }\n\n let tag_a = compute_tag(io_pattern, domain_separator_a);\n let tag_b = compute_tag(io_pattern, domain_separator_b);\n\n // Tags MUST be different because domain separators are different.\n // If they were the same, it would mean the domain separator was truncated/ignored.\n assert(tag_a != tag_b, "Domain separator must affect tag even for large IO patterns");\n}\n', path: "interfold/circuits/lib/src/math/safe.nr", function_locations: [{ start: 4164, name: "SafeSponge<L>::start" }, { start: 5046, name: "SafeSponge<L>::absorb" }, { start: 6826, name: "SafeSponge<L>::squeeze" }, { start: 8494, name: "SafeSponge<L>::finish" }, { start: 9156, name: "SafeSponge<L>::permute" }, { start: 9830, name: "compute_tag" }, { start: 14128, name: "test_safe_hashing" }, { start: 15104, name: "test_merkle_node" }, { start: 16281, name: "test_commitment_scheme" }, { start: 17357, name: "test_domain_separation" }, { start: 18710, name: "test_multiple_squeeze" }, { start: 19639, name: "test_zero_length_operations" }, { start: 20415, name: "test_tag_computation" }, { start: 21477, name: "test_tag_computation_debug" }, { start: 22681, name: "test_consecutive_absorb_aggregation" }, { start: 24750, name: "test_consecutive_squeeze_aggregation" }, { start: 26760, name: "test_mixed_consecutive_aggregation" }, { start: 28520, name: "test_large_io_pattern" }, { start: 29333, name: "test_domain_separator_not_truncated" }] } } };
|
|
621
621
|
|
|
622
622
|
// src/vote.ts
|
|
623
623
|
var _bbApi = null;
|