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
34 changes: 34 additions & 0 deletions docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,40 @@ Aztec is in active development. Each version may introduce breaking changes that

## TBD

### [Aztec.nr] `attempt_note_discovery` is no longer exposed; use `process_private_note_msg`

`attempt_note_discovery` is now crate-private. Custom message handlers (implementations of `CustomMessageHandler`) that previously called it directly should call `process_private_note_msg` instead, which runs the standard private note message decoding and discovery pipeline.

`process_private_note_msg` takes the raw `msg_metadata` and `msg_content` rather than already-decoded note fields, so it handles decoding (and silently discards undecodable messages) on your behalf:

```diff
- attempt_note_discovery(
- contract_address,
- tx_hash,
- unique_note_hashes_in_tx,
- first_nullifier_in_tx,
- compute_note_hash,
- compute_note_nullifier,
- owner,
- storage_slot,
- randomness,
- note_type_id,
- packed_note,
- );
+ process_private_note_msg(
+ contract_address,
+ tx_hash,
+ unique_note_hashes_in_tx,
+ first_nullifier_in_tx,
+ compute_note_hash,
+ compute_note_nullifier,
+ msg_metadata,
+ msg_content,
+ );
```

**Impact**: Custom message handlers that reused the standard note message processing pipeline must switch to `process_private_note_msg`. Contracts using only built-in private note handling are unaffected.

### [Aztec.nr] TXE `call_public_incognito` no longer takes a `from` parameter

`TestEnvironment::call_public_incognito` previously accepted a `from` address that was silently ignored (the function always uses a null `msg_sender`). The `from` parameter has been removed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,12 @@ comptime fn generate_contract_library_method_compute_note_hash() -> Quoted {
let if_note_type_id_match_statements = if_note_type_id_match_statements_list.join(quote {});

quote {
/// Unpacks an array into a note corresponding to `note_type_id` and then computes its note hash (non-siloed).
/// Unpacks an array into a note corresponding to `note_type_id` and then computes its note hash
/// (non-siloed).
///
/// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteHash` type, and so it can be used to call functions from that module such as `do_sync_state` and `attempt_note_discovery`.
/// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteHash` type,
/// and so it can be used to call functions from that module such as `do_sync_state` and
/// `process_private_note_msg`.
///
/// This function is automatically injected by the `#[aztec]` macro.
#[contract_library_method]
Expand Down Expand Up @@ -240,7 +243,9 @@ comptime fn generate_contract_library_method_compute_note_nullifier() -> Quoted
quote {
/// Computes a note's inner nullifier (non-siloed) given its unique note hash, preimage and extra data.
///
/// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteNullifier` type, and so it can be used to call functions from that module such as `do_sync_state` and `attempt_note_discovery`.
/// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteNullifier`
/// type, and so it can be used to call functions from that module such as `do_sync_state` and
/// `process_private_note_msg`.
///
/// This function is automatically injected by the `#[aztec]` macro.
#[contract_library_method]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,26 @@ use crate::{
protocol::{address::AztecAddress, constants::MAX_NOTE_HASHES_PER_TX, traits::ToField},
};

pub(crate) unconstrained fn process_private_note_msg(
/// Processes a private note message, attempting to discover and enqueue any notes it contains.
///
/// For each note recovered from the message whose computed note hash matches a unique note hash in the transaction,
/// a [`NoteValidationRequest`](crate::messages::processing::NoteValidationRequest) is pushed onto the ephemeral array
/// at `NOTE_VALIDATION_REQUESTS_ARRAY_BASE_SLOT` via
/// [`enqueue_note_for_validation`](crate::messages::processing::enqueue_note_for_validation). PXE later drains this
/// array during `validate_and_store_enqueued_notes_and_events`, after which the notes are retrievable via `get_notes`.
///
/// Messages that fail to decode, or whose computed note hash matches nothing in the transaction, are discarded
/// (with a debug or warning log respectively) and produce no validation requests. Decode failures are not treated
/// as errors since messages may originate from malicious senders and we don't want them to be able to brick message
/// processing.
///
/// ## Use Cases
///
/// This function is invoked automatically by aztec-nr when handling messages with the built-in private note type id,
/// so contracts do not normally need to call it directly. It is exposed for use by custom message handlers (see
/// [`CustomMessageHandler`](crate::messages::discovery::CustomMessageHandler)) that might want to use the standard
/// note message processing pipeline while extending it with custom logic.
pub unconstrained fn process_private_note_msg(
contract_address: AztecAddress,
tx_hash: Field,
unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
Expand Down Expand Up @@ -48,7 +67,7 @@ pub(crate) unconstrained fn process_private_note_msg(

/// Attempts discovery of a note given information about its contents and the transaction in which it is suspected the
/// note was created.
pub unconstrained fn attempt_note_discovery(
unconstrained fn attempt_note_discovery(
contract_address: AztecAddress,
tx_hash: Field,
unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
Expand Down
31 changes: 27 additions & 4 deletions noir-projects/aztec-nr/aztec/src/messages/logs/note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ pub(crate) global PRIVATE_NOTE_MSG_PLAINTEXT_RANDOMNESS_INDEX: u32 = 2;
/// encryption overhead and extra fields in the message (e.g. message type id, storage slot, randomness, etc.).
pub global MAX_NOTE_PACKED_LEN: u32 = MAX_MESSAGE_CONTENT_LEN - PRIVATE_NOTE_MSG_PLAINTEXT_RESERVED_FIELDS_LEN;

/// Creates the plaintext for a private note message (i.e. one of type [`PRIVATE_NOTE_MSG_TYPE_ID`]).
/// Creates the plaintext for a private note message with the given `msg_type_id`.
///
/// This plaintext is meant to be decoded via [`decode_private_note_message`].
pub fn encode_private_note_message<Note>(
/// Shared encoder used by [`encode_private_note_message`] and by custom message handlers that use the same format as
/// standard private note message but perform additional validation logic.
pub fn encode_private_note_message_with_msg_type_id<Note>(
msg_type_id: u64,
note: Note,
owner: AztecAddress,
storage_slot: Field,
Expand All @@ -49,7 +51,28 @@ where
}

// Notes use the note type id for metadata
encode_message(PRIVATE_NOTE_MSG_TYPE_ID, Note::get_id() as u64, msg_content)
encode_message(msg_type_id, Note::get_id() as u64, msg_content)
}

/// Creates the plaintext for a private note message (i.e. one of type [`PRIVATE_NOTE_MSG_TYPE_ID`]).
///
/// This plaintext is meant to be decoded via [`decode_private_note_message`].
pub fn encode_private_note_message<Note>(
note: Note,
owner: AztecAddress,
storage_slot: Field,
randomness: Field,
) -> [Field; PRIVATE_NOTE_MSG_PLAINTEXT_RESERVED_FIELDS_LEN + <Note as Packable>::N + MESSAGE_EXPANDED_METADATA_LEN]
where
Note: NoteType + Packable,
{
encode_private_note_message_with_msg_type_id(
PRIVATE_NOTE_MSG_TYPE_ID,
note,
owner,
storage_slot,
randomness,
)
}

/// Decodes the plaintext from a private note message (i.e. one of type [`PRIVATE_NOTE_MSG_TYPE_ID`]).
Expand Down
9 changes: 4 additions & 5 deletions noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ pub mod offchain;
mod message_context;
pub use message_context::MessageContext;

pub(crate) mod note_validation_request;
mod note_validation_request;
pub use note_validation_request::NoteValidationRequest;

pub(crate) mod log_retrieval_request;
pub(crate) mod log_retrieval_response;
pub(crate) mod pending_tagged_log;
Expand All @@ -17,10 +19,7 @@ use crate::{
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,
note_validation_request::NoteValidationRequest,
},
processing::{log_retrieval_request::LogRetrievalRequest, log_retrieval_response::LogRetrievalResponse},
},
oracle::message_processing,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::messages::logs::note::MAX_NOTE_PACKED_LEN;
use crate::protocol::{address::AztecAddress, traits::Serialize};
use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};

/// Intermediate struct used to perform batch note validation by PXE. The
/// `aztec_utl_validateAndStoreEnqueuedNotesAndEvents` oracle expects for values of this type to be stored in a
/// `EphemeralArray`.
#[derive(Serialize)]
pub(crate) struct NoteValidationRequest {
#[derive(Serialize, Deserialize)]
pub struct NoteValidationRequest {
pub contract_address: AztecAddress,
pub owner: AztecAddress,
pub storage_slot: Field,
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/note/confirmed_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct ConfirmedNote<Note> {
/// The note hash used to prove existence.
///
/// Whether this note hash is unsiloed or unique depends on the note's metadata.
pub(crate) proven_note_hash: Field,
pub proven_note_hash: Field,
}

impl<Note> ConfirmedNote<Note> {
Expand Down
21 changes: 15 additions & 6 deletions noir-projects/aztec-nr/aztec/src/note/lifecycle.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@ use crate::{
use crate::protocol::{address::AztecAddress, traits::Packable};

/// A note that was created in the current contract call.
///
/// This struct holds a freshly created note along with the side-effect counter that the kernel uses to order note
/// creations within a transaction. It is produced by [`create_note`] and is typically wrapped in a
/// [`NoteMessage`](crate::note::NoteMessage), which is responsible for delivering the note's information to its
/// recipient so that it is not lost.
///
/// Unlike [`ConfirmedNote`](crate::note::ConfirmedNote), which represents a note whose existence has been proven
/// (either by reading it from PXE or by checking historical state), a `NewNote` represents a note whose creation is
/// still pending in the current transaction's side-effect stream. Its note hash has been pushed into the
/// [`PrivateContext`] but has not yet been siloed nor inserted into the note hash tree.
pub struct NewNote<Note> {
pub(crate) note: Note,
pub(crate) owner: AztecAddress,
pub(crate) storage_slot: Field,
pub(crate) randomness: Field,
/// The [`PrivateContext`] side-effect counter associated with the creation of this note.
pub(crate) note_hash_counter: u32,
pub note: Note,
pub owner: AztecAddress,
pub storage_slot: Field,
pub randomness: Field,
pub note_hash_counter: u32,
}

impl<Note> NewNote<Note> {
Expand Down
102 changes: 100 additions & 2 deletions noir-projects/aztec-nr/aztec/src/oracle/get_membership_witness.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ unconstrained fn get_note_hash_membership_witness_oracle(
note_hash: Field,
) -> MembershipWitness<NOTE_HASH_TREE_HEIGHT> {}

#[oracle(aztec_utl_getBlockHashMembershipWitness)]
#[oracle(aztec_utl_getBlockHashMembershipWitnessV2)]
unconstrained fn get_block_hash_membership_witness_oracle(
anchor_block_hash: Field,
block_hash: Field,
) -> MembershipWitness<ARCHIVE_HEIGHT> {}
) -> Option<MembershipWitness<ARCHIVE_HEIGHT>> {}

// Note: get_nullifier_membership_witness function is implemented in get_nullifier_membership_witness.nr

/// Returns a membership witness for a `note_hash` in the note hash tree whose root is defined in
// `anchor_block_header`.
// TODO(https://linear.app/aztec-labs/issue/F-652): add Noir tests for this oracle
pub unconstrained fn get_note_hash_membership_witness(
anchor_block_header: BlockHeader,
note_hash: Field,
Expand All @@ -37,6 +38,103 @@ pub unconstrained fn get_block_hash_membership_witness(
anchor_block_header: BlockHeader,
block_hash: Field,
) -> MembershipWitness<ARCHIVE_HEIGHT> {
let anchor_block_hash = anchor_block_header.hash();
get_block_hash_membership_witness_oracle(anchor_block_hash, block_hash).expect(
f"Block hash {block_hash} not found in the archive tree at anchor block {anchor_block_hash}.",
)
}

/// Same as [`get_block_hash_membership_witness`], but returns `None` instead of erroring when the block is not present
/// in the archive tree. Intended for callers that need to handle absence (e.g. existence checks via `.is_some()`).
pub unconstrained fn get_maybe_block_hash_membership_witness(
anchor_block_header: BlockHeader,
block_hash: Field,
) -> Option<MembershipWitness<ARCHIVE_HEIGHT>> {
let anchor_block_hash = anchor_block_header.hash();
get_block_hash_membership_witness_oracle(anchor_block_hash, block_hash)
}

mod test {
use crate::oracle::block_header::get_block_header_at;
use crate::protocol::{merkle_tree::root::root_from_sibling_path, traits::Hash};
use crate::test::helpers::test_environment::TestEnvironment;
use super::{get_block_hash_membership_witness, get_maybe_block_hash_membership_witness};

#[test]
unconstrained fn get_block_hash_membership_witness_returns_valid_witness_for_known_block() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();
env.mine_block();
env.mine_block();

env.private_context(|context| {
let anchor = context.anchor_block_header;
let target_block_number = anchor.block_number() - 2;

let target_header = get_block_header_at(target_block_number, *context);
let target_hash = target_header.hash();

let witness = get_block_hash_membership_witness(anchor, target_hash);

assert_eq(
root_from_sibling_path(target_hash, witness.leaf_index, witness.sibling_path),
anchor.last_archive.root,
);
});
}

#[test(should_fail_with = "not found in the archive tree at anchor block")]
unconstrained fn get_block_hash_membership_witness_panics_for_unknown_block() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();

env.private_context(|context| {
let anchor = context.anchor_block_header;
let _witness = get_block_hash_membership_witness(anchor, 0xdeadbeef);
});
}

#[test]
unconstrained fn get_maybe_block_hash_membership_witness_returns_some_for_known_block() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();
env.mine_block();
env.mine_block();

env.private_context(|context| {
let anchor = context.anchor_block_header;
let target_block_number = anchor.block_number() - 2;

let target_header = get_block_header_at(target_block_number, *context);
let target_hash = target_header.hash();

let witness = get_maybe_block_hash_membership_witness(anchor, target_hash).expect(
f"Expected Some witness for known block hash",
);

assert_eq(
root_from_sibling_path(target_hash, witness.leaf_index, witness.sibling_path),
anchor.last_archive.root,
);
});
}

#[test]
unconstrained fn get_maybe_block_hash_membership_witness_returns_none_for_unknown_block() {
let env = TestEnvironment::new();

env.mine_block();
env.mine_block();

env.private_context(|context| {
let anchor = context.anchor_block_header;
assert(get_maybe_block_hash_membership_witness(anchor, 0xdeadbeef).is_none());
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::messages::processing::{
pending_tagged_log::PendingTaggedLog,
};
use crate::protocol::address::AztecAddress;
use crate::protocol::blob_data::TxEffect;

/// Finds new private logs that may have been sent to all registered accounts in PXE in the current contract and
/// returns them in an ephemeral array with an oracle-allocated base slot.
Expand Down Expand Up @@ -62,3 +63,11 @@ pub(crate) unconstrained fn get_message_contexts_by_tx_hash(

#[oracle(aztec_utl_getMessageContextsByTxHash_v2)]
unconstrained fn get_message_contexts_by_tx_hash_v2_oracle(request_array_slot: Field) -> Field {}

/// Fetches all effects of a settled transaction by its hash.
pub unconstrained fn get_tx_effect(tx_hash: Field) -> Option<TxEffect> {
get_tx_effect_oracle(tx_hash)
}

#[oracle(aztec_utl_getTxEffect)]
unconstrained fn get_tx_effect_oracle(tx_hash: Field) -> Option<TxEffect> {}
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/oracle/version.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/// immediately if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency
/// without actually using any of the new oracles then there is no reason to throw.
pub global ORACLE_VERSION_MAJOR: Field = 22;
pub global ORACLE_VERSION_MINOR: Field = 2;
pub global ORACLE_VERSION_MINOR: Field = 3;

/// Asserts that the version of the oracle is compatible with the version expected by the contract.
pub fn assert_compatible_oracle_version() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ error: InvalidNote has a packed length of 9 fields, which exceeds the maximum al
6: process_message_plaintext
at <repo>/noir-projects/aztec-nr/aztec/src/messages/discovery/process_message.nr:76:13
7: process_private_note_msg
at <repo>/noir-projects/aztec-nr/aztec/src/messages/discovery/private_notes.nr:27:9
at <repo>/noir-projects/aztec-nr/aztec/src/messages/discovery/private_notes.nr:46:9
8: attempt_note_discovery
at <repo>/noir-projects/aztec-nr/aztec/src/messages/discovery/private_notes.nr:64:28
at <repo>/noir-projects/aztec-nr/aztec/src/messages/discovery/private_notes.nr:83:28
9: attempt_note_nonce_discovery
at <repo>/noir-projects/aztec-nr/aztec/src/messages/discovery/nonce_discovery.nr:46:27
10: generate_contract_library_method_compute_note_hash
Expand Down
Loading
Loading