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 protocol_types::{address::AztecAddress, debug_log::debug_log};

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

Expand Down
142 changes: 142 additions & 0 deletions noir-projects/aztec-nr/aztec/src/discovery/pending_tagged_log.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use protocol_types::{
address::AztecAddress,
constants::{MAX_NOTE_HASHES_PER_TX, PRIVATE_LOG_SIZE_IN_FIELDS},
hash::sha256_to_field,
traits::{Deserialize, Serialize},
};

// Base slot for the pending tagged log array to which the syncNotes oracle inserts found private logs.
pub(crate) global PENDING_TAGGED_LOG_ARRAY_BASE_SLOT: Field = sha256_to_field(
"AZTEC_NR::PENDING_TAGGED_PENDING_TAGGED_LOG_ARRAY_BASE_SLOT".as_bytes(),
);

/// Represents a log as it is stored in the pending tagged log array to which the syncNotes oracle inserts found private log.
#[derive(Deserialize, Serialize)]
pub(crate) struct PendingTaggedLog {
pub log: BoundedVec<Field, PRIVATE_LOG_SIZE_IN_FIELDS>,
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,
pub log_index_in_tx: Field,
}

mod test {
use crate::discovery::pending_tagged_log::PendingTaggedLog;
use protocol_types::{address::AztecAddress, traits::{FromField, Serialize}};

#[test]
unconstrained fn pending_tagged_log_serialization_matches_typescript() {
let log = BoundedVec::from_array([1, 2, 3]);
let tx_hash = 123;
let unique_note_hashes = BoundedVec::from_array([4, 5]);
let first_nullifier = 6;
let recipient = AztecAddress::from_field(789);
let log_index_in_tx = 10;
let pending_log = PendingTaggedLog {
log,
tx_hash,
unique_note_hashes_in_tx: unique_note_hashes,
first_nullifier_in_tx: first_nullifier,
recipient,
log_index_in_tx,
};

let serialized = pending_log.serialize();

// The following value was generated by `pending_tagged_log.test.ts`
// --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data.
let serialized_pending_tagged_log_from_typescript = [
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000003,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000003,
0x000000000000000000000000000000000000000000000000000000000000007b,
0x0000000000000000000000000000000000000000000000000000000000000004,
0x0000000000000000000000000000000000000000000000000000000000000005,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000006,
0x0000000000000000000000000000000000000000000000000000000000000315,
0x000000000000000000000000000000000000000000000000000000000000000a,
];

assert_eq(serialized, serialized_pending_tagged_log_from_typescript);
}
}
83 changes: 53 additions & 30 deletions noir-projects/aztec-nr/aztec/src/discovery/private_logs.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use crate::{
capsules::CapsuleArray,
discovery::{
ComputeNoteHashAndNullifier, MAX_LOG_CONTENT_LEN,
partial_notes::process_partial_note_private_log, PRIVATE_LOG_EXPANDED_METADATA_LEN,
ComputeNoteHashAndNullifier,
MAX_LOG_CONTENT_LEN,
partial_notes::process_partial_note_private_log,
pending_tagged_log::{PENDING_TAGGED_LOG_ARRAY_BASE_SLOT, PendingTaggedLog},
PRIVATE_LOG_EXPANDED_METADATA_LEN,
private_notes::process_private_note_log,
},
encrypted_logs::{
Expand All @@ -19,7 +23,6 @@ use crate::{
use protocol_types::{
abis::event_selector::EventSelector,
address::AztecAddress,
constants::{MAX_NOTE_HASHES_PER_TX, PRIVATE_LOG_SIZE_IN_FIELDS},
debug_log::{debug_log, debug_log_format},
traits::FromField,
};
Expand All @@ -31,14 +34,31 @@ use crate::encrypted_logs::log_encryption::PRIVATE_LOG_PLAINTEXT_SIZE_IN_FIELDS;
/// 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.
pub unconstrained fn fetch_and_process_private_tagged_logs<Env>(
_contract_address: AztecAddress,
_compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<Env>,
contract_address: AztecAddress,
compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<Env>,
) {
// We will eventually fetch tagged logs, decrypt and process them here, but for now we simply call the `syncNotes`
// oracle. This has PXE perform tag synchronization, log download, decryption, and finally calls to the the
// `process_log` contract function with the decrypted payload, which will in turn call `do_process_log` with a
// decrypted log, letting us continue the work outside of PXE.
sync_notes();
// We will eventually perform log discovery via tagging here, but for now we simply call the `syncNotes` oracle.
// This makes PXE synchronize tags, download logs and store the pending tagged logs in capsule array which are then
// retrieved and processed here.
sync_notes(PENDING_TAGGED_LOG_ARRAY_BASE_SLOT);

// Get logs from capsules
let logs =
CapsuleArray::<PendingTaggedLog>::at(contract_address, PENDING_TAGGED_LOG_ARRAY_BASE_SLOT);
let len = logs.len();

// We iterate over the logs in reverse order to avoid shifting elements.
let mut i = len;
while i > 0 {
i -= 1;

// Get and process each log
let log = logs.get(i);
process_log(contract_address, compute_note_hash_and_nullifier, log);

// Remove the log from the capsule array
logs.remove(i);
}
}

/// Processes a log's ciphertext by decrypting it and then searching the plaintext for private notes or partial notes.
Expand All @@ -50,21 +70,20 @@ pub unconstrained fn fetch_and_process_private_tagged_logs<Env>(
///
/// 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>(
unconstrained fn process_log<Env>(
contract_address: AztecAddress,
log: BoundedVec<Field, PRIVATE_LOG_SIZE_IN_FIELDS>,
tx_hash: Field,
unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
first_nullifier_in_tx: Field,
log_index_in_tx: Field,
recipient: AztecAddress,
compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier<Env>,
pending_tagged_log: PendingTaggedLog,
) {
debug_log_format("Processing log with tag {0}", [log.get(0)]);
debug_log_format(
"Processing log with tag {0}",
[pending_tagged_log.log.get(0)],
);

// The tag is ignored for now.
let ciphertext = array::subbvec(pending_tagged_log.log, 1);

// The first field of the log is the tag, which we don't need right now, and the rest is all ciphertext.
let ciphertext = array::subbvec(log, 1);
let log_plaintext = AES128::decrypt_log(ciphertext, recipient);
let log_plaintext = AES128::decrypt_log(ciphertext, pending_tagged_log.recipient);

// The first thing to do after decrypting the log is to determine what type of private log we're processing. We
// have 3 log types: private note logs, partial note logs and event logs.
Expand All @@ -76,18 +95,23 @@ pub unconstrained fn do_process_log<Env>(

process_private_note_log(
contract_address,
tx_hash,
unique_note_hashes_in_tx,
first_nullifier_in_tx,
recipient,
pending_tagged_log.tx_hash,
pending_tagged_log.unique_note_hashes_in_tx,
pending_tagged_log.first_nullifier_in_tx,
pending_tagged_log.recipient,
compute_note_hash_and_nullifier,
log_metadata,
log_content,
);
} else if log_type_id == PARTIAL_NOTE_PRIVATE_LOG_TYPE_ID {
debug_log("Processing partial note private log");

process_partial_note_private_log(contract_address, recipient, log_metadata, log_content);
process_partial_note_private_log(
contract_address,
pending_tagged_log.recipient,
log_metadata,
log_content,
);
} else if log_type_id == PRIVATE_EVENT_LOG_TYPE_ID {
debug_log("Processing private event log");

Expand All @@ -96,14 +120,13 @@ pub unconstrained fn do_process_log<Env>(

store_private_event_log(
contract_address,
recipient,
pending_tagged_log.recipient,
event_selector,
log_content,
tx_hash,
log_index_in_tx,
pending_tagged_log.tx_hash,
pending_tagged_log.log_index_in_tx,
);
} else {
// TODO(#11569): handle events
debug_log_format(
"Unknown log type id {0} (probably belonging to an event log)",
[log_type_id as Field],
Expand Down
59 changes: 0 additions & 59 deletions noir-projects/aztec-nr/aztec/src/macros/aztec.nr
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pub comptime fn aztec(m: Module) -> Quoted {

let contract_library_method_compute_note_hash_and_nullifier =
generate_contract_library_method_compute_note_hash_and_nullifier();
let process_log = generate_process_log();
let note_exports = generate_note_exports();
let public_dispatch = generate_public_dispatch(m);
let sync_notes = generate_sync_notes();
Expand All @@ -31,7 +30,6 @@ pub comptime fn aztec(m: Module) -> Quoted {
$note_exports
$interface
$contract_library_method_compute_note_hash_and_nullifier
$process_log
$public_dispatch
$sync_notes
}
Expand Down Expand Up @@ -256,63 +254,6 @@ comptime fn generate_contract_library_method_compute_note_hash_and_nullifier() -
}
}

comptime fn generate_process_log() -> Quoted {
// This mandatory function processes a log emitted by the contract. This is currently used to process private logs
// and perform message discovery, resulting in new private notes, partial notes and events.
// The bulk of the work of this function is done by aztec::discovery::do_process_log, so all we need to do is call
// that function.

// We'll produce the entire body of the function in one go and then insert it into the function.
let notes = NOTES.entries();

if notes.len() > 0 {
quote {
unconstrained fn process_log(
log_ciphertext: BoundedVec<Field, dep::aztec::protocol_types::constants::PRIVATE_LOG_SIZE_IN_FIELDS>,
tx_hash: Field,
unique_note_hashes_in_tx: BoundedVec<Field, dep::aztec::protocol_types::constants::MAX_NOTE_HASHES_PER_TX>,
first_nullifier_in_tx: Field,
log_index_in_tx: Field,
recipient: aztec::protocol_types::address::AztecAddress,
) {
// Because this unconstrained function is injected after the contract is processed by the macros, it'll
// not be modified by the macros that alter unconstrained functions. As such, we need to manually inject
// the unconstrained execution context since it will not be available otherwise.
let context = dep::aztec::context::unconstrained_context::UnconstrainedContext::new();

// TODO(#10727): allow other contracts to process logs and deliver notes
let contract_address = context.this_address();

aztec::discovery::private_logs::do_process_log(
contract_address,
log_ciphertext,
tx_hash,
unique_note_hashes_in_tx,
first_nullifier_in_tx,
log_index_in_tx,
recipient,
_compute_note_hash_and_nullifier,
);
}
}
} else {
// Contracts with no notes still implement this function to avoid having special-casing, the implementation
// simply throws immediately.
quote {
unconstrained fn process_log(
_log_ciphertext: BoundedVec<Field, dep::aztec::protocol_types::constants::PRIVATE_LOG_SIZE_IN_FIELDS>,
_tx_hash: Field,
_unique_note_hashes_in_tx: BoundedVec<Field, dep::aztec::protocol_types::constants::MAX_NOTE_HASHES_PER_TX>,
_first_nullifier_in_tx: Field,
_log_index_in_tx: Field,
_recipient: aztec::protocol_types::address::AztecAddress,
) {
panic(f"This contract does not use private notes")
}
}
}
}

comptime fn generate_note_exports() -> Quoted {
let notes = NOTES.values();
// Second value in each tuple is `note_packed_len` and that is ignored here because it's only used when
Expand Down
Loading