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
38 changes: 13 additions & 25 deletions noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub fn get_notes<Note, N, M, FILTER_ARGS>(
context: &mut PrivateContext,
storage_slot: Field,
options: NoteGetterOptions<Note, N, M, FILTER_ARGS>
) -> BoundedVec<Note, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL> where Note: NoteInterface<N, M> {
) -> BoundedVec<Note, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL> where Note: NoteInterface<N, M> + Eq {
let opt_notes = get_notes_internal(storage_slot, options);

constrain_get_notes_internal(context, storage_slot, opt_notes, options)
Expand All @@ -115,9 +115,7 @@ fn constrain_get_notes_internal<Note, N, M, FILTER_ARGS>(
storage_slot: Field,
opt_notes: [Option<Note>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL],
options: NoteGetterOptions<Note, N, M, FILTER_ARGS>
) -> BoundedVec<Note, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL> where Note: NoteInterface<N, M> {
let mut returned_notes = BoundedVec::new();

) -> BoundedVec<Note, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL> where Note: NoteInterface<N, M> + Eq {
// The filter is applied first to avoid pushing note read requests for notes we're not interested in. Note that
// while the filter function can technically mutate the contents of the notes (as opposed to simply removing some),
// the private kernel will later validate that these note actually exist, so transformations would cause for that
Expand All @@ -126,11 +124,18 @@ fn constrain_get_notes_internal<Note, N, M, FILTER_ARGS>(
let filter_args = options.filter_args;
let filtered_notes = filter_fn(opt_notes, filter_args);

let notes = crate::utils::collapse(filtered_notes);

// We have now collapsed the sparse array of Options into a BoundedVec. This is a more ergonomic type and also
// results in reduced gate counts when setting a limit value, since we guarantee that the limit is an upper bound
// for the runtime length, and can therefore have fewer loop iterations.
assert(notes.len() <= options.limit, "Got more notes than limit.");
assert(notes.len() != 0, "Cannot return zero notes");

let mut prev_fields = [0; N];
for i in 0..options.limit {
let opt_note = filtered_notes[i];
if opt_note.is_some() {
let note = opt_note.unwrap_unchecked();
if i < notes.len() {
let note = notes.get_unchecked(i);
let fields = note.serialize_content();
check_note_header(*context, storage_slot, note);
check_note_fields(fields, options.selects);
Expand All @@ -143,27 +148,10 @@ fn constrain_get_notes_internal<Note, N, M, FILTER_ARGS>(
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1410): test to ensure
// failure if malicious oracle injects 0 nonce here for a "pre-existing" note.
context.push_note_hash_read_request(note_hash_for_read_request);

// The below code is used to collapse a sparse array into one where the values are guaranteed to be at the
// front of the array. This is highly useful because the caller knows that the returned array won't have
// more than option.limits notes, and can therefore loop over this limit value instead of the entire array,
// resulting in a smaller circuit and faster proving times.
// We write at returned_notes[num_notes] because num_notes is only advanced when we have a value in
// filtered_notes.
returned_notes.push(note);
};
}

// As long as we only loop till `options.limit` the array will be guaranteed to be at most of length `options.limit`.
assert(returned_notes.len() <= options.limit, "Got more notes than limit.");
// We will however check that nothing else was returned after the limit.
for i in options.limit..filtered_notes.len() {
assert(filtered_notes[i].is_none(), "Got more notes than limit.");
}

assert(returned_notes.len() != 0, "Cannot return zero notes");

returned_notes
notes
}

unconstrained fn get_note_internal<Note, N, M>(storage_slot: Field) -> Note where Note: NoteInterface<N, M> {
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl<Note> PrivateSet<Note, &mut PrivateContext> {
pub fn get_notes<N, M, FILTER_ARGS>(
self,
options: NoteGetterOptions<Note, N, M, FILTER_ARGS>
) -> BoundedVec<Note, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL> where Note: NoteInterface<N, M> {
) -> BoundedVec<Note, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL> where Note: NoteInterface<N, M> + Eq {
get_notes(self.context, self.storage_slot, options)
}
// docs:end:get_notes
Expand Down
8 changes: 8 additions & 0 deletions noir-projects/aztec-nr/value-note/src/value_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,11 @@ impl Serialize<7> for ValueNote {
[self.value, self.npk_m_hash, self.randomness, header[0], header[1], header[2], header[3]]
}
}

impl Eq for ValueNote {
fn eq(self, other: Self) -> bool {
(self.value == other.value) &
(self.npk_m_hash == other.npk_m_hash) &
(self.randomness == other.randomness)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl<T> BalancesMap<T, &mut PrivateContext> {
self: Self,
owner_npk_m_hash: Field,
subtrahend: U128
) -> OuterNoteEmission<T> where T: NoteInterface<T_SERIALIZED_LEN, T_SERIALIZED_BYTES_LEN> + OwnedNote {
) -> OuterNoteEmission<T> where T: NoteInterface<T_SERIALIZED_LEN, T_SERIALIZED_BYTES_LEN> + OwnedNote + Eq {
let mut options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend);
let notes = self.map.get_notes(
options.select(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ impl NoteInterface<TOKEN_NOTE_LEN, TOKEN_NOTE_BYTES_LEN> for TokenNote {
}
}

impl Eq for TokenNote {
fn eq(self, other: Self) -> bool {
(self.amount == other.amount) &
(self.npk_m_hash == other.npk_m_hash) &
(self.randomness == other.randomness)
}
}

impl OwnedNote for TokenNote {
fn new(amount: U128, owner_npk_m_hash: Field) -> Self {
Self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,9 @@ impl TestNote {
TestNote { value, header: NoteHeader::empty() }
}
}

impl Eq for TestNote {
fn eq(self, other: Self) -> bool {
self.value == other.value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl<T> BalancesMap<T, &mut PrivateContext> {
self: Self,
owner: AztecAddress,
subtrahend: U128
) -> OuterNoteEmission<T> where T: NoteInterface<T_SERIALIZED_LEN, T_SERIALIZED_BYTES_LEN> + OwnedNote {
) -> OuterNoteEmission<T> where T: NoteInterface<T_SERIALIZED_LEN, T_SERIALIZED_BYTES_LEN> + OwnedNote + Eq {
// docs:start:get_notes
let options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend);
let notes = self.map.at(owner).get_notes(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ impl NoteInterface<TOKEN_NOTE_LEN, TOKEN_NOTE_BYTES_LEN> for TokenNote {
}
}

impl Eq for TokenNote {
fn eq(self, other: Self) -> bool {
(self.amount == other.amount) &
(self.npk_m_hash == other.npk_m_hash) &
(self.randomness == other.randomness)
}
}

impl OwnedNote for TokenNote {
fn new(amount: U128, owner_npk_m_hash: Field) -> Self {
Self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,10 @@ impl TransparentNote {
}
}
}

impl Eq for TransparentNote {
fn eq(self, other: Self) -> bool {
(self.amount == other.amount) & (self.secret_hash == other.secret_hash)
}
}
// docs:end:token_types_all
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl<T> BalancesMap<T, &mut PrivateContext> {
self: Self,
owner: AztecAddress,
subtrahend: U128
) -> OuterNoteEmission<T> where T: NoteInterface<T_SERIALIZED_LEN, T_SERIALIZED_BYTES_LEN> + OwnedNote {
) -> OuterNoteEmission<T> where T: NoteInterface<T_SERIALIZED_LEN, T_SERIALIZED_BYTES_LEN> + OwnedNote + Eq {
// docs:start:get_notes
let options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend);
let notes = self.map.at(owner).get_notes(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,11 @@ impl OwnedNote for TokenNote {
self.amount
}
}

impl Eq for TokenNote {
fn eq(self, other: Self) -> bool {
(self.amount == other.amount) &
(self.npk_m_hash == other.npk_m_hash) &
(self.randomness == other.randomness)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,11 @@ impl TransparentNote {
}
}
}
// docs:end:token_types_all

impl Eq for TransparentNote {
fn eq(self, other: Self) -> bool {
(self.amount == other.amount) & (self.secret_hash == other.secret_hash)
}
}

// docs:end:token_types_all