Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions docs/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,37 @@
# Add new error codes sequentially (1, 2, 3, ...).
# =============================================================================

# Example (uncomment and modify when adding error codes):
# [[redirects]]
# from = "/errors/1"
# to = "/developers/docs/aztec-nr/framework-description/functions/how_to_define_functions"
[[redirects]]
from = "/errors/1"
# A warning that is shown when `aztec compile` is run and the contract crate contains tests
to = "/developers/docs/aztec-nr/testing_contracts#keep-tests-in-the-test-crate"

[[redirects]]
# Aztec-nr: custom message was received but no handler was configured
from = "/errors/2"
to = "/aztec-nr-api/nightly/noir_aztec/macros/struct.AztecConfig.html"

[[redirects]]
# Aztec-nr: message received with message type ID in the range reserved for future aztec.nr built-in message types
from = "/errors/3"
to = "/aztec-nr-api/nightly/noir_aztec/messages/msg_type/index.html"

[[redirects]]
# Aztec-nr: note packed length exceeds MAX_NOTE_PACKED_LEN
from = "/errors/4"
to = "/aztec-nr-api/nightly/noir_aztec/macros/notes/fn.note.html"

[[redirects]]
# Aztec-nr: event serialized length exceeds MAX_EVENT_SERIALIZED_LEN
from = "/errors/5"
to = "/aztec-nr-api/nightly/noir_aztec/macros/events/fn.event.html"

[[redirects]]
# Aztec-nr: direct invocation of contract functions is not supported
from = "/errors/6"
to = "/developers/docs/aztec-nr/framework-description/calling_contracts"

[[redirects]]
# Aztec-nr: user-defined 'offchain_receive' is not allowed
from = "/errors/7"
to = "/aztec-nr-api/nightly/noir_aztec/messages/processing/offchain/fn.receive.html"
51 changes: 49 additions & 2 deletions noir-projects/aztec-nr/aztec/src/macros/aztec.nr
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ pub comptime fn aztec(m: Module) -> Quoted {
quote {}
};

if m.functions().any(|f| f.name() == quote { offchain_receive }) {
panic(
"User-defined 'offchain_receive' is not allowed. The function is auto-injected by the #[aztec] macro. See https://docs.aztec.network/errors/7",
);
}
let offchain_receive_fn_and_abi_export = generate_offchain_receive();

let process_message_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { process_message }) {
generate_process_message()
} else {
Expand All @@ -65,6 +72,7 @@ pub comptime fn aztec(m: Module) -> Quoted {
$public_dispatch
$sync_state_fn_and_abi_export
$process_message_fn_and_abi_export
$offchain_receive_fn_and_abi_export
}
}

Expand Down Expand Up @@ -263,6 +271,7 @@ comptime fn generate_contract_library_method_compute_note_hash_and_nullifier() -
}
}

/// Generates the `sync_state` utility function that performs message discovery.
comptime fn generate_sync_state() -> Quoted {
quote {
pub struct sync_state_parameters {}
Expand All @@ -275,8 +284,11 @@ comptime fn generate_sync_state() -> Quoted {
#[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]
unconstrained fn sync_state() {
let address = aztec::context::UtilityContext::new().this_address();

aztec::messages::discovery::do_sync_state(address, _compute_note_hash_and_nullifier);
aztec::messages::discovery::do_sync_state(
address,
_compute_note_hash_and_nullifier,
Option::some(aztec::messages::processing::offchain::sync_inbox),
);
}
}
}
Expand Down Expand Up @@ -314,6 +326,41 @@ comptime fn generate_process_message() -> Quoted {
}
}

/// Generates an `offchain_receive` utility function that lets callers add messages to the offchain message inbox.
///
/// For more details, see `aztec::messages::processing::offchain::receive`.
comptime fn generate_offchain_receive() -> Quoted {
quote {
pub struct offchain_receive_parameters {
pub messages: BoundedVec<
aztec::messages::processing::offchain::OffchainMessage,
aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,
>,
}

#[abi(functions)]
pub struct offchain_receive_abi {
parameters: offchain_receive_parameters,
}

/// Receives offchain messages into this contract's offchain inbox for subsequent processing.
///
/// For more details, see `aztec::messages::processing::offchain::receive`.
///
/// This function is automatically injected by the `#[aztec]` macro.
#[aztec::macros::internals_functions_generation::abi_attributes::abi_utility]
unconstrained fn offchain_receive(
messages: BoundedVec<
aztec::messages::processing::offchain::OffchainMessage,
aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL,
>,
) {
let address = aztec::context::UtilityContext::new().this_address();
aztec::messages::processing::offchain::receive(address, messages);
}
}
}

/// Checks that all functions in the module have a context macro applied.
///
/// Non-macroified functions are not allowed in contracts. They must all be one of
Expand Down
20 changes: 19 additions & 1 deletion noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ pub mod private_notes;
pub mod process_message;

use crate::{
capsules::CapsuleArray,
messages::{
discovery::process_message::process_message_ciphertext,
logs::note::MAX_NOTE_PACKED_LEN,
processing::{
get_private_logs, pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events,
get_private_logs, offchain::OffchainInboxSync, OffchainMessageWithContext,
pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events,
},
},
utils::array,
Expand Down Expand Up @@ -77,6 +79,7 @@ randomness */ Field, /* note nonce */ Field) -> Option<NoteHashAndNullifier>;
pub unconstrained fn do_sync_state<Env>(
contract_address: AztecAddress,
compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<Env>,
offchain_inbox_sync: Option<OffchainInboxSync<()>>,
) {
debug_log("Performing state synchronization");

Expand All @@ -101,6 +104,21 @@ pub unconstrained fn do_sync_state<Env>(
logs.remove(i);
});

if offchain_inbox_sync.is_some() {
let msgs: CapsuleArray<OffchainMessageWithContext> = offchain_inbox_sync.unwrap()(contract_address);
msgs.for_each(|i, msg| {
process_message_ciphertext(
contract_address,
compute_note_hash_and_nullifier,
msg.message_ciphertext,
msg.message_context,
);
// The inbox sync returns _a copy_ of messages to process, so we clear them as we do so. This is a
// volatile array with the to-process message, not the actual persistent storage of them.
msgs.remove(i);
});
}

// Then we process all pending partial notes, regardless of whether they were found in the current or previous
// executions.
partial_notes::fetch_and_process_partial_note_completion_logs(contract_address, compute_note_hash_and_nullifier);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
use crate::protocol::{address::AztecAddress, constants::MAX_NOTE_HASHES_PER_TX, traits::Deserialize};
use crate::protocol::{address::AztecAddress, constants::MAX_NOTE_HASHES_PER_TX, traits::{Deserialize, Serialize}};

/// Additional information needed to process a message.
///
/// All messages exist in the context of a transaction, and information about that transaction is typically required in
/// order to perform validation, store results, etc. For example, messages containing notes require knowledge of note
/// hashes and the first nullifier in order to find the note's nonce.
#[derive(Deserialize, Eq)]
#[derive(Serialize, Deserialize, Eq)]
pub struct MessageContext {
pub tx_hash: Field,
pub unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
pub first_nullifier_in_tx: Field,
pub recipient: AztecAddress,
}

/// Transaction context needed to process a message.
///
/// Like [`MessageContext`], but `MessageTxContext` does not include the recipient. MessageTxContext's are kind of
/// adhoc: they are just the minimal data structure that the contract needs to get from a PXE oracle to prepare
/// offchain messages to be processed. We reify it with a type just because it crosses Noir<->TS boundaries.
/// The contract knows how to pair the context data with a recipient: then it is able to build a `MessageContext` for
/// subsequent processing.
#[derive(Serialize, Deserialize, Eq)]
pub(crate) struct MessageTxContext {
pub tx_hash: Field,
pub unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
pub first_nullifier_in_tx: Field,
}

mod test {
use crate::messages::processing::MessageContext;
use crate::protocol::{address::AztecAddress, traits::{Deserialize, FromField}};
Expand Down
29 changes: 28 additions & 1 deletion noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub(crate) mod event_validation_request;
pub mod offchain;

mod message_context;
pub use message_context::MessageContext;
pub(crate) use message_context::MessageTxContext;

pub(crate) mod note_validation_request;
pub(crate) mod log_retrieval_request;
Expand All @@ -13,6 +15,7 @@ use crate::{
event::EventSelector,
messages::{
discovery::partial_notes::DeliveredPendingPartialNote,
encoding::MESSAGE_CIPHERTEXT_LEN,
logs::{event::MAX_EVENT_SERIALIZED_LEN, note::MAX_NOTE_PACKED_LEN},
processing::{
log_retrieval_request::LogRetrievalRequest, log_retrieval_response::LogRetrievalResponse,
Expand All @@ -21,7 +24,7 @@ use crate::{
},
oracle,
};
use crate::protocol::{address::AztecAddress, hash::sha256_to_field};
use crate::protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}};
use event_validation_request::EventValidationRequest;

// Base slot for the pending tagged log array to which the fetch_tagged_logs oracle inserts found private logs.
Expand All @@ -44,6 +47,13 @@ global LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT: Field = sha256_to_field(
"AZTEC_NR::LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT".as_bytes(),
);

/// An offchain-delivered message with resolved context, ready for processing during sync.
#[derive(Serialize, Deserialize)]
pub struct OffchainMessageWithContext {
pub message_ciphertext: BoundedVec<Field, MESSAGE_CIPHERTEXT_LEN>,
pub message_context: MessageContext,
}

/// Searches for private logs emitted by `contract_address` that might contain messages for one of the local accounts,
/// and stores them in a `CapsuleArray` which is then returned.
pub(crate) unconstrained fn get_private_logs(contract_address: AztecAddress) -> CapsuleArray<PendingTaggedLog> {
Expand Down Expand Up @@ -150,6 +160,23 @@ pub unconstrained fn validate_and_store_enqueued_notes_and_events(contract_addre
);
}

/// Resolves message contexts for a list of tx hashes stored in a CapsuleArray.
///
/// The `message_context_requests_array_base_slot` must point to a CapsuleArray<Field> containing tx hashes.
/// PXE will store `Option<MessageContextResponse>` values into the responses array at
/// `message_context_responses_array_base_slot`.
pub unconstrained fn resolve_message_contexts(
contract_address: AztecAddress,
message_context_requests_array_base_slot: Field,
message_context_responses_array_base_slot: Field,
) {
oracle::message_processing::resolve_message_contexts(
contract_address,
message_context_requests_array_base_slot,
message_context_responses_array_base_slot,
);
}

/// Efficiently queries the node for logs that result in the completion of all `DeliveredPendingPartialNote`s stored in
/// a `CapsuleArray` by performing all node communication concurrently. Returns a second `CapsuleArray` with Options
/// for the responses that correspond to the pending partial notes at the same index.
Expand Down
Loading
Loading