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
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/discovery/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::encrypted_logs::log_assembly_strategies::default_aes128::note::encryp
use dep::protocol_types::{address::AztecAddress, debug_log::debug_log};

pub mod private_logs;
pub mod private_notes;
pub mod partial_notes;
pub mod nonce_discovery;

Expand Down
37 changes: 36 additions & 1 deletion noir-projects/aztec-nr/aztec/src/discovery/partial_notes.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::{
capsules::CapsuleArray,
discovery::{
ComputeNoteHashAndNullifier,
MAX_NOTE_PACKED_LEN,
nonce_discovery::{attempt_note_nonce_discovery, DiscoveredNoteInfo},
private_logs::MAX_PARTIAL_NOTE_PRIVATE_PACKED_LEN,
},
oracle::message_discovery::{deliver_note, get_log_by_tag},
utils::array,
Expand All @@ -16,6 +16,12 @@ use dep::protocol_types::{
traits::{Deserialize, Serialize, ToField},
};

pub global PARTIAL_NOTE_COMPLETION_LOG_TAG_LEN: u32 = 1;
/// Partial notes have a maximum packed length of their private fields bound by extra content in their private log (i.e.
/// the note completion log tag).
pub global MAX_PARTIAL_NOTE_PRIVATE_PACKED_LEN: u32 =
MAX_NOTE_PACKED_LEN - PARTIAL_NOTE_COMPLETION_LOG_TAG_LEN;

/// The slot in the PXE capsules where we store a `CapsuleArray` of `DeliveredPendingPartialNote`.
// TODO(#11630): come up with some sort of slot allocation scheme.
pub global DELIVERED_PENDING_PARTIAL_NOTE_ARRAY_LENGTH_CAPSULES_SLOT: Field = 77;
Expand All @@ -42,6 +48,35 @@ pub(crate) struct DeliveredPendingPartialNote {
pub(crate) recipient: AztecAddress,
}

pub unconstrained fn process_partial_note_private_log(
contract_address: AztecAddress,
storage_slot: Field,
note_type_id: Field,
log_payload: BoundedVec<Field, MAX_NOTE_PACKED_LEN>,
recipient: AztecAddress,
) {
// We store the information of the partial note we found so that we can later search for the public log that will
// complete it. The tag is the first value in the payload, with the packed note content taking up the rest of it.
std::static_assert(
PARTIAL_NOTE_COMPLETION_LOG_TAG_LEN == 1,
"unexpected value for PARTIAL_NOTE_COMPLETION_LOG_TAG_LEN",
);

let pending = DeliveredPendingPartialNote {
note_completion_log_tag: log_payload.get(0),
storage_slot,
note_type_id,
packed_private_note_content: array::subbvec(log_payload, 1),
recipient,
};

CapsuleArray::at(
contract_address,
DELIVERED_PENDING_PARTIAL_NOTE_ARRAY_LENGTH_CAPSULES_SLOT,
)
.push(pending);
}

/// Searches for public logs that would result in the completion of pending partial notes, ultimately resulting in the
/// notes being delivered to PXE if completed.
pub unconstrained fn fetch_and_process_public_partial_note_completion_logs<Env>(
Expand Down
115 changes: 13 additions & 102 deletions noir-projects/aztec-nr/aztec/src/discovery/private_logs.nr
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
use std::static_assert;

use crate::{
capsules::CapsuleArray,
oracle::message_discovery::{deliver_note, sync_notes},
utils::array,
};
use crate::{oracle::message_discovery::sync_notes, utils::array};

use dep::protocol_types::{
address::AztecAddress,
Expand All @@ -13,24 +9,13 @@ use dep::protocol_types::{
};

use crate::discovery::{
ComputeNoteHashAndNullifier,
MAX_NOTE_PACKED_LEN,
nonce_discovery::{attempt_note_nonce_discovery, DiscoveredNoteInfo},
NOTE_PRIVATE_LOG_RESERVED_FIELDS,
partial_notes::{
DELIVERED_PENDING_PARTIAL_NOTE_ARRAY_LENGTH_CAPSULES_SLOT, DeliveredPendingPartialNote,
},
ComputeNoteHashAndNullifier, MAX_NOTE_PACKED_LEN, NOTE_PRIVATE_LOG_RESERVED_FIELDS,
partial_notes::process_partial_note_private_log, private_notes::process_private_note_log,
};
use crate::encrypted_logs::log_assembly_strategies::default_aes128::note::encryption::decrypt_log;
// TODO(#12750): don't make this value assume we're using AES.
use crate::encrypted_logs::log_assembly_strategies::default_aes128::note::encryption::PRIVATE_LOG_PLAINTEXT_SIZE_IN_FIELDS;

pub global PARTIAL_NOTE_COMPLETION_LOG_TAG_LEN: u32 = 1;
/// Partial notes have a maximum packed length of their private fields bound by extra content in their private log (i.e.
/// the note completion log tag).
pub global MAX_PARTIAL_NOTE_PRIVATE_PACKED_LEN: u32 =
MAX_NOTE_PACKED_LEN - PARTIAL_NOTE_COMPLETION_LOG_TAG_LEN;

/// Searches for private logs that signal new private notes that are then delivered to PXE, or new partial notes that
/// are stored in the PXE capsules so that `fetch_and_process_public_partial_note_completion_logs` can later search for
/// public logs that will complete them.
Expand All @@ -45,10 +30,15 @@ pub unconstrained fn fetch_and_process_private_tagged_logs<Env>(
sync_notes();
}

/// Processes a log's ciphertext by decrypting it and then searching the plaintext for private notes or partial notes. Private
/// notes result in nonce discovery being performed prior to delivery, which requires knowledge of the transaction hash in
/// which the notes would've been created (typically the same transaction in which the log was emitted), along with the
/// list of unique note hashes in said transaction and the `compute_note_hash_and_nullifier` function.
/// Processes a log's ciphertext by decrypting it and then searching the plaintext for private notes or partial notes.
///
/// Private notes result in nonce discovery being performed prior to delivery, which requires knowledge of the
/// transaction hash in which the notes would've been created (typically the same transaction in which the log was
/// emitted), along with the list of unique note hashes in said transaction and the `compute_note_hash_and_nullifier`
/// function.
///
/// Partial notes result in a pending partial note entry being stored in a PXE capsule, which will later be retrieved to
/// search for the note's completion public log.
pub unconstrained fn do_process_log<Env>(
contract_address: AztecAddress,
log: BoundedVec<Field, PRIVATE_LOG_SIZE_IN_FIELDS>,
Expand All @@ -70,7 +60,7 @@ pub unconstrained fn do_process_log<Env>(
if log_type_id == 0 {
debug_log("Processing private note log");

attempt_note_discovery(
process_private_note_log(
contract_address,
tx_hash,
unique_note_hashes_in_tx,
Expand Down Expand Up @@ -127,82 +117,3 @@ unconstrained fn destructure_log_plaintext(

(storage_slot, note_type_id, log_type_id, log_payload)
}

/// 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<Env>(
contract_address: AztecAddress,
tx_hash: Field,
unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
first_nullifier_in_tx: Field,
recipient: AztecAddress,
compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<Env>,
storage_slot: Field,
note_type_id: Field,
packed_note_content: BoundedVec<Field, MAX_NOTE_PACKED_LEN>,
) {
let discovered_notes = attempt_note_nonce_discovery(
unique_note_hashes_in_tx,
first_nullifier_in_tx,
compute_note_hash_and_nullifier,
contract_address,
storage_slot,
note_type_id,
packed_note_content,
);

debug_log_format(
"Discovered {0} notes from a private log",
[discovered_notes.len() as Field],
);

array::for_each_in_bounded_vec(
discovered_notes,
|discovered_note: DiscoveredNoteInfo, _| {
// TODO:(#10728): handle notes that fail delivery. This could be due to e.g. a temporary node connectivity
// issue, and we should perhaps not have marked the tag index as taken.
assert(
deliver_note(
contract_address,
storage_slot,
discovered_note.nonce,
packed_note_content,
discovered_note.note_hash,
discovered_note.inner_nullifier,
tx_hash,
recipient,
),
"Failed to deliver note",
);
},
);
}

unconstrained fn process_partial_note_private_log(
contract_address: AztecAddress,
storage_slot: Field,
note_type_id: Field,
log_payload: BoundedVec<Field, MAX_NOTE_PACKED_LEN>,
recipient: AztecAddress,
) {
// We store the information of the partial note we found so that we can later search for the public log that will
// complete it. The tag is the first value in the payload, with the packed note content taking up the rest of it.
static_assert(
PARTIAL_NOTE_COMPLETION_LOG_TAG_LEN == 1,
"unexpected value for PARTIAL_NOTE_COMPLETION_LOG_TAG_LEN",
);

let pending = DeliveredPendingPartialNote {
note_completion_log_tag: log_payload.get(0),
storage_slot,
note_type_id,
packed_private_note_content: array::subbvec(log_payload, 1),
recipient,
};

CapsuleArray::at(
contract_address,
DELIVERED_PENDING_PARTIAL_NOTE_ARRAY_LENGTH_CAPSULES_SLOT,
)
.push(pending);
}
90 changes: 90 additions & 0 deletions noir-projects/aztec-nr/aztec/src/discovery/private_notes.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use crate::{
discovery::{
ComputeNoteHashAndNullifier,
MAX_NOTE_PACKED_LEN,
nonce_discovery::{attempt_note_nonce_discovery, DiscoveredNoteInfo},
},
oracle,
utils::array,
};
use protocol_types::{
address::AztecAddress, constants::MAX_NOTE_HASHES_PER_TX, debug_log::debug_log_format,
};

pub unconstrained fn process_private_note_log<Env>(
contract_address: AztecAddress,
tx_hash: Field,
unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
first_nullifier_in_tx: Field,
recipient: AztecAddress,
compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<Env>,
storage_slot: Field,
note_type_id: Field,
packed_note_content: BoundedVec<Field, MAX_NOTE_PACKED_LEN>,
) {
// Currently a placeholder, since we want to both have a 'process log kind' fn and keep 'attempt_note_discovery'.
// This is where we'll soon extract note type id, storage slot and packed note content from the log metadata and log
// content.

attempt_note_discovery(
contract_address,
tx_hash,
unique_note_hashes_in_tx,
first_nullifier_in_tx,
recipient,
compute_note_hash_and_nullifier,
storage_slot,
note_type_id,
packed_note_content,
);
}

/// 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<Env>(
contract_address: AztecAddress,
tx_hash: Field,
unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
first_nullifier_in_tx: Field,
recipient: AztecAddress,
compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<Env>,
storage_slot: Field,
note_type_id: Field,
packed_note_content: BoundedVec<Field, MAX_NOTE_PACKED_LEN>,
) {
let discovered_notes = attempt_note_nonce_discovery(
unique_note_hashes_in_tx,
first_nullifier_in_tx,
compute_note_hash_and_nullifier,
contract_address,
storage_slot,
note_type_id,
packed_note_content,
);

debug_log_format(
"Discovered {0} notes from a private log",
[discovered_notes.len() as Field],
);

array::for_each_in_bounded_vec(
discovered_notes,
|discovered_note: DiscoveredNoteInfo, _| {
// TODO:(#10728): handle notes that fail delivery. This could be due to e.g. a temporary node connectivity
// issue, and we should perhaps not have marked the tag index as taken.
assert(
oracle::message_discovery::deliver_note(
contract_address,
storage_slot,
discovered_note.nonce,
packed_note_content,
discovered_note.note_hash,
discovered_note.inner_nullifier,
tx_hash,
recipient,
),
"Failed to deliver note",
);
},
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub contract Test {

use dep::aztec::{
deploy::deploy_contract as aztec_deploy_contract,
discovery::private_logs::attempt_note_discovery,
discovery::private_notes::attempt_note_discovery,
event::event_interface::EventInterface,
hash::{ArgsHasher, pedersen_hash},
history::note_inclusion::ProveNoteInclusion,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use dep::aztec::macros::aztec;
pub contract TokenBlacklist {
// Libs
use dep::aztec::{
discovery::private_logs::attempt_note_discovery,
discovery::private_notes::attempt_note_discovery,
encrypted_logs::log_assembly_strategies::default_aes128::note::{
encode_and_encrypt_note, encode_and_encrypt_note_unconstrained,
},
Expand Down