Skip to content

Commit

Permalink
refactor: partial notes macros (#8993)
Browse files Browse the repository at this point in the history
I realized that we need to provide a way to automatically encode the unencrypted log as well. For this reason I decided to refactor the partial notes macro such that we have a `FinalizationPayload` which takes the note hiding point and the nullable values as args to constructor. Along with this change it made sense to rename `PartialPayload` as `SetupPayload`.

I also went ahead and polished all the note macro code.
  • Loading branch information
benesjan authored Oct 4, 2024
1 parent 880c45f commit 567e9a8
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 164 deletions.
245 changes: 119 additions & 126 deletions noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,20 @@ comptime fn generate_note_interface(
) -> (Quoted, u32) {
let name = s.name();
let typ = s.as_type();
let (fields, aux_vars) = flatten_to_fields(quote { self }, typ, &[quote {self.header}]);
let aux_vars_for_serialization = if aux_vars.len() > 0 {
let joint = aux_vars.join(quote {;});

// First we compute note content serialization. We do that by passing the whole note struct
// to the `flatten_to_fields(...)` and omitting the header.
let (content_fields_list, content_aux_vars_list) = flatten_to_fields(quote { self }, typ, &[quote {self.header}]);

// If there are `aux_vars` we need to join them with `;` and add a trailing `;` to the joined string.
let content_aux_vars = if content_aux_vars_list.len() > 0 {
let joint = content_aux_vars_list.join(quote {;});
quote { $joint; }
} else {
quote {}
};
let serialized_fields = fields.join(quote {,});
let content_len = fields.len();
let content_fields = content_fields_list.join(quote {,});
let content_len = content_fields_list.len();

let (deserialized_content, _) = pack_from_fields(
quote { self },
Expand All @@ -52,11 +57,12 @@ comptime fn generate_note_interface(
&[(quote {header}, quote { aztec::note::note_header::NoteHeader::empty() })]
);

// `compute_note_hash()` is computed over all the fields so we need to merge fixed and nullable fields.
// Second we compute quotes for MSM
// `compute_note_hash()` is computed over all the fields so we need to merge fixed and nullable.
let merged_fields = indexed_fixed_fields.append(indexed_nullable_fields);
// Now we prefix each of the merged fields with `self.` since they refer to the struct members here.
let merged_fields = merged_fields.map(| (name, typ, index): (Quoted, Type, u32) | (quote { self.$name }, typ, index));
let (new_generators_list, new_scalars_list, _, new_aux_vars) = generate_multi_scalar_mul(merged_fields);
let prefixed_merged_fields = merged_fields.map(| (name, typ, index): (Quoted, Type, u32) | (quote { self.$name }, typ, index));
let (new_generators_list, new_scalars_list, _, new_aux_vars) = generate_multi_scalar_mul(prefixed_merged_fields);

let new_generators = new_generators_list.push_back(quote { aztec::generators::G_slot }).join(quote {,});
let new_scalars = new_scalars_list.push_back(quote { std::hash::from_field_unsafe(self.header.storage_slot) }).join(quote {,});
Expand Down Expand Up @@ -90,8 +96,8 @@ comptime fn generate_note_interface(
}

fn serialize_content(self) -> [Field; $content_len] {
$aux_vars_for_serialization
[$serialized_fields]
$content_aux_vars
[$content_fields]
}

fn get_note_type_id() -> Field {
Expand Down Expand Up @@ -224,117 +230,76 @@ comptime fn generate_multi_scalar_mul(indexed_fields: [(Quoted, Type, u32)]) ->
(generators_list, scalars_list, args_list, aux_vars)
}

comptime fn generate_note_hiding_point(
comptime fn generate_setup_payload(
s: StructDefinition,
indexed_fixed_fields: [(Quoted, Type, u32)],
indexed_nullable_fields: [(Quoted, Type, u32)]
) -> (Quoted, Quoted) {
let name = s.name();
let hiding_point_name = f"{name}HidingPoint".quoted_contents();
let setup_payload_name = f"{name}SetupPayload".quoted_contents();

let (finalize_generators_list, finalize_scalars_list, finalize_args_list, finalize_aux_vars) = generate_multi_scalar_mul(indexed_nullable_fields);

let finalize_args = if finalize_args_list.len() > 0 {
&[quote {self}].append(finalize_args_list).join(quote {,})
} else {
quote {self}
};
// First we get the MSM related quotes
let (new_generators_list, new_scalars_list, new_args_list, new_aux_vars) = generate_multi_scalar_mul(indexed_fixed_fields);
let new_args = &[quote {mut self}].append(new_args_list).push_back(quote { storage_slot: Field }).join(quote {,});
let new_generators = new_generators_list.push_back(quote { aztec::generators::G_slot }).join(quote {,});
let new_scalars = new_scalars_list.push_back(quote { std::hash::from_field_unsafe(storage_slot) }).join(quote {,});

let finalize_body = if indexed_nullable_fields.len() > 0 {
let finalize_generators = finalize_generators_list.join(quote {,});
let finalize_scalars = finalize_scalars_list.join(quote {,});
quote {
$finalize_aux_vars
let point = std::embedded_curve_ops::multi_scalar_mul(
[$finalize_generators],
[$finalize_scalars]
) + self.inner;
point.x
}
} else {
quote { self.inner.x }
};
// Then the log plaintext ones
let log_plaintext_length = indexed_fixed_fields.len() * 32 + 64;
let setup_log_plaintext = get_setup_log_plaintext_body(s, log_plaintext_length, indexed_nullable_fields);

(quote {
struct $hiding_point_name {
inner: aztec::protocol_types::point::Point
}

impl $hiding_point_name {
fn from_point(mut self, point: aztec::protocol_types::point::Point) -> $hiding_point_name {
self.inner = point;
self
}


fn finalize($finalize_args) -> Field {
$finalize_body
}

fn to_point(self) -> aztec::protocol_types::point::Point {
self.inner
}
struct $setup_payload_name {
log_plaintext: [u8; $log_plaintext_length],
hiding_point: aztec::protocol_types::point::Point
}

impl aztec::protocol_types::traits::Serialize<aztec::protocol_types::point::POINT_LENGTH> for $hiding_point_name {
fn serialize(self) -> [Field; aztec::protocol_types::point::POINT_LENGTH] {
self.inner.serialize()
}
}
impl $setup_payload_name {
fn new($new_args) -> $setup_payload_name {
$new_aux_vars
let hiding_point = std::embedded_curve_ops::multi_scalar_mul(
[$new_generators],
[$new_scalars]
);
$setup_log_plaintext

impl aztec::protocol_types::traits::Deserialize<aztec::protocol_types::point::POINT_LENGTH> for $hiding_point_name {
fn deserialize(serialized: [Field; aztec::protocol_types::point::POINT_LENGTH]) -> $hiding_point_name {
$hiding_point_name { inner: aztec::protocol_types::point::Point::deserialize(serialized) }
$setup_payload_name {
log_plaintext,
hiding_point
}
}
}

impl aztec::protocol_types::traits::Empty for $hiding_point_name {
impl aztec::protocol_types::traits::Empty for $setup_payload_name {
fn empty() -> Self {
Self { inner: aztec::protocol_types::point::Point::empty() }
}
}

impl Eq for $hiding_point_name {
fn eq(self, other: Self) -> bool {
self.inner == other.inner
Self { log_plaintext: [0; $log_plaintext_length], hiding_point: aztec::protocol_types::point::Point::empty() }
}
}

}, hiding_point_name)
}, setup_payload_name)
}

comptime fn generate_partial_payload(
comptime fn get_setup_log_plaintext_body(
s: StructDefinition,
hiding_point_name: Quoted,
indexed_fixed_fields: [(Quoted, Type, u32)],
log_plaintext_length: u32,
indexed_nullable_fields: [(Quoted, Type, u32)]
) -> (Quoted, Quoted) {
) -> Quoted {
let name = s.name();
let partial_payload_name = f"{name}PartialPayload".quoted_contents();

let (new_generators_list, new_scalars_list, new_args_list, new_aux_vars) = generate_multi_scalar_mul(indexed_fixed_fields);

let new_args = &[quote {mut self}].append(new_args_list).push_back(quote { storage_slot: Field }).join(quote {,});
let new_generators = new_generators_list.push_back(quote { aztec::generators::G_slot }).join(quote {,});
let new_scalars = new_scalars_list.push_back(quote { std::hash::from_field_unsafe(storage_slot) }).join(quote {,});

let log_plaintext_length = indexed_fixed_fields.len() * 32 + 64;

// Now we compute serialization of the fixed fields. We do that by passing the whole note struct
// to the flatten_to_fields function but we omit the NoteHeader and the nullable fields.
let typ = s.as_type();
let mut to_omit = indexed_nullable_fields.map(| (name, _, _): (Quoted, Type, u32) | name);
to_omit = to_omit.push_back(quote { header });
let (fields, aux_vars) = flatten_to_fields(quote { }, typ, to_omit);
let to_omit = indexed_nullable_fields.map(| (name, _, _): (Quoted, Type, u32) | name).push_back(quote { header });
let (fields_list, aux_vars) = flatten_to_fields(quote { }, s.as_type(), to_omit);

// If there are `aux_vars` we need to join them with `;` and add a trailing `;` to the joined string.
let aux_vars_for_serialization = if aux_vars.len() > 0 {
let joint = aux_vars.join(quote {;});
quote { $joint; }
} else {
quote {}
};
let serialized_fields = fields.join(quote {,});
let fields = fields_list.join(quote {,});

// indexed_fixed_fields has preserved order so we can used to serialize the note to log
let partial_note_log_plaintext = quote {
quote {
let mut log_plaintext: [u8; $log_plaintext_length] = [0; $log_plaintext_length];

let storage_slot_bytes: [u8; 32] = storage_slot.to_be_bytes();
Expand All @@ -346,61 +311,94 @@ comptime fn generate_partial_payload(
}

$aux_vars_for_serialization
let serialized_note = [$serialized_fields];
let serialized_note = [$fields];

for i in 0..serialized_note.len() {
let bytes: [u8; 32] = serialized_note[i].to_be_bytes();
for j in 0..32 {
log_plaintext[64 + i * 32 + j] = bytes[j];
}
}
}
}

comptime fn generate_finalization_payload(
s: StructDefinition,
indexed_fixed_fields: [(Quoted, Type, u32)],
indexed_nullable_fields: [(Quoted, Type, u32)]
) -> (Quoted, Quoted) {
let name = s.name();
let finalization_payload_name = f"{name}FinalizationPayload".quoted_contents();

// We compute serialization of the nullable fields which are to be emitted as an unencrypted log. We do that by
// passing the whole note struct to the `flatten_to_fields(...)` function but we omit the `NoteHeader` and
// the fixed fields.
let to_omit = indexed_fixed_fields.map(| (name, _, _): (Quoted, Type, u32) | name).push_back(quote { header });
let (fields_list, aux_vars) = flatten_to_fields(quote { }, s.as_type(), to_omit);

// If there are `aux_vars` we need to join them with `;` and add a trailing `;` to the joined string.
let aux_vars_for_serialization = if aux_vars.len() > 0 {
let joint = aux_vars.join(quote {;});
quote { $joint; }
} else {
quote {}
};

// We compute the log length and we concatenate the fields into a single quote.
let log_length = fields_list.len();
let fields = fields_list.join(quote {,});

// Now we compute quotes relevant to the multi-scalar multiplication.
let (generators_list, scalars_list, args_list, msm_aux_vars) = generate_multi_scalar_mul(indexed_nullable_fields);

let generators = generators_list.join(quote {,});
let scalars = scalars_list.join(quote {,});
let args = args_list.join(quote {,});

(quote {
struct $partial_payload_name {
log_plaintext: [u8; $log_plaintext_length],
hiding_point: $hiding_point_name
struct $finalization_payload_name {
log: [Field; $log_length],
note_hash: Field,
}

impl $partial_payload_name {
fn new($new_args) -> $partial_payload_name {
$new_aux_vars
let point = std::embedded_curve_ops::multi_scalar_mul(
[$new_generators],
[$new_scalars]
);
let hiding_point = $hiding_point_name::empty().from_point(point);
$partial_note_log_plaintext
impl $finalization_payload_name {
fn new(mut self, hiding_point: aztec::protocol_types::point::Point, $args) -> $finalization_payload_name {
$aux_vars_for_serialization
self.log = [$fields];

$partial_payload_name {
log_plaintext,
hiding_point
}
$msm_aux_vars
let finalization_hiding_point = std::embedded_curve_ops::multi_scalar_mul(
[$generators],
[$scalars]
) + hiding_point;

self.note_hash = finalization_hiding_point.x;
self
}
}

impl aztec::protocol_types::traits::Empty for $partial_payload_name {
impl aztec::protocol_types::traits::Empty for $finalization_payload_name {
fn empty() -> Self {
Self { log_plaintext: [0; $log_plaintext_length], hiding_point: $hiding_point_name::empty() }
Self { log: [0; $log_length], note_hash: 0 }
}
}
}, partial_payload_name)
}, finalization_payload_name)
}

comptime fn generate_partial_note_impl(
s: StructDefinition,
hiding_point_name: Quoted,
partial_payload_name: Quoted
setup_payload_name: Quoted,
finalization_payload_name: Quoted
) -> Quoted {
let name = s.name();
quote {
impl aztec::note::note_interface::PartialNote<$hiding_point_name, $partial_payload_name> for $name {
fn hiding_point() -> $hiding_point_name {
$hiding_point_name::empty()
impl aztec::note::note_interface::PartialNote<$setup_payload_name, $finalization_payload_name> for $name {
fn setup_payload() -> $setup_payload_name {
$setup_payload_name::empty()
}

fn partial_payload() -> $partial_payload_name {
$partial_payload_name::empty()
fn finalization_payload() -> $finalization_payload_name {
$finalization_payload_name::empty()
}
}
}
Expand Down Expand Up @@ -477,15 +475,10 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) ->
let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, nullable_fields);

let (common, note_type_id) = common_note_annotation(s);
let (note_hiding_point, hiding_point_name) = generate_note_hiding_point(s, indexed_nullable_fields);
let (partial_payload_impl, partial_payload_name) = generate_partial_payload(
s,
hiding_point_name,
indexed_fixed_fields,
indexed_nullable_fields
);
let (setup_payload_impl, setup_payload_name) = generate_setup_payload(s, indexed_fixed_fields, indexed_nullable_fields);
let (finalization_payload_impl, finalization_payload_name) = generate_finalization_payload(s, indexed_fixed_fields, indexed_nullable_fields);
let (note_interface_impl, note_serialized_len) = generate_note_interface(s, note_type_id, indexed_fixed_fields, indexed_nullable_fields);
let partial_note_impl = generate_partial_note_impl(s, hiding_point_name, partial_payload_name);
let partial_note_impl = generate_partial_note_impl(s, setup_payload_name, finalization_payload_name);
register_note(
s,
note_serialized_len,
Expand All @@ -496,8 +489,8 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) ->

quote {
$common
$note_hiding_point
$partial_payload_impl
$setup_payload_impl
$finalization_payload_impl
$note_interface_impl
$partial_note_impl
}
Expand Down
6 changes: 3 additions & 3 deletions noir-projects/aztec-nr/aztec/src/note/note_interface.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ pub trait NoteProperties<T> {
fn properties() -> T;
}

pub trait PartialNote<T, P> {
fn hiding_point() -> T;
pub trait PartialNote<S, F> {
fn setup_payload() -> S;

fn partial_payload() -> P;
fn finalization_payload() -> F;
}

pub trait NullifiableNote {
Expand Down
Loading

0 comments on commit 567e9a8

Please sign in to comment.