Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: note hash read requests fixes and refactoring #6125

Merged
merged 11 commits into from
May 2, 2024
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ library Constants {
uint256 internal constant HEADER_LENGTH = APPEND_ONLY_TREE_SNAPSHOT_LENGTH
+ CONTENT_COMMITMENT_LENGTH + STATE_REFERENCE_LENGTH + GLOBAL_VARIABLES_LENGTH;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = CALL_CONTEXT_LENGTH + 3
+ MAX_BLOCK_NUMBER_LENGTH + (SIDE_EFFECT_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL)
+ MAX_BLOCK_NUMBER_LENGTH + (READ_REQUEST_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL)
+ (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL)
+ (NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL)
+ (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL)
Expand Down
4 changes: 2 additions & 2 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct PrivateContext {

max_block_number: MaxBlockNumber,

note_hash_read_requests: BoundedVec<SideEffect, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL>,
note_hash_read_requests: BoundedVec<ReadRequest, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL>,
nullifier_read_requests: BoundedVec<ReadRequest, MAX_NULLIFIER_READ_REQUESTS_PER_CALL>,
nullifier_key_validation_requests: BoundedVec<NullifierKeyValidationRequest, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL>,

Expand Down Expand Up @@ -192,7 +192,7 @@ impl PrivateContext {
}

pub fn push_note_hash_read_request(&mut self, note_hash: Field) {
let side_effect = SideEffect { value: note_hash, counter: self.side_effect_counter };
let side_effect = ReadRequest { value: note_hash, counter: self.side_effect_counter };
self.note_hash_read_requests.push(side_effect);
self.side_effect_counter = self.side_effect_counter + 1;
}
Expand Down
4 changes: 2 additions & 2 deletions noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use dep::protocol_types::{constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, abis::side_effect::SideEffect};
use dep::protocol_types::{constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, abis::read_request::ReadRequest};
use crate::context::{PrivateContext, PublicContext, Context};
use crate::note::{
constants::MAX_NOTES_PER_PAGE, lifecycle::{create_note, create_note_hash_from_public, destroy_note},
Expand Down Expand Up @@ -59,7 +59,7 @@ impl<Note> PrivateSet<Note> {
pub fn remove<N>(self, note: Note) where Note: NoteInterface<N> {
let context = self.context.private.unwrap();
let note_hash = compute_note_hash_for_consumption(note);
let has_been_read = context.note_hash_read_requests.any(|r: SideEffect| r.value == note_hash);
let has_been_read = context.note_hash_read_requests.any(|r: ReadRequest| r.value == note_hash);
assert(has_been_read, "Can only remove a note that has been read from the set.");

destroy_note(context, note);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ use dep::std;
use dep::types::{
abis::{
call_request::CallRequest, accumulated_data::PrivateAccumulatedData,
membership_witness::NoteHashReadRequestMembershipWitness,
private_circuit_public_inputs::PrivateCircuitPublicInputs,
private_kernel::private_call_data::PrivateCallData, side_effect::SideEffect
private_kernel::private_call_data::PrivateCallData
},
address::{AztecAddress, PartialAddress}, contract_class_id::ContractClassId,
constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL,
hash::{private_functions_root_from_siblings, stdlib_recursion_verification_key_compress_native_vk},
merkle_tree::check_membership, utils::{arrays::{array_length, validate_array}},
traits::{is_empty, is_empty_array}
utils::{arrays::{array_length, validate_array}}, traits::{is_empty, is_empty_array}
};

fn validate_arrays(app_public_inputs: PrivateCircuitPublicInputs) {
Expand All @@ -30,48 +27,6 @@ fn validate_arrays(app_public_inputs: PrivateCircuitPublicInputs) {
validate_array(app_public_inputs.unencrypted_logs_hashes);
}

// Validate all read requests against the historical note hash tree root.
// Use their membership witnesses to do so. If the historical root is not yet
// initialized, initialize it using the first read request here (if present).
//
// More info here:
// - https://discourse.aztec.network/t/to-read-or-not-to-read/178
// - https://discourse.aztec.network/t/spending-notes-which-havent-yet-been-inserted/180
pub fn validate_note_hash_read_requests(
historical_note_hash_tree_root: Field,
read_requests: [SideEffect; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL],
read_request_membership_witnesses: [NoteHashReadRequestMembershipWitness; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]
) {
// membership witnesses must resolve to the same note hash tree root
// for every request in all kernel iterations
for rr_idx in 0..MAX_NOTE_HASH_READ_REQUESTS_PER_CALL {
let read_request = read_requests[rr_idx].value;
let witness = read_request_membership_witnesses[rr_idx];

// A pending note hash is the one that is not yet added to note hash tree
// A "transient read" is when we try to "read" a pending note hash within a transaction
// between function calls, as opposed to reading the outputs of a previous transaction
// which is a "pending read".
// A transient read is when we try to "read" a pending note hash
// We determine if it is a transient read depending on the leaf index from the membership witness
// Note that the Merkle membership proof would be null and void in case of an transient read
// but we use the leaf index as a placeholder to detect a 'pending note read'.

if (read_request != 0) & (witness.is_transient == false) {
assert(
check_membership(
read_request,
witness.leaf_index,
witness.sibling_path,
historical_note_hash_tree_root
), "note hash tree root mismatch"
);
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1354): do we need to enforce
// that a non-transient read_request was derived from the proper/current contract address?
}
}
}

fn perform_static_call_checks(private_call: PrivateCallData) {
let public_inputs = private_call.call_stack_item.public_inputs;
let is_static_call = public_inputs.call_context.is_static_call;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
use crate::common;
use dep::std::{cmp::Eq, option::Option};
use dep::reset_kernel_lib::{
NullifierReadRequestHints, PrivateValidationRequestProcessor,
verify_squashed_transient_note_hashes_and_nullifiers
};
use dep::reset_kernel_lib::verify_squashed_transient_note_hashes_and_nullifiers;
use dep::types::{
abis::{
kernel_data::PrivateKernelData,
kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder, PublicKernelCircuitPublicInputs},
note_hash::NoteHashContext, nullifier::Nullifier, side_effect::{SideEffect, Ordered}, gas::Gas
},
constants::{
MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX,
MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX,
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX,
MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, DA_BYTES_PER_FIELD, FIXED_DA_GAS,
DA_GAS_PER_BYTE
MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX,
MAX_UNENCRYPTED_LOGS_PER_TX
},
grumpkin_private_key::GrumpkinPrivateKey,
hash::{compute_note_hash_nonce, compute_unique_siloed_note_hash},
utils::arrays::{array_length, array_to_bounded_vec, assert_sorted_array}, traits::{Empty, is_empty}
utils::arrays::{array_length, array_to_bounded_vec, assert_sorted_array}
};

fn asc_sort_by_counters<T>(a: T, b: T) -> bool where T: Ordered {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ use dep::types::{
abis::{
call_request::CallRequest, combined_constant_data::CombinedConstantData,
kernel_circuit_public_inputs::{PrivateKernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder},
max_block_number::MaxBlockNumber, membership_witness::NoteHashReadRequestMembershipWitness,
nullifier::Nullifier, private_circuit_public_inputs::PrivateCircuitPublicInputs,
side_effect::SideEffect
max_block_number::MaxBlockNumber, nullifier::Nullifier,
private_circuit_public_inputs::PrivateCircuitPublicInputs
},
address::AztecAddress,
constants::{
Expand All @@ -19,7 +18,6 @@ struct DataSource {
private_call_public_inputs: PrivateCircuitPublicInputs,
storage_contract_address: AztecAddress,
note_hash_nullifier_counters: [u32; MAX_NEW_NOTE_HASHES_PER_CALL],
note_hash_read_request_membership_witnesses: [NoteHashReadRequestMembershipWitness; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL],
private_call_requests: [CallRequest; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL],
public_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL],
}
Expand Down Expand Up @@ -80,7 +78,6 @@ impl PrivateKernelCircuitPublicInputsComposer {
&mut self,
private_call_public_inputs: PrivateCircuitPublicInputs,
note_hash_nullifier_counters: [u32; MAX_NEW_NOTE_HASHES_PER_CALL],
note_hash_read_request_membership_witnesses: [NoteHashReadRequestMembershipWitness; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL],
private_call_requests: [CallRequest; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL],
public_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]
) -> Self {
Expand All @@ -89,7 +86,6 @@ impl PrivateKernelCircuitPublicInputsComposer {
storage_contract_address,
private_call_public_inputs,
note_hash_nullifier_counters,
note_hash_read_request_membership_witnesses,
private_call_requests,
public_call_requests
};
Expand Down Expand Up @@ -118,19 +114,11 @@ impl PrivateKernelCircuitPublicInputsComposer {
}

fn propagate_note_hash_read_requests(&mut self, source: DataSource) {
// TODO: Propagate all requests to verify in a reset circuit.
// Transient read requests and witnesses are accumulated in public_inputs.end
// We silo the read requests (domain separation per contract address)
let read_requests = source.private_call_public_inputs.note_hash_read_requests;
for i in 0..read_requests.len() {
let read_request = read_requests[i];
let witness = source.note_hash_read_request_membership_witnesses[i];
if witness.is_transient & (read_request.value != 0) { // only forward transient to public inputs
let siloed_read_request = SideEffect {
counter: read_request.counter,
value: silo_note_hash(source.storage_contract_address, read_request.value)
};
self.public_inputs.validation_requests.note_hash_read_requests.push(siloed_read_request);
let request = read_requests[i];
if !is_empty(request) {
self.public_inputs.validation_requests.note_hash_read_requests.push(request.to_context(source.storage_contract_address));
}
}
}
Expand Down
Loading
Loading