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
3 changes: 2 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@
"doesnt",
"dont",
"ecdh",
"ecdsasecp",
"elif",
"endgroup",
"enrs",
"entrypoints",
"erc",
"ecdsasecp",
"falsey",
"fargate",
"Fieldable",
Expand Down Expand Up @@ -306,6 +306,7 @@
"unexclude",
"unexcluded",
"unfinalised",
"unkonstrained",
"unnullify",
"unpadded",
"unprefixed",
Expand Down
2 changes: 2 additions & 0 deletions noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use dep::protocol_types::{
traits::Packable,
};

mod test;

// docs:start:struct
pub struct PrivateSet<Note, Context> {
pub context: Context,
Expand Down
173 changes: 173 additions & 0 deletions noir-projects/aztec-nr/aztec/src/state_vars/private_set/test.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
use crate::{
context::{PrivateContext, UnconstrainedContext},
note::{note_getter_options::NoteGetterOptions, note_viewer_options::NoteViewerOptions},
oracle::random::random,
state_vars::private_set::PrivateSet,
};
use crate::test::{helpers::test_environment::TestEnvironment, mocks::mock_note::MockNote};

global storage_slot: Field = 17;

unconstrained fn setup() -> TestEnvironment {
let mut env = TestEnvironment::new();

// Advance 1 block so we can read historic state from private
env.advance_block_by(1);

env
}

unconstrained fn in_private(
env: &mut TestEnvironment,
) -> PrivateSet<MockNote, &mut PrivateContext> {
PrivateSet::new(&mut env.private(), storage_slot)
}

unconstrained fn in_unconstrained(
env: TestEnvironment,
) -> PrivateSet<MockNote, UnconstrainedContext> {
PrivateSet::new(env.unkonstrained(), storage_slot)
}

#[test]
unconstrained fn get_empty() {
let mut env = setup();

let state_var = in_private(&mut env);
let notes = state_var.get_notes(NoteGetterOptions::new());

assert_eq(notes.len(), 0);

assert_eq(state_var.context.note_hash_read_requests.len(), 0);
}

#[test]
unconstrained fn view_empty() {
let mut env = setup();

let state_var = in_unconstrained(env);

let notes = state_var.view_notes(NoteViewerOptions::new());
assert_eq(notes.len(), 0);
}

#[test]
unconstrained fn insert_side_effects() {
let mut env = setup();
let state_var = in_private(&mut env);

let value = 42;
let new_note = MockNote::new(value).build_note();

let emission = state_var.insert(new_note);

// insert creates a new note
assert_eq(state_var.context.note_hashes.len(), 1);

assert_eq(emission.note, new_note);
assert_eq(emission.storage_slot, state_var.storage_slot);
}

unconstrained fn insert_note(env: &mut TestEnvironment) -> MockNote {
let value = random();
let new_note = MockNote::new(value).build_note();

let state_var = in_private(env);
let _ = state_var.insert(new_note);

new_note
}

// TODO(#13269): we can't test settled notes due to TXE lacking a way to commit state changes that happen outside of a
// contract call.

#[test]
unconstrained fn insert_and_get_pending() {
let mut env = setup();
let note = insert_note(&mut env);

let state_var = in_private(&mut env);
let notes = state_var.get_notes(NoteGetterOptions::new());

assert_eq(notes.len(), 1);

// get_notes returns the notes, but does *not* nullify them
assert_eq(state_var.context.note_hash_read_requests.len(), 1);
assert_eq(state_var.context.nullifiers.len(), 0);

// Instead we get RetrievedNotes, which can be later used for nullification
let retrieved_note = notes.get(0);
assert(retrieved_note.metadata.is_pending_same_phase());
assert_eq(retrieved_note.note, note);
}

#[test]
unconstrained fn insert_and_view_pending() {
let mut env = setup();
let note = insert_note(&mut env);

let state_var = in_unconstrained(env);

let notes = state_var.view_notes(NoteViewerOptions::new());
assert_eq(notes.len(), 1);
assert_eq(notes.get(0), note);
}

#[test]
unconstrained fn insert_and_pop_pending() {
let mut env = setup();
let note = insert_note(&mut env);

let state_var = in_private(&mut env);
let notes = state_var.pop_notes(NoteGetterOptions::new());

assert_eq(notes.len(), 1);
assert_eq(notes.get(0), note);

// pop_notes returns the notes *and* nullifies them
assert_eq(state_var.context.note_hash_read_requests.len(), 1);
assert_eq(state_var.context.nullifiers.len(), 1);
}

#[test]
unconstrained fn insert_pop_and_read_again_pending() {
let mut env = setup();
let _ = insert_note(&mut env);

let state_var = in_private(&mut env);
let _ = state_var.pop_notes(NoteGetterOptions::new());

// Now that we've deleted the note, the oracle should know this and not return them any more.
assert_eq(state_var.pop_notes(NoteGetterOptions::new()).len(), 0);
assert_eq(state_var.get_notes(NoteGetterOptions::new()).len(), 0);
assert_eq(in_unconstrained(env).view_notes(NoteViewerOptions::new()).len(), 0);
}

#[test]
unconstrained fn insert_and_remove_pending() {
let mut env = setup();
let _ = insert_note(&mut env);

let state_var = in_private(&mut env);
let retrieved = state_var.get_notes(NoteGetterOptions::new());

state_var.remove(retrieved.get(0));

// remove nullifies the note
assert_eq(state_var.context.nullifiers.len(), 1);
}

#[test]
unconstrained fn insert_remove_and_read_again_pending() {
let mut env = setup();
let _ = insert_note(&mut env);

let state_var = in_private(&mut env);
let retrieved = state_var.get_notes(NoteGetterOptions::new());
state_var.remove(retrieved.get(0));

// Now that we've deleted the notes, the oracle should know this and not return them any more.
assert_eq(state_var.pop_notes(NoteGetterOptions::new()).len(), 0);
assert_eq(state_var.get_notes(NoteGetterOptions::new()).len(), 0);
assert_eq(in_unconstrained(env).view_notes(NoteViewerOptions::new()).len(), 0);
}