Skip to content

Commit 175c684

Browse files
authored
chore: Accumulated backports to v4-next (#23182)
This PR accumulates backport commits throughout the day and will be auto-merged overnight. Latest backport: #22979 - feat: upstream oxide aztec-nr changes 🤖 This PR is managed automatically by the backport workflow.
2 parents 7c959e7 + 794dbee commit 175c684

32 files changed

Lines changed: 514 additions & 63 deletions

File tree

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,40 @@ Aztec is in active development. Each version may introduce breaking changes that
99

1010
## TBD
1111

12+
### [Aztec.nr] `attempt_note_discovery` is no longer exposed; use `process_private_note_msg`
13+
14+
`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.
15+
16+
`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:
17+
18+
```diff
19+
- attempt_note_discovery(
20+
- contract_address,
21+
- tx_hash,
22+
- unique_note_hashes_in_tx,
23+
- first_nullifier_in_tx,
24+
- compute_note_hash,
25+
- compute_note_nullifier,
26+
- owner,
27+
- storage_slot,
28+
- randomness,
29+
- note_type_id,
30+
- packed_note,
31+
- );
32+
+ process_private_note_msg(
33+
+ contract_address,
34+
+ tx_hash,
35+
+ unique_note_hashes_in_tx,
36+
+ first_nullifier_in_tx,
37+
+ compute_note_hash,
38+
+ compute_note_nullifier,
39+
+ msg_metadata,
40+
+ msg_content,
41+
+ );
42+
```
43+
44+
**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.
45+
1246
### [aztec-up] Bundled binaries are no longer exposed under bare names on `PATH`
1347

1448
The Aztec installer previously placed bundled binaries directly into `$HOME/.aztec/current/bin` under bare names (`forge`, `nargo`, `bb`, `pxe`, ...). Anything with the same name in your own `PATH` was silently shadowed in unrelated projects.

noir-projects/aztec-nr/aztec/src/macros/aztec/compute_note_hash_and_nullifier.nr

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,12 @@ comptime fn generate_contract_library_method_compute_note_hash() -> Quoted {
126126
let if_note_type_id_match_statements = if_note_type_id_match_statements_list.join(quote {});
127127

128128
quote {
129-
/// Unpacks an array into a note corresponding to `note_type_id` and then computes its note hash (non-siloed).
129+
/// Unpacks an array into a note corresponding to `note_type_id` and then computes its note hash
130+
/// (non-siloed).
130131
///
131-
/// 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`.
132+
/// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteHash` type,
133+
/// and so it can be used to call functions from that module such as `do_sync_state` and
134+
/// `process_private_note_msg`.
132135
///
133136
/// This function is automatically injected by the `#[aztec]` macro.
134137
#[contract_library_method]
@@ -240,7 +243,9 @@ comptime fn generate_contract_library_method_compute_note_nullifier() -> Quoted
240243
quote {
241244
/// Computes a note's inner nullifier (non-siloed) given its unique note hash, preimage and extra data.
242245
///
243-
/// 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`.
246+
/// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteNullifier`
247+
/// type, and so it can be used to call functions from that module such as `do_sync_state` and
248+
/// `process_private_note_msg`.
244249
///
245250
/// This function is automatically injected by the `#[aztec]` macro.
246251
#[contract_library_method]

noir-projects/aztec-nr/aztec/src/messages/discovery/private_notes.nr

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,26 @@ use crate::{
99
protocol::{address::AztecAddress, constants::MAX_NOTE_HASHES_PER_TX, traits::ToField},
1010
};
1111

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

4968
/// Attempts discovery of a note given information about its contents and the transaction in which it is suspected the
5069
/// note was created.
51-
pub unconstrained fn attempt_note_discovery(
70+
unconstrained fn attempt_note_discovery(
5271
contract_address: AztecAddress,
5372
tx_hash: Field,
5473
unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,

noir-projects/aztec-nr/aztec/src/messages/logs/note.nr

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ pub(crate) global PRIVATE_NOTE_MSG_PLAINTEXT_RANDOMNESS_INDEX: u32 = 2;
1919
/// encryption overhead and extra fields in the message (e.g. message type id, storage slot, randomness, etc.).
2020
pub global MAX_NOTE_PACKED_LEN: u32 = MAX_MESSAGE_CONTENT_LEN - PRIVATE_NOTE_MSG_PLAINTEXT_RESERVED_FIELDS_LEN;
2121

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

5153
// Notes use the note type id for metadata
52-
encode_message(PRIVATE_NOTE_MSG_TYPE_ID, Note::get_id() as u64, msg_content)
54+
encode_message(msg_type_id, Note::get_id() as u64, msg_content)
55+
}
56+
57+
/// Creates the plaintext for a private note message (i.e. one of type [`PRIVATE_NOTE_MSG_TYPE_ID`]).
58+
///
59+
/// This plaintext is meant to be decoded via [`decode_private_note_message`].
60+
pub fn encode_private_note_message<Note>(
61+
note: Note,
62+
owner: AztecAddress,
63+
storage_slot: Field,
64+
randomness: Field,
65+
) -> [Field; PRIVATE_NOTE_MSG_PLAINTEXT_RESERVED_FIELDS_LEN + <Note as Packable>::N + MESSAGE_EXPANDED_METADATA_LEN]
66+
where
67+
Note: NoteType + Packable,
68+
{
69+
encode_private_note_message_with_msg_type_id(
70+
PRIVATE_NOTE_MSG_TYPE_ID,
71+
note,
72+
owner,
73+
storage_slot,
74+
randomness,
75+
)
5376
}
5477

5578
/// Decodes the plaintext from a private note message (i.e. one of type [`PRIVATE_NOTE_MSG_TYPE_ID`]).

noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ pub mod offchain;
44
mod message_context;
55
pub use message_context::MessageContext;
66

7-
pub(crate) mod note_validation_request;
7+
mod note_validation_request;
8+
pub use note_validation_request::NoteValidationRequest;
9+
810
pub(crate) mod log_retrieval_request;
911
pub(crate) mod log_retrieval_response;
1012
pub(crate) mod pending_tagged_log;
@@ -17,10 +19,7 @@ use crate::{
1719
discovery::partial_notes::DeliveredPendingPartialNote,
1820
encoding::MESSAGE_CIPHERTEXT_LEN,
1921
logs::{event::MAX_EVENT_SERIALIZED_LEN, note::MAX_NOTE_PACKED_LEN},
20-
processing::{
21-
log_retrieval_request::LogRetrievalRequest, log_retrieval_response::LogRetrievalResponse,
22-
note_validation_request::NoteValidationRequest,
23-
},
22+
processing::{log_retrieval_request::LogRetrievalRequest, log_retrieval_response::LogRetrievalResponse},
2423
},
2524
oracle::message_processing,
2625
};

noir-projects/aztec-nr/aztec/src/messages/processing/note_validation_request.nr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::messages::logs::note::MAX_NOTE_PACKED_LEN;
2-
use crate::protocol::{address::AztecAddress, traits::Serialize};
2+
use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};
33

44
/// Intermediate struct used to perform batch note validation by PXE. The
55
/// `aztec_utl_validateAndStoreEnqueuedNotesAndEvents` oracle expects for values of this type to be stored in a
66
/// `EphemeralArray`.
7-
#[derive(Serialize)]
8-
pub(crate) struct NoteValidationRequest {
7+
#[derive(Serialize, Deserialize)]
8+
pub struct NoteValidationRequest {
99
pub contract_address: AztecAddress,
1010
pub owner: AztecAddress,
1111
pub storage_slot: Field,

noir-projects/aztec-nr/aztec/src/note/confirmed_note.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub struct ConfirmedNote<Note> {
3333
/// The note hash used to prove existence.
3434
///
3535
/// Whether this note hash is unsiloed or unique depends on the note's metadata.
36-
pub(crate) proven_note_hash: Field,
36+
pub proven_note_hash: Field,
3737
}
3838

3939
impl<Note> ConfirmedNote<Note> {

noir-projects/aztec-nr/aztec/src/note/lifecycle.nr

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,22 @@ use crate::{
1111
use crate::protocol::{address::AztecAddress, traits::Packable};
1212

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

2332
impl<Note> NewNote<Note> {

noir-projects/aztec-nr/aztec/src/oracle/get_membership_witness.nr

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ unconstrained fn get_note_hash_membership_witness_oracle(
1111
note_hash: Field,
1212
) -> MembershipWitness<NOTE_HASH_TREE_HEIGHT> {}
1313

14-
#[oracle(aztec_utl_getBlockHashMembershipWitness)]
14+
#[oracle(aztec_utl_getBlockHashMembershipWitnessV2)]
1515
unconstrained fn get_block_hash_membership_witness_oracle(
1616
anchor_block_hash: Field,
1717
block_hash: Field,
18-
) -> MembershipWitness<ARCHIVE_HEIGHT> {}
18+
) -> Option<MembershipWitness<ARCHIVE_HEIGHT>> {}
1919

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

2222
/// Returns a membership witness for a `note_hash` in the note hash tree whose root is defined in
2323
// `anchor_block_header`.
24+
// TODO(https://linear.app/aztec-labs/issue/F-652): add Noir tests for this oracle
2425
pub unconstrained fn get_note_hash_membership_witness(
2526
anchor_block_header: BlockHeader,
2627
note_hash: Field,
@@ -37,6 +38,103 @@ pub unconstrained fn get_block_hash_membership_witness(
3738
anchor_block_header: BlockHeader,
3839
block_hash: Field,
3940
) -> MembershipWitness<ARCHIVE_HEIGHT> {
41+
let anchor_block_hash = anchor_block_header.hash();
42+
get_block_hash_membership_witness_oracle(anchor_block_hash, block_hash).expect(
43+
f"Block hash {block_hash} not found in the archive tree at anchor block {anchor_block_hash}.",
44+
)
45+
}
46+
47+
/// Same as [`get_block_hash_membership_witness`], but returns `None` instead of erroring when the block is not present
48+
/// in the archive tree. Intended for callers that need to handle absence (e.g. existence checks via `.is_some()`).
49+
pub unconstrained fn get_maybe_block_hash_membership_witness(
50+
anchor_block_header: BlockHeader,
51+
block_hash: Field,
52+
) -> Option<MembershipWitness<ARCHIVE_HEIGHT>> {
4053
let anchor_block_hash = anchor_block_header.hash();
4154
get_block_hash_membership_witness_oracle(anchor_block_hash, block_hash)
4255
}
56+
57+
mod test {
58+
use crate::oracle::block_header::get_block_header_at;
59+
use crate::protocol::{merkle_tree::root::root_from_sibling_path, traits::Hash};
60+
use crate::test::helpers::test_environment::TestEnvironment;
61+
use super::{get_block_hash_membership_witness, get_maybe_block_hash_membership_witness};
62+
63+
#[test]
64+
unconstrained fn get_block_hash_membership_witness_returns_valid_witness_for_known_block() {
65+
let env = TestEnvironment::new();
66+
67+
env.mine_block();
68+
env.mine_block();
69+
env.mine_block();
70+
env.mine_block();
71+
72+
env.private_context(|context| {
73+
let anchor = context.anchor_block_header;
74+
let target_block_number = anchor.block_number() - 2;
75+
76+
let target_header = get_block_header_at(target_block_number, *context);
77+
let target_hash = target_header.hash();
78+
79+
let witness = get_block_hash_membership_witness(anchor, target_hash);
80+
81+
assert_eq(
82+
root_from_sibling_path(target_hash, witness.leaf_index, witness.sibling_path),
83+
anchor.last_archive.root,
84+
);
85+
});
86+
}
87+
88+
#[test(should_fail_with = "not found in the archive tree at anchor block")]
89+
unconstrained fn get_block_hash_membership_witness_panics_for_unknown_block() {
90+
let env = TestEnvironment::new();
91+
92+
env.mine_block();
93+
env.mine_block();
94+
95+
env.private_context(|context| {
96+
let anchor = context.anchor_block_header;
97+
let _witness = get_block_hash_membership_witness(anchor, 0xdeadbeef);
98+
});
99+
}
100+
101+
#[test]
102+
unconstrained fn get_maybe_block_hash_membership_witness_returns_some_for_known_block() {
103+
let env = TestEnvironment::new();
104+
105+
env.mine_block();
106+
env.mine_block();
107+
env.mine_block();
108+
env.mine_block();
109+
110+
env.private_context(|context| {
111+
let anchor = context.anchor_block_header;
112+
let target_block_number = anchor.block_number() - 2;
113+
114+
let target_header = get_block_header_at(target_block_number, *context);
115+
let target_hash = target_header.hash();
116+
117+
let witness = get_maybe_block_hash_membership_witness(anchor, target_hash).expect(
118+
f"Expected Some witness for known block hash",
119+
);
120+
121+
assert_eq(
122+
root_from_sibling_path(target_hash, witness.leaf_index, witness.sibling_path),
123+
anchor.last_archive.root,
124+
);
125+
});
126+
}
127+
128+
#[test]
129+
unconstrained fn get_maybe_block_hash_membership_witness_returns_none_for_unknown_block() {
130+
let env = TestEnvironment::new();
131+
132+
env.mine_block();
133+
env.mine_block();
134+
135+
env.private_context(|context| {
136+
let anchor = context.anchor_block_header;
137+
assert(get_maybe_block_hash_membership_witness(anchor, 0xdeadbeef).is_none());
138+
});
139+
}
140+
}

noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::messages::processing::{
44
pending_tagged_log::PendingTaggedLog,
55
};
66
use crate::protocol::address::AztecAddress;
7+
use crate::protocol::blob_data::TxEffect;
78

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

6364
#[oracle(aztec_utl_getMessageContextsByTxHash_v2)]
6465
unconstrained fn get_message_contexts_by_tx_hash_v2_oracle(request_array_slot: Field) -> Field {}
66+
67+
/// Fetches all effects of a settled transaction by its hash.
68+
pub unconstrained fn get_tx_effect(tx_hash: Field) -> Option<TxEffect> {
69+
get_tx_effect_oracle(tx_hash)
70+
}
71+
72+
#[oracle(aztec_utl_getTxEffect)]
73+
unconstrained fn get_tx_effect_oracle(tx_hash: Field) -> Option<TxEffect> {}

0 commit comments

Comments
 (0)