Skip to content

Commit

Permalink
feat: nullable note fields info in ABI (#8901)
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan authored Oct 1, 2024
1 parent 2bb9ca6 commit e0d5e06
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 12 deletions.
12 changes: 8 additions & 4 deletions noir-projects/aztec-nr/aztec/src/macros/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use dispatch::generate_public_dispatch;

/// Marks a contract as an Aztec contract, generating the interfaces for its functions and notes, as well as injecting
/// the `compute_note_hash_and_optionally_a_nullifier` function PXE requires in order to validate notes.
///
/// Note: This is a module annotation, so the returned quote gets injected inside the module (contract) itself.
pub comptime fn aztec(m: Module) -> Quoted {
let interface = generate_contract_interface(m);
let unconstrained_functions = m.functions().filter(
Expand Down Expand Up @@ -106,14 +108,14 @@ comptime fn generate_compute_note_hash_and_optionally_a_nullifier() -> Quoted {
let mut max_note_length: u32 = 0;
let notes = NOTES.entries();
let body = if notes.len() > 0 {
max_note_length = notes.fold(0, | acc, (_, (_, len, _)): (Type, (StructDefinition, u32, Field)) | {
max_note_length = notes.fold(0, | acc, (_, (_, len, _, _)): (Type, (StructDefinition, u32, Field, [(Quoted, u32, bool)])) | {
acc + len
});

let mut if_statements_list = &[];

for i in 0..notes.len() {
let (typ, (_, _, _)) = notes[i];
let (typ, (_, _, _, _)) = notes[i];
let if_or_else_if = if i == 0 {
quote { if }
} else {
Expand Down Expand Up @@ -157,9 +159,11 @@ comptime fn generate_compute_note_hash_and_optionally_a_nullifier() -> Quoted {

comptime fn generate_note_exports() -> Quoted {
let notes = NOTES.values();
// Second value in each tuple is `note_serialized_len` and that is ignored here because it's only used when
// generating the `compute_note_hash_and_optionally_a_nullifier` function.
notes.map(
| (s, _, note_type_id): (StructDefinition, u32, Field) | {
generate_note_export(s, note_type_id)
| (s, _, note_type_id, fields): (StructDefinition, u32, Field, [(Quoted, u32, bool)]) | {
generate_note_export(s, note_type_id, fields)
}
).join(quote {})
}
87 changes: 79 additions & 8 deletions noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ use crate::note::{note_header::NoteHeader, note_getter_options::PropertySelector

comptime global NOTE_HEADER_TYPE = type_of(NoteHeader::empty());

comptime mut global NOTES: UHashMap<Type, (StructDefinition, u32, Field), BuildHasherDefault<Poseidon2Hasher>> = UHashMap::default();
// A map from note type to (note_struct_definition, serialized_note_length, note_type_id, fields).
// `fields` is an array of tuples where each tuple contains the name of the field/struct member (e.g. `amount`
// in `TokenNote`), the index of where the serialized member starts in the serialized note and a flag indicating
// whether the field is nullable or not.
comptime mut global NOTES: UHashMap<Type, (StructDefinition, u32, Field, [(Quoted, u32, bool)]), BuildHasherDefault<Poseidon2Hasher>> = UHashMap::default();

comptime fn compute_note_type_id(name: Quoted) -> Field {
let name_as_str_quote = name.as_str_quote();
Expand Down Expand Up @@ -144,14 +148,37 @@ comptime fn generate_note_properties(s: StructDefinition) -> Quoted {
}
}

pub(crate) comptime fn generate_note_export(s: StructDefinition, note_type_id: Field) -> Quoted {
/// Generates note export for a given note struct. The export is a global variable that contains note type id,
/// note name and information about note fields (field name, index and whether the field is nullable or not).
pub(crate) comptime fn generate_note_export(
s: StructDefinition,
note_type_id: Field,
fields: [(Quoted, u32, bool)]
) -> Quoted {
let name = s.name();
let global_export_name = f"{name}_EXPORTS".quoted_contents();
let note_fields_name = f"{name}Fields".quoted_contents();
let note_name_as_str = name.as_str_quote();
let note_name_str_len = unquote!(quote { $note_name_as_str.as_bytes().len() });

let mut note_fields = &[];
let mut note_field_constructors = &[];
for field in fields {
let (name, index, nullable) = field;
note_fields = note_fields.push_back(quote { $name: aztec::note::note_field::NoteField });
note_field_constructors = note_field_constructors.push_back(quote { $name: aztec::note::note_field::NoteField { index: $index, nullable: $nullable }});
}

let note_fields = note_fields.join(quote {,});
let note_field_constructors = note_field_constructors.join(quote {,});

quote {
struct $note_fields_name {
$note_fields
}

#[abi(notes)]
global $global_export_name: (Field, str<$note_name_str_len>) = ($note_type_id,$note_name_as_str);
global $global_export_name: (Field, str<$note_name_str_len>, $note_fields_name) = ($note_type_id,$note_name_as_str, $note_fields_name { $note_field_constructors });
}
}

Expand Down Expand Up @@ -286,10 +313,32 @@ comptime fn generate_partial_note_impl(s: StructDefinition, hiding_point_name: Q
}
}

comptime fn register_note(note: StructDefinition, note_serialized_len: u32, note_type_id: Field) {
NOTES.insert(note.as_type(), (note, note_serialized_len, note_type_id));
comptime fn register_note(
note: StructDefinition,
note_serialized_len: u32,
note_type_id: Field,
fixed_fields: [(Quoted, Type, u32)],
nullable_fields: [(Quoted, Type, u32)]
) {
let mut fields = &[];
for field in fixed_fields {
let (name, _, index) = field;
fields = fields.push_back((name, index, false));
}
for field in nullable_fields {
let (name, _, index) = field;
fields = fields.push_back((name, index, true));
}

NOTES.insert(
note.as_type(),
(note, note_serialized_len, note_type_id, fields)
);
}

/// Separates note struct members into fixed and nullable ones. It also stores the index of where each struct member
/// starts in the serialized note. Note that each struct member can occupy multiple fields (as in Field type).
/// An example of a struct member occupying multiple fields is `amount` in `TokenNote` that uses `U128` type.
comptime fn index_note_fields(
s: StructDefinition,
nullable_fields: [Quoted]
Expand All @@ -307,6 +356,7 @@ comptime fn index_note_fields(
}
}
let (flattened, _) = flatten_to_fields(name, typ, &[]);
// Each struct member can occupy multiple fields so we need to increment the counter accordingly
counter+=flattened.len();
}
(indexed_fixed_fields, indexed_nullable_fields)
Expand All @@ -329,6 +379,8 @@ comptime fn common_note_annotation(s: StructDefinition) -> (Quoted, Field) {

#[varargs]
pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) -> Quoted {
// We separate struct members into fixed ones and nullable ones and we store info about the start index of each
// member in the serialized note array.
let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, nullable_fields);

let (common, note_type_id) = common_note_annotation(s);
Expand All @@ -341,7 +393,13 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) ->
indexed_nullable_fields
);
let partial_note_impl = generate_partial_note_impl(s, hiding_point_name);
register_note(s, note_serialized_len, note_type_id);
register_note(
s,
note_serialized_len,
note_type_id,
indexed_fixed_fields,
indexed_nullable_fields
);

quote {
$common
Expand All @@ -362,7 +420,13 @@ pub comptime fn note(s: StructDefinition) -> Quoted {
indexed_fixed_fields,
indexed_nullable_fields
);
register_note(s, note_serialized_len, note_type_id);
register_note(
s,
note_serialized_len,
note_type_id,
indexed_fixed_fields,
indexed_nullable_fields
);

quote {
$common
Expand All @@ -379,7 +443,14 @@ pub comptime fn note_custom_interface(s: StructDefinition) -> Quoted {

let note_serialized_len = note_interface_impl.expect(f"Note {name} must implement NoteInterface trait").trait_generic_args()[0].as_constant().unwrap();

register_note(s, note_serialized_len, note_type_id);
let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, &[]);
register_note(
s,
note_serialized_len,
note_type_id,
indexed_fixed_fields,
indexed_nullable_fields
);

quote {
$common
Expand Down
3 changes: 3 additions & 0 deletions noir-projects/aztec-nr/aztec/src/macros/storage/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub comptime fn storage(s: StructDefinition) -> Quoted {
let (name, typ) = field;
let (storage_field_constructor, serialized_size) = generate_storage_field_constructor(typ, quote { $slot }, false);
storage_vars_constructors = storage_vars_constructors.push_back(quote { $name: $storage_field_constructor });
// We have `Storable` in a separate `.nr` file instead of defining it in the last quote of this function
// because that way a dev gets a more reasonable error if he defines a struct with the same name in
// a contract.
storage_layout_fields = storage_layout_fields.push_back(quote { $name: dep::aztec::prelude::Storable });
storage_layout_constructors = storage_layout_constructors.push_back(quote { $name: dep::aztec::prelude::Storable { slot: $slot } });
//let with_context_generic = add_context_generic(typ, context_generic);
Expand Down
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/note/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ mod note_interface;
mod note_viewer_options;
mod utils;
mod note_emission;
mod note_field;
5 changes: 5 additions & 0 deletions noir-projects/aztec-nr/aztec/src/note/note_field.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Used by macros when generating note export.
pub struct NoteField {
index: u32,
nullable: bool
}

0 comments on commit e0d5e06

Please sign in to comment.