Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
dc63a8a
merge with merge-train
mverzilli Feb 18, 2026
3d45e98
simplify SimulationReturn
mverzilli Feb 17, 2026
7796706
add migration notes
mverzilli Feb 17, 2026
6313c8b
add offchainEffects usage after send() e2e
mverzilli Feb 17, 2026
8d1c752
fix playground
mverzilli Feb 17, 2026
6accc0c
fix boxes
mverzilli Feb 17, 2026
03192e5
fix aztec.js for boxes
mverzilli Feb 17, 2026
3efe757
sort out types
mverzilli Feb 17, 2026
57af637
fix authwit types
mverzilli Feb 17, 2026
3a2718f
fix e2e
mverzilli Feb 17, 2026
2cb9c2c
fix more e2e
mverzilli Feb 17, 2026
660ee5e
fix e2e tests
mverzilli Feb 17, 2026
70aaf1a
update docs examples
mverzilli Feb 17, 2026
0f78a1e
fix more e2e tests
mverzilli Feb 18, 2026
71c5d25
merge
mverzilli Feb 18, 2026
6b58772
another e2e fix
mverzilli Feb 18, 2026
fe599bc
fix e2es after rebase
mverzilli Feb 18, 2026
9f368e0
merge
mverzilli Feb 24, 2026
37bca03
bring migration notes up to date
mverzilli Feb 24, 2026
db86e05
Merge branch 'merge-train/fairies' into mv/expose-offchain-messages
mverzilli Feb 24, 2026
0a7c6cb
fix lint issue
mverzilli Feb 24, 2026
6802923
fix new e2es
mverzilli Feb 24, 2026
1303d5f
trivial versions of offchain enqueue and inbox
mverzilli Feb 25, 2026
0598298
resolve message context via oracles
mverzilli Feb 25, 2026
1196587
customizable offchain discovery
mverzilli Feb 26, 2026
90a32a2
refactor to use capsule arrays
mverzilli Feb 26, 2026
179f59d
handle reorgs
mverzilli Feb 26, 2026
007a57a
clean up reorg test
mverzilli Feb 26, 2026
df8d9b0
make offchain reception a builtin feature
mverzilli Feb 26, 2026
ae40fd1
move DiscoverOffchainMessages to a more reasonable location
mverzilli Feb 27, 2026
d6e55cf
document discover_offchain_messages
mverzilli Feb 27, 2026
c996539
rename to offchain_message_handler
mverzilli Feb 27, 2026
cec2345
hook -> handler
mverzilli Feb 27, 2026
dfef1bd
enqueue -> receive
mverzilli Feb 27, 2026
fd71740
nicer doc for offchain_receive
mverzilli Feb 27, 2026
d0e7098
document OffchainMessageHandler
mverzilli Feb 27, 2026
0f0b27c
offchain_inbox -> offchain
mverzilli Feb 27, 2026
e41e847
offchain_sync_inbox -> default_handler
mverzilli Feb 27, 2026
642a023
receive_offchain_message -> receive
mverzilli Feb 27, 2026
485854d
better docs
mverzilli Feb 27, 2026
49bf4cf
more docs, and visibility restriction
mverzilli Feb 27, 2026
f40df63
more cleanup of offchain inbox handler
mverzilli Mar 2, 2026
e116475
cleanup
mverzilli Mar 2, 2026
db98953
doc comments on MessageTxContext
mverzilli Mar 2, 2026
eca5012
move MessageTxContext
mverzilli Mar 2, 2026
ab48b79
rename MessageContextResponse on TS side
mverzilli Mar 2, 2026
e92d6f7
add tx context serialization tests on ts side
mverzilli Mar 2, 2026
36facfa
consider anchor block
mverzilli Mar 2, 2026
8882923
extract MessageContextService
mverzilli Mar 2, 2026
463709e
tests for oracle
mverzilli Mar 2, 2026
e9e9457
add MessageContextService unit tests
mverzilli Mar 2, 2026
2e18798
rename
mverzilli Mar 2, 2026
f71c4ab
fix private oracle tests
mverzilli Mar 2, 2026
b38b65d
merge with merge-train
mverzilli Feb 18, 2026
06e28d0
simplify SimulationReturn
mverzilli Feb 17, 2026
67809c9
add migration notes
mverzilli Feb 17, 2026
71e3d6a
add offchainEffects usage after send() e2e
mverzilli Feb 17, 2026
9e48615
fix playground
mverzilli Feb 17, 2026
28a9838
fix boxes
mverzilli Feb 17, 2026
bb3fc26
fix aztec.js for boxes
mverzilli Feb 17, 2026
e08d59d
sort out types
mverzilli Feb 17, 2026
c742fd8
fix authwit types
mverzilli Feb 17, 2026
6fc1f3a
fix e2e
mverzilli Feb 17, 2026
2cf5d3a
fix more e2e
mverzilli Feb 17, 2026
1c42899
fix e2e tests
mverzilli Feb 17, 2026
7aadeeb
update docs examples
mverzilli Feb 17, 2026
9d9dd3e
fix more e2e tests
mverzilli Feb 18, 2026
64321b2
merge
mverzilli Feb 18, 2026
70754f0
another e2e fix
mverzilli Feb 18, 2026
cc8685e
fix e2es after rebase
mverzilli Feb 18, 2026
1e855a5
bring migration notes up to date
mverzilli Feb 24, 2026
59f75e4
fix lint issue
mverzilli Feb 24, 2026
3711ec8
fix new e2es
mverzilli Feb 24, 2026
bb6d848
separate offchainMessages from offchainEffects
mverzilli Mar 3, 2026
e24c4db
migration_notes
mverzilli Mar 3, 2026
71f0bac
fix bootstrap
mverzilli Mar 3, 2026
2aa9f7b
update migration notes
mverzilli Mar 3, 2026
9614920
fix e2e
mverzilli Mar 3, 2026
2532396
SimulationReturn -> SimulationResult
mverzilli Mar 3, 2026
548851c
update wallet schema
mverzilli Mar 3, 2026
63bea95
fix batch_call test
mverzilli Mar 3, 2026
566b695
fix units
mverzilli Mar 3, 2026
d3bdfde
add todo
mverzilli Mar 3, 2026
0ede901
Merge branch 'mv/expose-offchain-messages' into martin/poc-offchain-r…
mverzilli Mar 3, 2026
b3d02cc
fix imports
mverzilli Mar 3, 2026
efe87d0
use new apis in e2e
mverzilli Mar 3, 2026
2fbd2dc
fix reorg test
mverzilli Mar 3, 2026
3d806c0
more explanatary comments on inbox loop
mverzilli Mar 3, 2026
f98a26a
wip: receive many msgs at once
mverzilli Mar 9, 2026
f6bc97d
merge from fairies
mverzilli Mar 9, 2026
c0ff5a0
receive many offchain messages on a single call
mverzilli Mar 9, 2026
85491ae
add message ttl data
mverzilli Mar 9, 2026
6c22f22
accept expiration
mverzilli Mar 9, 2026
84e3f6b
remove extension point
mverzilli Mar 9, 2026
accae75
reject contracts with an offchain_receive fn
mverzilli Mar 9, 2026
3cb806f
move offchain stuff from delivery to processing
mverzilli Mar 9, 2026
62de4b3
reduce visibility of mods
mverzilli Mar 9, 2026
3458acf
move more stuff to offchain mod
mverzilli Mar 9, 2026
84d5b66
remove accidental comment
mverzilli Mar 9, 2026
e639cb9
OffchainMessageHandler -> OffchainInboxSync
mverzilli Mar 9, 2026
93f1dda
offchain::default_handler -> offchain::sync_inbox
mverzilli Mar 9, 2026
2bca4e1
reduce visibility of OffchainInboxSync
mverzilli Mar 9, 2026
6e401de
better comments
mverzilli Mar 9, 2026
0844a69
wat auto-formatter
mverzilli Mar 9, 2026
ea21ed1
nit
mverzilli Mar 9, 2026
ac69d74
bound by tx ttl
mverzilli Mar 9, 2026
0685eb3
fix bug in loop and better comments
mverzilli Mar 9, 2026
c743282
bump tolerance to 2 hours
mverzilli Mar 9, 2026
4f6eaa9
mark MessageTxContext as Serialize
mverzilli Mar 9, 2026
04120d7
better comments for MessageTxContext
mverzilli Mar 9, 2026
2ab7299
nit
mverzilli Mar 9, 2026
2ba8b7e
extract test aux for reorg
mverzilli Mar 10, 2026
046bbfe
make it a bit more robust
mverzilli Mar 10, 2026
e4336c5
throw on malformed tx effects
mverzilli Mar 10, 2026
ac0a5c4
remove unnecessary oracle version bump
mverzilli Mar 10, 2026
11303bd
lint disable camel case warnings
mverzilli Mar 10, 2026
15fe102
add tests for offchain sync
mverzilli Mar 10, 2026
36a5701
merge from fairies
mverzilli Mar 10, 2026
3c60911
bump oracle version
mverzilli Mar 10, 2026
54225cb
cleanup test cases
mverzilli Mar 10, 2026
81de2db
fix message context tests
mverzilli Mar 10, 2026
e51d360
add test case for resolved message
mverzilli Mar 10, 2026
f368122
code review comments
mverzilli Mar 11, 2026
4de1d5f
Merge branch 'merge-train/fairies' into martin/poc-offchain-reception
mverzilli Mar 11, 2026
52de33f
update oracle version hash
mverzilli Mar 11, 2026
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
4 changes: 4 additions & 0 deletions docs/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -796,3 +796,7 @@
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"
54 changes: 51 additions & 3 deletions noir-projects/aztec-nr/aztec/src/macros/aztec.nr
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl AztecConfig {
/// This enables contracts to process non-standard messages (i.e. any with a message type that is not in
/// [`crate::messages::msg_type`]).
///
/// `handler` must be a `#[contract_library_method]` function that conforms to the
/// `handler` must be a function that conforms to the
/// [`crate::messages::discovery::CustomMessageHandler`] type signature.
pub comptime fn custom_message_handler(_self: Self, handler: CustomMessageHandler<()>) -> Self {
Self { custom_message_handler: Option::some(handler) }
Expand Down Expand Up @@ -115,12 +115,23 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted {
quote { Option::<aztec::messages::discovery::CustomMessageHandler<()>>::none() }
};

let offchain_inbox_sync_option = quote {
Option::some(aztec::messages::processing::offchain::sync_inbox)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So none would be disabling this feature, no?

Copy link
Contributor Author

@mverzilli mverzilli Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, maybe I grew too attached to the first implementation that used extensions

};

let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) {
generate_sync_state(process_custom_message_option)
generate_sync_state(process_custom_message_option, offchain_inbox_sync_option)
} else {
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(process_custom_message_option)
} else {
Expand All @@ -138,6 +149,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> 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 @@ -337,7 +349,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(process_custom_message_option: Quoted) -> Quoted {
comptime fn generate_sync_state(process_custom_message_option: Quoted, offchain_inbox_sync_option: Quoted) -> Quoted {
quote {
pub struct sync_state_parameters {}

Expand All @@ -353,6 +365,7 @@ comptime fn generate_sync_state(process_custom_message_option: Quoted) -> Quoted
address,
_compute_note_hash_and_nullifier,
$process_custom_message_option,
$offchain_inbox_sync_option,
);
}
}
Expand Down Expand Up @@ -393,6 +406,41 @@ comptime fn generate_process_message(process_custom_message_option: Quoted) -> Q
}
}

/// 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
22 changes: 20 additions & 2 deletions noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ pub mod private_notes;
pub mod process_message;

use crate::{
capsules::CapsuleArray,
messages::{
discovery::process_message::process_message_ciphertext,
encoding::MAX_MESSAGE_CONTENT_LEN,
logs::note::MAX_NOTE_PACKED_LEN,
processing::{
get_private_logs, MessageContext, pending_tagged_log::PendingTaggedLog,
validate_and_store_enqueued_notes_and_events,
get_private_logs, MessageContext, offchain::OffchainInboxSync, OffchainMessageWithContext,
pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events,
},
},
utils::array,
Expand Down Expand Up @@ -107,6 +108,7 @@ pub unconstrained fn do_sync_state<ComputeNoteHashAndNullifierEnv, CustomMessage
contract_address: AztecAddress,
compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<ComputeNoteHashAndNullifierEnv>,
process_custom_message: Option<CustomMessageHandler<CustomMessageHandlerEnv>>,
offchain_inbox_sync: Option<OffchainInboxSync<()>>,
) {
aztecnr_debug_log!("Performing state synchronization");

Expand Down Expand Up @@ -134,6 +136,22 @@ pub unconstrained fn do_sync_state<ComputeNoteHashAndNullifierEnv, CustomMessage
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,
process_custom_message,
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 @@ -159,6 +169,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