Skip to content

Commit

Permalink
refactor: moving storage slot out of NoteHeader (#11904)
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan authored Feb 13, 2025
1 parent c4ce913 commit 8c4bb1c
Show file tree
Hide file tree
Showing 39 changed files with 268 additions and 173 deletions.
55 changes: 55 additions & 0 deletions docs/docs/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,61 @@ keywords: [sandbox, aztec, notes, migration, updating, upgrading]

Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them.

### TBD

### [Aztec.nr] Changes to `NoteInterface`
We are in a process of discontinuing `NoteHeader` from notes.
This led us to do the following changes to `NoteInterface`:

```diff
pub trait NullifiableNote {
...
- unconstrained fn compute_nullifier_without_context(self) -> Field;
+ unconstrained fn compute_nullifier_without_context(self, storage_slot: Field) -> Field;
}

pub trait NoteInterface<let N: u32> {
- fn compute_note_hash(self) -> Field;
+ fn compute_note_hash(self, storage_slot: Field) -> Field;
}
```

If you are using `#[note]` or `#[partial_note(...)]` macros this should not affect you as these functions are auto-generated.
If you use `#[note_custom_interface]` macro you will need to update your notes.
These are the changes that needed to be done to our `EcdsaPublicKeyNote`:

```diff
+ use dep::aztec::protocol_types::utils::arrays::array_concat;

impl NoteInterface<ECDSA_PUBLIC_KEY_NOTE_LEN> for EcdsaPublicKeyNote {
...
- fn compute_note_hash(self) -> Field {
+ fn compute_note_hash(self, storage_slot: Field) -> Field {
- poseidon2_hash_with_separator(self.pack_content(), GENERATOR_INDEX__NOTE_HASH)
+ let inputs = array_concat(self.pack_content(), [storage_slot]);
poseidon2_hash_with_separator(inputs, GENERATOR_INDEX__NOTE_HASH)
}
}

impl NullifiableNote for EcdsaPublicKeyNote {
...
- unconstrained fn compute_nullifier_without_context(self) -> Field {
- let note_hash_for_nullify = compute_note_hash_for_nullify(self);
+ unconstrained fn compute_nullifier_without_context(self, storage_slot: Field) -> Field {
+ let note_hash_for_nullify = compute_note_hash_for_nullify(self, storage_slot);
let owner_npk_m_hash = get_public_keys(self.owner).npk_m.hash();
let secret = get_nsk_app(owner_npk_m_hash);
poseidon2_hash_with_separator(
[
note_hash_for_nullify,
secret
],
GENERATOR_INDEX__NOTE_NULLIFIER as Field
)
}
}
```

### 0.75.0

### Changes to `TokenBridge` interface
Expand Down
4 changes: 2 additions & 2 deletions noir-projects/aztec-nr/address-note/src/address_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ impl NullifiableNote for AddressNote {
)
}

unconstrained fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self);
unconstrained fn compute_nullifier_without_context(self, storage_slot: Field) -> Field {
let note_hash_for_nullify = compute_note_hash_for_nullify(self, storage_slot);
let owner_npk_m_hash = get_public_keys(self.owner).npk_m.hash();
let secret = get_nsk_app(owner_npk_m_hash);
poseidon2_hash_with_separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,16 @@ where
/// note_id and the storage_slot) to be converted into bytes, because the aes function
/// operates on bytes; not fields.
/// NB: The extra `+ 64` bytes is for the note_id and the storage_slot of the note:
fn compute_note_plaintext_for_this_strategy<Note, let N: u32>(note: Note) -> [u8; N * 32 + 64]
fn compute_note_plaintext_for_this_strategy<Note, let N: u32>(
note: Note,
storage_slot: Field,
) -> [u8; N * 32 + 64]
where
Note: NoteInterface<N>,
{
let packed_note = note.pack_content();

let note_header = note.get_header();
let storage_slot = note_header.storage_slot;
let storage_slot_bytes: [u8; 32] = storage_slot.to_be_bytes();

// TODO(#10952): The following can be reduced to 7 bits
Expand All @@ -250,6 +252,7 @@ where
fn compute_log<Note, let N: u32>(
context: PrivateContext,
note: Note,
storage_slot: Field,
recipient: AztecAddress,
sender: AztecAddress,
) -> [Field; PRIVATE_LOG_SIZE_IN_FIELDS]
Expand All @@ -272,7 +275,7 @@ where
// Compute the plaintext
// *****************************************************************************

let final_plaintext_bytes = compute_note_plaintext_for_this_strategy(note);
let final_plaintext_bytes = compute_note_plaintext_for_this_strategy(note, storage_slot);

// *****************************************************************************
// Convert the plaintext into whatever format the encryption function expects
Expand Down Expand Up @@ -413,13 +416,14 @@ where
unconstrained fn compute_log_unconstrained<Note, let N: u32>(
context: PrivateContext,
note: Note,
storage_slot: Field,
recipient: AztecAddress,
sender: AztecAddress,
) -> [Field; PRIVATE_LOG_SIZE_IN_FIELDS]
where
Note: NoteInterface<N>,
{
compute_log(context, note, recipient, sender)
compute_log(context, note, storage_slot, recipient, sender)
}

// This function seems to be affected by the following Noir bug:
Expand All @@ -436,11 +440,12 @@ where
{
|e: NoteEmission<Note>| {
let note = e.note;
let storage_slot = e.storage_slot;
assert_note_exists(*context, note);

let note_hash_counter = note.get_header().note_hash_counter;

let encrypted_log = compute_log(*context, note, recipient, sender);
let encrypted_log = compute_log(*context, note, storage_slot, recipient, sender);
context.emit_raw_note_log(encrypted_log, note_hash_counter);
}
}
Expand All @@ -459,6 +464,7 @@ where
{
|e: NoteEmission<Note>| {
let note = e.note;
let storage_slot = e.storage_slot;
assert_note_exists(*context, note);

let note_hash_counter = note.get_header().note_hash_counter;
Expand All @@ -473,7 +479,8 @@ where
// It's important here that we do not
// return the log from this function to the app, otherwise it could try to do stuff with it and then that might
// be wrong.
let encrypted_log = unsafe { compute_log_unconstrained(*context, note, recipient, sender) };
let encrypted_log =
unsafe { compute_log_unconstrained(*context, note, storage_slot, recipient, sender) };
context.emit_raw_note_log(encrypted_log, note_hash_counter);
}
}
Expand All @@ -496,18 +503,15 @@ mod test {
); // This is an address copied to match the typescript one.

let storage_slot = 42;
let note = MockNote::new(1234)
.contract_address(context.this_address())
.storage_slot(storage_slot)
.build();
let note = MockNote::new(1234).contract_address(context.this_address()).build();
let contract_address = context.this_address();

// All the values in this test were copied over from `encrypted_log_payload.test.ts`
let contract_address = AztecAddress::from_field(
0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04,
);

let plaintext = super::compute_note_plaintext_for_this_strategy(note);
let plaintext = super::compute_note_plaintext_for_this_strategy(note, storage_slot);

let eph_sk = 0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538;
let _ = OracleMock::mock("getRandomField").returns(eph_sk).times(1);
Expand All @@ -527,7 +531,7 @@ mod test {

let _ = OracleMock::mock("incrementAppTaggingSecretIndexAsSender").returns(());

let payload = super::compute_log(context, note, recipient, sender);
let payload = super::compute_log(context, note, storage_slot, recipient, sender);

// The following value was generated by `encrypted_log_payload.test.ts`
// --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data.
Expand Down
6 changes: 3 additions & 3 deletions noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ use crate::{
};

trait ProveNoteInclusion {
fn prove_note_inclusion<Note, let N: u32>(header: BlockHeader, note: Note)
fn prove_note_inclusion<Note, let N: u32>(header: BlockHeader, note: Note, storage_slot: Field)
where
Note: NoteInterface<N> + NullifiableNote;
}

impl ProveNoteInclusion for BlockHeader {
fn prove_note_inclusion<Note, let N: u32>(self, note: Note)
fn prove_note_inclusion<Note, let N: u32>(self, note: Note, storage_slot: Field)
where
Note: NoteInterface<N> + NullifiableNote,
{
let note_hash = compute_note_hash_for_nullify(note);
let note_hash = compute_note_hash_for_nullify(note, storage_slot);

/// Safety: The witness is only used as a "magical value" that makes the merkle proof below pass. Hence it's safe.
let witness = unsafe {
Expand Down
12 changes: 9 additions & 3 deletions noir-projects/aztec-nr/aztec/src/history/note_validity.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@ trait ProveNoteValidity {
fn prove_note_validity<Note, let N: u32>(
header: BlockHeader,
note: Note,
storage_slot: Field,
context: &mut PrivateContext,
)
where
Note: NoteInterface<N> + NullifiableNote;
}

impl ProveNoteValidity for BlockHeader {
fn prove_note_validity<Note, let N: u32>(self, note: Note, context: &mut PrivateContext)
fn prove_note_validity<Note, let N: u32>(
self,
note: Note,
storage_slot: Field,
context: &mut PrivateContext,
)
where
Note: NoteInterface<N> + NullifiableNote,
{
self.prove_note_inclusion(note);
self.prove_note_not_nullified(note, context);
self.prove_note_inclusion(note, storage_slot);
self.prove_note_not_nullified(note, storage_slot, context);
}
}

10 changes: 8 additions & 2 deletions noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ trait ProveNoteIsNullified {
fn prove_note_is_nullified<Note, let N: u32>(
header: BlockHeader,
note: Note,
storage_slot: Field,
context: &mut PrivateContext,
)
where
Expand All @@ -50,11 +51,16 @@ trait ProveNoteIsNullified {

impl ProveNoteIsNullified for BlockHeader {
// docs:start:prove_note_is_nullified
fn prove_note_is_nullified<Note, let N: u32>(self, note: Note, context: &mut PrivateContext)
fn prove_note_is_nullified<Note, let N: u32>(
self,
note: Note,
storage_slot: Field,
context: &mut PrivateContext,
)
where
Note: NoteInterface<N> + NullifiableNote,
{
let nullifier = compute_siloed_nullifier(note, context);
let nullifier = compute_siloed_nullifier(note, storage_slot, context);

self.prove_nullifier_inclusion(nullifier);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ trait ProveNoteNotNullified {
fn prove_note_not_nullified<Note, let N: u32>(
header: BlockHeader,
note: Note,
storage_slot: Field,
context: &mut PrivateContext,
)
where
Expand All @@ -63,11 +64,16 @@ trait ProveNoteNotNullified {

impl ProveNoteNotNullified for BlockHeader {
// docs:start:prove_note_not_nullified
fn prove_note_not_nullified<Note, let N: u32>(self, note: Note, context: &mut PrivateContext)
fn prove_note_not_nullified<Note, let N: u32>(
self,
note: Note,
storage_slot: Field,
context: &mut PrivateContext,
)
where
Note: NoteInterface<N> + NullifiableNote,
{
let nullifier = compute_siloed_nullifier(note, context);
let nullifier = compute_siloed_nullifier(note, storage_slot, context);

self.prove_nullifier_non_inclusion(nullifier);
}
Expand Down
13 changes: 7 additions & 6 deletions noir-projects/aztec-nr/aztec/src/macros/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ comptime fn generate_compute_note_hash_and_optionally_a_nullifier() -> Quoted {
if_statements_list = if_statements_list.push_back(
quote {
$if_or_else_if note_type_id == $typ::get_note_type_id() {
aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack_content, note_header, compute_nullifier, packed_note_content)
aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack_content, note_header, compute_nullifier, storage_slot, packed_note_content)
}
},
);
Expand All @@ -147,10 +147,10 @@ comptime fn generate_compute_note_hash_and_optionally_a_nullifier() -> Quoted {
let if_statements = if_statements_list.join(quote {});

quote {
let note_header = aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot);
let note_header = aztec::prelude::NoteHeader::new(contract_address, nonce);
$if_statements
else {
panic(f"Unknown note type ID")
panic(f"Unknown note type ID: {note_type_id}")
}
}
} else {
Expand Down Expand Up @@ -184,13 +184,14 @@ comptime fn generate_process_log() -> Quoted {

// A typical implementation of the lambda looks something like this:
// ```
// |packed_note_content: BoundedVec<Field, MAX_NOTE_SERIALIZED_LEN>, note_header: NoteHeader, note_type_id: Field| {
// |packed_note_content: BoundedVec<Field, MAX_NOTE_SERIALIZED_LEN>, note_header: NoteHeader, storage_slot: Field, note_type_id: Field| {
// let hashes = if note_type_id == MyNoteType::get_note_type_id() {
// assert(packed_note_content.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH);
// dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier(
// MyNoteType::unpack_content,
// note_header,
// true,
// storage_slot,
// packed_note_content.storage(),
// )
// } else {
Expand Down Expand Up @@ -234,7 +235,7 @@ comptime fn generate_process_log() -> Quoted {
f"Expected note content of length {expected_len} but got {actual_len} for note type id {note_type_id}"
);

aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack_content, note_header, true, packed_note_content.storage())
aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack_content, note_header, true, storage_slot, packed_note_content.storage())
}
},
);
Expand All @@ -256,7 +257,7 @@ comptime fn generate_process_log() -> Quoted {
unique_note_hashes_in_tx,
first_nullifier_in_tx,
recipient,
|packed_note_content: BoundedVec<Field, _>, note_header, note_type_id| {
|packed_note_content: BoundedVec<Field, _>, note_header, storage_slot, note_type_id| {
let hashes = $if_note_type_id_match_statements
else {
panic(f"Unknown note type id {note_type_id}")
Expand Down
6 changes: 3 additions & 3 deletions noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ comptime fn get_next_note_type_id() -> Field {
/// ...
/// }
///
/// fn compute_note_hash(self) -> Field {
/// fn compute_note_hash(self, storage_slot: Field) -> Field {
/// ...
/// }
/// }
Expand Down Expand Up @@ -123,7 +123,7 @@ comptime fn generate_note_interface(

let merged_fields_len = merged_fields.len() + 1; // +1 for the storage slot appended below
let new_scalars = new_scalars_list
.push_back(quote { std::hash::from_field_unsafe(self.header.storage_slot) })
.push_back(quote { std::hash::from_field_unsafe(storage_slot) })
.push_back(quote { std::hash::from_field_unsafe($merged_fields_len) })
.join(quote {,});

Expand Down Expand Up @@ -151,7 +151,7 @@ comptime fn generate_note_interface(
self.header
}

fn compute_note_hash(self) -> Field {
fn compute_note_hash(self, storage_slot: Field) -> Field {
$new_aux_vars
let point = std::embedded_curve_ops::multi_scalar_mul(
[$new_generators],
Expand Down
Loading

0 comments on commit 8c4bb1c

Please sign in to comment.