@aztec/accounts 0.0.1-commit.b33fc05d0 → 0.0.1-commit.b64cb54f6

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.
@@ -3809,7 +3809,7 @@
3809
3809
  }
3810
3810
  },
3811
3811
  {
3812
- "name": "expiration_timestamp",
3812
+ "name": "anchor_block_timestamp",
3813
3813
  "type": {
3814
3814
  "kind": "integer",
3815
3815
  "sign": "unsigned",
@@ -3857,8 +3857,8 @@
3857
3857
  }
3858
3858
  }
3859
3859
  },
3860
- "bytecode": "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",
3861
- "debug_symbols": "tZnRblo5EIbfhetc2DNje5xXqaqKprRCQiSiyUqrKu++M/j8BrSyRaC5KR+F8x17zozHDn9WPzbf33592+5/Pv9ePX75s/p+2O5221/fds9P69ft897+988q+D+Z6uqRH1aZ7W3x17h6VH+l5ZWXV1k9RjFI9sWYHASQABlQDNyWFGC3idUgB0AEEIABAkiADCgABcBcYC4wF5gLzAXmAnOBucBcYC4wK8wKs8KsMCvMCrPCrDArzNWuougggASw7xA51AYlBIDdnZIDARgggATIALs7RwcF1AViAJiZ/RaRAAwwM4uDmTk7ZEABKKAuQAEQAQRggABgJpgJZnJzcagLeJ42cLM6EIABdrkEB7tcLLuL2JfFoyH2ZfHLhQE2DPGrJAEyoAAUUBfwKmjg5upAAAYIwMzJ4+wF0qAAzJw8zl4gR/ACaRABBGCAANzsE/QCaVAACnCzz90LpEEEuNnj7AXSQAAJkAEFoAA3ewy9QBpEAAG4rRvFCyR5NLxAGmSAmbM/Ly+QBnWBGgARQAAGmDl7ML2IGmRAAehyLy8rB/WyarCYNRCAAQJIgAwoAG23UC+rI3hZNYgAN7MDAwTgZnHIADcnBwXUBbysGrjZB+9lVfwWXlYNBJAAGVAACjBzsUTS4/J/BDMXH5iXVQMGCMA9PlSvrwZ1AQmt4vRYcUdwj4/ZK66BABIgAwpAAW62ZFOvuAYRQACvuPj+/rBCg/v2ethsvL+ddTzrgy/rw2b/unrcv+12D6t/1ru345d+v6z3x9fX9cE+tae62f+wVxP+3O42Tu8Pp6vD+NJYxVfN4+XGuXZFDJeSOJYI5UUhUrqg0MX1NL6eE2bARU8DKOUDs6jaZ5EyD2chY0kOimHkGOJJkfhCke4MRL43ELM5RG+PyxyYhnOYKdjrtCm41KGizpNqUVCIp4mkeplRkyfKFQYJ55HIl4ZpUnJX8NnjFBvQhWOWmCXDwXoWzv85eDIO9bbSxqHlVKJC12dmVO5PtdabnipRT26aJEacZSdFFBkTS3fkDxgUucUcysigk2jGPgaJPDJMA9FXCjs65GEgaJKcp6ywzcZZUlxfYnIag9xWYrYxxIJne7tRidEkMWMKSImYEg8Vk4nYFmsxKKdhlc4M1nZhyDQ2THKKQkUkKIbh46AyGURf8bSOFxuapKUdBfu6aye9YZHT5JHaYa1nhe1zhg6eZCb5maHFIpezUJTbsqIMs2K6+utpR1FlZGCetWLqC15I4wYyW/6j9l5MUYZLN6dZLPpEzurDdFc3kEh95bbOHMcO/twmFO0wXft6EWnchnj2ZIOEHhFjvtXSE91YaNwSJ883S+wVZ61+7KBZaxbprTnloWMeWe5LmHEq45FMlkHOp42fDLNkapDeWVMdG/L90Zg5iDEPSnTTKKh0g+ahgfX+zEjh/ljMHNfFYmq4Khaz7Lw6FvIXYiF3x0I+MxZXz0M/13FlLPTuWMy6I8faD0eTnpL/Rm+b1VnsTTrLcOdVp7u/eNr91Ru2KxR7KOx8k0aGnO89rOYyMwRMgyuNO2Ke5WYo/WhkUbnRkVN3lHEvK3eve7N4XpcVswNW7mdu247z8ID1gW0KpZu2GFc2RLqp2L/a2/XT9nDxM9m7qw7b9ffdZnn7823/dPbp678v+AQ/s70cnp82P94OGzedfmuzf77YnyweVPXrw8r+Zvkl229jmYq9i8cPbZXSzP42+lvbY2qNX999aP8B"
3860
+ "bytecode": "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",
3861
+ "debug_symbols": "tZnRbio5DIbfZa57kThO7PRVqqqiLT1CQrTitCutqr772sz8AbRKxMKem+abQj4Sj51k4Ht6XT9//Xra7N7ef0/3D9/T836z3W5+PW3fX1afm/ed/fd7Cv6nUJ3u091Ukl1mb+PS0tyyTvfF2kxLm5aWlzZP9+ptWVqZ7mNwUEBdoARABBAgARiQAQUAc4G5wCwwC8wCs8AsMAvMArPALDALzAqzwqwwK8wKs8KsMFfvRQ4ESAB/j0e+CkAB/ulyN0kIgAggQAIwwD6dkkMBCEABZiY2iAEQAWam4mDmFBwYkAEFIAAF1AUoACKAADATzAQzuZkcBKAAN/sIPUVniADv7mNO3r062Js5GrC9mX3KHAE2DPZenAAMyIACEIAC3GwVITkAIoAAbvZ74dUxQwa4WR0EoIC6gBfIDBFAADf7BL1AZsiAAjBz9rl7gcxQF/ACyR5ML5AZCJAADMiAAnCzx9ALZIa6gBfIDG72QHmBZL8pXiAzMMDNHg0vkBkEoIC6QA2ACHCzR8OLaAYGZICZi2eUl9UMCjBzsWiol9UMZi7kQIAEYICZS3FwszoIQAF1AS+rGSKAAG6uDgwwswSHAhCALuBFJD5UL6IZEoDnnNdDER3APT5mL6IZFFAX8CKaIQII4ObkwIAMKAAvT/n5uZuwlzx97tdr30pONhfbcj5W+/Xuc7rffW23d9Nfq+3X4U2/P1a7Q/u52turNsX17tVaE75ttmunn7tj79DvGmtJsnQ3Fm6KGM4lsS9hX/EOCmZpAqGz/tTvnzJmkESPAxC5fBa2krdZWOV2Z8F9SebCi8MwHxU5XazIEZHIOdF1ipyaYjAKGYSToiKelI53tFwu0ApBCtIR1EFCxDYCjqknGEXBTlCIQuHajUIcZKWt9ovCluBm4Hj5GPQ4Br3uZtpJAwo7GvSnMXBE23KR2rZrNkeu54o8UOSAAouWXl1FGQRTsErYcnZSouVigzKiqYX6Bu0bKFREgmLo3tI4SEwtSGyt2h0EDZaacsxMO1QfHUznCw0NMtMSC+VhiVX7jsGaSX5UmmNR5CQUcl1WSD8rRrlZteVmrdxT0GAUJRDKtISs142CoaAQuwoarXgVBg6DvNDRkqdtE6TIJ3dEzx11dEdaNE+q1HSXj4JSm0gKsTuKNMjOSNr20hRjdxwjRzouvklPNsJ/jSP9WUe0h7baVs9IoVtraVQpgds67pyutbSyN2bqWwaZWji29cfudt8xyhJlnHRYc+k7hpFNbUE3ztK18GA1TeV48uNupg0N3I4qufYNdHs0Rg5KmAdlumoUJM2gpV9zfHtmcPkfYlFujkW5ORbp9ljkcHssRo7LYjE03BwLG7y0eUi4qtovjkX+s44L45lvjedwj02xtj12sC/levseOzqzcHvWtAfX/oGahwfqeDxQ12vOXhRbMOypM/cUJd169io8MgTMI1Xq76tllJ1B2iOrheVKR8nNIf0dscitGT6K52V5MXpqrW1XNizdp9b/cNihfNVB5bJgDA39cn+0y9XLZn/2e86Pq/ab1fN2vVy+fe1eTl79/PsDr+D3oI/9+8v69Wu/dtPxRyH786D2Xa4KPd5N9q3gQ7FfGAqJXcXDi3YeUD5cRr+0rwZV8uOPD+0f"
3862
3862
  },
3863
3863
  {
3864
3864
  "name": "process_message",
@@ -4080,7 +4080,7 @@
4080
4080
  }
4081
4081
  },
4082
4082
  "bytecode": "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",
4083
- "debug_symbols": "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"
4083
+ "debug_symbols": "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cbLEyRInS5wscbLEyRInS5wscbLEyRInS5wscbLEyRInS5wscbLEyRInS5wscbLEyRInS5wscbLEyRInS5wscbJkkCWDLBlkySBLBlkyyJJBlgyyZJAlgywZZMkgSwZZMsiSQZYMsmSQJYMsGWTJIEsGWTLIkkGWDLJkkCWDLBlkySBLBlkyyJJBlgyyZJAlgywZZMkgSwZZMsiScWTJCBwLjyw5cN0HjFrACk5FPO0fK9MSDXRwLIwAOVFABQtYQWwNW8PWsDVsHVvH1rF1bB1bx9axdWwdW8dm2AybYTNshs2wGTbDZtgMm2NzbI7NsTk2x+bYHJtjc2wD28A2sA1sA9vANrANbAPbSJvGQrlEARUsYAUb2EEDHcQm2ASbYBNsgk2wCTbBJtgEm2JTbIpNsSk2xabYFJtiU2wFW8FWsBVsBVvBVrAVbAVbwVaxVWwVW8VWsVVsFVvFVrFVbA1bw9awNWwNW8PWsDVsDVvD1rF1bB1bx9axdWwdW8fWsXVshs2wGTbDZtgMm2EzbIbNsDk2x+bYHJtjc2yOzbE5Nsc2sA1sA9vANrANbAPbwDawkSVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVClghZImSJkCVKlihZomSJkiVKlihZomSJkiV6ZEkLHAuPLDkwLsc1sIAVbGAHDXRwLDzuZg4UEJtiU2yKTbEpNsWm2Aq2gq1gK9gKtoKtYCvYCraCrWKr2Cq2iq1iq9gqtoqtYqvYGraGrWFr2Bq2hq1ha9gatoatY+vYOraOrWPr2Dq2jq1j69gMm2EzbIbNsBk2w2bYDJthc2yOzbE5Nsfm2BybY3Nsjm1gG9gGtoFtYIsAmdNrqse86YEGRpcegSOxHAFy4LTN54w01g0mFnDWnY8IaSwWTHRwLIyoOFFABQtYwQZiE2yCTbApNsWm2BSbYlNs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"
4084
4084
  },
4085
4085
  {
4086
4086
  "name": "public_dispatch",
@@ -4308,7 +4308,7 @@
4308
4308
  }
4309
4309
  },
4310
4310
  {
4311
- "name": "expiration_timestamp",
4311
+ "name": "anchor_block_timestamp",
4312
4312
  "type": {
4313
4313
  "kind": "integer",
4314
4314
  "sign": "unsigned",
@@ -4499,7 +4499,7 @@
4499
4499
  },
4500
4500
  "100": {
4501
4501
  "path": "/home/aztec-dev/aztec-packages/noir-projects/aztec-nr/aztec/src/macros/aztec.nr",
4502
- "source": "use crate::{\n macros::{\n calls_generation::{\n external_functions::{generate_external_function_calls, generate_external_function_self_calls_structs},\n internal_functions::generate_call_internal_struct,\n },\n dispatch::generate_public_dispatch,\n emit_public_init_nullifier::generate_emit_public_init_nullifier,\n internals_functions_generation::{create_fn_abi_exports, process_functions},\n notes::NOTES,\n storage::STORAGE_LAYOUT_NAME,\n utils::{\n get_trait_impl_method, is_fn_contract_library_method, is_fn_external, is_fn_internal, is_fn_test,\n module_has_storage,\n },\n },\n messages::discovery::CustomMessageHandler,\n};\n\n/// Configuration for the [`aztec`] macro.\n///\n/// This type lets users override different parts of the default aztec-nr contract behavior, such\n/// as message handling. These are advanced features that require careful understanding of\n/// the behavior of these systems.\n///\n/// ## Examples\n///\n/// ```noir\n/// #[aztec(aztec::macros::AztecConfig::new().custom_message_handler(my_handler))]\n/// contract MyContract { ... }\n/// ```\npub struct AztecConfig {\n custom_message_handler: Option<CustomMessageHandler<()>>,\n}\n\nimpl AztecConfig {\n /// Creates a new `AztecConfig` with default values.\n ///\n /// Calling `new` is equivalent to invoking the [`aztec`] macro with no parameters. The different methods\n /// (e.g. [`AztecConfig::custom_message_handler`]) can then be used to change the default behavior.\n pub comptime fn new() -> Self {\n Self { custom_message_handler: Option::none() }\n }\n\n /// Sets a handler for custom messages.\n ///\n /// This enables contracts to process non-standard messages (i.e. any with a message type that is not in\n /// [`crate::messages::msg_type`]).\n ///\n /// `handler` must be a function that conforms to the\n /// [`crate::messages::discovery::CustomMessageHandler`] type signature.\n pub comptime fn custom_message_handler(_self: Self, handler: CustomMessageHandler<()>) -> Self {\n Self { custom_message_handler: Option::some(handler) }\n }\n}\n\n/// Enables aztec-nr features on a `contract`.\n///\n/// All aztec-nr contracts should have this macro invoked on them, as it is the one that processes all contract\n/// functions, notes, storage, generates interfaces for external calls, and creates the message processing\n/// boilerplate.\n///\n/// ## Examples\n///\n/// Most contracts can simply invoke the macro with no parameters, resulting in default aztec-nr behavior:\n/// ```noir\n/// #[aztec]\n/// contract MyContract { ... }\n/// ```\n///\n/// Advanced contracts can use [`AztecConfig`] to customize parts of its behavior, such as message\n/// processing.\n/// ```noir\n/// #[aztec(aztec::macros::AztecConfig::new().custom_message_handler(my_handler))]\n/// contract MyAdvancedContract { ... }\n/// ```\n#[varargs]\npub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted {\n let num_args = args.len();\n let config = if num_args == 0 {\n AztecConfig::new()\n } else if num_args == 1 {\n args[0]\n } else {\n panic(f\"#[aztec] expects 0 or 1 arguments, got {num_args}\")\n };\n\n // Functions that don't have #[external(...)], #[contract_library_method], or #[test] are not allowed in contracts.\n check_each_fn_macroified(m);\n\n // We generate new functions prefixed with `__aztec_nr_internals__` and we replace the original functions' bodies\n // with `static_assert(false, ...)` to prevent them from being called directly from within the contract.\n let functions = process_functions(m);\n\n // We generate structs and their implementations necessary for convenient functions calls.\n let interface = generate_contract_interface(m);\n let self_call_structs = generate_external_function_self_calls_structs(m);\n let call_internal_struct = generate_call_internal_struct(m);\n\n // We generate ABI exports for all the external functions in the contract.\n let fn_abi_exports = create_fn_abi_exports(m);\n\n // We generate `_compute_note_hash_and_nullifier`, `sync_state` and `process_message` functions only if they are\n // not already implemented. If they are implemented we just insert empty quotes.\n let contract_library_method_compute_note_hash_and_nullifier = if !m.functions().any(|f| {\n f.name() == quote { _compute_note_hash_and_nullifier }\n }) {\n generate_contract_library_method_compute_note_hash_and_nullifier()\n } else {\n quote {}\n };\n let process_custom_message_option = if config.custom_message_handler.is_some() {\n let handler = config.custom_message_handler.unwrap();\n quote { Option::some($handler) }\n } else {\n quote { Option::<aztec::messages::discovery::CustomMessageHandler<()>>::none() }\n };\n\n let offchain_inbox_sync_option = quote {\n Option::some(aztec::messages::processing::offchain::sync_inbox)\n };\n\n let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) {\n generate_sync_state(process_custom_message_option, offchain_inbox_sync_option)\n } else {\n quote {}\n };\n\n if m.functions().any(|f| f.name() == quote { offchain_receive }) {\n panic(\n \"User-defined 'offchain_receive' is not allowed. The function is auto-injected by the #[aztec] macro. See https://docs.aztec.network/errors/7\",\n );\n }\n let offchain_receive_fn_and_abi_export = generate_offchain_receive();\n\n let process_message_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { process_message }) {\n generate_process_message(process_custom_message_option)\n } else {\n quote {}\n };\n let (has_public_init_nullifier_fn, emit_public_init_nullifier_fn_body) = generate_emit_public_init_nullifier(m);\n let public_dispatch = generate_public_dispatch(m, has_public_init_nullifier_fn);\n\n quote {\n $interface\n $self_call_structs\n $call_internal_struct\n $functions\n $fn_abi_exports\n $contract_library_method_compute_note_hash_and_nullifier\n $public_dispatch\n $sync_state_fn_and_abi_export\n $process_message_fn_and_abi_export\n $emit_public_init_nullifier_fn_body\n $offchain_receive_fn_and_abi_export\n }\n}\n\ncomptime fn generate_contract_interface(m: Module) -> Quoted {\n let calls = generate_external_function_calls(m);\n\n let module_name = m.name();\n\n let has_storage_layout = module_has_storage(m) & STORAGE_LAYOUT_NAME.get(m).is_some();\n let storage_layout_getter = if has_storage_layout {\n let storage_layout_name = STORAGE_LAYOUT_NAME.get(m).unwrap();\n quote {\n pub fn storage_layout() -> StorageLayoutFields {\n $storage_layout_name.fields\n }\n }\n } else {\n quote {}\n };\n\n let library_storage_layout_getter = if has_storage_layout {\n quote {\n #[contract_library_method]\n $storage_layout_getter\n }\n } else {\n quote {}\n };\n\n quote {\n pub struct $module_name {\n pub target_contract: aztec::protocol::address::AztecAddress\n }\n\n impl $module_name {\n $calls\n\n pub fn at(\n addr: aztec::protocol::address::AztecAddress\n ) -> Self {\n Self { target_contract: addr }\n }\n\n pub fn interface() -> Self {\n Self { target_contract: aztec::protocol::address::AztecAddress::zero() }\n }\n\n $storage_layout_getter\n }\n\n #[contract_library_method]\n pub fn at(\n addr: aztec::protocol::address::AztecAddress\n ) -> $module_name {\n $module_name { target_contract: addr }\n }\n\n #[contract_library_method]\n pub fn interface() -> $module_name {\n $module_name { target_contract: aztec::protocol::address::AztecAddress::zero() }\n }\n\n $library_storage_layout_getter\n\n }\n}\n\n/// Generates a contract library method called `_compute_note_hash_and_nullifier` which is used for note discovery (to\n/// create the `aztec::messages::discovery::ComputeNoteHashAndNullifier` function) and to implement the\n/// `compute_note_hash_and_nullifier` unconstrained contract function.\ncomptime fn generate_contract_library_method_compute_note_hash_and_nullifier() -> Quoted {\n if NOTES.len() > 0 {\n // Contracts that do define notes produce an if-else chain where `note_type_id` is matched against the\n // `get_note_type_id()` function of each note type that we know of, in order to identify the note type. Once we\n // know it we call we correct `unpack` method from the `Packable` trait to obtain the underlying note type, and\n // compute the note hash (non-siloed) and inner nullifier (also non-siloed).\n\n let mut if_note_type_id_match_statements_list = @[];\n for i in 0..NOTES.len() {\n let typ = NOTES.get(i);\n\n let get_note_type_id = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteType },\n quote { get_id },\n );\n let unpack = get_trait_impl_method(\n typ,\n quote { crate::protocol::traits::Packable },\n quote { unpack },\n );\n\n let compute_note_hash = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteHash },\n quote { compute_note_hash },\n );\n\n let compute_nullifier_unconstrained = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteHash },\n quote { compute_nullifier_unconstrained },\n );\n\n let if_or_else_if = if i == 0 {\n quote { if }\n } else {\n quote { else if }\n };\n\n if_note_type_id_match_statements_list = if_note_type_id_match_statements_list.push_back(\n quote {\n $if_or_else_if note_type_id == $get_note_type_id() {\n // As an extra safety check we make sure that the packed_note BoundedVec has the expected\n // length, since we're about to interpret its raw storage as a fixed-size array by calling the\n // unpack function on it.\n let expected_len = <$typ as $crate::protocol::traits::Packable>::N;\n let actual_len = packed_note.len();\n if actual_len != expected_len {\n aztec::protocol::logging::warn_log_format(\n \"[aztec-nr] Packed note length mismatch for note type id {2}: expected {0} fields, got {1}. Skipping note.\",\n [expected_len as Field, actual_len as Field, note_type_id],\n );\n Option::none()\n } else {\n let note = $unpack(aztec::utils::array::subarray(packed_note.storage(), 0));\n\n let note_hash = $compute_note_hash(note, owner, storage_slot, randomness);\n\n // The message discovery process finds settled notes, that is, notes that were created in\n // prior transactions and are therefore already part of the note hash tree. We therefore\n // compute the nullification note hash by treating the note as a settled note with the\n // provided note nonce.\n let note_hash_for_nullification =\n aztec::note::utils::compute_note_hash_for_nullification(\n aztec::note::HintedNote {\n note,\n contract_address,\n owner,\n randomness,\n storage_slot,\n metadata:\n aztec::note::note_metadata::SettledNoteMetadata::new(\n note_nonce,\n )\n .into(),\n },\n );\n\n let inner_nullifier = $compute_nullifier_unconstrained(\n note,\n owner,\n note_hash_for_nullification,\n );\n\n Option::some(\n aztec::messages::discovery::NoteHashAndNullifier {\n note_hash,\n inner_nullifier,\n },\n )\n }\n }\n },\n );\n }\n\n let if_note_type_id_match_statements = if_note_type_id_match_statements_list.join(quote {});\n\n quote {\n /// Unpacks an array into a note corresponding to `note_type_id` and then computes its note hash (non-siloed) and inner nullifier (non-siloed) assuming the note has been inserted into the note hash tree with `note_nonce`.\n ///\n /// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteHashAndNullifier` type, and so it can be used to call functions from that module such as `do_sync_state` and `attempt_note_discovery`.\n ///\n /// This function is automatically injected by the `#[aztec]` macro.\n #[contract_library_method]\n unconstrained fn _compute_note_hash_and_nullifier(\n packed_note: BoundedVec<Field, aztec::messages::logs::note::MAX_NOTE_PACKED_LEN>,\n owner: aztec::protocol::address::AztecAddress,\n storage_slot: Field,\n note_type_id: Field,\n contract_address: aztec::protocol::address::AztecAddress,\n randomness: Field,\n note_nonce: Field,\n ) -> Option<aztec::messages::discovery::NoteHashAndNullifier> {\n $if_note_type_id_match_statements\n else {\n Option::none()\n }\n }\n }\n } else {\n // Contracts with no notes still implement this function to avoid having special-casing, the implementation\n // simply throws immediately.\n quote {\n /// This contract does not use private notes, so this function should never be called as it will unconditionally fail.\n ///\n /// This function is automatically injected by the `#[aztec]` macro.\n #[contract_library_method]\n unconstrained fn _compute_note_hash_and_nullifier(\n _packed_note: BoundedVec<Field, aztec::messages::logs::note::MAX_NOTE_PACKED_LEN>,\n _owner: aztec::protocol::address::AztecAddress,\n _storage_slot: Field,\n _note_type_id: Field,\n _contract_address: aztec::protocol::address::AztecAddress,\n _randomness: Field,\n _nonce: Field,\n ) -> Option<aztec::messages::discovery::NoteHashAndNullifier> {\n panic(f\"This contract does not use private notes\")\n }\n }\n }\n}\n\n/// Generates the `sync_state` utility function that performs message discovery.\ncomptime fn generate_sync_state(process_custom_message_option: Quoted, offchain_inbox_sync_option: Quoted) -> Quoted {\n quote {\n pub struct sync_state_parameters {}\n\n #[abi(functions)]\n pub struct sync_state_abi {\n parameters: sync_state_parameters,\n }\n\n #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]\n unconstrained fn sync_state() {\n let address = aztec::context::UtilityContext::new().this_address();\n aztec::messages::discovery::do_sync_state(\n address,\n _compute_note_hash_and_nullifier,\n $process_custom_message_option,\n $offchain_inbox_sync_option,\n );\n }\n }\n}\n\n/// Generates the `process_message` utility function that processes a single message ciphertext.\ncomptime fn generate_process_message(process_custom_message_option: Quoted) -> Quoted {\n quote {\n pub struct process_message_parameters {\n pub message_ciphertext: BoundedVec<Field, aztec::messages::encoding::MESSAGE_CIPHERTEXT_LEN>,\n pub message_context: aztec::messages::processing::MessageContext,\n }\n\n #[abi(functions)]\n pub struct process_message_abi {\n parameters: process_message_parameters,\n }\n\n #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]\n unconstrained fn process_message(\n message_ciphertext: BoundedVec<Field, aztec::messages::encoding::MESSAGE_CIPHERTEXT_LEN>,\n message_context: aztec::messages::processing::MessageContext,\n ) {\n let address = aztec::context::UtilityContext::new().this_address();\n\n aztec::messages::discovery::process_message::process_message_ciphertext(\n address,\n _compute_note_hash_and_nullifier,\n $process_custom_message_option,\n message_ciphertext,\n message_context,\n );\n\n // At this point, the note is pending validation and storage in the database. We must call\n // validate_and_store_enqueued_notes_and_events to complete that process.\n aztec::messages::processing::validate_and_store_enqueued_notes_and_events(address);\n }\n }\n}\n\n/// Generates an `offchain_receive` utility function that lets callers add messages to the offchain message inbox.\n///\n/// For more details, see `aztec::messages::processing::offchain::receive`.\ncomptime fn generate_offchain_receive() -> Quoted {\n quote {\n pub struct offchain_receive_parameters {\n pub messages: BoundedVec<\n aztec::messages::processing::offchain::OffchainMessage,\n aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,\n >,\n }\n\n #[abi(functions)]\n pub struct offchain_receive_abi {\n parameters: offchain_receive_parameters,\n }\n\n /// Receives offchain messages into this contract's offchain inbox for subsequent processing.\n ///\n /// For more details, see `aztec::messages::processing::offchain::receive`.\n ///\n /// This function is automatically injected by the `#[aztec]` macro.\n #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]\n unconstrained fn offchain_receive(\n messages: BoundedVec<\n aztec::messages::processing::offchain::OffchainMessage,\n aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,\n >,\n ) {\n let address = aztec::context::UtilityContext::new().this_address();\n aztec::messages::processing::offchain::receive(address, messages);\n }\n }\n}\n\n/// Checks that all functions in the module have a context macro applied.\n///\n/// Non-macroified functions are not allowed in contracts. They must all be one of\n/// [`crate::macros::functions::external`], [`crate::macros::functions::internal`] or `test`.\ncomptime fn check_each_fn_macroified(m: Module) {\n for f in m.functions() {\n let name = f.name();\n if !is_fn_external(f) & !is_fn_contract_library_method(f) & !is_fn_internal(f) & !is_fn_test(f) {\n // We don't suggest that #[contract_library_method] is allowed because we don't want to introduce another\n // concept\n panic(\n f\"Function {name} must be marked as either #[external(...)], #[internal(...)], or #[test]\",\n );\n }\n }\n}\n"
4502
+ "source": "use crate::{\n macros::{\n calls_generation::{\n external_functions::{generate_external_function_calls, generate_external_function_self_calls_structs},\n internal_functions::generate_call_internal_struct,\n },\n dispatch::generate_public_dispatch,\n emit_public_init_nullifier::generate_emit_public_init_nullifier,\n internals_functions_generation::{create_fn_abi_exports, process_functions},\n notes::NOTES,\n storage::STORAGE_LAYOUT_NAME,\n utils::{\n get_trait_impl_method, is_fn_contract_library_method, is_fn_external, is_fn_internal, is_fn_test,\n module_has_storage,\n },\n },\n messages::discovery::CustomMessageHandler,\n};\n\n/// Configuration for the [`aztec`] macro.\n///\n/// This type lets users override different parts of the default aztec-nr contract behavior, such\n/// as message handling. These are advanced features that require careful understanding of\n/// the behavior of these systems.\n///\n/// ## Examples\n///\n/// ```noir\n/// #[aztec(aztec::macros::AztecConfig::new().custom_message_handler(my_handler))]\n/// contract MyContract { ... }\n/// ```\npub struct AztecConfig {\n custom_message_handler: Option<CustomMessageHandler<()>>,\n}\n\nimpl AztecConfig {\n /// Creates a new `AztecConfig` with default values.\n ///\n /// Calling `new` is equivalent to invoking the [`aztec`] macro with no parameters. The different methods\n /// (e.g. [`AztecConfig::custom_message_handler`]) can then be used to change the default behavior.\n pub comptime fn new() -> Self {\n Self { custom_message_handler: Option::none() }\n }\n\n /// Sets a handler for custom messages.\n ///\n /// This enables contracts to process non-standard messages (i.e. any with a message type that is not in\n /// [`crate::messages::msg_type`]).\n ///\n /// `handler` must be a function that conforms to the\n /// [`crate::messages::discovery::CustomMessageHandler`] type signature.\n pub comptime fn custom_message_handler(_self: Self, handler: CustomMessageHandler<()>) -> Self {\n Self { custom_message_handler: Option::some(handler) }\n }\n}\n\n/// Enables aztec-nr features on a `contract`.\n///\n/// All aztec-nr contracts should have this macro invoked on them, as it is the one that processes all contract\n/// functions, notes, storage, generates interfaces for external calls, and creates the message processing\n/// boilerplate.\n///\n/// ## Examples\n///\n/// Most contracts can simply invoke the macro with no parameters, resulting in default aztec-nr behavior:\n/// ```noir\n/// #[aztec]\n/// contract MyContract { ... }\n/// ```\n///\n/// Advanced contracts can use [`AztecConfig`] to customize parts of its behavior, such as message\n/// processing.\n/// ```noir\n/// #[aztec(aztec::macros::AztecConfig::new().custom_message_handler(my_handler))]\n/// contract MyAdvancedContract { ... }\n/// ```\n#[varargs]\npub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted {\n let num_args = args.len();\n let config = if num_args == 0 {\n AztecConfig::new()\n } else if num_args == 1 {\n args[0]\n } else {\n panic(f\"#[aztec] expects 0 or 1 arguments, got {num_args}\")\n };\n\n // Functions that don't have #[external(...)], #[contract_library_method], or #[test] are not allowed in contracts.\n check_each_fn_macroified(m);\n\n // We generate new functions prefixed with `__aztec_nr_internals__` and we replace the original functions' bodies\n // with `static_assert(false, ...)` to prevent them from being called directly from within the contract.\n let functions = process_functions(m);\n\n // We generate structs and their implementations necessary for convenient functions calls.\n let interface = generate_contract_interface(m);\n let self_call_structs = generate_external_function_self_calls_structs(m);\n let call_internal_struct = generate_call_internal_struct(m);\n\n // We generate ABI exports for all the external functions in the contract.\n let fn_abi_exports = create_fn_abi_exports(m);\n\n // We generate `_compute_note_hash_and_nullifier`, `sync_state` and `process_message` functions only if they are\n // not already implemented. If they are implemented we just insert empty quotes.\n let contract_library_method_compute_note_hash_and_nullifier = if !m.functions().any(|f| {\n f.name() == quote { _compute_note_hash_and_nullifier }\n }) {\n generate_contract_library_method_compute_note_hash_and_nullifier()\n } else {\n quote {}\n };\n let process_custom_message_option = if config.custom_message_handler.is_some() {\n let handler = config.custom_message_handler.unwrap();\n quote { Option::some($handler) }\n } else {\n quote { Option::<aztec::messages::discovery::CustomMessageHandler<()>>::none() }\n };\n\n let offchain_inbox_sync_option = quote {\n Option::some(aztec::messages::processing::offchain::sync_inbox)\n };\n\n let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) {\n generate_sync_state(process_custom_message_option, offchain_inbox_sync_option)\n } else {\n quote {}\n };\n\n if m.functions().any(|f| f.name() == quote { offchain_receive }) {\n panic(\n \"User-defined 'offchain_receive' is not allowed. The function is auto-injected by the #[aztec] macro. See https://docs.aztec.network/errors/7\",\n );\n }\n let offchain_receive_fn_and_abi_export = generate_offchain_receive();\n\n let process_message_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { process_message }) {\n generate_process_message(process_custom_message_option)\n } else {\n quote {}\n };\n let (has_public_init_nullifier_fn, emit_public_init_nullifier_fn_body) = generate_emit_public_init_nullifier(m);\n let public_dispatch = generate_public_dispatch(m, has_public_init_nullifier_fn);\n\n quote {\n $interface\n $self_call_structs\n $call_internal_struct\n $functions\n $fn_abi_exports\n $contract_library_method_compute_note_hash_and_nullifier\n $public_dispatch\n $sync_state_fn_and_abi_export\n $process_message_fn_and_abi_export\n $emit_public_init_nullifier_fn_body\n $offchain_receive_fn_and_abi_export\n }\n}\n\ncomptime fn generate_contract_interface(m: Module) -> Quoted {\n let calls = generate_external_function_calls(m);\n\n let module_name = m.name();\n\n let has_storage_layout = module_has_storage(m) & STORAGE_LAYOUT_NAME.get(m).is_some();\n let storage_layout_getter = if has_storage_layout {\n let storage_layout_name = STORAGE_LAYOUT_NAME.get(m).unwrap();\n quote {\n pub fn storage_layout() -> StorageLayoutFields {\n $storage_layout_name.fields\n }\n }\n } else {\n quote {}\n };\n\n let library_storage_layout_getter = if has_storage_layout {\n quote {\n #[contract_library_method]\n $storage_layout_getter\n }\n } else {\n quote {}\n };\n\n quote {\n pub struct $module_name {\n pub target_contract: aztec::protocol::address::AztecAddress\n }\n\n impl $module_name {\n $calls\n\n pub fn at(\n addr: aztec::protocol::address::AztecAddress\n ) -> Self {\n Self { target_contract: addr }\n }\n\n pub fn interface() -> Self {\n Self { target_contract: aztec::protocol::address::AztecAddress::zero() }\n }\n\n $storage_layout_getter\n }\n\n #[contract_library_method]\n pub fn at(\n addr: aztec::protocol::address::AztecAddress\n ) -> $module_name {\n $module_name { target_contract: addr }\n }\n\n #[contract_library_method]\n pub fn interface() -> $module_name {\n $module_name { target_contract: aztec::protocol::address::AztecAddress::zero() }\n }\n\n $library_storage_layout_getter\n\n }\n}\n\n/// Generates a contract library method called `_compute_note_hash_and_nullifier` which is used for note discovery (to\n/// create the `aztec::messages::discovery::ComputeNoteHashAndNullifier` function) and to implement the\n/// `compute_note_hash_and_nullifier` unconstrained contract function.\ncomptime fn generate_contract_library_method_compute_note_hash_and_nullifier() -> Quoted {\n if NOTES.len() > 0 {\n // Contracts that do define notes produce an if-else chain where `note_type_id` is matched against the\n // `get_note_type_id()` function of each note type that we know of, in order to identify the note type. Once we\n // know it we call we correct `unpack` method from the `Packable` trait to obtain the underlying note type, and\n // compute the note hash (non-siloed) and inner nullifier (also non-siloed).\n\n let mut if_note_type_id_match_statements_list = @[];\n for i in 0..NOTES.len() {\n let typ = NOTES.get(i);\n\n let get_note_type_id = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteType },\n quote { get_id },\n );\n let unpack = get_trait_impl_method(\n typ,\n quote { crate::protocol::traits::Packable },\n quote { unpack },\n );\n\n let compute_note_hash = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteHash },\n quote { compute_note_hash },\n );\n\n let compute_nullifier_unconstrained = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteHash },\n quote { compute_nullifier_unconstrained },\n );\n\n let if_or_else_if = if i == 0 {\n quote { if }\n } else {\n quote { else if }\n };\n\n if_note_type_id_match_statements_list = if_note_type_id_match_statements_list.push_back(\n quote {\n $if_or_else_if note_type_id == $get_note_type_id() {\n // As an extra safety check we make sure that the packed_note BoundedVec has the expected\n // length, since we're about to interpret its raw storage as a fixed-size array by calling the\n // unpack function on it.\n let expected_len = <$typ as $crate::protocol::traits::Packable>::N;\n let actual_len = packed_note.len();\n if actual_len != expected_len {\n aztec::protocol::logging::warn_log_format(\n \"[aztec-nr] Packed note length mismatch for note type id {2}: expected {0} fields, got {1}. Skipping note.\",\n [expected_len as Field, actual_len as Field, note_type_id],\n );\n Option::none()\n } else {\n let note = $unpack(aztec::utils::array::subarray(packed_note.storage(), 0));\n\n let note_hash = $compute_note_hash(note, owner, storage_slot, randomness);\n\n // The message discovery process finds settled notes, that is, notes that were created in\n // prior transactions and are therefore already part of the note hash tree. We therefore\n // compute the nullification note hash by treating the note as a settled note with the\n // provided note nonce.\n let note_hash_for_nullification =\n aztec::note::utils::compute_note_hash_for_nullification(\n aztec::note::HintedNote {\n note,\n contract_address,\n owner,\n randomness,\n storage_slot,\n metadata:\n aztec::note::note_metadata::SettledNoteMetadata::new(\n note_nonce,\n )\n .into(),\n },\n );\n\n let inner_nullifier = $compute_nullifier_unconstrained(\n note,\n owner,\n note_hash_for_nullification,\n );\n\n Option::some(\n aztec::messages::discovery::NoteHashAndNullifier {\n note_hash,\n inner_nullifier,\n },\n )\n }\n }\n },\n );\n }\n\n let if_note_type_id_match_statements = if_note_type_id_match_statements_list.join(quote {});\n\n quote {\n /// Unpacks an array into a note corresponding to `note_type_id` and then computes its note hash (non-siloed) and inner nullifier (non-siloed) assuming the note has been inserted into the note hash tree with `note_nonce`.\n ///\n /// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteHashAndNullifier` type, and so it can be used to call functions from that module such as `do_sync_state` and `attempt_note_discovery`.\n ///\n /// This function is automatically injected by the `#[aztec]` macro.\n #[contract_library_method]\n unconstrained fn _compute_note_hash_and_nullifier(\n packed_note: BoundedVec<Field, aztec::messages::logs::note::MAX_NOTE_PACKED_LEN>,\n owner: aztec::protocol::address::AztecAddress,\n storage_slot: Field,\n note_type_id: Field,\n contract_address: aztec::protocol::address::AztecAddress,\n randomness: Field,\n note_nonce: Field,\n ) -> Option<aztec::messages::discovery::NoteHashAndNullifier> {\n $if_note_type_id_match_statements\n else {\n aztec::protocol::logging::warn_log_format(\n \"[aztec-nr] Unknown note type id {0}. Skipping note.\",\n [note_type_id],\n );\n Option::none()\n }\n }\n }\n } else {\n // Contracts with no notes still implement this function to avoid having special-casing, the implementation\n // simply throws immediately.\n quote {\n /// This contract does not use private notes, so this function should never be called as it will unconditionally fail.\n ///\n /// This function is automatically injected by the `#[aztec]` macro.\n #[contract_library_method]\n unconstrained fn _compute_note_hash_and_nullifier(\n _packed_note: BoundedVec<Field, aztec::messages::logs::note::MAX_NOTE_PACKED_LEN>,\n _owner: aztec::protocol::address::AztecAddress,\n _storage_slot: Field,\n _note_type_id: Field,\n _contract_address: aztec::protocol::address::AztecAddress,\n _randomness: Field,\n _nonce: Field,\n ) -> Option<aztec::messages::discovery::NoteHashAndNullifier> {\n panic(f\"This contract does not use private notes\")\n }\n }\n }\n}\n\n/// Generates the `sync_state` utility function that performs message discovery.\ncomptime fn generate_sync_state(process_custom_message_option: Quoted, offchain_inbox_sync_option: Quoted) -> Quoted {\n quote {\n pub struct sync_state_parameters {}\n\n #[abi(functions)]\n pub struct sync_state_abi {\n parameters: sync_state_parameters,\n }\n\n #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]\n unconstrained fn sync_state() {\n let address = aztec::context::UtilityContext::new().this_address();\n aztec::messages::discovery::do_sync_state(\n address,\n _compute_note_hash_and_nullifier,\n $process_custom_message_option,\n $offchain_inbox_sync_option,\n );\n }\n }\n}\n\n/// Generates the `process_message` utility function that processes a single message ciphertext.\ncomptime fn generate_process_message(process_custom_message_option: Quoted) -> Quoted {\n quote {\n pub struct process_message_parameters {\n pub message_ciphertext: BoundedVec<Field, aztec::messages::encoding::MESSAGE_CIPHERTEXT_LEN>,\n pub message_context: aztec::messages::processing::MessageContext,\n }\n\n #[abi(functions)]\n pub struct process_message_abi {\n parameters: process_message_parameters,\n }\n\n #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]\n unconstrained fn process_message(\n message_ciphertext: BoundedVec<Field, aztec::messages::encoding::MESSAGE_CIPHERTEXT_LEN>,\n message_context: aztec::messages::processing::MessageContext,\n ) {\n let address = aztec::context::UtilityContext::new().this_address();\n\n aztec::messages::discovery::process_message::process_message_ciphertext(\n address,\n _compute_note_hash_and_nullifier,\n $process_custom_message_option,\n message_ciphertext,\n message_context,\n );\n\n // At this point, the note is pending validation and storage in the database. We must call\n // validate_and_store_enqueued_notes_and_events to complete that process.\n aztec::messages::processing::validate_and_store_enqueued_notes_and_events(address);\n }\n }\n}\n\n/// Generates an `offchain_receive` utility function that lets callers add messages to the offchain message inbox.\n///\n/// For more details, see `aztec::messages::processing::offchain::receive`.\ncomptime fn generate_offchain_receive() -> Quoted {\n quote {\n pub struct offchain_receive_parameters {\n pub messages: BoundedVec<\n aztec::messages::processing::offchain::OffchainMessage,\n aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,\n >,\n }\n\n #[abi(functions)]\n pub struct offchain_receive_abi {\n parameters: offchain_receive_parameters,\n }\n\n /// Receives offchain messages into this contract's offchain inbox for subsequent processing.\n ///\n /// For more details, see `aztec::messages::processing::offchain::receive`.\n ///\n /// This function is automatically injected by the `#[aztec]` macro.\n #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]\n unconstrained fn offchain_receive(\n messages: BoundedVec<\n aztec::messages::processing::offchain::OffchainMessage,\n aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,\n >,\n ) {\n let address = aztec::context::UtilityContext::new().this_address();\n aztec::messages::processing::offchain::receive(address, messages);\n }\n }\n}\n\n/// Checks that all functions in the module have a context macro applied.\n///\n/// Non-macroified functions are not allowed in contracts. They must all be one of\n/// [`crate::macros::functions::external`], [`crate::macros::functions::internal`] or `test`.\ncomptime fn check_each_fn_macroified(m: Module) {\n for f in m.functions() {\n let name = f.name();\n if !is_fn_external(f) & !is_fn_contract_library_method(f) & !is_fn_internal(f) & !is_fn_test(f) {\n // We don't suggest that #[contract_library_method] is allowed because we don't want to introduce another\n // concept\n panic(\n f\"Function {name} must be marked as either #[external(...)], #[internal(...)], or #[test]\",\n );\n }\n }\n}\n"
4503
4503
  },
4504
4504
  "115": {
4505
4505
  "path": "/home/aztec-dev/aztec-packages/noir-projects/aztec-nr/aztec/src/macros/internals_functions_generation/external/private.nr",
@@ -4555,7 +4555,7 @@
4555
4555
  },
4556
4556
  "153": {
4557
4557
  "path": "/home/aztec-dev/aztec-packages/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr",
4558
- "source": "use crate::{\n capsules::CapsuleArray,\n context::UtilityContext,\n messages::{\n encoding::MESSAGE_CIPHERTEXT_LEN,\n processing::{MessageContext, MessageTxContext, OffchainMessageWithContext, resolve_message_contexts},\n },\n protocol::{\n address::AztecAddress,\n constants::MAX_TX_LIFETIME,\n hash::sha256_to_field,\n traits::{Deserialize, Serialize},\n },\n};\n\n/// Base capsule slot for the persistent inbox of [`PendingOffchainMsg`] entries.\n///\n/// This is the slot where we accumulate messages received through [`receive`].\nglobal OFFCHAIN_INBOX_SLOT: Field = sha256_to_field(\"AZTEC_NR::OFFCHAIN_INBOX_SLOT\".as_bytes());\n\n/// Capsule array slot used by [`sync_inbox`] to pass tx hash resolution requests to PXE.\nglobal OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field(\"AZTEC_NR::OFFCHAIN_CONTEXT_REQUESTS_SLOT\".as_bytes());\n\n/// Capsule array slot used by [`sync_inbox`] to read tx context responses from PXE.\nglobal OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field(\"AZTEC_NR::OFFCHAIN_CONTEXT_RESPONSES_SLOT\".as_bytes());\n\n/// Capsule array slot used by [`sync_inbox`] to collect messages ready for processing.\nglobal OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field(\"AZTEC_NR::OFFCHAIN_READY_MESSAGES_SLOT\".as_bytes());\n\n/// Maximum number of offchain messages accepted by `offchain_receive` in a single call.\npub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16;\n\n/// Tolerance added to the `MAX_TX_LIFETIME` cap for message expiration.\nglobal TX_EXPIRATION_TOLERANCE: u64 = 7200; // 2 hours\n\n/// Maximum time-to-live for a tx-bound offchain message: `MAX_TX_LIFETIME + TX_EXPIRATION_CAP_TOLERANCE`.\n///\n/// After `received_at + MAX_MSG_TTL`, the message is evicted regardless of the sender-provided expiration.\nglobal MAX_MSG_TTL: u64 = MAX_TX_LIFETIME + TX_EXPIRATION_TOLERANCE;\n\n/// A function that manages offchain-delivered messages for processing during sync.\n///\n/// Offchain messages are messages that are not broadcasted via onchain logs. They are instead delivered to the\n/// recipient by calling the `offchain_receive` utility function (injected by the `#[aztec]` macro). Message transport\n/// is the app's responsibility. Typical examples of transport methods are: messaging apps, email, QR codes, etc.\n///\n/// Once offchain messages are delivered to the recipient's private environment via `offchain_receive`, messages are\n/// locally stored in a persistent inbox.\n///\n/// This function determines when each message in said inbox is ready for processing, when it can be safely disposed\n/// of, etc.\n///\n/// The only current implementation of an `OffchainInboxSync` is [`sync_inbox`], which manages an inbox with expiration\n/// based eviction and automatic transaction context resolution.\npub(crate) type OffchainInboxSync<Env> = unconstrained fn[Env](\n/* contract_address */AztecAddress) -> CapsuleArray<OffchainMessageWithContext>;\n\n/// A message delivered via the `offchain_receive` utility function.\npub struct OffchainMessage {\n /// The encrypted message payload.\n pub ciphertext: BoundedVec<Field, MESSAGE_CIPHERTEXT_LEN>,\n /// The intended recipient of the message.\n pub recipient: AztecAddress,\n /// The hash of the transaction that produced this message. `Option::none` indicates a tx-less message.\n pub tx_hash: Option<Field>,\n /// The timestamp after which this message can be evicted from the inbox.\n pub expiration_timestamp: u64,\n}\n\n/// An offchain message awaiting processing (or re-processing) in the inbox.\n///\n/// Messages remain in the inbox until they expire, even if they have already been processed. This is necessary to\n/// handle reorgs: a processed message may need to be re-processed if the transaction that provided its context is\n/// reverted. On each sync, resolved messages are promoted to [`OffchainMessageWithContext`] for processing.\n#[derive(Serialize, Deserialize)]\nstruct PendingOffchainMsg {\n /// The encrypted message payload.\n ciphertext: BoundedVec<Field, MESSAGE_CIPHERTEXT_LEN>,\n /// The intended recipient of the message.\n recipient: AztecAddress,\n /// The hash of the transaction that produced this message. A value of 0 indicates a tx-less message.\n tx_hash: Field,\n /// The timestamp after which this message can be evicted from the inbox (as provided by the sender).\n expiration_timestamp: u64,\n /// The timestamp at which this message was received (i.e. added to the inbox via `receive`).\n ///\n /// Used to cap the effective expiration of messages with an associated transaction: since a tx can only live for\n /// `MAX_TX_LIFETIME`, we don't need to keep the message around longer than `received_at + MAX_MSG_TTL`,\n /// regardless of the sender-provided `expiration_timestamp`.\n received_at: u64,\n}\n\n/// Delivers offchain messages to the given contract's offchain inbox for subsequent processing.\n///\n/// Offchain messages are transaction effects that are not broadcasted via onchain logs. Instead, the sender shares the\n/// message to the recipient through an external channel (e.g. a URL accessible by the recipient). The recipient then\n/// calls this function to hand the messages to the contract so they can be processed through the same mechanisms as\n/// onchain messages.\n///\n/// Messages are processed when their originating transaction is found onchain (providing the context needed to\n/// validate resulting notes and events).\n///\n/// Messages are kept in the inbox until their expiration timestamp is reached. For messages with an associated\n/// transaction, the effective expiration is capped to `received_at + MAX_MSG_TTL`, since there's no point in\n/// keeping a message around longer than its originating transaction could possibly live.\n///\n/// Processing order is not guaranteed.\npub unconstrained fn receive(\n contract_address: AztecAddress,\n messages: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL>,\n) {\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT);\n let now = UtilityContext::new().timestamp();\n let mut i = 0;\n let messages_len = messages.len();\n while i < messages_len {\n let msg = messages.get(i);\n let tx_hash = if msg.tx_hash.is_some() {\n msg.tx_hash.unwrap()\n } else {\n 0\n };\n inbox.push(\n PendingOffchainMsg {\n ciphertext: msg.ciphertext,\n recipient: msg.recipient,\n tx_hash,\n expiration_timestamp: msg.expiration_timestamp,\n received_at: now,\n },\n );\n i += 1;\n }\n\n // TODO: Invoke an oracle to invalidate contract sync state cache\n}\n\n/// Returns offchain-delivered messages to process during sync.\n///\n/// Messages remain in the inbox and are reprocessed on each sync until their originating transaction is no longer at\n/// risk of being dropped by a reorg.\npub unconstrained fn sync_inbox(address: AztecAddress) -> CapsuleArray<OffchainMessageWithContext> {\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n let context_resolution_requests: CapsuleArray<Field> = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT);\n let resolved_contexts: CapsuleArray<Option<MessageTxContext>> =\n CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT);\n let ready_to_process: CapsuleArray<OffchainMessageWithContext> =\n CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT);\n\n // Clear any stale ready messages from a previous run.\n ready_to_process.for_each(|i, _| { ready_to_process.remove(i); });\n\n // Clear any stale context resolution requests/responses from a previous run.\n context_resolution_requests.for_each(|i, _| { context_resolution_requests.remove(i); });\n resolved_contexts.for_each(|i, _| { resolved_contexts.remove(i); });\n\n // Build a request list aligned with the inbox indices.\n let mut i = 0;\n let inbox_len = inbox.len();\n while i < inbox_len {\n let msg = inbox.get(i);\n context_resolution_requests.push(msg.tx_hash);\n i += 1;\n }\n\n // Ask PXE to resolve contexts for all requested tx hashes.\n resolve_message_contexts(\n address,\n OFFCHAIN_CONTEXT_REQUESTS_SLOT,\n OFFCHAIN_CONTEXT_RESPONSES_SLOT,\n );\n\n assert_eq(resolved_contexts.len(), inbox_len);\n\n let now = UtilityContext::new().timestamp();\n\n let mut j = inbox_len;\n while j > 0 {\n // This loop decides what to do with each message in the offchain message inbox. We need to handle 3\n // different scenarios for each message.\n //\n // 1. The TX that emitted this message is still not known to PXE: in this case we can't yet process this\n // message, as any notes or events discovered will fail to be validated. So we leave the message in the inbox,\n // awaiting for future syncs to detect that the TX became available.\n //\n // 2. The message is not associated to a TX to begin with. The current version of offchain message processing\n // does not support this case, but in the future it will. Right now, a message without an associated TX will\n // sit in the inbox until it expires.\n //\n // 3. The TX that emitted this message has been found by PXE. That gives us all the information needed to\n // process the message. We add the message to the `ready_to_process` CapsuleArray so that the `sync_state` loop\n // processes it.\n //\n // In all cases, if the message has expired, or the associated TX is older than than `MAX_TX_LIFETIME`\n // (24 hours), we remove it from the inbox.\n //\n // Note: the loop runs backwards because it might call `inbox.remove(j)` to purge expired messages and we also\n // need to align it with `resolved_contexts.get(j)`. Going from last to first simplifies the algorithm as\n // not yet visited element indexes remain stable.\n j -= 1;\n let maybe_ctx = resolved_contexts.get(j);\n let msg = inbox.get(j);\n\n // Compute the message's effective expiration timestamp to determine if we can purge it from the inbox.\n let effective_expiration = if msg.tx_hash != 0 {\n // For messages with an associated transaction, we cap the sender-provided expiration timestamp to\n // `received_at + MAX_MSG_TTL`: there's no point in keeping a message once the TX\n // can no longer possibly be included in a block.\n let tx_lifetime_cap = msg.received_at + MAX_MSG_TTL;\n if msg.expiration_timestamp < tx_lifetime_cap {\n msg.expiration_timestamp\n } else {\n tx_lifetime_cap\n }\n } else {\n // Messages with no associated TX expire on their `expiration_timestamp`\n msg.expiration_timestamp\n };\n\n // Message expired. We remove it from the inbox.\n if now > effective_expiration {\n inbox.remove(j);\n }\n\n // Scenario 1: associated TX not yet available. We keep the message in the inbox, as it might become\n // processable as new blocks get mined.\n // Scenario 2: no TX associated to message. The message will sit in the inbox until it expires.\n if maybe_ctx.is_none() {\n continue;\n }\n\n // Scenario 3: Message is ready to process, add to result array. Note we still keep it in the inbox unless we\n // consider it has expired: this is because we need to account for reorgs. If reorg occurs after we processed\n // a message, the effects of processing the message get rewind. However, the associated TX can be included in\n // a subsequent block. Should that happen, the message must be re-processed to ensure consistency.\n let ctx = maybe_ctx.unwrap();\n let message_context = MessageContext {\n tx_hash: ctx.tx_hash,\n unique_note_hashes_in_tx: ctx.unique_note_hashes_in_tx,\n first_nullifier_in_tx: ctx.first_nullifier_in_tx,\n recipient: msg.recipient,\n };\n ready_to_process.push(OffchainMessageWithContext { message_ciphertext: msg.ciphertext, message_context });\n }\n\n ready_to_process\n}\n\nmod test {\n use crate::{\n capsules::CapsuleArray,\n oracle::random::random,\n protocol::{address::AztecAddress, traits::FromField},\n test::helpers::test_environment::TestEnvironment,\n };\n use super::{\n MAX_MSG_TTL, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL, OFFCHAIN_INBOX_SLOT, OffchainMessage, PendingOffchainMsg,\n receive, sync_inbox,\n };\n\n /// Creates an `OffchainMessage` with dummy ciphertext/recipient.\n fn make_msg(tx_hash: Option<Field>, expiration_timestamp: u64) -> OffchainMessage {\n OffchainMessage {\n ciphertext: BoundedVec::new(),\n recipient: AztecAddress::from_field(42),\n tx_hash,\n expiration_timestamp,\n }\n }\n\n /// Advances the TXE block timestamp by `offset` seconds and returns the resulting timestamp.\n unconstrained fn advance_by(env: TestEnvironment, offset: u64) -> u64 {\n env.advance_next_block_timestamp_by(offset);\n env.mine_block();\n env.last_block_timestamp()\n }\n\n #[test]\n unconstrained fn empty_inbox_returns_empty_result() {\n let env = TestEnvironment::new();\n env.utility_context(|context| {\n let result = sync_inbox(context.this_address());\n let inbox: CapsuleArray<PendingOffchainMsg> =\n CapsuleArray::at(context.this_address(), OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0);\n assert_eq(inbox.len(), 0);\n });\n }\n\n #[test]\n unconstrained fn tx_bound_msg_expires_by_sender_timestamp() {\n let env = TestEnvironment::new();\n let receive_time = advance_by(env, 10);\n // Msg expiration attribute is well before the lifetime cap, so it is the effective expiration.\n let sender_expiration_offset = 100;\n\n // Receive message with expiration at receive_time + offset.\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs\n .push(\n make_msg(\n Option::some(random()),\n receive_time + sender_expiration_offset,\n ),\n );\n receive(context.this_address(), msgs);\n });\n\n // Advance past sender expiration but before the lifetime cap.\n let _now = advance_by(env, sender_expiration_offset + 1);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // context is None, not ready\n assert_eq(inbox.len(), 0); // expired, removed\n });\n }\n\n #[test]\n unconstrained fn tx_bound_msg_expires_by_lifetime_cap() {\n let env = TestEnvironment::new();\n let receive_time = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n // Sender's expiration is far in the future, so the standard TX lifetime cap kicks in.\n msgs.push(make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL));\n receive(context.this_address(), msgs);\n });\n\n // Advance past the lifetime cap.\n let _now = advance_by(env, MAX_MSG_TTL + 1);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0);\n assert_eq(inbox.len(), 0); // capped expiration exceeded, removed\n });\n }\n\n #[test]\n unconstrained fn tx_bound_msg_not_expired_sender_timestamp_binding() {\n let env = TestEnvironment::new();\n let receive_time = advance_by(env, 10);\n // Sender expiration is in the future and below the cap, so it is the binding expiration.\n let sender_expiration_offset = 100;\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs\n .push(\n make_msg(\n Option::some(random()),\n receive_time + sender_expiration_offset,\n ),\n );\n receive(context.this_address(), msgs);\n });\n\n // Advance, but not past sender expiration.\n let _now = advance_by(env, sender_expiration_offset - 1);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // context is None, not ready\n assert_eq(inbox.len(), 1); // not expired, stays\n });\n }\n\n #[test]\n unconstrained fn tx_bound_msg_not_expired_lifetime_cap_binding() {\n let env = TestEnvironment::new();\n\n // Sender expiration is way beyond the cap, but `now` is still before the cap.\n let receive_time = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL));\n receive(context.this_address(), msgs);\n });\n\n // Advance, but not past the lifetime cap.\n let _now = advance_by(env, 100);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // context is None, not ready\n assert_eq(inbox.len(), 1); // not expired, stays\n });\n }\n\n #[test]\n unconstrained fn tx_less_msg_expires_by_sender_timestamp() {\n let env = TestEnvironment::new();\n let receive_time = advance_by(env, 10);\n let sender_expiration_offset = 100;\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::none(), receive_time + sender_expiration_offset));\n receive(context.this_address(), msgs);\n });\n\n let _now = advance_by(env, sender_expiration_offset + 1);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0);\n assert_eq(inbox.len(), 0); // expired, removed\n });\n }\n\n #[test]\n unconstrained fn tx_less_msg_with_large_expiration_is_not_capped() {\n let env = TestEnvironment::new();\n let receive_time = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::none(), receive_time + 10 * MAX_MSG_TTL));\n receive(context.this_address(), msgs);\n });\n\n // Advance past what the lifetime cap would be for a tx-bound message.\n let _now = advance_by(env, MAX_MSG_TTL + 1);\n\n // The message is tx-less, so the lifetime cap does not apply and the sender's large\n // expiration is trusted. Even though `now` is past what the cap would be for a tx-bound\n // message, the message stays.\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // context is None, not ready\n assert_eq(inbox.len(), 1); // tx-less, not capped, stays\n });\n }\n\n #[test]\n unconstrained fn unresolved_tx_stays_in_inbox() {\n let env = TestEnvironment::new();\n let receive_time = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::some(random()), receive_time + MAX_MSG_TTL));\n receive(context.this_address(), msgs);\n });\n\n let _now = advance_by(env, 100);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // not resolved, not ready\n assert_eq(inbox.len(), 1); // not expired, stays\n });\n }\n\n #[test]\n unconstrained fn multiple_messages_mixed_expiration() {\n let env = TestEnvironment::new();\n let receive_time = advance_by(env, 10);\n\n let survivor_tx_hash = random();\n\n env.utility_context(|context| {\n let address = context.this_address();\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n // Message 0: tx-bound, will expire after 50s.\n msgs.push(make_msg(Option::some(random()), receive_time + 50));\n // Message 1: tx-bound, will expire after 50_000s (the survivor).\n msgs.push(make_msg(Option::some(survivor_tx_hash), receive_time + 50_000));\n // Message 2: tx-less, will expire after 50s.\n msgs.push(make_msg(Option::none(), receive_time + 50));\n receive(address, msgs);\n });\n\n // Advance past 50s but before 50_000s.\n let _now = advance_by(env, 51);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // all contexts are None\n // Only message 1 should remain: messages 0 and 2 expired.\n assert_eq(inbox.len(), 1);\n assert_eq(inbox.get(0).tx_hash, survivor_tx_hash);\n });\n }\n\n // -- Resolved context (ready to process) ------------------------------\n\n #[test]\n unconstrained fn resolved_msg_is_ready_to_process() {\n let env = TestEnvironment::new();\n // TestEnvironment::new() deploys protocol contracts, creating blocks with tx effects.\n // In TXE, tx hashes equal Fr(blockNumber), so Fr(1) is the tx effect from block 1.\n // We use this as a \"known resolvable\" tx hash.\n let known_tx_hash: Field = 1;\n let receive_time = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::some(known_tx_hash), receive_time + MAX_MSG_TTL));\n receive(context.this_address(), msgs);\n });\n\n let _now = advance_by(env, 100);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n // The message should be ready to process since its tx context was resolved.\n assert_eq(result.len(), 1);\n\n let ctx = result.get(0).message_context;\n assert_eq(ctx.tx_hash, known_tx_hash);\n assert_eq(ctx.recipient, AztecAddress::from_field(42));\n assert(ctx.first_nullifier_in_tx != 0, \"resolved context must have a first nullifier\");\n\n // Message stays in inbox (not expired) for potential reorg reprocessing.\n assert_eq(inbox.len(), 1);\n });\n }\n}\n"
4558
+ "source": "use crate::{\n capsules::CapsuleArray,\n context::UtilityContext,\n messages::{\n encoding::MESSAGE_CIPHERTEXT_LEN,\n processing::{MessageContext, MessageTxContext, OffchainMessageWithContext, resolve_message_contexts},\n },\n protocol::{\n address::AztecAddress,\n constants::MAX_TX_LIFETIME,\n hash::sha256_to_field,\n traits::{Deserialize, Serialize},\n },\n};\n\n/// Base capsule slot for the persistent inbox of [`PendingOffchainMsg`] entries.\n///\n/// This is the slot where we accumulate messages received through [`receive`].\nglobal OFFCHAIN_INBOX_SLOT: Field = sha256_to_field(\"AZTEC_NR::OFFCHAIN_INBOX_SLOT\".as_bytes());\n\n/// Capsule array slot used by [`sync_inbox`] to pass tx hash resolution requests to PXE.\nglobal OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field(\"AZTEC_NR::OFFCHAIN_CONTEXT_REQUESTS_SLOT\".as_bytes());\n\n/// Capsule array slot used by [`sync_inbox`] to read tx context responses from PXE.\nglobal OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field(\"AZTEC_NR::OFFCHAIN_CONTEXT_RESPONSES_SLOT\".as_bytes());\n\n/// Capsule array slot used by [`sync_inbox`] to collect messages ready for processing.\nglobal OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field(\"AZTEC_NR::OFFCHAIN_READY_MESSAGES_SLOT\".as_bytes());\n\n/// Maximum number of offchain messages accepted by `offchain_receive` in a single call.\npub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16;\n\n/// Tolerance added to the `MAX_TX_LIFETIME` cap for message expiration.\nglobal TX_EXPIRATION_TOLERANCE: u64 = 7200; // 2 hours\n\n/// Maximum time-to-live for a tx-bound offchain message.\n///\n/// After `anchor_block_timestamp + MAX_MSG_TTL`, the message is evicted from the inbox.\nglobal MAX_MSG_TTL: u64 = MAX_TX_LIFETIME + TX_EXPIRATION_TOLERANCE;\n\n/// A function that manages offchain-delivered messages for processing during sync.\n///\n/// Offchain messages are messages that are not broadcasted via onchain logs. They are instead delivered to the\n/// recipient by calling the `offchain_receive` utility function (injected by the `#[aztec]` macro). Message transport\n/// is the app's responsibility. Typical examples of transport methods are: messaging apps, email, QR codes, etc.\n///\n/// Once offchain messages are delivered to the recipient's private environment via `offchain_receive`, messages are\n/// locally stored in a persistent inbox.\n///\n/// This function determines when each message in said inbox is ready for processing, when it can be safely disposed\n/// of, etc.\n///\n/// The only current implementation of an `OffchainInboxSync` is [`sync_inbox`], which manages an inbox with expiration\n/// based eviction and automatic transaction context resolution.\npub(crate) type OffchainInboxSync<Env> = unconstrained fn[Env](\n/* contract_address */AztecAddress) -> CapsuleArray<OffchainMessageWithContext>;\n\n/// A message delivered via the `offchain_receive` utility function.\npub struct OffchainMessage {\n /// The encrypted message payload.\n pub ciphertext: BoundedVec<Field, MESSAGE_CIPHERTEXT_LEN>,\n /// The intended recipient of the message.\n pub recipient: AztecAddress,\n /// The hash of the transaction that produced this message. `Option::none` indicates a tx-less message.\n pub tx_hash: Option<Field>,\n /// Anchor block timestamp at message emission.\n pub anchor_block_timestamp: u64,\n}\n\n/// An offchain message awaiting processing (or re-processing) in the inbox.\n///\n/// Messages remain in the inbox until they expire, even if they have already been processed. This is necessary to\n/// handle reorgs: a processed message may need to be re-processed if the transaction that provided its context is\n/// reverted. On each sync, resolved messages are promoted to [`OffchainMessageWithContext`] for processing.\n#[derive(Serialize, Deserialize)]\nstruct PendingOffchainMsg {\n /// The encrypted message payload.\n ciphertext: BoundedVec<Field, MESSAGE_CIPHERTEXT_LEN>,\n /// The intended recipient of the message.\n recipient: AztecAddress,\n /// The hash of the transaction that produced this message. A value of 0 indicates a tx-less message.\n tx_hash: Field,\n /// Anchor block timestamp at message emission.\n ///\n /// Used to compute the effective expiration: messages are evicted after\n /// `anchor_block_timestamp + MAX_MSG_TTL`.\n anchor_block_timestamp: u64,\n}\n\n/// Delivers offchain messages to the given contract's offchain inbox for subsequent processing.\n///\n/// Offchain messages are transaction effects that are not broadcasted via onchain logs. Instead, the sender shares the\n/// message to the recipient through an external channel (e.g. a URL accessible by the recipient). The recipient then\n/// calls this function to hand the messages to the contract so they can be processed through the same mechanisms as\n/// onchain messages.\n///\n/// Messages are processed when their originating transaction is found onchain (providing the context needed to\n/// validate resulting notes and events).\n///\n/// Messages are kept in the inbox until they expire. The effective expiration is\n/// `anchor_block_timestamp + MAX_MSG_TTL`.\n///\n/// Processing order is not guaranteed.\npub unconstrained fn receive(\n contract_address: AztecAddress,\n messages: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL>,\n) {\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT);\n let mut i = 0;\n let messages_len = messages.len();\n while i < messages_len {\n let msg = messages.get(i);\n let tx_hash = if msg.tx_hash.is_some() {\n msg.tx_hash.unwrap()\n } else {\n 0\n };\n inbox.push(\n PendingOffchainMsg {\n ciphertext: msg.ciphertext,\n recipient: msg.recipient,\n tx_hash,\n anchor_block_timestamp: msg.anchor_block_timestamp,\n },\n );\n i += 1;\n }\n\n // TODO: Invoke an oracle to invalidate contract sync state cache\n}\n\n/// Returns offchain-delivered messages to process during sync.\n///\n/// Messages remain in the inbox and are reprocessed on each sync until their originating transaction is no longer at\n/// risk of being dropped by a reorg.\npub unconstrained fn sync_inbox(address: AztecAddress) -> CapsuleArray<OffchainMessageWithContext> {\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n let context_resolution_requests: CapsuleArray<Field> = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT);\n let resolved_contexts: CapsuleArray<Option<MessageTxContext>> =\n CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT);\n let ready_to_process: CapsuleArray<OffchainMessageWithContext> =\n CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT);\n\n // Clear any stale ready messages from a previous run.\n ready_to_process.for_each(|i, _| { ready_to_process.remove(i); });\n\n // Clear any stale context resolution requests/responses from a previous run.\n context_resolution_requests.for_each(|i, _| { context_resolution_requests.remove(i); });\n resolved_contexts.for_each(|i, _| { resolved_contexts.remove(i); });\n\n // Build a request list aligned with the inbox indices.\n let mut i = 0;\n let inbox_len = inbox.len();\n while i < inbox_len {\n let msg = inbox.get(i);\n context_resolution_requests.push(msg.tx_hash);\n i += 1;\n }\n\n // Ask PXE to resolve contexts for all requested tx hashes.\n resolve_message_contexts(\n address,\n OFFCHAIN_CONTEXT_REQUESTS_SLOT,\n OFFCHAIN_CONTEXT_RESPONSES_SLOT,\n );\n\n assert_eq(resolved_contexts.len(), inbox_len);\n\n let now = UtilityContext::new().timestamp();\n\n let mut j = inbox_len;\n while j > 0 {\n // This loop decides what to do with each message in the offchain message inbox. We need to handle 3\n // different scenarios for each message.\n //\n // 1. The TX that emitted this message is still not known to PXE: in this case we can't yet process this\n // message, as any notes or events discovered will fail to be validated. So we leave the message in the inbox,\n // awaiting for future syncs to detect that the TX became available.\n //\n // 2. The message is not associated to a TX to begin with. The current version of offchain message processing\n // does not support this case, but in the future it will. Right now, a message without an associated TX will\n // sit in the inbox until it expires.\n //\n // 3. The TX that emitted this message has been found by PXE. That gives us all the information needed to\n // process the message. We add the message to the `ready_to_process` CapsuleArray so that the `sync_state` loop\n // processes it.\n //\n // In all cases, if the message has expired (i.e. `now > anchor_block_timestamp + MAX_MSG_TTL`), we remove it\n // from the inbox.\n //\n // Note: the loop runs backwards because it might call `inbox.remove(j)` to purge expired messages and we also\n // need to align it with `resolved_contexts.get(j)`. Going from last to first simplifies the algorithm as\n // not yet visited element indexes remain stable.\n j -= 1;\n let maybe_ctx = resolved_contexts.get(j);\n let msg = inbox.get(j);\n\n // Compute the message's effective expiration timestamp to determine if we can purge it from the inbox.\n let effective_expiration = msg.anchor_block_timestamp + MAX_MSG_TTL;\n\n // Message expired. We remove it from the inbox.\n if now > effective_expiration {\n inbox.remove(j);\n }\n\n // Scenario 1: associated TX not yet available. We keep the message in the inbox, as it might become\n // processable as new blocks get mined.\n // Scenario 2: no TX associated to message. The message will sit in the inbox until it expires.\n if maybe_ctx.is_none() {\n continue;\n }\n\n // Scenario 3: Message is ready to process, add to result array. Note we still keep it in the inbox unless we\n // consider it has expired: this is because we need to account for reorgs. If reorg occurs after we processed\n // a message, the effects of processing the message get rewind. However, the associated TX can be included in\n // a subsequent block. Should that happen, the message must be re-processed to ensure consistency.\n let ctx = maybe_ctx.unwrap();\n let message_context = MessageContext {\n tx_hash: ctx.tx_hash,\n unique_note_hashes_in_tx: ctx.unique_note_hashes_in_tx,\n first_nullifier_in_tx: ctx.first_nullifier_in_tx,\n recipient: msg.recipient,\n };\n ready_to_process.push(OffchainMessageWithContext { message_ciphertext: msg.ciphertext, message_context });\n }\n\n ready_to_process\n}\n\nmod test {\n use crate::{\n capsules::CapsuleArray,\n oracle::random::random,\n protocol::{address::AztecAddress, traits::FromField},\n test::helpers::test_environment::TestEnvironment,\n };\n use super::{\n MAX_MSG_TTL, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL, OFFCHAIN_INBOX_SLOT, OffchainMessage, PendingOffchainMsg,\n receive, sync_inbox,\n };\n\n /// Creates an `OffchainMessage` with dummy ciphertext/recipient.\n fn make_msg(tx_hash: Option<Field>, anchor_block_timestamp: u64) -> OffchainMessage {\n OffchainMessage {\n ciphertext: BoundedVec::new(),\n recipient: AztecAddress::from_field(42),\n tx_hash,\n anchor_block_timestamp,\n }\n }\n\n /// Advances the TXE block timestamp by `offset` seconds and returns the resulting timestamp.\n unconstrained fn advance_by(env: TestEnvironment, offset: u64) -> u64 {\n env.advance_next_block_timestamp_by(offset);\n env.mine_block();\n env.last_block_timestamp()\n }\n\n #[test]\n unconstrained fn empty_inbox_returns_empty_result() {\n let env = TestEnvironment::new();\n env.utility_context(|context| {\n let result = sync_inbox(context.this_address());\n let inbox: CapsuleArray<PendingOffchainMsg> =\n CapsuleArray::at(context.this_address(), OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0);\n assert_eq(inbox.len(), 0);\n });\n }\n\n #[test]\n unconstrained fn tx_bound_msg_expires_after_max_msg_ttl() {\n let env = TestEnvironment::new();\n let anchor_ts = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::some(random()), anchor_ts));\n receive(context.this_address(), msgs);\n });\n\n // Advance past anchor_ts + MAX_MSG_TTL.\n let _now = advance_by(env, MAX_MSG_TTL + 1);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // context is None, not ready\n assert_eq(inbox.len(), 0); // expired, removed\n });\n }\n\n #[test]\n unconstrained fn tx_bound_msg_not_expired_before_max_msg_ttl() {\n let env = TestEnvironment::new();\n let anchor_ts = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::some(random()), anchor_ts));\n receive(context.this_address(), msgs);\n });\n\n // Advance, but not past anchor_ts + MAX_MSG_TTL.\n let _now = advance_by(env, 100);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // context is None, not ready\n assert_eq(inbox.len(), 1); // not expired, stays\n });\n }\n\n #[test]\n unconstrained fn tx_less_msg_expires_after_max_msg_ttl() {\n let env = TestEnvironment::new();\n let anchor_ts = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::none(), anchor_ts));\n receive(context.this_address(), msgs);\n });\n\n // Advance past anchor_ts + MAX_MSG_TTL.\n let _now = advance_by(env, MAX_MSG_TTL + 1);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // context is None, not ready\n assert_eq(inbox.len(), 0); // expired, removed\n });\n }\n\n #[test]\n unconstrained fn unresolved_tx_stays_in_inbox() {\n let env = TestEnvironment::new();\n let anchor_ts = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::some(random()), anchor_ts));\n receive(context.this_address(), msgs);\n });\n\n let _now = advance_by(env, 100);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // not resolved, not ready\n assert_eq(inbox.len(), 1); // not expired, stays\n });\n }\n\n #[test]\n unconstrained fn multiple_messages_mixed_expiration() {\n let env = TestEnvironment::new();\n let anchor_ts = advance_by(env, 10);\n\n let survivor_tx_hash = random();\n\n env.utility_context(|context| {\n let address = context.this_address();\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n // Message 0: tx-bound, anchor_ts in the past so it expires at\n // anchor_ts + MAX_MSG_TTL. We set anchor to 0 so it expires quickly.\n msgs.push(make_msg(Option::some(random()), 0));\n // Message 1: tx-bound, anchor_ts is recent so it survives.\n msgs.push(make_msg(Option::some(survivor_tx_hash), anchor_ts));\n // Message 2: tx-less, anchor_ts=0 so it also expires.\n msgs.push(make_msg(Option::none(), 0));\n receive(address, msgs);\n });\n\n // Advance past MAX_MSG_TTL for anchor_ts=0, but not for anchor_ts=anchor_ts.\n let _now = advance_by(env, MAX_MSG_TTL);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n assert_eq(result.len(), 0); // all contexts are None\n // Message 0 expired (anchor=0), message 1 survived (anchor=anchor_ts),\n // message 2 expired (anchor=0).\n assert_eq(inbox.len(), 1);\n assert_eq(inbox.get(0).tx_hash, survivor_tx_hash);\n });\n }\n\n // -- Resolved context (ready to process) ------------------------------\n\n #[test]\n unconstrained fn resolved_msg_is_ready_to_process() {\n let env = TestEnvironment::new();\n // TestEnvironment::new() deploys protocol contracts, creating blocks with tx effects.\n // In TXE, tx hashes equal Fr(blockNumber), so Fr(1) is the tx effect from block 1.\n // We use this as a \"known resolvable\" tx hash.\n let known_tx_hash: Field = 1;\n let anchor_ts = advance_by(env, 10);\n\n env.utility_context(|context| {\n let mut msgs: BoundedVec<OffchainMessage, MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL> = BoundedVec::new();\n msgs.push(make_msg(Option::some(known_tx_hash), anchor_ts));\n receive(context.this_address(), msgs);\n });\n\n let _now = advance_by(env, 100);\n\n env.utility_context(|context| {\n let address = context.this_address();\n let result = sync_inbox(address);\n let inbox: CapsuleArray<PendingOffchainMsg> = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT);\n\n // The message should be ready to process since its tx context was resolved.\n assert_eq(result.len(), 1);\n\n let ctx = result.get(0).message_context;\n assert_eq(ctx.tx_hash, known_tx_hash);\n assert_eq(ctx.recipient, AztecAddress::from_field(42));\n assert(ctx.first_nullifier_in_tx != 0, \"resolved context must have a first nullifier\");\n\n // Message stays in inbox (not expired) for potential reorg reprocessing.\n assert_eq(inbox.len(), 1);\n });\n }\n}\n"
4559
4559
  },
4560
4560
  "16": {
4561
4561
  "path": "std/field/bn254.nr",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@aztec/accounts",
3
3
  "homepage": "https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/accounts",
4
4
  "description": "Implementation of sample account contracts for Aztec Network",
5
- "version": "0.0.1-commit.b33fc05d0",
5
+ "version": "0.0.1-commit.b64cb54f6",
6
6
  "type": "module",
7
7
  "exports": {
8
8
  "./defaults": "./dest/defaults/index.js",
@@ -79,11 +79,11 @@
79
79
  ]
80
80
  },
81
81
  "dependencies": {
82
- "@aztec/aztec.js": "0.0.1-commit.b33fc05d0",
83
- "@aztec/entrypoints": "0.0.1-commit.b33fc05d0",
84
- "@aztec/ethereum": "0.0.1-commit.b33fc05d0",
85
- "@aztec/foundation": "0.0.1-commit.b33fc05d0",
86
- "@aztec/stdlib": "0.0.1-commit.b33fc05d0",
82
+ "@aztec/aztec.js": "0.0.1-commit.b64cb54f6",
83
+ "@aztec/entrypoints": "0.0.1-commit.b64cb54f6",
84
+ "@aztec/ethereum": "0.0.1-commit.b64cb54f6",
85
+ "@aztec/foundation": "0.0.1-commit.b64cb54f6",
86
+ "@aztec/stdlib": "0.0.1-commit.b64cb54f6",
87
87
  "tslib": "^2.4.0"
88
88
  },
89
89
  "devDependencies": {