@aztec/protocol-contracts 0.87.0 → 0.87.1

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.
@@ -2780,7 +2780,7 @@
2780
2780
  },
2781
2781
  "294": {
2782
2782
  "path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr",
2783
- "source": "use super::traits::{Deserialize, Packable, Serialize};\n\n/// Returns the typed expression of a trait method implementation.\n///\n/// This helper function is preferred over directly inlining with `$typ::target_method()` in a quote,\n/// as direct inlining would result in missing import warnings in the generated code (specifically,\n/// warnings that the trait implementation is not in scope).\n///\n/// # Note\n/// A copy of this function exists in `aztec-nr/aztec/src/macros/utils.nr`. We maintain separate copies\n/// because importing it there from here would cause the `target_trait` to be interpreted in the context\n/// of this crate, making it impossible to compile code for traits from that crate (e.g. NoteType).\ncomptime fn get_trait_impl_method(\n typ: Type,\n target_trait: Quoted,\n target_method: Quoted,\n) -> TypedExpr {\n let trait_constraint = target_trait.as_trait_constraint();\n typ\n .get_trait_impl(trait_constraint)\n .expect(f\"Could not find impl for {target_trait} for type {typ}\")\n .methods()\n .filter(|m| m.name() == target_method)[0]\n .as_typed_expr()\n}\n\n/// Generates code that deserializes a struct, primitive type, array or string from a field array.\n///\n/// # Parameters\n/// - `name`: The name of the current field being processed, used to identify fields for replacement.\n/// - `typ`: The type of the struct or field being deserialized (e.g., a custom struct, array, or primitive).\n/// - `field_array_name`: The name of the field array containing serialized field data (e.g., `\"values\"`).\n/// - `num_already_consumed`: The number of fields already processed in previous recursion calls.\n/// - `should_unpack`: A boolean indicating whether the type should be unpacked (see description of `Packable`\n/// and `Serialize` trait for more information about the difference between packing and serialization).\n///\n/// # Returns\n/// A tuple containing:\n/// - `Quoted`: A code that deserializes a given struct, primitive type, array, or string from the field array.\n/// - `u32`: The total number of fields consumed during deserialization (used for recursion).\n///\n/// # Nested Struct Example\n/// Given the following setup:\n/// ```\n/// struct UintNote {\n/// value: u128,\n/// owner: AztecAddress,\n/// randomness: Field,\n/// }\n///\n/// struct AztecAddress {\n/// inner: Field,\n/// }\n/// ```\n///\n/// If `UintNote` is the input type, the function will generate the following deserialization code:\n/// ```\n/// UintNote {\n/// value: fields[0] as u128,\n/// owner: AztecAddress {\n/// inner: fields[1],\n/// },\n/// randomness: fields[2],\n/// }\n/// ```\n/// # Nested Struct Example with Unpacking\n/// - given the same setup as above and given that u128, AztecAddress and Field implement the `Packable` trait\n/// the result we get is:\n/// ```\n/// UintNote {\n/// value: aztec::protocol_types::traits::Packable::unpack([fields[0]]),\n/// owner: aztec::protocol_types::traits::Packable::unpack([fields[1]]),\n/// randomness: aztec::protocol_types::traits::Packable::unpack([fields[2]]),\n/// }\n/// ```\n///\n/// # Panics\n/// - If the deserialization logic encounters a type it does not support.\n/// - If an incorrect number of fields are consumed when deserializing a string.\npub comptime fn generate_deserialize_from_fields(\n name: Quoted,\n typ: Type,\n field_array_name: Quoted,\n num_already_consumed: u32,\n should_unpack: bool,\n) -> (Quoted, u32) {\n let mut result = quote {};\n // Counter for the number of fields consumed\n let mut consumed_counter: u32 = 0;\n\n // If the type implements `Packable`, its length will be assigned to the `maybe_packed_len_typ` variable.\n let maybe_packed_len_typ = std::meta::typ::fresh_type_variable();\n let packable_constraint = quote { Packable<$maybe_packed_len_typ> }.as_trait_constraint();\n\n if (should_unpack & typ.implements(packable_constraint)) {\n // Unpacking is enabled and the given type implements the `Packable` trait so we call the `unpack()`\n // method, add the resulting field array to `aux_vars` and each field to `fields`.\n let packed_len = maybe_packed_len_typ.as_constant().unwrap();\n\n // We copy the packed fields into a new array and pass that to the unpack function in a quote\n let mut packed_fields_quotes = &[];\n for i in 0..packed_len {\n let index_in_field_array = i + num_already_consumed;\n packed_fields_quotes =\n packed_fields_quotes.push_back(quote { $field_array_name[$index_in_field_array] });\n }\n let packed_fields = packed_fields_quotes.join(quote {,});\n\n // Now we call unpack on the type\n let unpack_method = get_trait_impl_method(typ, quote { Packable<_> }, quote { unpack });\n result = quote { $unpack_method([ $packed_fields ]) };\n\n consumed_counter = packed_len;\n } else if typ.is_field() | typ.as_integer().is_some() | typ.is_bool() {\n // The field is a primitive so we just reference it in the field array\n result = quote { $field_array_name[$num_already_consumed] as $typ };\n consumed_counter = 1;\n } else if typ.as_data_type().is_some() {\n // The field is a struct so we iterate over each struct field and recursively call\n // `generate_deserialize_from_fields`\n let (nested_def, generics) = typ.as_data_type().unwrap();\n let nested_name = nested_def.name();\n let mut deserialized_fields_list = &[];\n\n // Iterate over each field in the struct\n for field in nested_def.fields(generics) {\n let (field_name, field_type) = field;\n // Recursively call `generate_deserialize_from_fields` for each field in the struct\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n field_name,\n field_type,\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n // We increment the consumed counter by the number of fields consumed in the recursion\n consumed_counter += num_consumed_in_recursion;\n // We add the deserialized field to the list of deserialized fields.\n // E.g. `value: u128 { lo: fields[0], hi: fields[1] }`\n deserialized_fields_list =\n deserialized_fields_list.push_back(quote { $field_name: $deserialized_field });\n }\n\n // We can construct the struct from the deserialized fields\n let deserialized_fields = deserialized_fields_list.join(quote {,});\n result = quote {\n $nested_name {\n $deserialized_fields\n }\n };\n } else if typ.as_array().is_some() {\n // The field is an array so we iterate over each element and recursively call\n // `generate_deserialize_from_fields`\n let (element_type, array_len) = typ.as_array().unwrap();\n let array_len = array_len.as_constant().unwrap();\n let mut array_fields_list = &[];\n\n // Iterate over each element in the array\n for _ in 0..array_len {\n // Recursively call `generate_deserialize_from_fields` for each element in the array\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n name,\n element_type,\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n // We increment the consumed counter by the number of fields consumed in the recursion\n consumed_counter += num_consumed_in_recursion;\n // We add the deserialized field to the list of deserialized fields.\n array_fields_list = array_fields_list.push_back(deserialized_field);\n }\n\n // We can construct the array from the deserialized fields\n let array_fields = array_fields_list.join(quote {,});\n result = quote { [ $array_fields ] };\n } else if typ.as_str().is_some() {\n // The field is a string and we expect each byte of the string to be represented as 1 field in the field\n // array. So we iterate over the string length and deserialize each character as u8 in the recursive call\n // to `generate_deserialize_from_fields`.\n let length_type = typ.as_str().unwrap();\n let str_len = length_type.as_constant().unwrap();\n let mut byte_list = &[];\n\n // Iterate over each character in the string\n for _ in 0..str_len {\n // Recursively call `generate_deserialize_from_fields` for each character in the string\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n name,\n quote {u8}.as_type(),\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n\n // We should consume just one field in the recursion so we sanity check that\n assert_eq(\n num_consumed_in_recursion,\n 1,\n \"Incorrect number of fields consumed in string deserialization\",\n );\n\n // We increment the consumed counter by 1 as we have consumed one field\n consumed_counter += 1;\n\n // We add the deserialized field to the list of deserialized fields.\n // E.g. `fields[6] as u8`\n byte_list = byte_list.push_back(deserialized_field);\n }\n\n // We construct the string from the deserialized fields\n let bytes = byte_list.join(quote {,});\n result = quote { [ $bytes ].as_str_unchecked() };\n } else {\n panic(\n f\"Unsupported type for serialization of argument {name} and type {typ}\",\n )\n }\n\n (result, consumed_counter)\n}\n\n/// Generates code that serializes a type into an array of fields. Also generates auxiliary variables if necessary\n/// for serialization. If `should_pack` is true, we check if the type implements the `Packable` trait and pack it\n/// if it does.\n///\n/// # Parameters\n/// - `name`: The base identifier (e.g., `self`, `some_var`).\n/// - `typ`: The type being serialized (e.g., a custom struct, array, or primitive type).\n/// - `should_pack`: A boolean indicating whether the type should be packed.\n///\n/// # Returns\n/// A tuple containing:\n/// - A flattened array of `Quoted` field references representing the serialized fields.\n/// - An array of `Quoted` auxiliary variables needed for serialization, such as byte arrays for strings.\n///\n/// # Examples\n///\n/// ## Struct\n/// Given the following struct:\n/// ```rust\n/// struct MockStruct {\n/// a: Field,\n/// b: Field,\n/// }\n/// ```\n///\n/// Serializing the struct:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_mock_struct }, MockStruct, false)\n/// // Returns:\n/// // ([`my_mock_struct.a`, `my_mock_struct.b`], [])\n/// ```\n///\n/// ## Nested Struct\n/// For a more complex struct:\n/// ```rust\n/// struct NestedStruct {\n/// m1: MockStruct,\n/// m2: MockStruct,\n/// }\n/// ```\n///\n/// Serialization output:\n/// ```rust\n/// generate_serialize_to_fields(quote { self }, NestedStruct, false)\n/// // Returns:\n/// // ([`self.m1.a`, `self.m1.b`, `self.m2.a`, `self.m2.b`], [])\n/// ```\n///\n/// ## Array\n/// For an array type:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_array }, [Field; 3], false)\n/// // Returns:\n/// // ([`my_array[0]`, `my_array[1]`, `my_array[2]`], [])\n/// ```\n///\n/// ## String\n/// For a string field, where each character is serialized as a `Field`:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_string }, StringType, false)\n/// // Returns:\n/// // ([`my_string_as_bytes[0] as Field`, `my_string_as_bytes[1] as Field`, ...],\n/// // [`let my_string_as_bytes = my_string.as_bytes()`])\n/// ```\n///\n/// ## Nested Struct with packing enabled\n/// - u128 has a `Packable` implementation hence it will be packed.\n///\n/// For a more complex struct:\n/// ```rust\n/// struct MyStruct {\n/// value: u128,\n/// value2: Field,\n/// }\n/// ```\n///\n/// # Panics\n/// - If the type is unsupported for serialization.\n/// - If the provided `typ` contains invalid constants or incompatible structures.\npub comptime fn generate_serialize_to_fields(\n name: Quoted,\n typ: Type,\n should_pack: bool,\n) -> ([Quoted], [Quoted]) {\n let mut fields = &[];\n let mut aux_vars = &[];\n\n // If the type implements `Packable`, its length will be assigned to the `maybe_packed_len_typ` variable.\n let maybe_packed_len_typ = std::meta::typ::fresh_type_variable();\n let packable_constraint =\n quote { crate::traits::Packable<$maybe_packed_len_typ> }.as_trait_constraint();\n\n if (should_pack & typ.implements(packable_constraint)) {\n // Packing is enabled and the given type implements the `Packable` trait so we call the `pack()`\n // method, add the resulting field array to `aux_vars` and each field to `fields`.\n let packed_len = maybe_packed_len_typ.as_constant().unwrap();\n\n // We collapse the name to a one that gets tokenized as a single token (e.g. \"self.value\" -> \"self_value\").\n let name_at_one_token = collapse_to_one_token(name);\n let packed_struct_name = f\"{name_at_one_token}_aux_var\".quoted_contents();\n\n // We add the individual fields to the fields array\n let pack_method = get_trait_impl_method(\n typ,\n quote { crate::traits::Packable<$packed_len> },\n quote { pack },\n );\n let packed_struct = quote { let $packed_struct_name = $pack_method($name) };\n for i in 0..packed_len {\n fields = fields.push_back(quote { $packed_struct_name[$i] });\n }\n\n // We add the new auxiliary variable to the aux_vars array\n aux_vars = aux_vars.push_back(packed_struct);\n } else if typ.is_field() {\n // For field we just add the value to fields\n fields = fields.push_back(name);\n } else if typ.as_integer().is_some() | typ.is_bool() {\n // For integer and bool we just cast to Field and add the value to fields\n fields = fields.push_back(quote { $name as Field });\n } else if typ.as_data_type().is_some() {\n // For struct we pref\n let nested_struct = typ.as_data_type().unwrap();\n let params = nested_struct.0.fields(nested_struct.1);\n let struct_flattened = params.map(|(param_name, param_type): (Quoted, Type)| {\n let maybe_prefixed_name = if name == quote {} {\n // Triggered when the param name is of a value available in the current scope (e.g. a function\n // argument) --> then we don't prefix the name with anything.\n param_name\n } else {\n // Triggered when we want to prefix the param name with the `name` from function input. This\n // can typically be `self` when implementing a method on a struct.\n quote { $name.$param_name }\n };\n generate_serialize_to_fields(quote {$maybe_prefixed_name}, param_type, should_pack)\n });\n let struct_flattened_fields = struct_flattened.fold(\n &[],\n |acc: [Quoted], (fields, _): (_, [Quoted])| acc.append(fields),\n );\n let struct_flattened_aux_vars = struct_flattened.fold(\n &[],\n |acc: [Quoted], (_, aux_vars): ([Quoted], _)| acc.append(aux_vars),\n );\n fields = fields.append(struct_flattened_fields);\n aux_vars = aux_vars.append(struct_flattened_aux_vars);\n } else if typ.as_array().is_some() {\n // For array we recursively call `generate_serialize_to_fields(...)` for each element\n let (element_type, array_len) = typ.as_array().unwrap();\n let array_len = array_len.as_constant().unwrap();\n for i in 0..array_len {\n let (element_fields, element_aux_vars) =\n generate_serialize_to_fields(quote { $name[$i] }, element_type, should_pack);\n fields = fields.append(element_fields);\n aux_vars = aux_vars.append(element_aux_vars);\n }\n } else if typ.as_str().is_some() {\n // For string we convert the value to bytes, we store the `as_bytes` in an auxiliary variables and\n // then we add each byte to fields as a Field\n let length_type = typ.as_str().unwrap();\n let str_len = length_type.as_constant().unwrap();\n let as_member = name.as_expr().unwrap().as_member_access();\n let var_name = if as_member.is_some() {\n as_member.unwrap().1\n } else {\n name\n };\n let as_bytes_name = f\"{var_name}_as_bytes\".quoted_contents();\n let as_bytes = quote { let $as_bytes_name = $name.as_bytes() };\n for i in 0..str_len {\n fields = fields.push_back(quote { $as_bytes_name[$i] as Field });\n }\n aux_vars = aux_vars.push_back(as_bytes);\n } else {\n panic(\n f\"Unsupported type for serialization of argument {name} and type {typ}\",\n )\n }\n\n (fields, aux_vars)\n}\n\n/// From a quote that gets tokenized to a multiple tokens we collapse it to a single token by replacing all `.` with `_`.\n/// E.g. \"self.values[0]\" -> \"self_values_0_\"\ncomptime fn collapse_to_one_token(q: Quoted) -> Quoted {\n let tokens = q.tokens();\n\n let mut single_token = quote {};\n for token in tokens {\n let new_token = if ((token == quote {.}) | (token == quote {[}) | (token == quote {]})) {\n quote {_}\n } else {\n token\n };\n single_token = f\"{single_token}{new_token}\".quoted_contents();\n }\n single_token\n}\n\npub(crate) comptime fn derive_serialize(s: TypeDefinition) -> Quoted {\n let typ = s.as_type();\n let (fields, aux_vars) = generate_serialize_to_fields(quote { self }, typ, false);\n let aux_vars_for_serialization = if aux_vars.len() > 0 {\n let joint = aux_vars.join(quote {;});\n quote { $joint; }\n } else {\n quote {}\n };\n\n let field_serializations = fields.join(quote {,});\n let serialized_len = fields.len();\n quote {\n impl Serialize<$serialized_len> for $typ {\n fn serialize(self) -> [Field; $serialized_len] {\n $aux_vars_for_serialization\n [ $field_serializations ]\n }\n }\n }\n}\n\npub(crate) comptime fn derive_deserialize(s: TypeDefinition) -> Quoted {\n let typ = s.as_type();\n let (fields, _) = generate_serialize_to_fields(quote { self }, typ, false);\n let serialized_len = fields.len();\n let (deserialized, _) =\n generate_deserialize_from_fields(quote { self }, typ, quote { serialized }, 0, false);\n quote {\n impl Deserialize<$serialized_len> for $typ {\n fn deserialize(serialized: [Field; $serialized_len]) -> Self {\n $deserialized\n }\n }\n }\n}\n\n/// Generates `Packable` implementation for a given struct and returns the packed length.\n///\n/// Note: We are having this function separate from `derive_packable` because we use this in the note macros to get\n/// the packed length of a note as well as the `Packable` implementation. We need the length to be able to register\n/// the note in the global `NOTES` map. There the length is used to generate partial note helper functions.\npub comptime fn derive_packable_and_get_packed_len(s: TypeDefinition) -> (Quoted, u32) {\n let packing_enabled = true;\n\n let typ = s.as_type();\n let (fields, aux_vars) = generate_serialize_to_fields(quote { self }, typ, packing_enabled);\n let aux_vars_for_packing = if aux_vars.len() > 0 {\n let joint = aux_vars.join(quote {;});\n quote { $joint; }\n } else {\n quote {}\n };\n\n let (unpacked, _) =\n generate_deserialize_from_fields(quote { self }, typ, quote { packed }, 0, packing_enabled);\n\n let field_packings = fields.join(quote {,});\n let packed_len = fields.len();\n let packable_trait: TraitConstraint = quote { Packable<$packed_len> }.as_trait_constraint();\n (\n quote {\n impl $packable_trait for $typ {\n fn pack(self) -> [Field; $packed_len] {\n $aux_vars_for_packing\n [ $field_packings ]\n }\n\n fn unpack(packed: [Field; $packed_len]) -> Self {\n $unpacked\n }\n }\n },\n packed_len,\n )\n}\n\npub(crate) comptime fn derive_packable(s: TypeDefinition) -> Quoted {\n let (packable_impl, _) = derive_packable_and_get_packed_len(s);\n packable_impl\n}\n\n#[derive(Packable, Serialize, Deserialize, Eq)]\npub struct Smol {\n a: Field,\n b: Field,\n}\n\n#[derive(Serialize, Deserialize, Eq)]\npub struct HasArray {\n a: [Field; 2],\n b: bool,\n}\n\n#[derive(Serialize, Deserialize, Eq)]\npub struct Fancier {\n a: Smol,\n b: [Field; 2],\n c: [u8; 3],\n d: str<16>,\n}\n\nfn main() {\n assert(false);\n}\n\n#[test]\nfn smol_test() {\n let smol = Smol { a: 1, b: 2 };\n let serialized = smol.serialize();\n assert(serialized == [1, 2], serialized);\n let deserialized = Smol::deserialize(serialized);\n assert(deserialized == smol);\n\n // None of the struct members implements the `Packable` trait so the packed and serialized data should be the same\n let packed = smol.pack();\n assert_eq(packed, serialized, \"Packed does not match serialized\");\n}\n\n#[test]\nfn has_array_test() {\n let has_array = HasArray { a: [1, 2], b: true };\n let serialized = has_array.serialize();\n assert(serialized == [1, 2, 1], serialized);\n let deserialized = HasArray::deserialize(serialized);\n assert(deserialized == has_array);\n}\n\n#[test]\nfn fancier_test() {\n let fancier =\n Fancier { a: Smol { a: 1, b: 2 }, b: [0, 1], c: [1, 2, 3], d: \"metaprogramming!\" };\n let serialized = fancier.serialize();\n assert(\n serialized\n == [\n 1, 2, 0, 1, 1, 2, 3, 0x6d, 0x65, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61,\n 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x21,\n ],\n serialized,\n );\n let deserialized = Fancier::deserialize(serialized);\n assert(deserialized == fancier);\n}\n"
2783
+ "source": "use super::traits::{Deserialize, Packable, Serialize};\n\n/// Returns the typed expression of a trait method implementation.\n///\n/// This helper function is preferred over directly inlining with `$typ::target_method()` in a quote,\n/// as direct inlining would result in missing import warnings in the generated code (specifically,\n/// warnings that the trait implementation is not in scope).\n///\n/// # Note\n/// A copy of this function exists in `aztec-nr/aztec/src/macros/utils.nr`. We maintain separate copies\n/// because importing it there from here would cause the `target_trait` to be interpreted in the context\n/// of this crate, making it impossible to compile code for traits from that crate (e.g. NoteType).\ncomptime fn get_trait_impl_method(\n typ: Type,\n target_trait: Quoted,\n target_method: Quoted,\n) -> TypedExpr {\n let trait_constraint = target_trait.as_trait_constraint();\n typ\n .get_trait_impl(trait_constraint)\n .expect(f\"Could not find impl for {target_trait} for type {typ}\")\n .methods()\n .filter(|m| m.name() == target_method)[0]\n .as_typed_expr()\n}\n\n/// Generates code that deserializes a struct, primitive type, array or string from a field array.\n///\n/// # Parameters\n/// - `name`: The name of the current field being processed, used to identify fields for replacement.\n/// - `typ`: The type of the struct or field being deserialized (e.g., a custom struct, array, or primitive).\n/// - `field_array_name`: The name of the field array containing serialized field data (e.g., `\"values\"`).\n/// - `num_already_consumed`: The number of fields already processed in previous recursion calls.\n/// - `should_unpack`: A boolean indicating whether the type should be unpacked (see description of `Packable`\n/// and `Serialize` trait for more information about the difference between packing and serialization).\n///\n/// # Returns\n/// A tuple containing:\n/// - `Quoted`: A code that deserializes a given struct, primitive type, array, or string from the field array.\n/// - `u32`: The total number of fields consumed during deserialization (used for recursion).\n///\n/// # Nested Struct Example\n/// Given the following setup:\n/// ```\n/// struct UintNote {\n/// value: u128,\n/// owner: AztecAddress,\n/// randomness: Field,\n/// }\n///\n/// struct AztecAddress {\n/// inner: Field,\n/// }\n/// ```\n///\n/// If `UintNote` is the input type, the function will generate the following deserialization code:\n/// ```\n/// UintNote {\n/// value: fields[0] as u128,\n/// owner: AztecAddress {\n/// inner: fields[1],\n/// },\n/// randomness: fields[2],\n/// }\n/// ```\n/// # Nested Struct Example with Unpacking\n/// - given the same setup as above and given that u128, AztecAddress and Field implement the `Packable` trait\n/// the result we get is:\n/// ```\n/// UintNote {\n/// value: aztec::protocol_types::traits::Packable::unpack([fields[0]]),\n/// owner: aztec::protocol_types::traits::Packable::unpack([fields[1]]),\n/// randomness: aztec::protocol_types::traits::Packable::unpack([fields[2]]),\n/// }\n/// ```\n///\n/// # Panics\n/// - If the deserialization logic encounters a type it does not support.\n/// - If an incorrect number of fields are consumed when deserializing a string.\npub comptime fn generate_deserialize_from_fields(\n name: Quoted,\n typ: Type,\n field_array_name: Quoted,\n num_already_consumed: u32,\n should_unpack: bool,\n) -> (Quoted, u32) {\n let mut result = quote {};\n // Counter for the number of fields consumed\n let mut consumed_counter: u32 = 0;\n\n // If the type implements `Packable`, its length will be assigned to the `maybe_packed_len_typ` variable.\n let maybe_packed_len_typ = std::meta::typ::fresh_type_variable();\n let packable_constraint = quote { Packable<$maybe_packed_len_typ> }.as_trait_constraint();\n\n if (should_unpack & typ.implements(packable_constraint)) {\n // Unpacking is enabled and the given type implements the `Packable` trait so we call the `unpack()`\n // method, add the resulting field array to `aux_vars` and each field to `fields`.\n let packed_len = maybe_packed_len_typ.as_constant().unwrap();\n\n // We copy the packed fields into a new array and pass that to the unpack function in a quote\n let mut packed_fields_quotes = &[];\n for i in 0..packed_len {\n let index_in_field_array = i + num_already_consumed;\n packed_fields_quotes =\n packed_fields_quotes.push_back(quote { $field_array_name[$index_in_field_array] });\n }\n let packed_fields = packed_fields_quotes.join(quote {,});\n\n // Now we call unpack on the type\n let unpack_method = get_trait_impl_method(typ, quote { Packable<_> }, quote { unpack });\n result = quote { $unpack_method([ $packed_fields ]) };\n\n consumed_counter = packed_len;\n } else if typ.is_field() | typ.as_integer().is_some() | typ.is_bool() {\n // The field is a primitive so we just reference it in the field array\n result = quote { $field_array_name[$num_already_consumed] as $typ };\n consumed_counter = 1;\n } else if typ.as_data_type().is_some() {\n // The field is a struct so we iterate over each struct field and recursively call\n // `generate_deserialize_from_fields`\n let (nested_def, generics) = typ.as_data_type().unwrap();\n let nested_name = nested_def.name();\n let mut deserialized_fields_list = &[];\n\n // Iterate over each field in the struct\n for field in nested_def.fields(generics) {\n let (field_name, field_type) = field;\n // Recursively call `generate_deserialize_from_fields` for each field in the struct\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n field_name,\n field_type,\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n // We increment the consumed counter by the number of fields consumed in the recursion\n consumed_counter += num_consumed_in_recursion;\n // We add the deserialized field to the list of deserialized fields.\n // E.g. `value: u128 { lo: fields[0], hi: fields[1] }`\n deserialized_fields_list =\n deserialized_fields_list.push_back(quote { $field_name: $deserialized_field });\n }\n\n // We can construct the struct from the deserialized fields\n let deserialized_fields = deserialized_fields_list.join(quote {,});\n result = quote {\n $nested_name {\n $deserialized_fields\n }\n };\n } else if typ.as_array().is_some() {\n // The field is an array so we iterate over each element and recursively call\n // `generate_deserialize_from_fields`\n let (element_type, array_len) = typ.as_array().unwrap();\n let array_len = array_len.as_constant().unwrap();\n let mut array_fields_list = &[];\n\n // Iterate over each element in the array\n for _ in 0..array_len {\n // Recursively call `generate_deserialize_from_fields` for each element in the array\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n name,\n element_type,\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n // We increment the consumed counter by the number of fields consumed in the recursion\n consumed_counter += num_consumed_in_recursion;\n // We add the deserialized field to the list of deserialized fields.\n array_fields_list = array_fields_list.push_back(deserialized_field);\n }\n\n // We can construct the array from the deserialized fields\n let array_fields = array_fields_list.join(quote {,});\n result = quote { [ $array_fields ] };\n } else if typ.as_str().is_some() {\n // The field is a string and we expect each byte of the string to be represented as 1 field in the field\n // array. So we iterate over the string length and deserialize each character as u8 in the recursive call\n // to `generate_deserialize_from_fields`.\n let length_type = typ.as_str().unwrap();\n let str_len = length_type.as_constant().unwrap();\n let mut byte_list = &[];\n\n // Iterate over each character in the string\n for _ in 0..str_len {\n // Recursively call `generate_deserialize_from_fields` for each character in the string\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n name,\n quote {u8}.as_type(),\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n\n // We should consume just one field in the recursion so we sanity check that\n assert_eq(\n num_consumed_in_recursion,\n 1,\n \"Incorrect number of fields consumed in string deserialization\",\n );\n\n // We increment the consumed counter by 1 as we have consumed one field\n consumed_counter += 1;\n\n // We add the deserialized field to the list of deserialized fields.\n // E.g. `fields[6] as u8`\n byte_list = byte_list.push_back(deserialized_field);\n }\n\n // We construct the string from the deserialized fields\n let bytes = byte_list.join(quote {,});\n result = quote { [ $bytes ].as_str_unchecked() };\n } else {\n panic(\n f\"Unsupported type for serialization of argument {name} and type {typ}\",\n )\n }\n\n (result, consumed_counter)\n}\n\n/// Generates code that serializes a type into an array of fields. Also generates auxiliary variables if necessary\n/// for serialization. If `should_pack` is true, we check if the type implements the `Packable` trait and pack it\n/// if it does.\n///\n/// # Parameters\n/// - `name`: The base identifier (e.g., `self`, `some_var`).\n/// - `typ`: The type being serialized (e.g., a custom struct, array, or primitive type).\n/// - `should_pack`: A boolean indicating whether the type should be packed.\n///\n/// # Returns\n/// A tuple containing:\n/// - A flattened array of `Quoted` field references representing the serialized fields.\n/// - An array of `Quoted` auxiliary variables needed for serialization, such as byte arrays for strings.\n///\n/// # Examples\n///\n/// ## Struct\n/// Given the following struct:\n/// ```rust\n/// struct MockStruct {\n/// a: Field,\n/// b: Field,\n/// }\n/// ```\n///\n/// Serializing the struct:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_mock_struct }, MockStruct, false)\n/// // Returns:\n/// // ([`my_mock_struct.a`, `my_mock_struct.b`], [])\n/// ```\n///\n/// ## Nested Struct\n/// For a more complex struct:\n/// ```rust\n/// struct NestedStruct {\n/// m1: MockStruct,\n/// m2: MockStruct,\n/// }\n/// ```\n///\n/// Serialization output:\n/// ```rust\n/// generate_serialize_to_fields(quote { self }, NestedStruct, false)\n/// // Returns:\n/// // ([`self.m1.a`, `self.m1.b`, `self.m2.a`, `self.m2.b`], [])\n/// ```\n///\n/// ## Array\n/// For an array type:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_array }, [Field; 3], false)\n/// // Returns:\n/// // ([`my_array[0]`, `my_array[1]`, `my_array[2]`], [])\n/// ```\n///\n/// ## String\n/// For a string field, where each character is serialized as a `Field`:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_string }, StringType, false)\n/// // Returns:\n/// // ([`my_string_as_bytes[0] as Field`, `my_string_as_bytes[1] as Field`, ...],\n/// // [`let my_string_as_bytes = my_string.as_bytes()`])\n/// ```\n///\n/// ## Nested Struct with packing enabled\n/// - u128 has a `Packable` implementation hence it will be packed.\n///\n/// For a more complex struct:\n/// ```rust\n/// struct MyStruct {\n/// value: u128,\n/// value2: Field,\n/// }\n/// ```\n///\n/// # Panics\n/// - If the type is unsupported for serialization.\n/// - If the provided `typ` contains invalid constants or incompatible structures.\npub comptime fn generate_serialize_to_fields(\n name: Quoted,\n typ: Type,\n should_pack: bool,\n) -> ([Quoted], [Quoted]) {\n let mut fields = &[];\n let mut aux_vars = &[];\n\n // If the type implements `Packable`, its length will be assigned to the `maybe_packed_len_typ` variable.\n let maybe_packed_len_typ = std::meta::typ::fresh_type_variable();\n let packable_constraint =\n quote { crate::traits::Packable<$maybe_packed_len_typ> }.as_trait_constraint();\n\n if (should_pack & typ.implements(packable_constraint)) {\n // Packing is enabled and the given type implements the `Packable` trait so we call the `pack()`\n // method, add the resulting field array to `aux_vars` and each field to `fields`.\n let packed_len = maybe_packed_len_typ.as_constant().unwrap();\n\n // We collapse the name to a one that gets tokenized as a single token (e.g. \"self.value\" -> \"self_value\").\n let name_at_one_token = collapse_to_one_token(name);\n let packed_struct_name = f\"{name_at_one_token}_aux_var\".quoted_contents();\n\n // We add the individual fields to the fields array\n let pack_method = get_trait_impl_method(\n typ,\n quote { crate::traits::Packable<$packed_len> },\n quote { pack },\n );\n let packed_struct = quote { let $packed_struct_name = $pack_method($name) };\n for i in 0..packed_len {\n fields = fields.push_back(quote { $packed_struct_name[$i] });\n }\n\n // We add the new auxiliary variable to the aux_vars array\n aux_vars = aux_vars.push_back(packed_struct);\n } else if typ.is_field() {\n // For field we just add the value to fields\n fields = fields.push_back(name);\n } else if typ.as_integer().is_some() | typ.is_bool() {\n // For integer and bool we just cast to Field and add the value to fields\n fields = fields.push_back(quote { $name as Field });\n } else if typ.as_data_type().is_some() {\n // For struct we pref\n let nested_struct = typ.as_data_type().unwrap();\n let params = nested_struct.0.fields(nested_struct.1);\n let struct_flattened = params.map(|(param_name, param_type): (Quoted, Type)| {\n let maybe_prefixed_name = if name == quote {} {\n // Triggered when the param name is of a value available in the current scope (e.g. a function\n // argument) --> then we don't prefix the name with anything.\n param_name\n } else {\n // Triggered when we want to prefix the param name with the `name` from function input. This\n // can typically be `self` when implementing a method on a struct.\n quote { $name.$param_name }\n };\n generate_serialize_to_fields(quote {$maybe_prefixed_name}, param_type, should_pack)\n });\n let struct_flattened_fields = struct_flattened.fold(\n &[],\n |acc: [Quoted], (fields, _): (_, [Quoted])| acc.append(fields),\n );\n let struct_flattened_aux_vars = struct_flattened.fold(\n &[],\n |acc: [Quoted], (_, aux_vars): ([Quoted], _)| acc.append(aux_vars),\n );\n fields = fields.append(struct_flattened_fields);\n aux_vars = aux_vars.append(struct_flattened_aux_vars);\n } else if typ.as_array().is_some() {\n // For array we recursively call `generate_serialize_to_fields(...)` for each element\n let (element_type, array_len) = typ.as_array().unwrap();\n let array_len = array_len.as_constant().unwrap();\n for i in 0..array_len {\n let (element_fields, element_aux_vars) =\n generate_serialize_to_fields(quote { $name[$i] }, element_type, should_pack);\n fields = fields.append(element_fields);\n aux_vars = aux_vars.append(element_aux_vars);\n }\n } else if typ.as_str().is_some() {\n // For string we convert the value to bytes, we store the `as_bytes` in an auxiliary variables and\n // then we add each byte to fields as a Field\n let length_type = typ.as_str().unwrap();\n let str_len = length_type.as_constant().unwrap();\n let as_member = name.as_expr().unwrap().as_member_access();\n let var_name = if as_member.is_some() {\n as_member.unwrap().1\n } else {\n name\n };\n let as_bytes_name = f\"{var_name}_as_bytes\".quoted_contents();\n let as_bytes = quote { let $as_bytes_name = $name.as_bytes() };\n for i in 0..str_len {\n fields = fields.push_back(quote { $as_bytes_name[$i] as Field });\n }\n aux_vars = aux_vars.push_back(as_bytes);\n } else {\n panic(\n f\"Unsupported type for serialization of argument {name} and type {typ}\",\n )\n }\n\n (fields, aux_vars)\n}\n\n/// From a quote that gets tokenized to a multiple tokens we collapse it to a single token by replacing all `.` with `_`.\n/// E.g. \"self.values[0]\" -> \"self_values_0_\"\ncomptime fn collapse_to_one_token(q: Quoted) -> Quoted {\n let tokens = q.tokens();\n\n let mut single_token = quote {};\n for token in tokens {\n let new_token = if ((token == quote {.}) | (token == quote {[}) | (token == quote {]})) {\n quote {_}\n } else {\n token\n };\n single_token = f\"{single_token}{new_token}\".quoted_contents();\n }\n single_token\n}\n\npub(crate) comptime fn derive_serialize(s: TypeDefinition) -> Quoted {\n let typ = s.as_type();\n let (fields, aux_vars) = generate_serialize_to_fields(quote { self }, typ, false);\n let aux_vars_for_serialization = if aux_vars.len() > 0 {\n let joint = aux_vars.join(quote {;});\n quote { $joint; }\n } else {\n quote {}\n };\n\n let field_serializations = fields.join(quote {,});\n let serialized_len = fields.len();\n quote {\n impl Serialize<$serialized_len> for $typ {\n #[inline_always]\n fn serialize(self) -> [Field; $serialized_len] {\n $aux_vars_for_serialization\n [ $field_serializations ]\n }\n }\n }\n}\n\npub(crate) comptime fn derive_deserialize(s: TypeDefinition) -> Quoted {\n let typ = s.as_type();\n let (fields, _) = generate_serialize_to_fields(quote { self }, typ, false);\n let serialized_len = fields.len();\n let (deserialized, _) =\n generate_deserialize_from_fields(quote { self }, typ, quote { serialized }, 0, false);\n quote {\n impl Deserialize<$serialized_len> for $typ {\n #[inline_always]\n fn deserialize(serialized: [Field; $serialized_len]) -> Self {\n $deserialized\n }\n }\n }\n}\n\n/// Generates `Packable` implementation for a given struct and returns the packed length.\n///\n/// Note: We are having this function separate from `derive_packable` because we use this in the note macros to get\n/// the packed length of a note as well as the `Packable` implementation. We need the length to be able to register\n/// the note in the global `NOTES` map. There the length is used to generate partial note helper functions.\npub comptime fn derive_packable_and_get_packed_len(s: TypeDefinition) -> (Quoted, u32) {\n let packing_enabled = true;\n\n let typ = s.as_type();\n let (fields, aux_vars) = generate_serialize_to_fields(quote { self }, typ, packing_enabled);\n let aux_vars_for_packing = if aux_vars.len() > 0 {\n let joint = aux_vars.join(quote {;});\n quote { $joint; }\n } else {\n quote {}\n };\n\n let (unpacked, _) =\n generate_deserialize_from_fields(quote { self }, typ, quote { packed }, 0, packing_enabled);\n\n let field_packings = fields.join(quote {,});\n let packed_len = fields.len();\n let packable_trait: TraitConstraint = quote { Packable<$packed_len> }.as_trait_constraint();\n (\n quote {\n impl $packable_trait for $typ {\n fn pack(self) -> [Field; $packed_len] {\n $aux_vars_for_packing\n [ $field_packings ]\n }\n\n fn unpack(packed: [Field; $packed_len]) -> Self {\n $unpacked\n }\n }\n },\n packed_len,\n )\n}\n\npub(crate) comptime fn derive_packable(s: TypeDefinition) -> Quoted {\n let (packable_impl, _) = derive_packable_and_get_packed_len(s);\n packable_impl\n}\n\n#[derive(Packable, Serialize, Deserialize, Eq)]\npub struct Smol {\n a: Field,\n b: Field,\n}\n\n#[derive(Serialize, Deserialize, Eq)]\npub struct HasArray {\n a: [Field; 2],\n b: bool,\n}\n\n#[derive(Serialize, Deserialize, Eq)]\npub struct Fancier {\n a: Smol,\n b: [Field; 2],\n c: [u8; 3],\n d: str<16>,\n}\n\nfn main() {\n assert(false);\n}\n\n#[test]\nfn smol_test() {\n let smol = Smol { a: 1, b: 2 };\n let serialized = smol.serialize();\n assert(serialized == [1, 2], serialized);\n let deserialized = Smol::deserialize(serialized);\n assert(deserialized == smol);\n\n // None of the struct members implements the `Packable` trait so the packed and serialized data should be the same\n let packed = smol.pack();\n assert_eq(packed, serialized, \"Packed does not match serialized\");\n}\n\n#[test]\nfn has_array_test() {\n let has_array = HasArray { a: [1, 2], b: true };\n let serialized = has_array.serialize();\n assert(serialized == [1, 2, 1], serialized);\n let deserialized = HasArray::deserialize(serialized);\n assert(deserialized == has_array);\n}\n\n#[test]\nfn fancier_test() {\n let fancier =\n Fancier { a: Smol { a: 1, b: 2 }, b: [0, 1], c: [1, 2, 3], d: \"metaprogramming!\" };\n let serialized = fancier.serialize();\n assert(\n serialized\n == [\n 1, 2, 0, 1, 1, 2, 3, 0x6d, 0x65, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61,\n 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x21,\n ],\n serialized,\n );\n let deserialized = Fancier::deserialize(serialized);\n assert(deserialized == fancier);\n}\n"
2784
2784
  },
2785
2785
  "297": {
2786
2786
  "path": "/home/aztec-dev/aztec-packages/noir-projects/noir-protocol-circuits/crates/types/src/poseidon2.nr",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@aztec/protocol-contracts",
3
3
  "homepage": "https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/protocol-contracts",
4
4
  "description": "Canonical Noir contracts for the Aztec Network",
5
- "version": "0.87.0",
5
+ "version": "0.87.1",
6
6
  "type": "module",
7
7
  "exports": {
8
8
  ".": "./dest/index.js",
@@ -69,9 +69,9 @@
69
69
  ]
70
70
  },
71
71
  "dependencies": {
72
- "@aztec/constants": "0.87.0",
73
- "@aztec/foundation": "0.87.0",
74
- "@aztec/stdlib": "0.87.0",
72
+ "@aztec/constants": "0.87.1",
73
+ "@aztec/foundation": "0.87.1",
74
+ "@aztec/stdlib": "0.87.1",
75
75
  "lodash.chunk": "^4.2.0",
76
76
  "lodash.omit": "^4.5.0",
77
77
  "tslib": "^2.4.0"