@aztec/protocol-contracts 6.0.0-nightly.20260728 → 6.0.0-nightly.20260730
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/artifacts/ContractClassRegistry.json +19 -19
- package/artifacts/ContractInstanceRegistry.json +26 -26
- package/artifacts/FeeJuice.json +24 -24
- package/dest/class-registry/contract_class_published_event.js +1 -1
- package/dest/scripts/generate_data.js +1 -1
- package/package.json +4 -4
- package/src/class-registry/contract_class_published_event.ts +1 -1
|
@@ -315,7 +315,7 @@
|
|
|
315
315
|
"start": 1012
|
|
316
316
|
}
|
|
317
317
|
],
|
|
318
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr",
|
|
318
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/types/src/contract_class_id.nr",
|
|
319
319
|
"source": "use crate::constants::DOM_SEP__CONTRACT_CLASS_ID;\nuse crate::traits::{Deserialize, Empty, FromField, Packable, Serialize, ToField};\nuse std::meta::derive;\n\n#[derive(Deserialize, Eq, Packable, Serialize)]\npub struct ContractClassId {\n pub inner: Field,\n}\n\nimpl ToField for ContractClassId {\n fn to_field(self) -> Field {\n self.inner\n }\n}\n\nimpl FromField for ContractClassId {\n fn from_field(value: Field) -> Self {\n Self { inner: value }\n }\n}\n\nimpl Empty for ContractClassId {\n fn empty() -> Self {\n Self { inner: 0 }\n }\n}\n\nimpl ContractClassId {\n pub fn compute(\n artifact_hash: Field,\n private_functions_root: Field,\n public_bytecode_commitment: Field,\n ) -> Self {\n let hash = crate::hash::poseidon2_hash_with_separator(\n [artifact_hash, private_functions_root, public_bytecode_commitment],\n DOM_SEP__CONTRACT_CLASS_ID,\n );\n ContractClassId::from_field(hash)\n }\n\n pub fn assert_is_zero(self) {\n assert(self.to_field() == 0);\n }\n}\n"
|
|
320
320
|
},
|
|
321
321
|
"17": {
|
|
@@ -615,7 +615,7 @@
|
|
|
615
615
|
"start": 17354
|
|
616
616
|
}
|
|
617
617
|
],
|
|
618
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/hash/poseidon2_chunks.nr",
|
|
618
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/types/src/hash/poseidon2_chunks.nr",
|
|
619
619
|
"source": "use crate::{constants::TWO_POW_64, poseidon2::Poseidon2Sponge};\n\n/// Computes, for a given sponge cache size, the number of items needed to reach cache size 1.\n/// Fails if the sponge cache size is greater than the permutation size.\nfn items_needed_for_cache_1<let PERMUTATION_SIZE: u32>(sponge_cache_size: u32) -> u32 {\n assert(\n sponge_cache_size <= PERMUTATION_SIZE,\n \"Sponge cache size is greater than permutation size\",\n );\n if sponge_cache_size == 0 {\n 1\n } else if sponge_cache_size == 1 {\n 0\n } else {\n (PERMUTATION_SIZE + 1) - sponge_cache_size\n }\n}\n\n/// Absorbs a number of items into a sponge from a larger array, one by one.\n/// Fails if the number of items is greater than the maximum number of items\n/// comptime defined.\nfn absorb_items<let N: u32, let MAX_ITEMS: u32>(\n mut sponge: Poseidon2Sponge,\n input: [Field; N],\n offset: u32,\n num_items: u32,\n) -> Poseidon2Sponge {\n let mut should_add = true;\n assert(num_items <= MAX_ITEMS, \"num_items is greater than MAX_ITEMS\");\n\n for i in 0..MAX_ITEMS {\n should_add &= i != num_items;\n if should_add {\n sponge.absorb(input[offset + i]);\n }\n }\n\n sponge\n}\n\n/// Absorbs a number of full permutations into a sponge from a larger array.\n/// Assumes permutations that can ingest 3 items at a time.\n/// Important: assumes that the sponge is in cache_size=1\n/// In order to use this function with non-zero num_permutations, you must first align the sponge to cache_size=1 using `items_needed_for_cache_1`.\nfn absorb_full_permutations<let NUM_ITEMS: u32, let MAX_PERMUTATIONS: u32>(\n mut sponge: Poseidon2Sponge,\n input: [Field; NUM_ITEMS],\n offset: u32,\n num_permutations: u32,\n) -> Poseidon2Sponge {\n std::static_assert(sponge.cache.len() == 3, \"Sponge cache must be 3 items\");\n assert(\n num_permutations <= MAX_PERMUTATIONS,\n \"num_permutations is greater than MAX_PERMUTATIONS\",\n );\n if (num_permutations != 0) {\n assert(sponge.cache_size == 1, \"Sponge must be in cache_size=1\");\n }\n let mut should_add = true;\n\n for i in 0..MAX_PERMUTATIONS {\n should_add &= i != num_permutations;\n let chunk_base_index = offset + i * 3;\n if should_add {\n sponge.cache[1] = input[chunk_base_index];\n sponge.cache[2] = input[chunk_base_index + 1];\n // Add cache to state before permutation (duplex operation)\n for j in 0..3 {\n sponge.state[j] += sponge.cache[j];\n }\n sponge.state = std::hash::poseidon2_permutation(sponge.state);\n sponge.cache[0] = input[chunk_base_index + 2];\n }\n }\n\n sponge\n}\n\n/// The below fn reduces gates of a conditional poseidon2 hash by approx 3x (thank you ~* Giant Brain Dev @IlyasRidhuan *~ for the idea)\n/// Why? Because when we call stdlib poseidon, we call absorb for each item. When absorbing is conditional, it seems the compiler does not know\n/// what cache_size will be when calling absorb, so it assigns the permutation gates for /each i/ rather than /every 3rd i/, which is actually required.\n/// The below code forces the compiler to:\n/// - absorb normally up to 2 times to set cache_size to 1\n/// - absorb in chunks of 3 to ensure perm. only happens every 3rd absorb\n/// - absorb normally up to 2 times to add any remaining values to the hash\n/// In fixed len hashes, the compiler is able to tell that it will only need to perform the permutation every 3 absorbs.\nfn absorb_in_chunks<let NUM_ITEMS: u32, let MAX_CHUNKS: u32>(\n mut sponge: Poseidon2Sponge,\n input: [Field; NUM_ITEMS],\n in_len: u32,\n) -> Poseidon2Sponge {\n std::static_assert(\n MAX_CHUNKS * 3 <= NUM_ITEMS,\n \"MAX_CHUNKS * 3 must be less than or equal to NUM_ITEMS\",\n );\n std::static_assert(\n (MAX_CHUNKS + 1) * 3 > NUM_ITEMS,\n \"MAX_CHUNKS + 1 * 3 must be greater than NUM_ITEMS\",\n );\n\n // In order to absorb full permutations, we need cache_size to be 1\n let prefix_items_to_align = items_needed_for_cache_1::<3>(sponge.cache_size);\n\n // Absorb up to cache size 1\n let num_prefix_items = std::cmp::min(prefix_items_to_align, in_len);\n\n sponge = absorb_items::<NUM_ITEMS, 2>(sponge, input, 0, num_prefix_items);\n\n // Now cache size is 1, we can absorb full permutations\n let num_full_permutations = (in_len - num_prefix_items) / 3;\n\n sponge = absorb_full_permutations::<NUM_ITEMS, MAX_CHUNKS>(\n sponge,\n input,\n num_prefix_items,\n num_full_permutations,\n );\n\n // Now we need to absorb any remaining items that don't complete a permutation\n let num_suffix_items = in_len - num_prefix_items - num_full_permutations * 3;\n\n sponge = absorb_items::<NUM_ITEMS, 2>(\n sponge,\n input,\n num_prefix_items + num_full_permutations * 3,\n num_suffix_items,\n );\n\n sponge\n}\n\npub fn poseidon2_absorb_in_chunks_existing_sponge<let NUM_ITEMS: u32>(\n mut sponge: Poseidon2Sponge,\n input: [Field; NUM_ITEMS],\n in_len: u32,\n) -> Poseidon2Sponge {\n assert(in_len <= NUM_ITEMS, \"Given in_len to absorb is larger than the input array len\");\n assert(!sponge.squeeze_mode, \"Cannot absorb in squeeze mode\");\n\n absorb_in_chunks::<NUM_ITEMS, (NUM_ITEMS - (NUM_ITEMS % 3)) / 3>(sponge, input, in_len)\n}\n\npub fn poseidon2_absorb_in_chunks<let N: u32>(input: [Field; N], in_len: u32) -> Poseidon2Sponge {\n let iv: Field = (in_len as Field) * TWO_POW_64;\n poseidon2_absorb_in_chunks_existing_sponge(Poseidon2Sponge::new(iv), input, in_len)\n}\n\nmod tests {\n use crate::constants::TWO_POW_64;\n use crate::poseidon2::Poseidon2Sponge;\n use super::{\n absorb_full_permutations, absorb_items, items_needed_for_cache_1,\n poseidon2_absorb_in_chunks, poseidon2_absorb_in_chunks_existing_sponge,\n };\n\n #[test]\n fn test_items_needed_for_cache_1() {\n // Test various cache sizes and their alignment requirements\n assert(items_needed_for_cache_1::<3>(0) == 1, \"Cache size 0 should need 1 item\");\n assert(items_needed_for_cache_1::<3>(1) == 0, \"Cache size 1 should need 0 items\");\n assert(items_needed_for_cache_1::<3>(2) == 2, \"Cache size 2 should need 2 items\");\n assert(items_needed_for_cache_1::<3>(3) == 1, \"Cache size 3 should need 1 item\");\n }\n\n #[test(should_fail_with = \"Sponge cache size is greater than permutation size\")]\n fn test_items_needed_for_cache_1_exceeds_permutation_size() {\n let _ = items_needed_for_cache_1::<3>(4);\n }\n\n #[test]\n fn test_absorb_items_zero_items() {\n let input: [Field; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n let sponge = Poseidon2Sponge::new(0);\n\n let mut result = absorb_items::<10, 2>(sponge, input, 0, 0);\n\n // Should produce same output as original sponge with no absorption\n assert(result.squeeze() == Poseidon2Sponge::new(0).squeeze(), \"No items absorbed\");\n }\n\n #[test]\n fn test_absorb_items_multiple() {\n let input: [Field; 10] = [42, 43, 3, 4, 5, 6, 7, 8, 9, 10];\n let sponge = Poseidon2Sponge::new(0);\n\n let mut result = absorb_items::<10, 2>(sponge, input, 0, 2);\n\n // Verify by comparing with manual absorption\n let mut expected = Poseidon2Sponge::new(0);\n expected.absorb(42);\n expected.absorb(43);\n\n assert(result.squeeze() == expected.squeeze(), \"Should absorb multiple items correctly\");\n }\n\n #[test]\n fn test_absorb_items_with_offset() {\n let input: [Field; 10] = [1, 2, 42, 43, 5, 6, 7, 8, 9, 10];\n let sponge = Poseidon2Sponge::new(0);\n\n let mut result = absorb_items::<10, 2>(sponge, input, 2, 2);\n\n // Verify items at offset 2 were absorbed\n let mut expected = Poseidon2Sponge::new(0);\n expected.absorb(42);\n expected.absorb(43);\n\n assert(result.squeeze() == expected.squeeze(), \"Should absorb items starting at offset\");\n }\n\n #[test(should_fail_with = \"num_items is greater than MAX_ITEMS\")]\n fn test_absorb_items_exceeds_max_items() {\n let input: [Field; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n let sponge = Poseidon2Sponge::new(0);\n\n let _ = absorb_items::<10, 2>(sponge, input, 0, 3);\n }\n\n #[test]\n fn test_absorb_full_permutations() {\n let input: [Field; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];\n let mut sponge = Poseidon2Sponge::new(0);\n // Set cache_size to 1 by absorbing one item\n sponge.absorb(100);\n\n let mut result = absorb_full_permutations::<12, 3>(sponge, input, 0, 2);\n\n // Verify by manually absorbing the same items\n let mut expected = Poseidon2Sponge::new(0);\n expected.absorb(100);\n for i in 0..6 {\n expected.absorb(input[i]);\n }\n\n assert(result.squeeze() == expected.squeeze(), \"Should absorb full permutations correctly\");\n }\n\n #[test]\n fn test_absorb_full_permutations_zero() {\n let input: [Field; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];\n let mut sponge = Poseidon2Sponge::new(0);\n sponge.absorb(100);\n\n let mut result = absorb_full_permutations::<12, 3>(sponge, input, 0, 0);\n\n // Should be same as original sponge with no additional absorption\n let mut expected = Poseidon2Sponge::new(0);\n expected.absorb(100);\n\n assert(\n result.squeeze() == expected.squeeze(),\n \"Zero permutations should not change sponge\",\n );\n }\n\n #[test(should_fail_with = \"Sponge must be in cache_size=1\")]\n fn test_absorb_full_permutations_wrong_cache_size() {\n let input: [Field; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];\n let sponge = Poseidon2Sponge::new(0); // cache_size is 0\n\n let _ = absorb_full_permutations::<12, 3>(sponge, input, 0, 1);\n }\n\n #[test(should_fail_with = \"num_permutations is greater than MAX_PERMUTATIONS\")]\n fn test_absorb_full_permutations_exceeds_max() {\n let input: [Field; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];\n let mut sponge = Poseidon2Sponge::new(0);\n sponge.absorb(100);\n\n let _ = absorb_full_permutations::<12, 2>(sponge, input, 0, 3);\n }\n\n #[test]\n fn existing_sponge_poseidon_chunks_matches_fixed() {\n let in_len = 501;\n let mut input: [Field; 4096] = [0; 4096];\n let fixed_input = [3; 501];\n assert(in_len == fixed_input.len()); // sanity check\n for i in 0..in_len {\n input[i] = 3;\n }\n // absorb 250 of the 501 things\n let empty_sponge = Poseidon2Sponge::new((in_len as Field) * TWO_POW_64);\n let first_sponge = poseidon2_absorb_in_chunks_existing_sponge(empty_sponge, input, 250);\n // now absorb the final 251 (since they are all 3s, im being lazy and not making a new array)\n let mut final_sponge = poseidon2_absorb_in_chunks_existing_sponge(first_sponge, input, 251);\n let fixed_len_hash = Poseidon2Sponge::hash(fixed_input, fixed_input.len());\n assert(final_sponge.squeeze() == fixed_len_hash);\n }\n\n #[test]\n fn poseidon_chunks_empty_inputs() {\n let in_len = 0;\n let input: [Field; 4096] = [0; 4096];\n let mut constructed_empty_sponge = poseidon2_absorb_in_chunks(input, in_len);\n let mut first_sponge =\n poseidon2_absorb_in_chunks_existing_sponge(constructed_empty_sponge, input, in_len);\n assert(first_sponge.squeeze() == constructed_empty_sponge.squeeze());\n }\n\n #[test]\n fn test_poseidon_chunks_various_lengths() {\n // Test multiple lengths to ensure correctness across different permutation boundaries\n let mut input: [Field; 20] = [0; 20];\n for i in 0..20 {\n input[i] = (i + 1) as Field;\n }\n\n // Test length 1\n let mut result = poseidon2_absorb_in_chunks(input, 1);\n assert(result.squeeze() == Poseidon2Sponge::hash([1], 1), \"Length 1 failed\");\n\n // Test length 3 (exactly one full permutation)\n let mut result = poseidon2_absorb_in_chunks(input, 3);\n assert(result.squeeze() == Poseidon2Sponge::hash([1, 2, 3], 3), \"Length 3 failed\");\n\n // Test length 7 (2 full permutations + 1)\n let mut result = poseidon2_absorb_in_chunks(input, 7);\n assert(\n result.squeeze() == Poseidon2Sponge::hash([1, 2, 3, 4, 5, 6, 7], 7),\n \"Length 7 failed\",\n );\n\n // Test length 10 (3 full permutations + 1)\n let mut result = poseidon2_absorb_in_chunks(input, 10);\n assert(\n result.squeeze() == Poseidon2Sponge::hash([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10),\n \"Length 10 failed\",\n );\n }\n\n #[test]\n fn test_poseidon_chunks_with_extra_non_zero_values() {\n let mut input: [Field; 10] = [0; 10];\n input[0] = 42;\n input[1] = 43;\n input[2] = 44;\n // Only absorb the 3 fields above.\n let in_len = 3;\n input[3] = 99;\n input[9] = 9999;\n\n let mut result = poseidon2_absorb_in_chunks(input, in_len);\n let expected = Poseidon2Sponge::hash([42, 43, 44], in_len);\n\n assert(result.squeeze() == expected);\n }\n\n #[test]\n fn test_incremental_absorption_matches_all_at_once() {\n let mut input: [Field; 30] = [0; 30];\n for i in 0..15 {\n input[i] = (i + 100) as Field;\n }\n\n // Absorb all 15 at once\n let iv = (15 as Field) * TWO_POW_64;\n let mut all_at_once =\n poseidon2_absorb_in_chunks_existing_sponge(Poseidon2Sponge::new(iv), input, 15);\n\n // Absorb incrementally: 7 + 8\n let mut incremental =\n poseidon2_absorb_in_chunks_existing_sponge(Poseidon2Sponge::new(iv), input, 7);\n let mut remaining: [Field; 30] = [0; 30];\n for i in 0..8 {\n remaining[i] = input[7 + i];\n }\n incremental = poseidon2_absorb_in_chunks_existing_sponge(incremental, remaining, 8);\n\n let all_at_once_hash = all_at_once.squeeze();\n let incremental_hash = incremental.squeeze();\n assert(\n all_at_once_hash == incremental_hash,\n \"Incremental absorption should match all-at-once\",\n );\n\n // verify that both match using the standard poseidon2 hash\n let mut exact_input = [0; 15];\n for i in 0..15 {\n exact_input[i] = (i + 100) as Field;\n }\n let exact_hash = Poseidon2Sponge::hash(exact_input, 15);\n assert(all_at_once_hash == exact_hash, \"Chunked should match standard poseidon2 hash\");\n }\n\n #[test(should_fail_with = \"Given in_len to absorb is larger than the input array len\")]\n fn test_poseidon_chunks_length_exceeds_array() {\n let input: [Field; 5] = [1, 2, 3, 4, 5];\n let _ = poseidon2_absorb_in_chunks(input, 10);\n }\n\n #[test]\n fn test_large_absorption() {\n // Test a larger number to ensure the optimization works correctly\n let mut input: [Field; 100] = [0; 100];\n for i in 0..50 {\n input[i] = (i + 1) as Field;\n }\n\n let mut result = poseidon2_absorb_in_chunks(input, 50);\n\n // Verify against standard approach\n let mut expected_input = [0; 50];\n for i in 0..50 {\n expected_input[i] = (i + 1) as Field;\n }\n let expected = Poseidon2Sponge::hash(expected_input, 50);\n\n assert(result.squeeze() == expected, \"Large absorption should work correctly\");\n }\n\n #[test]\n fn fuzz_7_items(items: [Field; 7], mut in_len: u32) {\n in_len = std::cmp::min(in_len, 7);\n let iv: Field = (in_len as Field) * TWO_POW_64;\n let mut sponge = Poseidon2Sponge::new(iv);\n\n for i in 0..7 {\n if i < in_len {\n sponge.absorb(items[i]);\n }\n }\n let expected_result = sponge.squeeze();\n let chunked_result = poseidon2_absorb_in_chunks(items, in_len).squeeze();\n assert(chunked_result == expected_result, \"Fuzz 7 items should match expected\");\n }\n\n #[test]\n fn fuzz_30_items(items: [Field; 30], mut in_len: u32) {\n in_len = std::cmp::min(in_len, 30);\n let iv: Field = (in_len as Field) * TWO_POW_64;\n let mut sponge = Poseidon2Sponge::new(iv);\n\n for i in 0..30 {\n if i < in_len {\n sponge.absorb(items[i]);\n }\n }\n let expected_result = sponge.squeeze();\n let chunked_result = poseidon2_absorb_in_chunks(items, in_len).squeeze();\n assert(chunked_result == expected_result, \"Fuzz 30 items should match expected\");\n }\n\n #[test]\n fn fuzz_1024_items(items: [Field; 1024], mut in_len: u32) {\n in_len = std::cmp::min(in_len, 1024);\n let iv: Field = (in_len as Field) * TWO_POW_64;\n let mut sponge = Poseidon2Sponge::new(iv);\n\n for i in 0..1024 {\n if i < in_len {\n sponge.absorb(items[i]);\n }\n }\n let expected_result = sponge.squeeze();\n let chunked_result = poseidon2_absorb_in_chunks(items, in_len).squeeze();\n assert(chunked_result == expected_result, \"Fuzz 1024 items should match expected\");\n }\n\n #[test(should_fail_with = \"Cannot absorb in squeeze mode\")]\n fn test_cannot_absorb_after_squeeze() {\n let input: [Field; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n let mut sponge = Poseidon2Sponge::new(0);\n\n // Absorb some data\n sponge.absorb(100);\n\n // Squeeze to enter squeeze mode\n let _ = sponge.squeeze();\n\n // Try to absorb more data using chunked function - should fail\n let _ = poseidon2_absorb_in_chunks_existing_sponge(sponge, input, 5);\n }\n\n}\n"
|
|
620
620
|
},
|
|
621
621
|
"182": {
|
|
@@ -737,7 +737,7 @@
|
|
|
737
737
|
"start": 16359
|
|
738
738
|
}
|
|
739
739
|
],
|
|
740
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr",
|
|
740
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/types/src/hash.nr",
|
|
741
741
|
"source": "mod poseidon2_chunks;\n\nuse crate::{\n abis::{\n contract_class_function_leaf_preimage::ContractClassFunctionLeafPreimage,\n function_selector::FunctionSelector, nullifier::Nullifier, private_log::PrivateLog,\n transaction::tx_request::TxRequest,\n },\n address::{AztecAddress, EthAddress},\n constants::{\n CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, DOM_SEP__NOTE_HASH_NONCE,\n DOM_SEP__PRIVATE_LOG_FIRST_FIELD, DOM_SEP__SILOED_NOTE_HASH, DOM_SEP__SILOED_NULLIFIER,\n DOM_SEP__UNIQUE_NOTE_HASH, FUNCTION_TREE_HEIGHT, NULL_MSG_SENDER_CONTRACT_ADDRESS,\n TWO_POW_64,\n },\n merkle_tree::root_from_sibling_path,\n messaging::l2_to_l1_message::L2ToL1Message,\n poseidon2::Poseidon2Sponge,\n side_effect::{Counted, Scoped},\n traits::{FromField, Hash, ToField},\n utils::field::{field_from_bytes, field_from_bytes_32_trunc},\n};\n\npub use poseidon2_chunks::poseidon2_absorb_in_chunks_existing_sponge;\nuse poseidon2_chunks::poseidon2_absorb_in_chunks;\nuse std::embedded_curve_ops::EmbeddedCurveScalar;\n\n// TODO: refactor these into their own files: sha256, poseidon2, some protocol-specific hash computations, some merkle computations.\n\npub fn sha256_to_field<let N: u32>(bytes_to_hash: [u8; N]) -> Field {\n let sha256_hashed = sha256::digest(bytes_to_hash);\n let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed);\n\n hash_in_a_field\n}\n\npub fn private_functions_root_from_siblings(\n selector: FunctionSelector,\n vk_hash: Field,\n function_leaf_index: Field,\n function_leaf_sibling_path: [Field; FUNCTION_TREE_HEIGHT],\n) -> Field {\n let function_leaf_preimage = ContractClassFunctionLeafPreimage { selector, vk_hash };\n let function_leaf = function_leaf_preimage.hash();\n root_from_sibling_path(\n function_leaf,\n function_leaf_index,\n function_leaf_sibling_path,\n )\n}\n\n/// Siloing in the context of Aztec refers to the process of hashing a note hash with a contract address (this way\n/// the note hash is scoped to a specific contract). This is used to prevent intermingling of notes between contracts.\npub fn compute_siloed_note_hash(contract_address: AztecAddress, note_hash: Field) -> Field {\n poseidon2_hash_with_separator(\n [contract_address.to_field(), note_hash],\n DOM_SEP__SILOED_NOTE_HASH,\n )\n}\n\n/// Computes unique, siloed note hashes from siloed note hashes.\n///\n/// The protocol injects uniqueness into every note_hash, so that every single note_hash in the\n/// tree is unique. This prevents faerie gold attacks, where a malicious sender could create\n/// two identical note_hashes for a recipient (meaning only one would be nullifiable in future).\n///\n/// Most privacy protocols will inject the note's leaf_index (its position in the Note Hashes Tree)\n/// into the note, but this requires the creator of a note to wait until their tx is included in\n/// a block to know the note's final note hash (the unique, siloed note hash), because inserting\n/// leaves into trees is the job of a block producer.\n///\n/// We took a different approach so that the creator of a note will know each note's unique, siloed\n/// note hash before broadcasting their tx to the network.\n/// (There was also a historical requirement relating to \"chained transactions\" -- a feature that\n/// Aztec Connect had to enable notes to be spent from distinct txs earlier in the same block,\n/// and hence before an archive block root had been established for that block -- but that feature\n/// was abandoned for the Aztec Network for having too many bad tradeoffs).\n///\n/// (\n/// Edit: it is no longer true that all final note_hashes will be known by the creator of a tx\n/// before they send it to the network. If a tx makes public function calls, then _revertible_\n/// note_hashes that are created in private will not be made unique in private by the Reset circuit,\n/// but will instead be made unique by the AVM, because the `note_index_in_tx` will not be known\n/// until the AVM has executed the public functions of the tx. (See an explanation in\n/// reset_output_composer.nr for why).\n/// For some such txs, the `note_index_in_tx` might still be predictable through simulation, but\n/// for txs whose public functions create a varying number of non-revertible notes (determined at\n/// runtime), the `note_index_in_tx` will not be deterministically derivable before submitting the\n/// tx to the network.\n/// )\n///\n/// We use the `first_nullifier` of a tx as a seed of uniqueness. We have a guarantee that there will\n/// always be at least one nullifier per tx, because the init circuit will create one if one isn't\n/// created naturally by any functions of the tx. (Search \"protocol_nullifier\").\n/// We combine the `first_nullifier` with the note's index (its position within this tx's new\n/// note_hashes array) (`note_index_in_tx`) to get a truly unique value to inject into a note, which\n/// we call a `note_nonce`.\npub fn compute_unique_note_hash(note_nonce: Field, siloed_note_hash: Field) -> Field {\n let inputs = [note_nonce, siloed_note_hash];\n poseidon2_hash_with_separator(inputs, DOM_SEP__UNIQUE_NOTE_HASH)\n}\n\npub fn compute_note_hash_nonce(first_nullifier_in_tx: Field, note_index_in_tx: u32) -> Field {\n // Hashing the first nullifier with note index in tx is guaranteed to be unique (because all nullifiers are also\n // unique).\n poseidon2_hash_with_separator(\n [first_nullifier_in_tx, note_index_in_tx as Field],\n DOM_SEP__NOTE_HASH_NONCE,\n )\n}\n\npub fn compute_note_nonce_and_unique_note_hash(\n siloed_note_hash: Field,\n first_nullifier: Field,\n note_index_in_tx: u32,\n) -> Field {\n let note_nonce = compute_note_hash_nonce(first_nullifier, note_index_in_tx);\n compute_unique_note_hash(note_nonce, siloed_note_hash)\n}\n\npub fn compute_siloed_nullifier(contract_address: AztecAddress, nullifier: Field) -> Field {\n poseidon2_hash_with_separator(\n [contract_address.to_field(), nullifier],\n DOM_SEP__SILOED_NULLIFIER,\n )\n}\n\npub fn create_protocol_nullifier(tx_request: TxRequest) -> Scoped<Counted<Nullifier>> {\n // The protocol nullifier is ascribed a special side-effect counter of 1. No other side-effect\n // can have counter 1 (see `validate_as_first_call` for that assertion).\n Nullifier { value: tx_request.hash(), note_hash: 0 }.count(1).scope(\n NULL_MSG_SENDER_CONTRACT_ADDRESS,\n )\n}\n\npub fn compute_log_tag(raw_tag: Field, dom_sep: u32) -> Field {\n poseidon2_hash_with_separator([raw_tag], dom_sep)\n}\n\npub fn compute_siloed_private_log_first_field(\n contract_address: AztecAddress,\n field: Field,\n) -> Field {\n poseidon2_hash_with_separator(\n [contract_address.to_field(), field],\n DOM_SEP__PRIVATE_LOG_FIRST_FIELD,\n )\n}\n\npub fn compute_siloed_private_log(contract_address: AztecAddress, log: PrivateLog) -> PrivateLog {\n let mut fields = log.fields;\n fields[0] = compute_siloed_private_log_first_field(contract_address, fields[0]);\n PrivateLog::new(fields, log.length)\n}\n\npub fn compute_contract_class_log_hash(log: [Field; CONTRACT_CLASS_LOG_SIZE_IN_FIELDS]) -> Field {\n poseidon2_hash(log)\n}\n\npub fn compute_app_siloed_secret_key(\n master_secret_key: EmbeddedCurveScalar,\n app_address: AztecAddress,\n key_type_domain_separator: Field,\n) -> Field {\n poseidon2_hash_with_separator(\n [master_secret_key.hi, master_secret_key.lo, app_address.to_field()],\n key_type_domain_separator,\n )\n}\n\npub fn compute_l2_to_l1_message_hash(\n message: Scoped<L2ToL1Message>,\n rollup_version_id: Field,\n chain_id: Field,\n) -> Field {\n let contract_address_bytes: [u8; 32] = message.contract_address.to_field().to_be_bytes();\n let recipient_bytes: [u8; 20] = message.inner.recipient.to_be_bytes();\n let content_bytes: [u8; 32] = message.inner.content.to_be_bytes();\n let rollup_version_id_bytes: [u8; 32] = rollup_version_id.to_be_bytes();\n let chain_id_bytes: [u8; 32] = chain_id.to_be_bytes();\n\n let mut bytes: [u8; 148] = std::mem::zeroed();\n for i in 0..32 {\n bytes[i] = contract_address_bytes[i];\n bytes[i + 32] = rollup_version_id_bytes[i];\n // 64 - 84 are for recipient.\n bytes[i + 84] = chain_id_bytes[i];\n bytes[i + 116] = content_bytes[i];\n }\n\n for i in 0..20 {\n bytes[64 + i] = recipient_bytes[i];\n }\n\n sha256_to_field(bytes)\n}\n\n// TODO: consider a variant that enables domain separation with a u32 (we seem to have standardised u32s for domain separators)\n/// Computes sha256 hash of 2 input fields.\n///\n/// @returns A truncated field (i.e., the first byte is always 0).\npub fn accumulate_sha256(v0: Field, v1: Field) -> Field {\n // Concatenate two fields into 32 x 2 = 64 bytes\n let v0_as_bytes: [u8; 32] = v0.to_be_bytes();\n let v1_as_bytes: [u8; 32] = v1.to_be_bytes();\n let hash_input_flattened = v0_as_bytes.concat(v1_as_bytes);\n\n sha256_to_field(hash_input_flattened)\n}\n\npub fn poseidon2_hash<let N: u32>(inputs: [Field; N]) -> Field {\n poseidon::poseidon2::Poseidon2::hash(inputs, N)\n}\n\n#[no_predicates]\npub fn poseidon2_hash_with_separator<let N: u32, T>(inputs: [Field; N], separator: T) -> Field\nwhere\n T: ToField,\n{\n let inputs_with_separator = [separator.to_field()].concat(inputs);\n poseidon2_hash(inputs_with_separator)\n}\n\n/// Computes a Poseidon2 hash over a dynamic-length subarray of the given input.\n/// Only the first `in_len` fields of `input` are absorbed; any remaining fields are ignored.\n/// The caller is responsible for ensuring that the input is padded with zeros if required.\n#[no_predicates]\npub fn poseidon2_hash_subarray<let N: u32>(input: [Field; N], in_len: u32) -> Field {\n let mut sponge = poseidon2_absorb_in_chunks(input, in_len);\n sponge.squeeze()\n}\n\n// This function is unconstrained because it is intended to be used in unconstrained context only as\n// in constrained contexts it would be too inefficient.\npub unconstrained fn poseidon2_hash_with_separator_bounded_vec<let N: u32, T>(\n inputs: BoundedVec<Field, N>,\n separator: T,\n) -> Field\nwhere\n T: ToField,\n{\n let in_len = inputs.len() + 1;\n let iv: Field = (in_len as Field) * TWO_POW_64;\n let mut sponge = Poseidon2Sponge::new(iv);\n sponge.absorb(separator.to_field());\n\n for i in 0..inputs.len() {\n sponge.absorb(inputs.get(i));\n }\n\n sponge.squeeze()\n}\n\n#[no_predicates]\npub fn poseidon2_hash_bytes<let N: u32>(inputs: [u8; N]) -> Field {\n let mut fields = [0; (N + 30) / 31];\n let mut field_index = 0;\n let mut current_field = [0; 31];\n for i in 0..inputs.len() {\n let index = i % 31;\n current_field[index] = inputs[i];\n if index == 30 {\n fields[field_index] = field_from_bytes(current_field, false);\n current_field = [0; 31];\n field_index += 1;\n }\n }\n if field_index != fields.len() {\n fields[field_index] = field_from_bytes(current_field, false);\n }\n poseidon2_hash(fields)\n}\n\n#[test]\nfn subarray_hash_matches_fixed() {\n let values_to_hash = [3; 17];\n let padded = values_to_hash.concat([0; 11]);\n let subarray_hash = poseidon2_hash_subarray(padded, values_to_hash.len());\n\n // Hash the entire values_to_hash.\n let fixed_len_hash = poseidon::poseidon2::Poseidon2::hash(values_to_hash, values_to_hash.len());\n\n assert_eq(subarray_hash, fixed_len_hash);\n}\n\n#[test]\nfn subarray_hash_matches_variable() {\n let values_to_hash = [3; 17];\n let padded = values_to_hash.concat([0; 11]);\n let subarray_hash = poseidon2_hash_subarray(padded, values_to_hash.len());\n\n // Hash up to values_to_hash.len() fields of the padded array.\n let variable_len_hash = poseidon::poseidon2::Poseidon2::hash(padded, values_to_hash.len());\n\n assert_eq(subarray_hash, variable_len_hash);\n}\n\n#[test]\nfn smoke_sha256_to_field() {\n let full_buffer = [\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\n 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\n 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,\n 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,\n 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,\n 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,\n 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,\n 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,\n ];\n let result = sha256_to_field(full_buffer);\n\n assert(result == 0x448ebbc9e1a31220a2f3830c18eef61b9bd070e5084b7fa2a359fe729184c7);\n\n // to show correctness of the current ver (truncate one byte) vs old ver (mod full bytes):\n let result_bytes = sha256::digest(full_buffer);\n let truncated_field = crate::utils::field::field_from_bytes_32_trunc(result_bytes);\n assert(truncated_field == result);\n let mod_res = result + (result_bytes[31] as Field);\n assert(mod_res == 0x448ebbc9e1a31220a2f3830c18eef61b9bd070e5084b7fa2a359fe729184e0);\n}\n\n#[test]\nfn unique_siloed_note_hash_matches_typescript() {\n let inner_note_hash = 1;\n let contract_address = AztecAddress::from_field(2);\n let first_nullifier = 3;\n let note_index_in_tx = 4;\n\n let siloed_note_hash = compute_siloed_note_hash(contract_address, inner_note_hash);\n let siloed_note_hash_from_ts =\n 0x1986a4bea3eddb1fff917d629a13e10f63f514f401bdd61838c6b475db949169;\n assert_eq(siloed_note_hash, siloed_note_hash_from_ts);\n\n let nonce: Field = compute_note_hash_nonce(first_nullifier, note_index_in_tx);\n let note_hash_nonce_from_ts =\n 0x28e7799791bf066a57bb51fdd0fbcaf3f0926414314c7db515ea343f44f5d58b;\n assert_eq(nonce, note_hash_nonce_from_ts);\n\n let unique_siloed_note_hash_from_nonce = compute_unique_note_hash(nonce, siloed_note_hash);\n let unique_siloed_note_hash = compute_note_nonce_and_unique_note_hash(\n siloed_note_hash,\n first_nullifier,\n note_index_in_tx,\n );\n assert_eq(unique_siloed_note_hash_from_nonce, unique_siloed_note_hash);\n\n let unique_siloed_note_hash_from_ts =\n 0x29949aef207b715303b24639737c17fbfeb375c1d965ecfa85c7e4f0febb7d16;\n assert_eq(unique_siloed_note_hash, unique_siloed_note_hash_from_ts);\n}\n\n#[test]\nfn siloed_nullifier_matches_typescript() {\n let contract_address = AztecAddress::from_field(123);\n let nullifier = 456;\n\n let res = compute_siloed_nullifier(contract_address, nullifier);\n\n let siloed_nullifier_from_ts =\n 0x169b50336c1f29afdb8a03d955a81e485f5ac7d5f0b8065673d1e407e5877813;\n\n assert_eq(res, siloed_nullifier_from_ts);\n}\n\n#[test]\nfn siloed_private_log_first_field_matches_typescript() {\n let contract_address = AztecAddress::from_field(123);\n let field = 456;\n let res = compute_siloed_private_log_first_field(contract_address, field);\n\n let siloed_private_log_first_field_from_ts =\n 0x29480984f7b9257fded523d50addbcfc8d1d33adcf2db73ef3390a8fd5cdffaa;\n\n assert_eq(res, siloed_private_log_first_field_from_ts);\n}\n\n#[test]\nfn empty_l2_to_l1_message_hash_matches_typescript() {\n // All zeroes\n let res = compute_l2_to_l1_message_hash(\n L2ToL1Message { recipient: EthAddress::zero(), content: 0 }.scope(AztecAddress::from_field(\n 0,\n )),\n 0,\n 0,\n );\n\n let empty_l2_to_l1_msg_hash_from_ts =\n 0x003b18c58c739716e76429634a61375c45b3b5cd470c22ab6d3e14cee23dd992;\n\n assert_eq(res, empty_l2_to_l1_msg_hash_from_ts);\n}\n\n#[test]\nfn l2_to_l1_message_hash_matches_typescript() {\n let message = L2ToL1Message { recipient: EthAddress::from_field(1), content: 2 }.scope(\n AztecAddress::from_field(3),\n );\n let version = 4;\n let chainId = 5;\n\n let hash = compute_l2_to_l1_message_hash(message, version, chainId);\n\n // The following value was generated by `yarn-project/stdlib/src/hash/hash.test.ts`\n let l2_to_l1_message_hash_from_ts =\n 0x0081edf209e087ad31b3fd24263698723d57190bd1d6e9fe056fc0c0a68ee661;\n\n assert_eq(hash, l2_to_l1_message_hash_from_ts);\n}\n\n#[test]\nunconstrained fn poseidon2_hash_with_separator_bounded_vec_matches_non_bounded_vec_version() {\n let inputs = BoundedVec::<Field, 4>::from_array([1, 2, 3]);\n let separator = 42;\n\n // Hash using bounded vec version\n let bounded_result = poseidon2_hash_with_separator_bounded_vec(inputs, separator);\n\n // Hash using regular version\n let regular_result = poseidon2_hash_with_separator([1, 2, 3], separator);\n\n // Results should match\n assert_eq(bounded_result, regular_result);\n}\n"
|
|
742
742
|
},
|
|
743
743
|
"184": {
|
|
@@ -811,7 +811,7 @@
|
|
|
811
811
|
"start": 2802
|
|
812
812
|
}
|
|
813
813
|
],
|
|
814
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/logging.nr",
|
|
814
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/types/src/logging.nr",
|
|
815
815
|
"source": "// Log levels matching the JS logger:\n\n// global SILENT_LOG_LEVEL: u8 = 0;\nglobal FATAL_LOG_LEVEL: u8 = 1;\nglobal ERROR_LOG_LEVEL: u8 = 2;\nglobal WARN_LOG_LEVEL: u8 = 3;\nglobal INFO_LOG_LEVEL: u8 = 4;\nglobal VERBOSE_LOG_LEVEL: u8 = 5;\nglobal DEBUG_LOG_LEVEL: u8 = 6;\nglobal TRACE_LOG_LEVEL: u8 = 7;\n\n// --- Per-level log functions (no format args) ---\n\npub fn fatal_log<let N: u32>(msg: str<N>) {\n fatal_log_format(msg, []);\n}\n\npub fn error_log<let N: u32>(msg: str<N>) {\n error_log_format(msg, []);\n}\n\npub fn warn_log<let N: u32>(msg: str<N>) {\n warn_log_format(msg, []);\n}\n\npub fn info_log<let N: u32>(msg: str<N>) {\n info_log_format(msg, []);\n}\n\npub fn verbose_log<let N: u32>(msg: str<N>) {\n verbose_log_format(msg, []);\n}\n\npub fn debug_log<let N: u32>(msg: str<N>) {\n debug_log_format(msg, []);\n}\n\npub fn trace_log<let N: u32>(msg: str<N>) {\n trace_log_format(msg, []);\n}\n\n// --- Per-level log functions (with format args) ---\n\npub fn fatal_log_format<let M: u32, let N: u32>(msg: str<M>, args: [Field; N]) {\n log_format(FATAL_LOG_LEVEL, msg, args);\n}\n\npub fn error_log_format<let M: u32, let N: u32>(msg: str<M>, args: [Field; N]) {\n log_format(ERROR_LOG_LEVEL, msg, args);\n}\n\npub fn warn_log_format<let M: u32, let N: u32>(msg: str<M>, args: [Field; N]) {\n log_format(WARN_LOG_LEVEL, msg, args);\n}\n\npub fn info_log_format<let M: u32, let N: u32>(msg: str<M>, args: [Field; N]) {\n log_format(INFO_LOG_LEVEL, msg, args);\n}\n\npub fn verbose_log_format<let M: u32, let N: u32>(msg: str<M>, args: [Field; N]) {\n log_format(VERBOSE_LOG_LEVEL, msg, args);\n}\n\npub fn debug_log_format<let M: u32, let N: u32>(msg: str<M>, args: [Field; N]) {\n log_format(DEBUG_LOG_LEVEL, msg, args);\n}\n\npub fn trace_log_format<let M: u32, let N: u32>(msg: str<M>, args: [Field; N]) {\n log_format(TRACE_LOG_LEVEL, msg, args);\n}\n\nfn log_format<let M: u32, let N: u32>(log_level: u8, msg: str<M>, args: [Field; N]) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe\n // to call.\n unsafe { log_oracle_wrapper(log_level, msg, args) };\n}\n\nunconstrained fn log_oracle_wrapper<let M: u32, let N: u32>(\n log_level: u8,\n msg: str<M>,\n args: [Field; N],\n) {\n log_oracle(log_level, msg, N, args);\n}\n\n// While the length parameter might seem unnecessary given that we have N, we keep it around because at the AVM\n// bytecode level we want to support non-comptime-known lengths for such opcodes, even if Noir code will not generally\n// take that route. The AVM transpiler maps this oracle to the DEBUGLOG opcode, which reads the fields size from memory.\n#[oracle(aztec_misc_log)]\nunconstrained fn log_oracle<let M: u32, let N: u32>(\n log_level: u8,\n msg: str<M>,\n length: u32,\n args: [Field; N],\n) {}\n"
|
|
816
816
|
},
|
|
817
817
|
"203": {
|
|
@@ -841,7 +841,7 @@
|
|
|
841
841
|
"start": 2544
|
|
842
842
|
}
|
|
843
843
|
],
|
|
844
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/poseidon2.nr",
|
|
844
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/types/src/poseidon2.nr",
|
|
845
845
|
"source": "use crate::constants::TWO_POW_64;\nuse crate::traits::{Deserialize, Serialize};\nuse std::meta::derive;\n// NB: This is a clone of noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr\n// It exists as we sometimes need to perform custom absorption, but the stdlib version\n// has a private absorb() method (it's also designed to just be a hasher)\n// Can be removed when standalone noir poseidon lib exists: See noir#6679\n// TODO: Poseidon is stand-alone now\n\nglobal RATE: u32 = 3;\n\n#[derive(Deserialize, Eq, Serialize)]\npub struct Poseidon2Sponge {\n pub cache: [Field; 3],\n pub state: [Field; 4],\n pub cache_size: u32,\n pub squeeze_mode: bool, // 0 => absorb, 1 => squeeze\n}\n\nimpl Poseidon2Sponge {\n #[no_predicates]\n pub fn hash<let N: u32>(input: [Field; N], message_size: u32) -> Field {\n Poseidon2Sponge::hash_internal(input, message_size, message_size != N)\n }\n\n pub(crate) fn new(iv: Field) -> Poseidon2Sponge {\n let mut result =\n Poseidon2Sponge { cache: [0; 3], state: [0; 4], cache_size: 0, squeeze_mode: false };\n result.state[RATE] = iv;\n result\n }\n\n fn perform_duplex(&mut self) {\n // add the cache into sponge state\n for i in 0..RATE {\n // We effectively zero-pad the cache by only adding to the state\n // cache that is less than the specified `cache_size`\n if i < self.cache_size {\n self.state[i] += self.cache[i];\n }\n }\n self.state = std::hash::poseidon2_permutation(self.state);\n }\n\n pub fn absorb(&mut self, input: Field) {\n assert(!self.squeeze_mode);\n if self.cache_size == RATE {\n // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache\n self.perform_duplex();\n self.cache[0] = input;\n self.cache_size = 1;\n } else {\n // If we're absorbing, and the cache is not full, add the input into the cache\n self.cache[self.cache_size] = input;\n self.cache_size += 1;\n }\n }\n\n pub fn squeeze(&mut self) -> Field {\n assert(!self.squeeze_mode);\n // If we're in absorb mode, apply sponge permutation to compress the cache.\n self.perform_duplex();\n self.squeeze_mode = true;\n\n // Pop one item off the top of the permutation and return it.\n self.state[0]\n }\n\n fn hash_internal<let N: u32>(\n input: [Field; N],\n in_len: u32,\n is_variable_length: bool,\n ) -> Field {\n let iv: Field = (in_len as Field) * TWO_POW_64;\n let mut sponge = Poseidon2Sponge::new(iv);\n for i in 0..input.len() {\n if i < in_len {\n sponge.absorb(input[i]);\n }\n }\n\n sponge.squeeze()\n }\n}\n"
|
|
846
846
|
},
|
|
847
847
|
"217": {
|
|
@@ -915,7 +915,7 @@
|
|
|
915
915
|
"start": 3138
|
|
916
916
|
}
|
|
917
917
|
],
|
|
918
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/traits/empty.nr",
|
|
918
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/types/src/traits/empty.nr",
|
|
919
919
|
"source": "// Trait: is_empty\n//\n// The general is_empty trait checks if a data type is empty,\n// and it defines empty for the basic data types as 0.\n//\n// If a Field is equal to zero, then it is regarded as zero.\n// We will go with this definition for now, however it can be problematic\n// if a value can actually be zero. In a future refactor, we can\n// use the optional type for safety. Doing it now would lead to a worse devex\n// and would make it harder to sync up with the cpp code.\n// Preferred over Default trait to convey intent, as default doesn't necessarily mean empty.\npub trait Empty: Eq {\n fn empty() -> Self;\n\n fn is_empty(self) -> bool {\n self.eq(Self::empty())\n }\n\n // Requires this Noir fix: https://github.com/noir-lang/noir/issues/9002\n // fn assert_not_empty<let U: u32>(self, msg: str<U>) { // This msg version was failing with weird compiler errors.\n // // We provide a default impl but it's likely inefficient.\n // // The reason we include this function is because there's a lot of\n // // opportunity for optimisation on a per-struct basis.\n // // You only need to show one element is not empty to know that the whole thing\n // // is not empty.\n // // If you know an element of your struct which should always be nonempty,\n // // you can write an impl that solely checks that that element is nonempty.\n // assert(!self.is_empty(), msg);\n // }\n\n // This default impl is overwritten by types like arrays, because there's a much\n // more efficient approach.\n fn assert_empty<let S: u32>(self, msg: str<S>) {\n assert(self.is_empty(), msg);\n }\n}\n\nimpl Empty for Field {\n #[inline_always]\n fn empty() -> Self {\n 0\n }\n}\n\nimpl Empty for bool {\n #[inline_always]\n fn empty() -> Self {\n false\n }\n}\n\nimpl Empty for u8 {\n #[inline_always]\n fn empty() -> Self {\n 0\n }\n}\nimpl Empty for u16 {\n fn empty() -> Self {\n 0\n }\n}\nimpl Empty for u32 {\n #[inline_always]\n fn empty() -> Self {\n 0\n }\n}\nimpl Empty for u64 {\n #[inline_always]\n fn empty() -> Self {\n 0\n }\n}\nimpl Empty for u128 {\n #[inline_always]\n fn empty() -> Self {\n 0\n }\n}\n\nimpl<T, let N: u32> Empty for [T; N]\nwhere\n T: Empty,\n{\n #[inline_always]\n fn empty() -> Self {\n [T::empty(); N]\n }\n\n fn is_empty(self) -> bool {\n self.all(|elem| elem.is_empty())\n }\n\n fn assert_empty<let S: u32>(self, msg: str<S>) -> () {\n self.for_each(|elem| elem.assert_empty(msg))\n }\n}\n\nimpl<T> Empty for [T]\nwhere\n T: Empty,\n{\n #[inline_always]\n fn empty() -> Self {\n [T::empty()]\n }\n\n fn is_empty(self) -> bool {\n self.all(|elem| elem.is_empty())\n }\n\n fn assert_empty<let S: u32>(self, msg: str<S>) -> () {\n self.for_each(|elem| elem.assert_empty(msg))\n }\n}\nimpl<A, B> Empty for (A, B)\nwhere\n A: Empty,\n B: Empty,\n{\n #[inline_always]\n fn empty() -> Self {\n (A::empty(), B::empty())\n }\n}\n\nimpl<T> Empty for Option<T>\nwhere\n T: Eq,\n{\n #[inline_always]\n fn empty() -> Self {\n Option::none()\n }\n}\n\n// pub fn is_empty<T>(item: T) -> bool\n// where\n// T: Empty,\n// {\n// item.eq(T::empty())\n// }\n\n// pub fn is_empty_array<T, let N: u32>(array: [T; N]) -> bool\n// where\n// T: Empty,\n// {\n// array.all(|elem| is_empty(elem))\n// }\n\n// pub fn assert_empty<T>(item: T) -> ()\n// where\n// T: Empty,\n// {\n// assert(item.eq(T::empty()))\n// }\n\n// pub fn assert_empty_array<T, let N: u32>(array: [T; N]) -> ()\n// where\n// T: Empty,\n// {\n// // A cheaper option than `is_empty_array` for if you don't need to gracefully\n// // handle a bool result.\n// // Avoids the `&` operator of `is_empty_array`'s `.all()` call.\n// for i in 0..N {\n// assert(is_empty(array[i]));\n// }\n// }\n"
|
|
920
920
|
},
|
|
921
921
|
"226": {
|
|
@@ -953,7 +953,7 @@
|
|
|
953
953
|
"start": 2251
|
|
954
954
|
}
|
|
955
955
|
],
|
|
956
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/find_index.nr",
|
|
956
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/types/src/utils/arrays/find_index.nr",
|
|
957
957
|
"source": "/// Helper function to find the index of the first element in an array that satisfies a given predicate.\n/// If the element is not found, the function returns the length of the array `N`\npub unconstrained fn find_first_index<T, let N: u32, Env>(\n array: [T; N],\n find: fn[Env](T) -> bool,\n) -> u32 {\n let mut index = N;\n for i in 0..N {\n if find(array[i]) {\n index = i;\n break;\n }\n }\n index\n}\n\n/// Helper function to find the index of the last element in an array that satisfies a given predicate.\n/// If the element is not found, the function returns the length of the array `N`\npub unconstrained fn find_last_index<T, let N: u32, Env>(\n array: [T; N],\n find: fn[Env](T) -> bool,\n) -> u32 {\n let mut index = N;\n for i in 0..N {\n let j = N - i - 1;\n if find(array[j]) {\n index = j;\n break;\n }\n }\n index\n}\n\n#[test]\nunconstrained fn find_first_index_unique_element() {\n let array = [11, 22, 33];\n\n assert_eq(find_first_index(array, |x| x == 11), 0);\n assert_eq(find_first_index(array, |x| x == 22), 1);\n assert_eq(find_first_index(array, |x| x == 33), 2);\n}\n\n#[test]\nunconstrained fn find_first_index_duplicate_elements() {\n let array = [11, 22, 33, 11, 22, 33];\n\n assert_eq(find_first_index(array, |x| x == 11), 0);\n assert_eq(find_first_index(array, |x| x == 22), 1);\n assert_eq(find_first_index(array, |x| x == 33), 2);\n}\n\n#[test]\nunconstrained fn find_first_index_not_found() {\n let array = [11, 22, 33];\n\n assert_eq(find_first_index(array, |x| x == 0), 3);\n assert_eq(find_first_index(array, |x| x == 44), 3);\n}\n\n#[test]\nunconstrained fn find_last_index_unique_element() {\n let array = [11, 22, 33];\n\n assert_eq(find_last_index(array, |x| x == 11), 0);\n assert_eq(find_last_index(array, |x| x == 22), 1);\n assert_eq(find_last_index(array, |x| x == 33), 2);\n}\n\n#[test]\nunconstrained fn find_last_index_duplicate_elements() {\n let array = [11, 22, 33, 11, 22, 33];\n\n assert_eq(find_last_index(array, |x| x == 11), 3);\n assert_eq(find_last_index(array, |x| x == 22), 4);\n assert_eq(find_last_index(array, |x| x == 33), 5);\n}\n\n#[test]\nunconstrained fn find_last_index_not_found() {\n let array = [11, 22, 33];\n\n assert_eq(find_last_index(array, |x| x == 0), 3);\n assert_eq(find_last_index(array, |x| x == 44), 3);\n}\n"
|
|
958
958
|
},
|
|
959
959
|
"229": {
|
|
@@ -1095,7 +1095,7 @@
|
|
|
1095
1095
|
"start": 12045
|
|
1096
1096
|
}
|
|
1097
1097
|
],
|
|
1098
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr",
|
|
1098
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/types/src/utils/arrays.nr",
|
|
1099
1099
|
"source": "pub(crate) mod assert_trailing_zeros;\npub(crate) mod splice_at_count;\npub(crate) mod find_index;\npub(crate) mod get_sorted_tuples;\npub(crate) mod claimed_length_array;\n\n// Re-exports.\npub use assert_trailing_zeros::assert_trailing_zeros;\npub use claimed_length_array::ClaimedLengthArray;\npub use find_index::{find_first_index, find_last_index};\npub use get_sorted_tuples::{get_sorted_tuples, SortedTuple};\npub use splice_at_count::splice_at_count;\n\nuse crate::traits::Empty;\n\n// TODO: Consider making this a part of the noir stdlib.\n/// Helper fn to create a subarray from a given array.\npub fn subarray<T, let N: u32, let M: u32>(array: [T; N], offset: u32) -> [T; M]\nwhere\n T: Empty,\n{\n let mut result: [T; M] = [T::empty(); M];\n for i in 0..M {\n result[i] = array[offset + i];\n }\n result\n}\n\n/// Helper function to count the number of non-empty elements in a validated array.\n/// Danger: This is only safe to call if the input arrays have been \"validated\" (dense lhs, empty rhs).\n/// 1. All elements before the first empty element are non-empty\n/// 2. All elements after and including the first empty element are empty\n/// 3. The array forms a contiguous sequence of non-empty elements followed by empty elements\npub fn array_length<T, let N: u32>(array: [T; N]) -> u32\nwhere\n T: Empty,\n{\n // We get the length by checking the index of the first empty element.\n\n // Safety: This is safe because we have validated the array (see function doc comment above) and the emptiness\n // of the element and non-emptiness of the previous element is checked below.\n let length = unsafe { find_first_index(array, |elem: T| elem.is_empty()) };\n\n validate_array_length_hint(array, length);\n\n length\n}\n\n// Extracted into a standalone function to enable testing of bad hints.\nfn validate_array_length_hint<T, let N: u32>(array: [T; N], length: u32)\nwhere\n T: Empty,\n{\n // Note: if length > N, the below `array` access will throw an out of bounds error.\n\n if length != N {\n array[length].assert_empty(\"Expected array[length] to be empty\");\n }\n\n if length != 0 {\n assert(\n !array[length - 1].is_empty(),\n \"Expected claimed final element of array (array[length - 1]) to be nonempty\",\n );\n }\n}\n\n// Returns an array length defined by fully trimming _all_ \"empty\" items\n// from the RHS.\npub unconstrained fn trimmed_array_length_hint<T, let N: u32>(array: [T; N]) -> u32\nwhere\n T: Empty,\n{\n let index_of_last_nonempty = find_last_index(array, |elem: T| !elem.is_empty());\n let length: u32 = if index_of_last_nonempty != N {\n 1 + index_of_last_nonempty\n } else {\n 0\n };\n length\n}\n\n// Returns the number of consecutive elements at the start of the array for which the predicate returns false.\n// This function ensures that any element after the first matching element (predicate returns true) also matches the predicate.\npub fn array_length_until<T, let N: u32, Env>(array: [T; N], predicate: fn[Env](T) -> bool) -> u32 {\n let mut length = 0;\n let mut stop = false;\n for i in 0..N {\n if predicate(array[i]) {\n stop = true;\n } else {\n assert(\n stop == false,\n \"non-matching element found after already encountering a matching element\",\n );\n length += 1;\n }\n }\n length\n}\n\npub fn check_permutation<T, let N: u32>(\n original_array: [T; N],\n permuted_array: [T; N],\n original_indexes: [u32; N],\n)\nwhere\n T: Eq,\n{\n let mut seen_value = [false; N];\n for i in 0..N {\n let index = original_indexes[i];\n let original_value = original_array[index];\n assert(permuted_array[i].eq(original_value), \"Invalid index\");\n assert(!seen_value[index], \"Duplicated index\");\n seen_value[index] = true;\n }\n}\n\n/// Returns true iff every element of `array` at index `>= from_index` equals `padded_with`.\n///\n/// `from_index` must be a valid index into the array, i.e. `from_index <= N` (the boundary\n/// `from_index == N` denotes a full prefix with no padding). Passing `from_index > N` is rejected:\n/// otherwise this check would pass vacuously (the loop never reaches `from_index`), letting a caller\n/// believe the array holds a `from_index`-length prefix followed by padding even though the array is\n/// shorter than `from_index`.\npub fn array_padded_with<T, let N: u32>(array: [T; N], from_index: u32, padded_with: T) -> bool\nwhere\n T: Eq,\n{\n assert(from_index <= N, \"from_index out of bounds\");\n let mut is_valid = true;\n let mut should_check = false;\n for i in 0..N {\n should_check |= i == from_index;\n is_valid &= !should_check | (array[i] == padded_with);\n }\n is_valid\n}\n\n// ==================== subarray tests ====================\n\n#[test]\nfn test_subarray() {\n assert_eq(subarray::<_, 5, 3>([10, 20, 30, 40, 50], 1), [20, 30, 40]);\n assert_eq(subarray::<_, 5, 2>([10, 20, 30, 40, 50], 0), [10, 20]);\n assert_eq(subarray::<_, 3, 0>([10, 20, 30], 2), []);\n assert_eq(subarray::<_, 3, 1>([10, 20, 30], 2), [30]);\n}\n\n#[test(should_fail_with = \"out of bounds\")]\nfn test_subarray_offset_out_of_bounds() {\n let _: [Field; 1] = subarray([10, 20, 30], 5);\n}\n\n#[test(should_fail_with = \"out of bounds\")]\nfn test_subarray_result_size_exceeds_available() {\n let _: [Field; 3] = subarray([10, 20, 30, 40, 50], 3);\n}\n\n// ==================== array_length tests ====================\n\n#[test]\nfn test_array_length_empty_array() {\n assert_eq(array_length([0]), 0);\n assert_eq(array_length([0, 0, 0]), 0);\n}\n\n#[test]\nfn test_array_length() {\n assert_eq(array_length([123]), 1);\n assert_eq(array_length([123, 0, 0]), 1);\n assert_eq(array_length([123, 456]), 2);\n assert_eq(array_length([123, 456, 0]), 2);\n}\n\n#[test]\nfn test_array_length_invalid_arrays() {\n // Result can be misleading (but correct) for invalid arrays.\n // This is why the arrays being passed-into `array_length` must already have been \"validated\"\n // (dense lhs, empty rhs).\n assert_eq(array_length([0, 0, 123]), 0);\n assert_eq(array_length([0, 123, 0]), 0);\n assert_eq(array_length([0, 123, 456]), 0);\n assert_eq(array_length([123, 0, 456]), 1);\n}\n\n// ==================== validate_array_length_hint tests ====================\n\n#[test]\nfn test_validate_array_length_hint_valid() {\n validate_array_length_hint([0, 0, 0], 0);\n validate_array_length_hint([10, 20, 0], 2);\n validate_array_length_hint([10, 20, 30], 3);\n}\n\n#[test(should_fail_with = \"Expected array[length] to be empty\")]\nfn test_validate_array_length_hint_claims_zero_when_not_empty() {\n // Invalid: hint says length 0, but first element is not empty\n validate_array_length_hint([10, 20, 30], 0);\n}\n\n#[test(should_fail_with = \"Expected array[length] to be empty\")]\nfn test_validate_array_length_hint_too_short() {\n // Invalid: hint says length 1, but element at index 1 is not empty\n validate_array_length_hint([10, 20, 0], 1);\n}\n\n#[test(should_fail_with = \"Expected claimed final element of array (array[length - 1]) to be nonempty\")]\nfn test_validate_array_length_hint_too_long() {\n // Invalid: hint says length 2, but element at index 1 is empty\n validate_array_length_hint([10, 0, 0], 2);\n}\n\n#[test(should_fail_with = \"Expected claimed final element of array (array[length - 1]) to be nonempty\")]\nfn test_validate_array_length_hint_of_empty_too_long() {\n // Invalid: hint says length 2, but element at index 1 is empty\n validate_array_length_hint([0, 0, 0], 2);\n}\n\n#[test(should_fail_with = \"out of bounds\")]\nfn test_validate_array_length_hint_out_of_bounds() {\n // Invalid: hint says length 4, but array only has 3 elements\n validate_array_length_hint([10, 20, 30], 4);\n}\n\n// ==================== trimmed_array_length_hint tests ====================\n\n#[test]\nunconstrained fn test_trimmed_array_length_hint_with_trailing_empties() {\n assert_eq(trimmed_array_length_hint([10, 20, 30, 0, 0]), 3);\n}\n\n#[test]\nunconstrained fn test_trimmed_array_length_hint_fully_empty() {\n assert_eq(trimmed_array_length_hint([0, 0, 0, 0, 0]), 0);\n}\n\n#[test]\nunconstrained fn test_trimmed_array_length_hint_no_empties() {\n assert_eq(trimmed_array_length_hint([10, 20, 30, 40, 50]), 5);\n}\n\n#[test]\nunconstrained fn test_trimmed_array_length_hint_with_gaps() {\n // Unlike array_length, this trims from the right only, so gaps don't matter\n assert_eq(trimmed_array_length_hint([10, 0, 30, 0, 0]), 3);\n}\n\n// ==================== array_length_until tests ====================\n\n#[test]\nfn test_array_length_until() {\n let array = [11, 22, 33, 44, 55];\n assert_eq(array_length_until(array, |x| x == 55), 4);\n assert_eq(array_length_until(array, |x| x == 56), 5);\n assert_eq(array_length_until(array, |x| x > 40), 3);\n assert_eq(array_length_until(array, |x| x > 10), 0);\n}\n\n#[test(should_fail_with = \"non-matching element found after already encountering a matching element\")]\nfn test_array_length_until_non_consecutive_fails() {\n let array = [1, 1, 0, 1, 0];\n let _ = array_length_until(array, |x| x == 0);\n}\n\n#[test(should_fail_with = \"non-matching element found after already encountering a matching element\")]\nfn test_array_length_until_first_non_matching_fails() {\n let array = [1, 0, 0, 0, 0];\n let _ = array_length_until(array, |x| x == 1);\n}\n\n// ==================== check_permutation tests ====================\n\n#[test]\nfn test_check_permutation() {\n let original_array = [1, 2, 3];\n let permuted_array = [3, 1, 2];\n let indexes = [2, 0, 1];\n check_permutation(original_array, permuted_array, indexes);\n}\n\n#[test(should_fail_with = \"Duplicated index\")]\nfn test_check_permutation_duplicated_index() {\n let original_array = [0, 1, 0];\n let permuted_array = [1, 0, 0];\n let indexes = [1, 0, 0];\n check_permutation(original_array, permuted_array, indexes);\n}\n\n#[test(should_fail_with = \"Invalid index\")]\nfn test_check_permutation_invalid_index() {\n let original_array = [0, 1, 2];\n let permuted_array = [1, 0, 0];\n let indexes = [1, 0, 2];\n check_permutation(original_array, permuted_array, indexes);\n}\n\n// ==================== array_padded_with tests ====================\n\n#[test]\nfn test_array_padded_with() {\n let array = [11, 22, 33, 44, 44];\n assert_eq(array_padded_with(array, 0, 44), false);\n assert_eq(array_padded_with(array, 1, 44), false);\n assert_eq(array_padded_with(array, 2, 44), false);\n assert_eq(array_padded_with(array, 3, 44), true);\n assert_eq(array_padded_with(array, 4, 44), true);\n assert_eq(array_padded_with(array, 4, 33), false);\n assert_eq(array_padded_with(array, 5, 44), true); // from_index == N: full prefix, no padding.\n assert_eq(array_padded_with(array, 0, 11), false);\n}\n\n#[test(should_fail_with = \"from_index out of bounds\")]\nfn test_array_padded_with_from_index_out_of_bounds() {\n let array = [11, 22, 33, 44, 44];\n let _ = array_padded_with(array, 6, 44);\n}\n\n#[test]\nfn test_array_padded_with_single_element() {\n // N == 1: from_index 0 checks the whole array; from_index 1 (== N) is the no-padding boundary.\n assert_eq(array_padded_with([7], 0, 7), true);\n assert_eq(array_padded_with([7], 0, 8), false);\n assert_eq(array_padded_with([7], 1, 8), true);\n}\n\n#[test]\nfn test_array_padded_with_requires_all_tail_padded() {\n // Padding must hold for every index >= from_index, not just the first one.\n let array = [1, 2, 9, 9, 9];\n assert_eq(array_padded_with(array, 2, 9), true);\n assert_eq(array_padded_with(array, 1, 9), false); // index 1 (value 2) != 9\n let broken_tail = [1, 2, 9, 8, 9];\n assert_eq(array_padded_with(broken_tail, 2, 9), false); // index 3 (value 8) != 9\n // from_index == 0 requires the whole array to equal the pad value.\n assert_eq(array_padded_with([9, 9, 9], 0, 9), true);\n assert_eq(array_padded_with([9, 9, 8], 0, 9), false);\n}\n\n#[test(should_fail_with = \"from_index out of bounds\")]\nfn test_array_padded_with_from_index_far_out_of_bounds() {\n let array = [11, 22, 33, 44, 44];\n let _ = array_padded_with(array, 100, 44);\n}\n"
|
|
1100
1100
|
},
|
|
1101
1101
|
"242": {
|
|
@@ -1187,7 +1187,7 @@
|
|
|
1187
1187
|
"start": 1426
|
|
1188
1188
|
}
|
|
1189
1189
|
],
|
|
1190
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/serde/src/reader.nr",
|
|
1190
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/serde/src/reader.nr",
|
|
1191
1191
|
"source": "pub struct Reader<let N: u32> {\n data: [Field; N],\n offset: u32,\n}\n\nimpl<let N: u32> Reader<N> {\n pub fn new(data: [Field; N]) -> Self {\n Self { data, offset: 0 }\n }\n\n pub fn read(&mut self) -> Field {\n let result = self.data[self.offset];\n self.offset += 1;\n result\n }\n\n pub fn read_u32(&mut self) -> u32 {\n self.read() as u32\n }\n\n pub fn read_u64(&mut self) -> u64 {\n self.read() as u64\n }\n\n pub fn read_bool(&mut self) -> bool {\n self.read() != 0\n }\n\n pub fn read_array<let K: u32>(&mut self) -> [Field; K] {\n let mut result = [0; K];\n for i in 0..K {\n result[i] = self.data[self.offset + i];\n }\n self.offset += K;\n result\n }\n\n pub fn read_struct<T, let K: u32>(&mut self, deserialise: fn([Field; K]) -> T) -> T {\n let result = deserialise(self.read_array());\n result\n }\n\n pub fn read_struct_array<T, let K: u32, let C: u32>(\n &mut self,\n deserialise: fn([Field; K]) -> T,\n mut result: [T; C],\n ) -> [T; C] {\n for i in 0..C {\n result[i] = self.read_struct(deserialise);\n }\n result\n }\n\n pub fn peek_offset(&mut self, offset: u32) -> Field {\n self.data[self.offset + offset]\n }\n\n pub fn advance_offset(&mut self, offset: u32) {\n self.offset += offset;\n }\n\n pub fn finish(self) {\n assert_eq(self.offset, self.data.len(), \"Reader did not read all data\");\n }\n}\n"
|
|
1192
1192
|
},
|
|
1193
1193
|
"248": {
|
|
@@ -1501,7 +1501,7 @@
|
|
|
1501
1501
|
"start": 21246
|
|
1502
1502
|
}
|
|
1503
1503
|
],
|
|
1504
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/serde/src/type_impls.nr",
|
|
1504
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-protocol-circuits/crates/serde/src/type_impls.nr",
|
|
1505
1505
|
"source": "use crate::{reader::Reader, serialization::{Deserialize, Serialize}, writer::Writer};\nuse std::embedded_curve_ops::EmbeddedCurvePoint;\nuse std::embedded_curve_ops::EmbeddedCurveScalar;\n\nglobal BOOL_SERIALIZED_LEN: u32 = 1;\nglobal U8_SERIALIZED_LEN: u32 = 1;\nglobal U16_SERIALIZED_LEN: u32 = 1;\nglobal U32_SERIALIZED_LEN: u32 = 1;\nglobal U64_SERIALIZED_LEN: u32 = 1;\nglobal U128_SERIALIZED_LEN: u32 = 1;\nglobal FIELD_SERIALIZED_LEN: u32 = 1;\nglobal I8_SERIALIZED_LEN: u32 = 1;\nglobal I16_SERIALIZED_LEN: u32 = 1;\nglobal I32_SERIALIZED_LEN: u32 = 1;\nglobal I64_SERIALIZED_LEN: u32 = 1;\n\nimpl Serialize for bool {\n let N: u32 = BOOL_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as Field);\n }\n}\n\nimpl Deserialize for bool {\n let N: u32 = BOOL_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> bool {\n reader.read() != 0\n }\n}\n\nimpl Serialize for u8 {\n let N: u32 = U8_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as Field);\n }\n}\n\nimpl Deserialize for u8 {\n let N: u32 = U8_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u8\n }\n}\n\nimpl Serialize for u16 {\n let N: u32 = U16_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as Field);\n }\n}\n\nimpl Deserialize for u16 {\n let N: u32 = U16_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u16\n }\n}\n\nimpl Serialize for u32 {\n let N: u32 = U32_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as Field);\n }\n}\n\nimpl Deserialize for u32 {\n let N: u32 = U32_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u32\n }\n}\n\nimpl Serialize for u64 {\n let N: u32 = U64_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as Field);\n }\n}\n\nimpl Deserialize for u64 {\n let N: u32 = U64_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u64\n }\n}\n\nimpl Serialize for u128 {\n let N: u32 = U128_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as Field);\n }\n}\n\nimpl Deserialize for u128 {\n let N: u32 = U128_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u128\n }\n}\n\nimpl Serialize for Field {\n let N: u32 = FIELD_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self);\n }\n}\n\nimpl Deserialize for Field {\n let N: u32 = FIELD_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read()\n }\n}\n\nimpl Serialize for i8 {\n let N: u32 = I8_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as u8 as Field);\n }\n}\n\nimpl Deserialize for i8 {\n let N: u32 = I8_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u8 as i8\n }\n}\n\nimpl Serialize for i16 {\n let N: u32 = I16_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as u16 as Field);\n }\n}\n\nimpl Deserialize for i16 {\n let N: u32 = I16_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u16 as i16\n }\n}\n\nimpl Serialize for i32 {\n let N: u32 = I32_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as u32 as Field);\n }\n}\n\nimpl Deserialize for i32 {\n let N: u32 = I32_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u32 as i32\n }\n}\n\nimpl Serialize for i64 {\n let N: u32 = I64_SERIALIZED_LEN;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self as u64 as Field);\n }\n}\n\nimpl Deserialize for i64 {\n let N: u32 = I64_SERIALIZED_LEN;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n reader.read() as u64 as i64\n }\n}\n\nimpl<T, let M: u32> Serialize for [T; M]\nwhere\n T: Serialize,\n{\n let N: u32 = <T as Serialize>::N * M;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n for i in 0..M {\n self[i].stream_serialize(writer);\n }\n }\n}\n\nimpl<T, let M: u32> Deserialize for [T; M]\nwhere\n T: Deserialize,\n{\n let N: u32 = <T as Deserialize>::N * M;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n let mut result: [T; M] = std::mem::zeroed();\n for i in 0..M {\n result[i] = T::stream_deserialize(reader);\n }\n result\n }\n}\n\nimpl<T> Serialize for Option<T>\nwhere\n T: Serialize,\n{\n let N: u32 = <T as Serialize>::N + 1;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write_bool(self.is_some());\n if self.is_some() {\n self.unwrap_unchecked().stream_serialize(writer);\n } else {\n writer.advance_offset(<T as Serialize>::N);\n }\n }\n}\n\nimpl<T> Deserialize for Option<T>\nwhere\n T: Deserialize,\n{\n let N: u32 = <T as Deserialize>::N + 1;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n if reader.read_bool() {\n Option::some(<T as Deserialize>::stream_deserialize(reader))\n } else {\n reader.advance_offset(<T as Deserialize>::N);\n Option::none()\n }\n }\n}\n\nglobal SCALAR_SIZE: u32 = 2;\n\nimpl Serialize for EmbeddedCurveScalar {\n\n let N: u32 = SCALAR_SIZE;\n\n fn serialize(self) -> [Field; SCALAR_SIZE] {\n [self.lo, self.hi]\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self.lo);\n writer.write(self.hi);\n }\n}\n\nimpl Deserialize for EmbeddedCurveScalar {\n let N: u32 = SCALAR_SIZE;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n Self { lo: fields[0], hi: fields[1] }\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n Self { lo: reader.read(), hi: reader.read() }\n }\n}\n\nglobal POINT_SIZE: u32 = 2;\n\nimpl Serialize for EmbeddedCurvePoint {\n let N: u32 = POINT_SIZE;\n\n fn serialize(self) -> [Field; Self::N] {\n [self.x, self.y]\n }\n\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n writer.write(self.x);\n writer.write(self.y);\n }\n}\n\nimpl Deserialize for EmbeddedCurvePoint {\n let N: u32 = POINT_SIZE;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n Self { x: fields[0], y: fields[1] }\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n Self { x: reader.read(), y: reader.read() }\n }\n}\n\nimpl<let M: u32> Deserialize for str<M> {\n let N: u32 = M;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n let u8_arr = <[u8; Self::N] as Deserialize>::stream_deserialize(reader);\n str::<Self::N>::from(u8_arr)\n }\n}\n\nimpl<let M: u32> Serialize for str<M> {\n let N: u32 = M;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n self.as_bytes().stream_serialize(writer);\n }\n}\n\n// Note: Not deriving this because it's not supported to call derive_serialize on a \"remote\" struct (and it will never\n// be supported).\nimpl<T, let M: u32> Deserialize for BoundedVec<T, M>\nwhere\n T: Deserialize,\n{\n let N: u32 = <T as Deserialize>::N * M + 1;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n let mut new_bounded_vec: BoundedVec<T, M> = BoundedVec::new();\n let payload_len = Self::N - 1;\n\n // Length is stored in the last field as we need to match intrinsic Noir serialization and the `len` struct\n // field is after `storage` struct field (see `bounded_vec.nr` in noir-stdlib)\n let len = reader.peek_offset(payload_len) as u32;\n\n for i in 0..M {\n if i < len {\n new_bounded_vec.push(<T as Deserialize>::stream_deserialize(reader));\n }\n }\n\n // +1 for the length of the BoundedVec\n reader.advance_offset((M - len) * <T as Deserialize>::N + 1);\n\n new_bounded_vec\n }\n}\n\n// This may cause issues if used as program input, because noir disallows empty arrays for program input.\n// I think this is okay because I don't foresee a unit type being used as input. But leaving this comment as a hint\n// if someone does run into this in the future.\nimpl Deserialize for () {\n let N: u32 = 0;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(_reader: &mut Reader<K>) -> Self {\n ()\n }\n}\n\n// Note: Not deriving this because it's not supported to call derive_serialize on a \"remote\" struct (and it will never\n// be supported).\nimpl<T, let M: u32> Serialize for BoundedVec<T, M>\nwhere\n T: Serialize,\n{\n let N: u32 = <T as Serialize>::N * M + 1; // +1 for the length of the BoundedVec\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: Writer<Self::N> = Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n self.storage().stream_serialize(writer);\n // Length is stored in the last field as we need to match intrinsic Noir serialization and the `len` struct\n // field is after `storage` struct field (see `bounded_vec.nr` in noir-stdlib)\n writer.write_u32(self.len() as u32);\n }\n}\n\n// Create a slice of the given length with each element made from `f(i)` where `i` is the current index\ncomptime fn make_slice<Env, T>(length: u32, f: fn[Env](u32) -> T) -> [T] {\n let mut slice = @[];\n for i in 0..length {\n slice = slice.push_back(f(i));\n }\n slice\n}\n\n// Implements Serialize and Deserialize for an arbitrary tuple type\ncomptime fn impl_serialize_for_tuple(_m: Module, length: u32) -> Quoted {\n // `T0`, `T1`, `T2`\n let type_names = make_slice(length, |i| f\"T{i}\".quoted_contents());\n\n // `result0`, `result1`, `result2`\n let result_names = make_slice(length, |i| f\"result{i}\".quoted_contents());\n\n // `T0, T1, T2`\n let field_generics = type_names.join(quote [,]);\n\n // `<T0 as Serialize>::N + <T1 as Serialize>::N + <T2 as Serialize>::N`\n let full_size_serialize = type_names\n .map(|type_name| quote {\n <$type_name as Serialize>::N\n })\n .join(quote [+]);\n\n // `<T0 as Deserialize>::N + <T1 as Deserialize>::N + <T2 as Deserialize>::N`\n let full_size_deserialize = type_names\n .map(|type_name| quote {\n <$type_name as Deserialize>::N\n })\n .join(quote [+]);\n\n // `T0: Serialize, T1: Serialize, T2: Serialize,`\n let serialize_constraints = type_names\n .map(|field_name| quote {\n $field_name: Serialize,\n })\n .join(quote []);\n\n // `T0: Deserialize, T1: Deserialize, T2: Deserialize,`\n let deserialize_constraints = type_names\n .map(|field_name| quote {\n $field_name: Deserialize,\n })\n .join(quote []);\n\n // Statements to serialize each field\n let serialized_fields = type_names\n .mapi(|i, _type_name| quote {\n $crate::serialization::Serialize::stream_serialize(self.$i, writer);\n })\n .join(quote []);\n\n // Statements to deserialize each field\n let deserialized_fields = type_names\n .mapi(|i, type_name| {\n let result_name = result_names[i];\n quote {\n let $result_name = <$type_name as $crate::serialization::Deserialize>::stream_deserialize(reader);\n }\n })\n .join(quote []);\n let deserialize_results = result_names.join(quote [,]);\n\n quote {\n impl<$field_generics> Serialize for ($field_generics) where $serialize_constraints {\n let N: u32 = $full_size_serialize;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: $crate::writer::Writer<Self::N> = $crate::writer::Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut $crate::writer::Writer<K>) {\n\n $serialized_fields\n }\n }\n\n impl<$field_generics> Deserialize for ($field_generics) where $deserialize_constraints {\n let N: u32 = $full_size_deserialize;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = $crate::reader::Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n \n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut $crate::reader::Reader<K>) -> Self {\n $deserialized_fields\n ($deserialize_results)\n }\n }\n }\n}\n\n// Keeping these manual impls. They are more efficient since they do not\n// require copying sub-arrays from any serialized arrays.\nimpl<T1> Serialize for (T1,)\nwhere\n T1: Serialize,\n{\n let N: u32 = <T1 as Serialize>::N;\n\n fn serialize(self) -> [Field; Self::N] {\n let mut writer: crate::writer::Writer<Self::N> = crate::writer::Writer::new();\n self.stream_serialize(&mut writer);\n writer.finish()\n }\n\n #[inline_always]\n fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {\n self.0.stream_serialize(writer);\n }\n}\n\nimpl<T1> Deserialize for (T1,)\nwhere\n T1: Deserialize,\n{\n let N: u32 = <T1 as Deserialize>::N;\n\n fn deserialize(fields: [Field; Self::N]) -> Self {\n let mut reader = crate::reader::Reader::new(fields);\n let result = Self::stream_deserialize(&mut reader);\n reader.finish();\n result\n }\n\n #[inline_always]\n fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {\n (<T1 as Deserialize>::stream_deserialize(reader),)\n }\n}\n\n#[impl_serialize_for_tuple(2)]\n#[impl_serialize_for_tuple(3)]\n#[impl_serialize_for_tuple(4)]\n#[impl_serialize_for_tuple(5)]\n#[impl_serialize_for_tuple(6)]\nmod impls {\n use crate::serialization::{Deserialize, Serialize};\n}\n\n#[test]\nunconstrained fn bounded_vec_serialization() {\n // Test empty BoundedVec\n let empty_vec: BoundedVec<Field, 3> = BoundedVec::from_array([]);\n let serialized = empty_vec.serialize();\n let deserialized = BoundedVec::<Field, 3>::deserialize(serialized);\n assert_eq(empty_vec, deserialized);\n assert_eq(deserialized.len(), 0);\n\n // Test partially filled BoundedVec\n let partial_vec: BoundedVec<[u32; 2], 3> = BoundedVec::from_array([[1, 2]]);\n let serialized = partial_vec.serialize();\n let deserialized = BoundedVec::<[u32; 2], 3>::deserialize(serialized);\n assert_eq(partial_vec, deserialized);\n assert_eq(deserialized.len(), 1);\n assert_eq(deserialized.get(0), [1, 2]);\n\n // Test full BoundedVec\n let full_vec: BoundedVec<[u32; 2], 3> = BoundedVec::from_array([[1, 2], [3, 4], [5, 6]]);\n let serialized = full_vec.serialize();\n let deserialized = BoundedVec::<[u32; 2], 3>::deserialize(serialized);\n assert_eq(full_vec, deserialized);\n assert_eq(deserialized.len(), 3);\n assert_eq(deserialized.get(0), [1, 2]);\n assert_eq(deserialized.get(1), [3, 4]);\n assert_eq(deserialized.get(2), [5, 6]);\n}\n"
|
|
1506
1506
|
},
|
|
1507
1507
|
"43": {
|
|
@@ -1999,7 +1999,7 @@
|
|
|
1999
1999
|
"start": 3499
|
|
2000
2000
|
}
|
|
2001
2001
|
],
|
|
2002
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-contracts/contracts/protocol/contract_class_registry_contract/src/main.nr",
|
|
2002
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-contracts/contracts/protocol/contract_class_registry_contract/src/main.nr",
|
|
2003
2003
|
"source": "mod events;\nmod validate_bytecode;\n\n/// Protocol contract that tracks which contract classes have been published to the network.\n///\n/// In Aztec, contracts are split into *classes* and *instances* (deployments of a class at a unique address). This\n/// contract handles publishing of contract classes.\n///\n/// Every contract class must be registered here before any instance of that class can be deployed via the\n/// ContractInstanceRegistry - this applies even to contracts with no public functions. Registration is also\n/// a prerequisite for contract upgrades: the *new* class must be published before an existing deployed contract\n/// instance can update its class id (a contract instance that is not registered in ContractInstanceRegistry cannot\n/// be upgraded).\n///\n/// Registration works by emitting a nullifier keyed to the contract class id. Other protocol contracts\n/// (e.g. ContractInstanceRegistry) verify registration by asserting this nullifier exists.\n///\n/// Contracts with public functions need to have their bytecode published in order for for the sequencer to be\n/// guaranteed to be able to execute a public function (this is achieved by checking contract instance address\n/// nullifier exists by the AVM - see ContractInstanceRegistry for more details). ACIR of private functions does not\n/// need to be published here as private functions are executed on user devices.\npub contract ContractClassRegistry {\n use crate::events::class_published::ContractClassPublished;\n use crate::validate_bytecode::validate_packed_public_bytecode;\n use aztec::{\n oracle::capsules,\n protocol::{\n address::AztecAddress,\n constants::{CONTRACT_CLASS_REGISTRY_BYTECODE_CAPSULE_SLOT, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS},\n contract_class_id::ContractClassId,\n traits::ToField,\n },\n };\n\n /// Publishes a contract class to the network.\n ///\n /// The caller provides the three components of the contract class id (artifact_hash, private_functions_root,\n /// public_bytecode_commitment) together with the packed public bytecode loaded via a capsule. Even contracts with\n /// no public functions have non-empty bytecode because the AVM transpiler adds a public dispatch function that\n /// simply reverts. Without it, publishing would fail due to the non-zero bytecode length check in\n /// `compute_public_bytecode_commitment`.\n ///\n /// Note that there is only one public bytecode commitment per contract instead of per contract-function as\n /// bytecode of public functions is merged into one `public_dispatch` function and the relevant code branch is then\n /// executed based on the function selector.\n ///\n /// This function:\n /// 1. Validates the packed bytecode against the provided commitment.\n /// 2. Recomputes and emits the contract class id as a nullifier (preventing duplicate registration and serving as\n /// a proof of publication).\n /// 3. Broadcasts a `ContractClassPublished` event containing the full class metadata and bytecode so that nodes\n /// can reconstruct the class.\n #[aztec::macros::internals_functions_generation::abi_attributes::abi_private]\n fn publish(\n inputs: aztec::context::inputs::PrivateContextInputs,\n artifact_hash: Field,\n private_functions_root: Field,\n public_bytecode_commitment: Field,\n ) -> return_data aztec::protocol::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs {\n // MACRO CODE START\n // Note: The macros initially inserted a phase check here, but since there is no phase change in this function\n // body, I have removed that check.\n aztec::oracle::version::assert_compatible_oracle_version();\n\n let serialized_params: [Field; 3] = [artifact_hash, private_functions_root, public_bytecode_commitment];\n\n let args_hash: Field = aztec::hash::hash_args(serialized_params);\n let mut context: aztec::context::PrivateContext = aztec::context::PrivateContext::new(inputs, args_hash);\n // MACRO CODE END\n\n // Safety: We load the bytecode via a capsule, which is unconstrained. In order to ensure the loaded bytecode\n // matches the expected one, we recompute the commitment and assert it matches the one provided by the caller.\n let packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS] = unsafe {\n capsules::load(\n context.this_address(),\n CONTRACT_CLASS_REGISTRY_BYTECODE_CAPSULE_SLOT,\n AztecAddress::zero(),\n )\n .unwrap()\n };\n\n validate_packed_public_bytecode(packed_public_bytecode, public_bytecode_commitment);\n\n // Compute contract class id from preimage\n let contract_class_id = ContractClassId::compute(\n artifact_hash,\n private_functions_root,\n public_bytecode_commitment,\n );\n\n // Emit the contract class id as a nullifier:\n // - to demonstrate that this contract class hasn't been published before\n // - to enable apps to read that this contract class has been published.\n // We use no domain separators because these are the only nullifiers this contract uses.\n context.push_nullifier(contract_class_id.to_field());\n\n // Broadcast class info including public bytecode\n aztec::oracle::logging::debug_log_format(\n \"ContractClassPublished: {}\",\n [contract_class_id.to_field(), artifact_hash, private_functions_root, public_bytecode_commitment],\n );\n\n let event = ContractClassPublished {\n contract_class_id,\n version: 1,\n artifact_hash,\n private_functions_root,\n packed_public_bytecode,\n };\n context.emit_contract_class_log(event.serialize_non_standard());\n\n // MACRO CODE START\n context.finish()\n // MACRO CODE END\n }\n\n // THE REST OF THE CODE IN THIS CONTRACT WAS ORIGINALLY INJECTED BY THE #[aztec] MACRO.\n\n pub struct publish_parameters {\n pub _artifact_hash: Field,\n pub _private_functions_root: Field,\n pub _public_bytecode_commitment: Field,\n }\n\n #[abi(functions)]\n pub struct publish_abi {\n parameters: publish_parameters,\n }\n}\n"
|
|
2004
2004
|
},
|
|
2005
2005
|
"55": {
|
|
@@ -2045,7 +2045,7 @@
|
|
|
2045
2045
|
"start": 6356
|
|
2046
2046
|
}
|
|
2047
2047
|
],
|
|
2048
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-contracts/contracts/protocol/contract_class_registry_contract/src/validate_bytecode.nr",
|
|
2048
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-contracts/contracts/protocol/contract_class_registry_contract/src/validate_bytecode.nr",
|
|
2049
2049
|
"source": "use aztec::{hash::compute_public_bytecode_commitment, protocol::constants::MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS};\n\npub(crate) fn validate_packed_public_bytecode(\n packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS],\n public_bytecode_commitment: Field,\n) {\n validate_bytes_encoding(packed_public_bytecode);\n\n assert_eq(compute_public_bytecode_commitment(packed_public_bytecode), public_bytecode_commitment);\n}\n\nfn validate_bytes_encoding(packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS]) {\n // Field 0 stores the bytecode length (in bytes).\n // It must fit within 32 bits.\n packed_public_bytecode[0].assert_max_bit_size::<32>();\n // Each subsequent field packs up to 31 bytes (= 248 bits).\n // The top 8 bits of the 32-byte field element must be zero.\n for i in 1..MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS {\n packed_public_bytecode[i].assert_max_bit_size::<248>();\n }\n\n // Ensure the final populated field does not contain garbage bytes beyond the declared bytecode length.\n let bytecode_length_in_bytes = packed_public_bytecode[0] as u32;\n let not_aligned_bytes = bytecode_length_in_bytes % 31;\n\n // Number of fields needed to store the bytecode payload (excluding field 0).\n let bytecode_length_in_fields = (bytecode_length_in_bytes / 31) + (not_aligned_bytes != 0) as u32;\n\n // We don't -1 because the first field is the bytes length\n let decomposition: [u8; 32] = packed_public_bytecode[bytecode_length_in_fields].to_be_bytes();\n\n // If perfectly aligned, the last field should contain 31 bytes.\n // Otherwise, it should contain only the remaining `not_aligned_bytes`.\n let expected_populated_bytes = if not_aligned_bytes == 0 {\n 31\n } else {\n not_aligned_bytes\n };\n\n let mut should_be_zero = false;\n for i in 0..32 {\n // The field is 32 bytes wide, but only the lower 31 bytes are allowed to be non-zero.\n // The most significant byte (index 0 in big-endian form) was already constrained to be zero via\n // `assert_max_bit_size::<248>()`.\n // Therefore, the actual bytecode data starts at index 1. We add +1 here to account for that skipped MSB.\n // Once we reach the first byte beyond the expected populated region, all remaining bytes must be zero.\n should_be_zero |= i == (expected_populated_bytes + 1);\n\n if should_be_zero {\n assert_eq(decomposition[i], 0, \"Extra bytes in the last field of the bytecode\");\n }\n }\n}\n\n#[test]\nfn test_validate_bytes_encoding_one_field_not_aligned() {\n let mut packed_public_bytecode = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS];\n packed_public_bytecode[0] = 30;\n // Upper bits clean, no extra trailing bytes\n let bytes = [\n 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,\n 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x00,\n ];\n packed_public_bytecode[1] = Field::from_be_bytes(bytes);\n\n validate_bytes_encoding(packed_public_bytecode);\n}\n\n#[test]\nfn test_validate_bytes_encoding_one_field_aligned() {\n let mut packed_public_bytecode = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS];\n packed_public_bytecode[0] = 31;\n\n // A field with 31 bytes\n let bytes = [\n 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,\n 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\n ];\n packed_public_bytecode[1] = Field::from_be_bytes(bytes);\n\n validate_bytes_encoding(packed_public_bytecode);\n}\n\n#[test]\nfn test_validate_bytes_encoding_two_fields_not_aligned() {\n let mut packed_public_bytecode = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS];\n packed_public_bytecode[0] = 32;\n\n // First field: 31 bytes\n let field_1_bytes = [\n 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,\n 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\n ];\n packed_public_bytecode[1] = Field::from_be_bytes(field_1_bytes);\n\n // Second field: 1 byte, no extra trailing bytes\n let mut field_2_bytes = [0; 31];\n field_2_bytes[0] = 0x20;\n packed_public_bytecode[2] = Field::from_be_bytes(field_2_bytes);\n\n validate_bytes_encoding(packed_public_bytecode);\n}\n\n#[test]\nfn test_validate_bytes_encoding_two_fields_aligned() {\n let mut packed_public_bytecode = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS];\n packed_public_bytecode[0] = 62;\n\n // Both fields have 31 bytes\n let bytes = [\n 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,\n 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\n ];\n packed_public_bytecode[1] = Field::from_be_bytes(bytes);\n packed_public_bytecode[2] = Field::from_be_bytes(bytes);\n\n validate_bytes_encoding(packed_public_bytecode);\n}\n\n#[test]\nfn test_validate_bytes_encoding_all_zeros() {\n let mut packed_public_bytecode = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS];\n // 63 bytes, all bytes are zero\n packed_public_bytecode[0] = 63;\n validate_bytes_encoding(packed_public_bytecode);\n}\n\n#[test(should_fail_with = \"assert_max_bit_size\")]\nfn test_should_fail_too_large_bytecode_length() {\n let mut packed_public_bytecode = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS];\n packed_public_bytecode[0] = 0x100000000;\n validate_bytes_encoding(packed_public_bytecode);\n}\n\n#[test(should_fail_with = \"assert_max_bit_size\")]\nfn test_should_fail_populated_upper_bits() {\n let mut packed_public_bytecode = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS];\n packed_public_bytecode[0] = 30;\n // one upper bit populated\n let bytes = [\n 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,\n 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x00,\n ];\n packed_public_bytecode[1] = Field::from_be_bytes(bytes);\n\n validate_bytes_encoding(packed_public_bytecode);\n}\n\n#[test(should_fail_with = \"Extra bytes in the last field of the bytecode\")]\nfn test_should_fail_extra_bytes_in_last_field() {\n let mut packed_public_bytecode = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS];\n packed_public_bytecode[0] = 30;\n // Last one should be zero\n let bytes = [\n 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,\n 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x01,\n ];\n packed_public_bytecode[1] = Field::from_be_bytes(bytes);\n\n validate_bytes_encoding(packed_public_bytecode);\n}\n"
|
|
2050
2050
|
},
|
|
2051
2051
|
"60": {
|
|
@@ -2147,7 +2147,7 @@
|
|
|
2147
2147
|
"start": 15805
|
|
2148
2148
|
}
|
|
2149
2149
|
],
|
|
2150
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-contracts/contracts/protocol/aztec_sublib/src/context/private_context.nr",
|
|
2150
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-contracts/contracts/protocol/aztec_sublib/src/context/private_context.nr",
|
|
2151
2151
|
"source": "use crate::{\n context::{inputs::PrivateContextInputs, NullifierExistenceRequest, ReturnsHash},\n hash::hash_args,\n messaging::process_l1_to_l2_message,\n oracle::{\n call_private_function::call_private_function_internal,\n public_call::validate_public_calldata,\n tx_phase::{in_revertible_phase, notify_revertible_phase_start},\n execution_cache,\n logs::notify_created_contract_class_log,\n nullifiers::notify_created_nullifier,\n },\n};\nuse crate::protocol::{\n abis::{\n block_header::BlockHeader,\n call_context::CallContext,\n function_selector::FunctionSelector,\n gas_settings::GasSettings,\n log_hash::LogHash,\n nullifier::Nullifier,\n private_call_request::PrivateCallRequest,\n private_circuit_public_inputs::PrivateCircuitPublicInputs,\n private_log::{PrivateLog, PrivateLogData},\n public_call_request::PublicCallRequest,\n },\n address::{AztecAddress, EthAddress},\n constants::{\n CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, MAX_CONTRACT_CLASS_LOGS_PER_CALL,\n MAX_ENQUEUED_CALLS_PER_CALL, MAX_TX_LIFETIME, MAX_L2_TO_L1_MSGS_PER_CALL,\n MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIERS_PER_CALL,\n MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PRIVATE_LOGS_PER_CALL,\n NULL_MSG_SENDER_CONTRACT_ADDRESS, PRIVATE_LOG_SIZE_IN_FIELDS,\n },\n hash::poseidon2_hash,\n messaging::l2_to_l1_message::L2ToL1Message,\n side_effect::{Counted, scoped::Scoped},\n traits::Empty,\n utils::arrays::{ClaimedLengthArray, trimmed_array_length_hint},\n};\n\n/// Minimal PrivateContext for protocol contracts going to audit.\n/// Contains only the methods actually used by: fee_juice, auth_registry, contract_class_registry, contract_instance_registry\n#[derive(Eq)]\npub struct PrivateContext {\n pub inputs: PrivateContextInputs,\n pub side_effect_counter: u32,\n\n pub min_revertible_side_effect_counter: u32,\n pub is_fee_payer: bool,\n\n pub args_hash: Field,\n pub return_hash: Field,\n\n pub expiration_timestamp: u64,\n\n pub nullifier_read_requests: BoundedVec<Scoped<Counted<Field>>, MAX_NULLIFIER_READ_REQUESTS_PER_CALL>,\n\n pub nullifiers: BoundedVec<Counted<Nullifier>, MAX_NULLIFIERS_PER_CALL>,\n\n pub private_call_requests: BoundedVec<PrivateCallRequest, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL>,\n pub public_call_requests: BoundedVec<Counted<PublicCallRequest>, MAX_ENQUEUED_CALLS_PER_CALL>,\n pub public_teardown_call_request: PublicCallRequest,\n pub l2_to_l1_msgs: BoundedVec<Counted<L2ToL1Message>, MAX_L2_TO_L1_MSGS_PER_CALL>,\n\n // Header of a block whose state is used during private execution (not the block the transaction is included in).\n pub anchor_block_header: BlockHeader,\n\n pub private_logs: BoundedVec<Counted<PrivateLogData>, MAX_PRIVATE_LOGS_PER_CALL>,\n pub contract_class_logs_hashes: BoundedVec<Counted<LogHash>, MAX_CONTRACT_CLASS_LOGS_PER_CALL>,\n\n pub expected_non_revertible_side_effect_counter: u32,\n pub expected_revertible_side_effect_counter: u32,\n}\n\nimpl PrivateContext {\n pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext {\n PrivateContext {\n inputs,\n side_effect_counter: inputs.start_side_effect_counter + 1,\n min_revertible_side_effect_counter: 0,\n is_fee_payer: false,\n args_hash,\n return_hash: 0,\n expiration_timestamp: inputs.anchor_block_header.global_variables.timestamp\n + MAX_TX_LIFETIME,\n nullifier_read_requests: BoundedVec::new(),\n nullifiers: BoundedVec::new(),\n anchor_block_header: inputs.anchor_block_header,\n private_call_requests: BoundedVec::new(),\n public_call_requests: BoundedVec::new(),\n public_teardown_call_request: PublicCallRequest::empty(),\n l2_to_l1_msgs: BoundedVec::new(),\n private_logs: BoundedVec::new(),\n contract_class_logs_hashes: BoundedVec::new(),\n expected_non_revertible_side_effect_counter: 0,\n expected_revertible_side_effect_counter: 0,\n }\n }\n\n /// Returns the contract address that initiated this function call (similar to msg.sender in Solidity).\n pub fn maybe_msg_sender(self) -> Option<AztecAddress> {\n let maybe_msg_sender = self.inputs.call_context.msg_sender;\n if maybe_msg_sender == NULL_MSG_SENDER_CONTRACT_ADDRESS {\n Option::none()\n } else {\n Option::some(maybe_msg_sender)\n }\n }\n\n /// Returns the contract address of the current function being executed.\n pub fn this_address(self) -> AztecAddress {\n self.inputs.call_context.contract_address\n }\n\n /// Returns the chain ID of the current network.\n pub fn chain_id(self) -> Field {\n self.inputs.tx_context.chain_id\n }\n\n /// Returns the protocol version.\n pub fn version(self) -> Field {\n self.inputs.tx_context.version\n }\n\n /// Returns the gas settings for the current transaction.\n pub fn gas_settings(self) -> GasSettings {\n self.inputs.tx_context.gas_settings\n }\n\n /// Returns the function selector of the currently executing function.\n pub fn selector(self) -> FunctionSelector {\n self.inputs.call_context.function_selector\n }\n\n /// Returns the hash of the arguments passed to the current function.\n pub fn get_args_hash(self) -> Field {\n self.args_hash\n }\n\n /// Returns the anchor block header.\n pub fn get_anchor_block_header(self) -> BlockHeader {\n self.anchor_block_header\n }\n\n /// Sets the hash of the return values for this private function.\n pub fn set_return_hash<let N: u32>(&mut self, serialized_return_values: [Field; N]) {\n let return_hash = hash_args(serialized_return_values);\n self.return_hash = return_hash;\n execution_cache::store(serialized_return_values, return_hash);\n }\n\n /// Builds the PrivateCircuitPublicInputs for this private function.\n pub fn finish(self) -> PrivateCircuitPublicInputs {\n PrivateCircuitPublicInputs {\n call_context: self.inputs.call_context,\n args_hash: self.args_hash,\n returns_hash: self.return_hash,\n min_revertible_side_effect_counter: self.min_revertible_side_effect_counter,\n is_fee_payer: self.is_fee_payer,\n expiration_timestamp: self.expiration_timestamp,\n note_hash_read_requests: ClaimedLengthArray::empty(), // Not used by protocol contracts\n nullifier_read_requests: ClaimedLengthArray::from_bounded_vec(\n self.nullifier_read_requests,\n ),\n key_validation_requests_and_separators: ClaimedLengthArray::empty(), // Not used by protocol contracts\n note_hashes: ClaimedLengthArray::empty(), // Not used by protocol contracts\n nullifiers: ClaimedLengthArray::from_bounded_vec(self.nullifiers),\n private_call_requests: ClaimedLengthArray::from_bounded_vec(self.private_call_requests),\n public_call_requests: ClaimedLengthArray::from_bounded_vec(self.public_call_requests),\n public_teardown_call_request: self.public_teardown_call_request,\n l2_to_l1_msgs: ClaimedLengthArray::from_bounded_vec(self.l2_to_l1_msgs),\n start_side_effect_counter: self.inputs.start_side_effect_counter,\n end_side_effect_counter: self.side_effect_counter,\n private_logs: ClaimedLengthArray::from_bounded_vec(self.private_logs),\n contract_class_logs_hashes: ClaimedLengthArray::from_bounded_vec(\n self.contract_class_logs_hashes,\n ),\n anchor_block_header: self.anchor_block_header,\n tx_context: self.inputs.tx_context,\n expected_non_revertible_side_effect_counter: self\n .expected_non_revertible_side_effect_counter,\n expected_revertible_side_effect_counter: self.expected_revertible_side_effect_counter,\n tx_request_salt: self.inputs.tx_request_salt,\n }\n }\n\n /// Declares the end of the \"setup phase\" of this tx. Used by fee_juice.\n pub fn end_setup(&mut self) {\n self.side_effect_counter += 1;\n self.min_revertible_side_effect_counter = self.next_counter();\n notify_revertible_phase_start(self.min_revertible_side_effect_counter);\n }\n\n pub fn in_revertible_phase(&mut self) -> bool {\n let current_counter = self.side_effect_counter;\n\n // Safety: Kernel will validate that the claim is correct by validating the expected counters.\n let is_revertible =\n unsafe { in_revertible_phase(current_counter) };\n\n if is_revertible {\n if (self.expected_revertible_side_effect_counter == 0)\n | (current_counter < self.expected_revertible_side_effect_counter) {\n self.expected_revertible_side_effect_counter = current_counter;\n }\n } else if current_counter > self.expected_non_revertible_side_effect_counter {\n self.expected_non_revertible_side_effect_counter = current_counter;\n }\n\n is_revertible\n }\n\n /// Sets a deadline for when this transaction must be included in a block.\n pub fn set_expiration_timestamp(&mut self, expiration_timestamp: u64) {\n self.expiration_timestamp = std::cmp::min(self.expiration_timestamp, expiration_timestamp);\n }\n\n /// Pushes a new nullifier. Used by class_registry and instance_registry.\n pub fn push_nullifier(&mut self, nullifier: Field) {\n notify_created_nullifier(nullifier);\n self.nullifiers.push(Nullifier { value: nullifier, note_hash: 0 }.count(self.next_counter()));\n }\n\n /// Asserts that a nullifier has been emitted. Used by instance_registry.\n pub fn assert_nullifier_exists(\n &mut self,\n nullifier_existence_request: NullifierExistenceRequest,\n ) {\n let nullifier = nullifier_existence_request.nullifier();\n let contract_address =\n nullifier_existence_request.maybe_contract_address().unwrap_or(AztecAddress::zero());\n\n let request = Scoped::new(\n Counted::new(nullifier, self.next_counter()),\n contract_address,\n );\n\n self.nullifier_read_requests.push(request);\n }\n\n /// Consumes a message sent from Ethereum (L1) to Aztec (L2). Used by fee_juice.\n pub fn consume_l1_to_l2_message(\n &mut self,\n content: Field,\n secret: Field,\n sender: EthAddress,\n leaf_index: Field,\n ) {\n let nullifier = process_l1_to_l2_message(\n self.anchor_block_header.state.l1_to_l2_message_tree.root,\n self.this_address(),\n sender,\n self.chain_id(),\n self.version(),\n content,\n secret,\n leaf_index,\n );\n\n // Push nullifier (and the \"commitment\" corresponding to this can be \"empty\")\n self.push_nullifier(nullifier)\n }\n\n /// Emits a private log. Used by instance_registry.\n pub fn emit_private_log(&mut self, log: [Field; PRIVATE_LOG_SIZE_IN_FIELDS], length: u32) {\n let counter = self.next_counter();\n let private_log = PrivateLogData { log: PrivateLog::new(log, length), note_hash_counter: 0 }\n .count(counter);\n self.private_logs.push(private_log);\n }\n\n /// Emits a contract class log. Used by class_registry.\n pub fn emit_contract_class_log<let N: u32>(&mut self, log: [Field; N]) {\n let contract_address = self.this_address();\n let counter = self.next_counter();\n\n let log_to_emit: [Field; CONTRACT_CLASS_LOG_SIZE_IN_FIELDS] =\n log.concat([0; CONTRACT_CLASS_LOG_SIZE_IN_FIELDS - N]);\n // Safety: The below length is constrained in the base rollup, which will make sure that all the fields beyond\n // length are zero. However, it won't be able to check that we didn't add extra padding (trailing zeroes) or\n // that we cut trailing zeroes from the end.\n let length = unsafe { trimmed_array_length_hint(log_to_emit) };\n // We hash the entire padded log to ensure a user cannot pass a shorter length and so emit incorrect shorter\n // bytecode.\n let log_hash = poseidon2_hash(log_to_emit);\n // Safety: the below only exists to broadcast the raw log, so we can provide it to the base rollup later to be\n // constrained.\n unsafe {\n notify_created_contract_class_log(contract_address, log_to_emit, length, counter);\n }\n\n self.contract_class_logs_hashes.push(LogHash { value: log_hash, length: length }.count(\n counter,\n ));\n }\n\n /// Makes a read-only call to a private function. Used by auth_registry for authwit.\n pub fn static_call_private_function<let ArgsCount: u32>(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args: [Field; ArgsCount],\n ) -> ReturnsHash {\n let args_hash = hash_args(args);\n execution_cache::store(args, args_hash);\n self.call_private_function_with_args_hash(\n contract_address,\n function_selector,\n args_hash,\n true,\n )\n }\n\n fn call_private_function_with_args_hash(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args_hash: Field,\n is_static_call: bool,\n ) -> ReturnsHash {\n let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;\n let start_side_effect_counter = self.side_effect_counter;\n\n // Safety: The oracle simulates the private call and returns the value of the side effects counter after\n // execution of the call.\n let (end_side_effect_counter, returns_hash) = unsafe {\n call_private_function_internal(\n contract_address,\n function_selector,\n args_hash,\n start_side_effect_counter,\n is_static_call,\n )\n };\n\n self.private_call_requests.push(\n PrivateCallRequest {\n call_context: CallContext {\n msg_sender: self.this_address(),\n contract_address,\n function_selector,\n is_static_call,\n },\n args_hash,\n returns_hash,\n start_side_effect_counter,\n end_side_effect_counter,\n },\n );\n\n self.side_effect_counter = end_side_effect_counter + 1;\n ReturnsHash::new(returns_hash)\n }\n\n /// Enqueues a call to a public function with a calldata hash. Used by fee_juice and auth_registry.\n pub fn call_public_function_with_calldata_hash(\n &mut self,\n contract_address: AztecAddress,\n calldata_hash: Field,\n is_static_call: bool,\n hide_msg_sender: bool,\n ) {\n let counter = self.next_counter();\n\n let is_static_call = is_static_call | self.inputs.call_context.is_static_call;\n\n validate_public_calldata(calldata_hash);\n\n let msg_sender = if hide_msg_sender {\n NULL_MSG_SENDER_CONTRACT_ADDRESS\n } else {\n self.this_address()\n };\n\n let call_request =\n PublicCallRequest { msg_sender, contract_address, is_static_call, calldata_hash };\n\n self.public_call_requests.push(Counted::new(call_request, counter));\n }\n\n fn next_counter(&mut self) -> u32 {\n let counter = self.side_effect_counter;\n self.side_effect_counter += 1;\n counter\n }\n}\n\nimpl Empty for PrivateContext {\n fn empty() -> Self {\n PrivateContext {\n inputs: PrivateContextInputs::empty(),\n side_effect_counter: 0 as u32,\n min_revertible_side_effect_counter: 0 as u32,\n is_fee_payer: false,\n args_hash: 0,\n return_hash: 0,\n expiration_timestamp: 0,\n nullifier_read_requests: BoundedVec::new(),\n nullifiers: BoundedVec::new(),\n private_call_requests: BoundedVec::new(),\n public_call_requests: BoundedVec::new(),\n public_teardown_call_request: PublicCallRequest::empty(),\n l2_to_l1_msgs: BoundedVec::new(),\n anchor_block_header: BlockHeader::empty(),\n private_logs: BoundedVec::new(),\n contract_class_logs_hashes: BoundedVec::new(),\n expected_non_revertible_side_effect_counter: 0,\n expected_revertible_side_effect_counter: 0,\n }\n }\n}\n"
|
|
2152
2152
|
},
|
|
2153
2153
|
"64": {
|
|
@@ -2177,7 +2177,7 @@
|
|
|
2177
2177
|
"start": 2994
|
|
2178
2178
|
}
|
|
2179
2179
|
],
|
|
2180
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-contracts/contracts/protocol/aztec_sublib/src/hash.nr",
|
|
2180
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-contracts/contracts/protocol/aztec_sublib/src/hash.nr",
|
|
2181
2181
|
"source": "//! Aztec hash functions.\n\nuse crate::protocol::{\n address::{AztecAddress, EthAddress},\n constants::{\n DOM_SEP__FUNCTION_ARGS, DOM_SEP__MESSAGE_NULLIFIER, DOM_SEP__PUBLIC_BYTECODE,\n DOM_SEP__PUBLIC_CALLDATA, DOM_SEP__SECRET_HASH, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,\n },\n hash::{poseidon2_hash_subarray, poseidon2_hash_with_separator, sha256_to_field},\n traits::ToField,\n};\n\npub use crate::protocol::hash::compute_siloed_nullifier;\n\npub fn compute_secret_hash(secret: Field) -> Field {\n poseidon2_hash_with_separator([secret], DOM_SEP__SECRET_HASH)\n}\n\npub fn compute_l1_to_l2_message_hash(\n sender: EthAddress,\n chain_id: Field,\n recipient: AztecAddress,\n version: Field,\n content: Field,\n secret_hash: Field,\n leaf_index: Field,\n) -> Field {\n let mut hash_bytes = [0 as u8; 224];\n let sender_bytes: [u8; 32] = sender.to_field().to_be_bytes();\n let chain_id_bytes: [u8; 32] = chain_id.to_be_bytes();\n let recipient_bytes: [u8; 32] = recipient.to_field().to_be_bytes();\n let version_bytes: [u8; 32] = version.to_be_bytes();\n let content_bytes: [u8; 32] = content.to_be_bytes();\n let secret_hash_bytes: [u8; 32] = secret_hash.to_be_bytes();\n let leaf_index_bytes: [u8; 32] = leaf_index.to_be_bytes();\n\n for i in 0..32 {\n hash_bytes[i] = sender_bytes[i];\n hash_bytes[i + 32] = chain_id_bytes[i];\n hash_bytes[i + 64] = recipient_bytes[i];\n hash_bytes[i + 96] = version_bytes[i];\n hash_bytes[i + 128] = content_bytes[i];\n hash_bytes[i + 160] = secret_hash_bytes[i];\n hash_bytes[i + 192] = leaf_index_bytes[i];\n }\n\n sha256_to_field(hash_bytes)\n}\n\n// The nullifier of a l1 to l2 message is the hash of the message salted with the secret\npub fn compute_l1_to_l2_message_nullifier(message_hash: Field, secret: Field) -> Field {\n poseidon2_hash_with_separator([message_hash, secret], DOM_SEP__MESSAGE_NULLIFIER)\n}\n\n// Computes the hash of input arguments or return values for private functions, or for authwit creation.\npub fn hash_args<let N: u32>(args: [Field; N]) -> Field {\n if args.len() == 0 {\n 0\n } else {\n poseidon2_hash_with_separator(args, DOM_SEP__FUNCTION_ARGS)\n }\n}\n\n// Computes the hash of calldata for public functions.\npub fn hash_calldata_array<let N: u32>(calldata: [Field; N]) -> Field {\n poseidon2_hash_with_separator(calldata, DOM_SEP__PUBLIC_CALLDATA)\n}\n\n/// Computes the public bytecode commitment for a contract class. The commitment is `hash([(length | separator),\n/// ...bytecode])`.\n///\n/// @param packed_bytecode - The packed bytecode of the contract class. 0th word is the length in bytes.\n/// packed_bytecode is mutable so that we can avoid copying the array to construct one starting with first_field\n/// instead of length. @returns The public bytecode commitment.\npub fn compute_public_bytecode_commitment(\n mut packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS],\n) -> Field {\n // First field element contains the length of the bytecode\n let bytecode_length_in_bytes: u32 = packed_public_bytecode[0] as u32;\n let bytecode_length_in_fields: u32 = (bytecode_length_in_bytes / 31) + (bytecode_length_in_bytes % 31 != 0) as u32;\n // Don't allow empty public bytecode. AVM doesn't handle execution of contracts that exist with empty bytecode.\n assert(bytecode_length_in_fields != 0);\n assert(bytecode_length_in_fields < MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS);\n\n // Packed_bytecode's 0th entry is the length. Append it to the separator before hashing.\n let first_field = DOM_SEP__PUBLIC_BYTECODE.to_field() + (packed_public_bytecode[0] as u64 << 32) as Field;\n packed_public_bytecode[0] = first_field;\n\n // `fields_to_hash` is the number of fields from the start of `packed_public_bytecode` that should be included in\n // the hash. Fields after this length are ignored. +1 to account for the separator.\n let num_fields_to_hash = bytecode_length_in_fields + 1;\n\n poseidon2_hash_subarray(packed_public_bytecode, num_fields_to_hash)\n}\n"
|
|
2182
2182
|
},
|
|
2183
2183
|
"76": {
|
|
@@ -2215,7 +2215,7 @@
|
|
|
2215
2215
|
"start": 3284
|
|
2216
2216
|
}
|
|
2217
2217
|
],
|
|
2218
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-contracts/contracts/protocol/aztec_sublib/src/oracle/capsules.nr",
|
|
2218
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-contracts/contracts/protocol/aztec_sublib/src/oracle/capsules.nr",
|
|
2219
2219
|
"source": "use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};\n\n/// Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `load`. If\n/// data was already stored at this slot, it is overwritten.\npub unconstrained fn store<T>(\n contract_address: AztecAddress,\n slot: Field,\n value: T,\n scope: AztecAddress,\n)\nwhere\n T: Serialize,\n{\n let serialized = value.serialize();\n store_oracle(contract_address, slot, serialized, scope);\n}\n\n/// Returns data previously stored via `storeCapsule` in the per-contract non-volatile database. Returns\n/// Option::none() if nothing was stored at the given slot.\npub unconstrained fn load<T>(\n contract_address: AztecAddress,\n slot: Field,\n scope: AztecAddress,\n) -> Option<T>\nwhere\n T: Deserialize,\n{\n let serialized_option = load_oracle(contract_address, slot, <T as Deserialize>::N, scope);\n serialized_option.map(|arr| Deserialize::deserialize(arr))\n}\n\n/// Deletes data in the per-contract non-volatile database. Does nothing if no data was present.\npub unconstrained fn delete(\n contract_address: AztecAddress,\n slot: Field,\n scope: AztecAddress,\n) {\n delete_oracle(contract_address, slot, scope);\n}\n\n/// Copies a number of contiguous entries in the per-contract non-volatile database. This allows for efficient data\n/// structures by avoiding repeated calls to `loadCapsule` and `storeCapsule`. Supports overlapping source and\n/// destination regions (which will result in the overlapped source values being overwritten). All copied slots must\n/// exist in the database (i.e. have been stored and not deleted)\npub unconstrained fn copy(\n contract_address: AztecAddress,\n src_slot: Field,\n dst_slot: Field,\n num_entries: u32,\n scope: AztecAddress,\n) {\n copy_oracle(contract_address, src_slot, dst_slot, num_entries, scope);\n}\n\n#[oracle(aztec_utl_setCapsule)]\n// TODO(F-498): review naming consistency\nunconstrained fn store_oracle<let N: u32>(\n contract_address: AztecAddress,\n slot: Field,\n values: [Field; N],\n scope: AztecAddress,\n) {}\n\n/// We need to pass in `array_len` (the value of N) as a parameter to tell the oracle how many fields the response must\n/// have.\n///\n/// Note that the oracle returns an Option<[Field; N]> because we cannot return an Option<T> directly. That would\n/// require for the oracle resolver to know the shape of T (e.g. if T were a struct of 3 u32 values then the expected\n/// response shape would be 3 single items, whereas it were a struct containing `u32, [Field;10], u32` then the\n/// expected shape would be single, array, single.). Instead, we return the serialization and deserialize in Noir.\n// TODO(F-498): review naming consistency\n#[oracle(aztec_utl_getCapsule)]\nunconstrained fn load_oracle<let N: u32>(\n contract_address: AztecAddress,\n slot: Field,\n array_len: u32,\n scope: AztecAddress,\n) -> Option<[Field; N]> {}\n\n#[oracle(aztec_utl_deleteCapsule)]\nunconstrained fn delete_oracle(contract_address: AztecAddress, slot: Field, scope: AztecAddress) {}\n\n#[oracle(aztec_utl_copyCapsule)]\nunconstrained fn copy_oracle(\n contract_address: AztecAddress,\n src_slot: Field,\n dst_slot: Field,\n num_entries: u32,\n scope: AztecAddress,\n) {}\n"
|
|
2220
2220
|
},
|
|
2221
2221
|
"80": {
|
|
@@ -2229,7 +2229,7 @@
|
|
|
2229
2229
|
"start": 658
|
|
2230
2230
|
}
|
|
2231
2231
|
],
|
|
2232
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-contracts/contracts/protocol/aztec_sublib/src/oracle/logs.nr",
|
|
2232
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-contracts/contracts/protocol/aztec_sublib/src/oracle/logs.nr",
|
|
2233
2233
|
"source": "use crate::protocol::address::AztecAddress;\n\n/// The below only exists to broadcast the raw log, so we can provide it to the base rollup later to be constrained.\npub unconstrained fn notify_created_contract_class_log<let N: u32>(\n contract_address: AztecAddress,\n message: [Field; N],\n length: u32,\n counter: u32,\n) {\n notify_created_contract_class_log_private_oracle(contract_address, message, length, counter)\n}\n\n#[oracle(aztec_prv_notifyCreatedContractClassLog)]\nunconstrained fn notify_created_contract_class_log_private_oracle<let N: u32>(\n contract_address: AztecAddress,\n message: [Field; N],\n length: u32,\n counter: u32,\n) {}\n"
|
|
2234
2234
|
},
|
|
2235
2235
|
"82": {
|
|
@@ -2259,7 +2259,7 @@
|
|
|
2259
2259
|
"start": 2433
|
|
2260
2260
|
}
|
|
2261
2261
|
],
|
|
2262
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-contracts/contracts/protocol/aztec_sublib/src/oracle/nullifiers.nr",
|
|
2262
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-contracts/contracts/protocol/aztec_sublib/src/oracle/nullifiers.nr",
|
|
2263
2263
|
"source": "//! Nullifier creation, existence checks, etc.\n\nuse crate::protocol::address::aztec_address::AztecAddress;\n\n/// Notifies the simulator that a nullifier has been created, so that its correct status (pending or settled) can be\n/// determined when reading nullifiers in subsequent private function calls. The first non-revertible nullifier emitted\n/// is also used to compute note nonces.\npub fn notify_created_nullifier(inner_nullifier: Field) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to\n // call.\n unsafe { notify_created_nullifier_oracle(inner_nullifier) };\n}\n\n#[oracle(aztec_prv_notifyCreatedNullifier)]\nunconstrained fn notify_created_nullifier_oracle(_inner_nullifier: Field) {}\n\n/// Returns true if the nullifier has been emitted in the same transaction, i.e. if [notify_created_nullifier] has been\n/// called for this inner nullifier from the contract with the specified address.\n///\n/// Note that despite sharing pending transaction information with the app, this is not a privacy leak: anyone in the\n/// network can always determine in which transaction a inner nullifier was emitted by a given contract by simply\n/// inspecting transaction effects. What _would_ constitute a leak would be to share the list of inner pending\n/// nullifiers, as that would reveal their preimages.\npub unconstrained fn is_nullifier_pending(\n inner_nullifier: Field,\n contract_address: AztecAddress,\n) -> bool {\n is_nullifier_pending_oracle(inner_nullifier, contract_address)\n}\n\n#[oracle(aztec_prv_isNullifierPending)]\nunconstrained fn is_nullifier_pending_oracle(\n _inner_nullifier: Field,\n _contract_address: AztecAddress,\n) -> bool {}\n\n/// Returns true if the nullifier exists. Note that a `true` value can be constrained by proving existence of the\n/// nullifier, but a `false` value should not be relied upon since other transactions may emit this nullifier before\n/// the current transaction is included in a block. While this might seem of little use at first, certain design\n/// patterns benefit from this abstraction (see e.g. `PrivateMutable`).\npub unconstrained fn check_nullifier_exists(inner_nullifier: Field) -> bool {\n check_nullifier_exists_oracle(inner_nullifier)\n}\n\n// TODO(F-498): review naming consistency\n#[oracle(aztec_utl_doesNullifierExist)]\nunconstrained fn check_nullifier_exists_oracle(_inner_nullifier: Field) -> bool {}\n"
|
|
2264
2264
|
},
|
|
2265
2265
|
"86": {
|
|
@@ -2285,7 +2285,7 @@
|
|
|
2285
2285
|
"start": 2151
|
|
2286
2286
|
}
|
|
2287
2287
|
],
|
|
2288
|
-
"path": "/home/aztec-dev/aztec-packages/noir-projects/noir-contracts/contracts/protocol/aztec_sublib/src/oracle/version.nr",
|
|
2288
|
+
"path": "/home/aztec-dev/aztec-packages/noir-projects/fnd/noir-contracts/contracts/protocol/aztec_sublib/src/oracle/version.nr",
|
|
2289
2289
|
"source": "/// The oracle version constants are used to check that the oracle interface is in sync between PXE and Aztec.nr.\n/// We version the oracle interface as `major.minor` where:\n/// - `major` = backward-breaking changes (must match exactly between PXE and Aztec.nr)\n/// - `minor` = oracle additions (non-breaking; PXE minor >= contract minor)\n///\n/// The TypeScript counterparts are in `oracle_version.ts`.\n///\n/// @dev Whenever a contract function or Noir test is run, the `aztec_misc_assertCompatibleOracleVersion` oracle is\n/// called. If the major version is incompatible, an error is thrown immediately. The minor version is recorded by\n/// the PXE and used to provide helpful error messages if a contract calls an oracle that doesn't exist. We don't throw\n/// immediately if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency\n/// without actually using any of the new oracles then there is no reason to throw.\npub global ORACLE_VERSION_MAJOR: Field = 30;\npub global ORACLE_VERSION_MINOR: Field = 0;\n\n/// Asserts that the version of the oracle is compatible with the version expected by the contract.\npub fn assert_compatible_oracle_version() {\n // Safety: This oracle call returns nothing: we only call it to check Aztec.nr and Oracle interface versions are\n // compatible. It is therefore always safe to call.\n unsafe {\n assert_compatible_oracle_version_wrapper();\n }\n}\n\nunconstrained fn assert_compatible_oracle_version_wrapper() {\n assert_compatible_oracle_version_oracle(ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR);\n}\n\n#[oracle(aztec_misc_assertCompatibleOracleVersion)]\nunconstrained fn assert_compatible_oracle_version_oracle(major: Field, minor: Field) {}\n\nmod test {\n use super::{\n assert_compatible_oracle_version_oracle, ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR,\n };\n\n #[test]\n unconstrained fn compatible_oracle_version() {\n assert_compatible_oracle_version_oracle(ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR);\n }\n\n #[test(should_fail_with = \"Incompatible aztec cli version:\")]\n unconstrained fn incompatible_oracle_version_major() {\n let arbitrary_incorrect_major = 318183437;\n assert_compatible_oracle_version_oracle(arbitrary_incorrect_major, ORACLE_VERSION_MINOR);\n }\n}\n"
|
|
2290
2290
|
}
|
|
2291
2291
|
},
|
|
@@ -4198,5 +4198,5 @@
|
|
|
4198
4198
|
}
|
|
4199
4199
|
},
|
|
4200
4200
|
"transpiled": true,
|
|
4201
|
-
"aztec_version": "6.0.0-nightly.
|
|
4201
|
+
"aztec_version": "6.0.0-nightly.20260730"
|
|
4202
4202
|
}
|