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

refactor: note emission #7003

Merged
merged 8 commits into from
Jun 12, 2024
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
6 changes: 3 additions & 3 deletions boxes/boxes/react/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
contract BoxReact {
use dep::aztec::prelude::{AztecAddress, PrivateMutable, Map, NoteInterface, NoteHeader};
use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint;

use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_with_keys;
use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN};

#[aztec(storage)]
Expand All @@ -20,7 +20,7 @@ contract BoxReact {
) {
let numbers = storage.numbers;
let mut new_number = ValueNote::new(number, owner_npk_m_hash);
numbers.at(owner).initialize(&mut new_number, owner_ovpk_m, owner_ivpk_m);
numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m));
}

#[aztec(private)]
Expand All @@ -33,7 +33,7 @@ contract BoxReact {
) {
let numbers = storage.numbers;
let mut new_number = ValueNote::new(number, owner_npk_m_hash);
numbers.at(owner).replace(&mut new_number, owner_ovpk_m, owner_ivpk_m);
numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m));
}

unconstrained fn getNumber(owner: AztecAddress) -> pub ValueNote {
Expand Down
22 changes: 17 additions & 5 deletions boxes/boxes/vanilla/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
contract Vanilla {
use dep::aztec::prelude::{AztecAddress, PrivateMutable, Map, NoteInterface, NoteHeader};
use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint;

use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_with_keys;
use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN};

#[aztec(storage)]
Expand All @@ -11,17 +11,29 @@ contract Vanilla {

#[aztec(private)]
#[aztec(initializer)]
fn constructor(number: Field, owner: AztecAddress, owner_npk_m_hash: Field, owner_ovpk_m: GrumpkinPoint, owner_ivpk_m: GrumpkinPoint) {
fn constructor(
number: Field,
owner: AztecAddress,
owner_npk_m_hash: Field,
owner_ovpk_m: GrumpkinPoint,
owner_ivpk_m: GrumpkinPoint
) {
let numbers = storage.numbers;
let mut new_number = ValueNote::new(number, owner_npk_m_hash);
numbers.at(owner).initialize(&mut new_number, owner_ovpk_m, owner_ivpk_m);
numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m));
}

#[aztec(private)]
fn setNumber(number: Field, owner: AztecAddress, owner_npk_m_hash: Field, owner_ovpk_m: GrumpkinPoint, owner_ivpk_m: GrumpkinPoint) {
fn setNumber(
number: Field,
owner: AztecAddress,
owner_npk_m_hash: Field,
owner_ovpk_m: GrumpkinPoint,
owner_ivpk_m: GrumpkinPoint
) {
let numbers = storage.numbers;
let mut new_number = ValueNote::new(number, owner_npk_m_hash);
numbers.at(owner).replace(&mut new_number, owner_ovpk_m, owner_ivpk_m);
numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m));
}

unconstrained fn getNumber(owner: AztecAddress) -> pub ValueNote {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ Then to register the recipient's complete address in PXE we would call `register
If a note recipient is one of the accounts inside the PXE, we don't need to register it as a recipient because we already have the public key available. You can register a recipient as shown [here](../how_to_deploy_contract.md)
:::

### Call encrypt_and_emit_note
### Call emit

To emit encrypted logs you don't need to import any library. You call the context method `encrypt_and_emit_note`:
To emit encrypted logs you can import the `encode_and_encrypt` or `encode_and_encrypt_with_keys` functions and pass them into the `emit` function after inserting a note. An example can be seen in the reference token contract's transfer function:

#include_code encrypted /noir-projects/aztec-nr/address-note/src/address_note.nr rust
#include_code encrypted /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust

Furthermore, if not emitting the note, one should explicitly `discard` the value returned from the note creation.

### Successfully process the encrypted event

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,3 @@ Beware that this hash computation is what the aztec.nr library is doing, and not
With this note structure, the contract can require that only notes sitting at specific storage slots can be used by specific operations, e.g., if transferring funds from `from` to `to`, the notes to destroy should be linked to `H(map_slot, from)` and the new notes (except the change-note) should be linked to `H(map_slot, to)`.

That way, we can have logical storage slots, without them really existing. This means that knowing the storage slot for a note is not enough to actually figure out what is in there (whereas it would be for looking up public state).

15 changes: 15 additions & 0 deletions docs/docs/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ 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] emit encrypted logs

Emitting or broadcasting encrypted notes are no longer done as part of the note creation, but must explicitly be either emitted or discarded instead.

```diff
+ use dep::aztec::encrypted_logs::encrypted_note_emission::{encode_and_encrypt, encode_and_encrypt_with_keys};

- storage.balances.sub(from, amount);
+ storage.balances.sub(from, amount).emit(encode_and_encrypt_with_keys(&mut context, from, from));
+ storage.balances.sub(from, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, from_ivpk));
+ storage.balances.sub(from, amount).discard();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nice!

```

## 0.42.0

### [Aztec.nr] Unconstrained Context
Expand Down
12 changes: 0 additions & 12 deletions noir-projects/aztec-nr/address-note/src/address_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,6 @@ impl NoteInterface<ADDRESS_NOTE_LEN, ADDRESS_NOTE_BYTES_LEN> for AddressNote {
GENERATOR_INDEX__NOTE_NULLIFIER as Field,
])
}

// Broadcasts the note as an encrypted log on L1.
fn broadcast(self, context: &mut PrivateContext, slot: Field, ovpk_m: GrumpkinPoint, ivpk_m: GrumpkinPoint) {
// docs:start:encrypted
context.encrypt_and_emit_note(
slot,
ovpk_m,
ivpk_m,
self,
);
// docs:end:encrypted
}
}

impl AddressNote {
Expand Down
23 changes: 0 additions & 23 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -351,29 +351,6 @@ impl PrivateContext {
emit_encrypted_event_log(contract_address, randomness, encrypted_log, counter);
}

pub fn encrypt_and_emit_note<Note, N, NB, M>(
&mut self,
storage_slot: Field,
ovpk_m: GrumpkinPoint,
ivpk_m: GrumpkinPoint,
note: Note
) where Note: NoteInterface<N, NB>, [Field; N]: LensForEncryptedLog<N, M> {
let note_hash_counter = note.get_header().note_hash_counter;
let note_exists_index = find_index(
self.new_note_hashes.storage,
|n: NoteHash| n.counter == note_hash_counter
);
assert(
note_exists_index as u32 != MAX_NEW_NOTE_HASHES_PER_CALL, "Can only emit a note log for an existing note."
);

let contract_address = self.this_address();
let ovsk_app = self.request_ovsk_app(ovpk_m.hash());

let encrypted_log: [u8; M] = compute_encrypted_note_log(contract_address, storage_slot, ovsk_app, ovpk_m, ivpk_m, note);
self.emit_raw_note_log(note_hash_counter, encrypted_log);
}

pub fn emit_raw_note_log<M>(&mut self, note_hash_counter: u32, encrypted_log: [u8; M]) {
let counter = self.next_counter();
let len = encrypted_log.len() as Field + 4;
Expand Down
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/encrypted_logs.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ mod header;
mod incoming_body;
mod outgoing_body;
mod payload;
mod encrypted_note_emission;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::{
context::PrivateContext, note::{note_emission::NoteEmission, note_interface::NoteInterface},
encrypted_logs::payload::compute_encrypted_note_log, oracle::logs_traits::LensForEncryptedLog
};
use dep::protocol_types::{
address::AztecAddress, grumpkin_point::GrumpkinPoint, abis::note_hash::NoteHash,
constants::MAX_NEW_NOTE_HASHES_PER_CALL, utils::arrays::find_index
};

fn emit_with_keys<Note, N, NB, M>(
context: &mut PrivateContext,
note: Note,
ovpk: GrumpkinPoint,
ivpk: GrumpkinPoint
) where Note: NoteInterface<N, NB>, [Field; N]: LensForEncryptedLog<N, M> {
let note_header = note.get_header();
let note_hash_counter = note_header.note_hash_counter;
let storage_slot = note_header.storage_slot;

let note_exists_index = find_index(
context.new_note_hashes.storage,
|n: NoteHash| n.counter == note_hash_counter
);
assert(
note_exists_index as u32 != MAX_NEW_NOTE_HASHES_PER_CALL, "Can only emit a note log for an existing note."
);

let contract_address: AztecAddress = context.this_address();
let ovsk_app: Field = context.request_ovsk_app(ovpk.hash());

let encrypted_log: [u8; M] = compute_encrypted_note_log(contract_address, storage_slot, ovsk_app, ovpk, ivpk, note);

context.emit_raw_note_log(note_hash_counter, encrypted_log);
}

pub fn encode_and_encrypt<Note, N, NB, M>(
context: &mut PrivateContext,
ov: AztecAddress,
iv: AztecAddress
) -> fn[(&mut PrivateContext, AztecAddress, AztecAddress)](NoteEmission<Note>) -> () where Note: NoteInterface<N, NB>, [Field; N]: LensForEncryptedLog<N, M> {
| e: NoteEmission<Note> | {
let header = context.get_header();
let ovpk = header.get_ovpk_m(context, ov);
let ivpk = header.get_ivpk_m(context, iv);
emit_with_keys(context, e.note, ovpk, ivpk);
}
}

pub fn encode_and_encrypt_with_keys<Note, N, NB, M>(
context: &mut PrivateContext,
ovpk: GrumpkinPoint,
ivpk: GrumpkinPoint
) -> fn[(&mut PrivateContext, GrumpkinPoint, GrumpkinPoint)](NoteEmission<Note>) -> () where Note: NoteInterface<N, NB>, [Field; N]: LensForEncryptedLog<N, M> {
| e: NoteEmission<Note> | {
emit_with_keys(context, e.note, ovpk, ivpk);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ mod test {

fn compute_nullifier_without_context(self) -> Field {1}

fn broadcast(self, context: &mut PrivateContext, slot: Field, ovpk_m: GrumpkinPoint, ivpk_m: GrumpkinPoint) {}

fn serialize_content(self) -> [Field; ADDRESS_NOTE_LEN] { [self.address.to_field(), self.owner.to_field(), self.randomness]}

fn deserialize_content(fields: [Field; ADDRESS_NOTE_LEN]) -> Self {
Expand Down
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ mod note_header;
mod note_interface;
mod note_viewer_options;
mod utils;
mod note_emission;
11 changes: 5 additions & 6 deletions noir-projects/aztec-nr/aztec/src/note/lifecycle.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ use dep::protocol_types::grumpkin_point::GrumpkinPoint;
use crate::context::{PrivateContext, PublicContext};
use crate::note::{
note_header::NoteHeader, note_interface::NoteInterface,
utils::{compute_note_hash_for_insertion, compute_note_hash_for_consumption}
utils::{compute_note_hash_for_insertion, compute_note_hash_for_consumption},
note_emission::NoteEmission
};
use crate::oracle::notes::{notify_created_note, notify_nullified_note};

pub fn create_note<Note, N, M>(
context: &mut PrivateContext,
storage_slot: Field,
note: &mut Note,
ovpk_m: GrumpkinPoint,
ivpk_m: GrumpkinPoint
) where Note: NoteInterface<N, M> {
note: &mut Note
) -> NoteEmission<Note> where Note: NoteInterface<N, M> {
let contract_address = (*context).this_address();
let note_hash_counter = context.side_effect_counter;

Expand All @@ -36,7 +35,7 @@ pub fn create_note<Note, N, M>(

context.push_new_note_hash(inner_note_hash);

Note::broadcast(*note, context, storage_slot, ovpk_m, ivpk_m);
NoteEmission::new(*note)
}

pub fn create_note_hash_from_public<Note, N, M>(
Expand Down
45 changes: 45 additions & 0 deletions noir-projects/aztec-nr/aztec/src/note/note_emission.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* A note emission struct containing the information required for emitting a note.
* The exact `emit` logic is passed in by the application code
*/
struct NoteEmission<Note> {
note: Note
}

impl<Note> NoteEmission<Note> {
pub fn new(note: Note) -> Self {
Self { note }
}

pub fn emit<Env>(self, _emit: fn[Env](Self) -> ()) {
_emit(self);
}

pub fn discard(self) {}
}

/**
* A struct wrapping note emission in `Option<T>`.
* This is the struct provided to application codes, which can be used to emit
* only when a note was actually inserted.
* It is fairly common to have cases where a function conditionally inserts,
* and this allows us to keep the same API for emission in both cases (e.g. inserting
* a change note in a token's transfer function only when there is "change" left).
*/
struct OuterNoteEmission<Note> {
emission: Option<NoteEmission<Note>>,
}

impl<Note> OuterNoteEmission<Note> {
pub fn new(emission: Option<NoteEmission<Note>>) -> Self {
Self { emission }
}

pub fn emit<Env>(self, _emit: fn[Env](NoteEmission<Note>) -> ()) {
if self.emission.is_some() {
_emit(self.emission.unwrap());
}
}

pub fn discard(self) {}
}
4 changes: 1 addition & 3 deletions noir-projects/aztec-nr/aztec/src/note/note_interface.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ trait NoteInterface<N, M> {
fn compute_nullifier(self, context: &mut PrivateContext) -> Field;

fn compute_nullifier_without_context(self) -> Field;

fn broadcast(self, context: &mut PrivateContext, slot: Field, ovpk_m: GrumpkinPoint, ivpk_m: GrumpkinPoint) -> ();


// Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation
fn serialize_content(self) -> [Field; N];

Expand Down
10 changes: 4 additions & 6 deletions noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use dep::protocol_types::{
use crate::context::{PrivateContext, UnconstrainedContext};
use crate::note::{
lifecycle::create_note, note_getter::{get_note, view_notes}, note_interface::NoteInterface,
note_viewer_options::NoteViewerOptions
note_viewer_options::NoteViewerOptions, note_emission::NoteEmission
};
use crate::oracle::notes::check_nullifier_exists;
use crate::state_vars::storage::Storage;
Expand Down Expand Up @@ -46,15 +46,13 @@ impl<Note> PrivateImmutable<Note, &mut PrivateContext> {
// docs:start:initialize
pub fn initialize<N, M>(
self,
note: &mut Note,
ovpk_m: GrumpkinPoint,
ivpk_m: GrumpkinPoint
) where Note: NoteInterface<N, M> {
note: &mut Note
) -> NoteEmission<Note> where Note: NoteInterface<N, M> {
// Nullify the storage slot.
let nullifier = self.compute_initialization_nullifier();
self.context.push_new_nullifier(nullifier, 0);

create_note(self.context, self.storage_slot, note, ovpk_m, ivpk_m);
create_note(self.context, self.storage_slot, note)
}
// docs:end:initialize

Expand Down
Loading
Loading