Skip to content

Commit

Permalink
feat!: automatic NoteInterface and NoteGetterOptions auto select (#4508)
Browse files Browse the repository at this point in the history
Partially addresses:
#4519 (moved
autogeneration to the macro, even if not incremental)
Closes: #3011

Added the `#[aztec(note)]` attribute, which automatically implements
most of the `NoteInterface` trait in a struct marked as such, plus
several utilities. Even if this adds a fair share of "magic" to the note
implementation logic, it is structured in a way that it's hopefully easy
to follow, including meaningful errors attached to the correct span
during the process.

![Screenshot 2024-03-14 at 14 59
07](https://github.com/AztecProtocol/aztec-packages/assets/5404052/84a3d6e4-e346-4cfe-93eb-ec317632f344)

Hey you! Implement the trait!

![Screenshot 2024-03-14 at 14 46
39](https://github.com/AztecProtocol/aztec-packages/assets/5404052/bebfb3dd-c178-44d0-b9bc-005b5c9f0f38)
But only the meat and potatoes though.

As long as the user doesn't want to do any custom stuff, `get_header`,
`set_header`, `compute_note_content_hash`, `get_note_type_id`,
`serialize_content` and `deserialize_content` get automatically
implemented. Any combination of them can be overridden by the developer
though.

A metadata struct is also added, which takes the following form:

```rust 
struct CardNote {
    points: FieldSelector,
    randomness: FieldSelector,
    owner: FieldSelector,
}
```

This is used to implement a `properties()` function, which in turn can
be used in conjunction with the `NoteGetterOptions.select` and `.sort`

<img width="697" alt="Screenshot 2024-03-18 at 15 27 27"
src="https://github.com/AztecProtocol/aztec-packages/assets/5404052/5da531b3-0b7f-4cf9-9908-300ff8d98c6d">
  • Loading branch information
Thunkar authored Mar 19, 2024
1 parent 30908eb commit b2df979
Show file tree
Hide file tree
Showing 51 changed files with 1,290 additions and 551 deletions.
1 change: 1 addition & 0 deletions avm-transpiler/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,11 @@ You can view the implementation [here](https://github.com/AztecProtocol/aztec-pa

### `selects: BoundedVec<Option<Select>, N>`

`selects` is a collection of filtering criteria, specified by `Select { field_index: u8, value: Field, comparator: u3 }` structs. It instructs the data oracle to find notes whose (`field_index`)th field matches the provided `value`, according to the `comparator`.
`selects` is a collection of filtering criteria, specified by `Select { property_selector: PropertySelector, value: Field, comparator: u3 }` structs. It instructs the data oracle to find notes whose serialized field (as specified by the PropertySelector) matches the provided `value`, according to the `comparator`. The PropertySelector is in turn specified as having an `index` (nth position of the selected field in the serialized note), an `offset` (byte offset inside the selected serialized field) and `length` (bytes to read of the field from the offset)

### `sorts: BoundedVec<Option<Sort>, N>`

`sorts` is a set of sorting instructions defined by `Sort { field_index: u8, order: u2 }` structs. This directs the data oracle to sort the matching notes based on the value of the specified field index and in the indicated order. The value of order is **1** for _DESCENDING_ and **2** for _ASCENDING_.
`sorts` is a set of sorting instructions defined by `Sort { property_selector: PropertySelector, order: u2 }` structs. This directs the data oracle to sort the matching notes based on the value of the specified PropertySelector and in the indicated order. The value of order is **1** for _DESCENDING_ and **2** for _ASCENDING_.

### `limit: u32`

Expand Down
153 changes: 153 additions & 0 deletions docs/docs/misc/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,159 @@ keywords: [sandbox, cli, 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.

## 0.28.0

### Automatic NoteInterface implementation and selector changes

Implementing a note required a fair amount of boilerplate code, which has been substituted by the `#[aztec(note)]` attribute.

Before:

```rust
struct AddressNote {
address: AztecAddress,
owner: AztecAddress,
randomness: Field,
header: NoteHeader
}

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

fn deserialize_content(serialized_note: [Field; ADDRESS_NOTE_LEN]) -> Self {
AddressNote {
address: AztecAddress::from_field(serialized_note[0]),
owner: AztecAddress::from_field(serialized_note[1]),
randomness: serialized_note[2],
header: NoteHeader::empty(),
}
}

fn compute_note_content_hash(self) -> Field {
pedersen_hash(self.serialize_content(), 0)
}

fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
let note_hash_for_nullify = compute_note_hash_for_consumption(self);
let secret = context.request_nullifier_secret_key(self.owner);
pedersen_hash([
note_hash_for_nullify,
secret.low,
secret.high,
],0)
}

fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_consumption(self);
let secret = get_nullifier_secret_key(self.owner);
pedersen_hash([
note_hash_for_nullify,
secret.low,
secret.high,
],0)
}

fn set_header(&mut self, header: NoteHeader) {
self.header = header;
}

fn get_header(note: Self) -> NoteHeader {
note.header
}

fn broadcast(self, context: &mut PrivateContext, slot: Field) {
let encryption_pub_key = get_public_key(self.owner);
emit_encrypted_log(
context,
(*context).this_address(),
slot,
Self::get_note_type_id(),
encryption_pub_key,
self.serialize_content(),
);
}

fn get_note_type_id() -> Field {
6510010011410111511578111116101
}
}

```

After:

```rust
#[aztec(note)]
struct AddressNote {
address: AztecAddress,
owner: AztecAddress,
randomness: Field,
}

impl NoteInterface<ADDRESS_NOTE_LEN> for AddressNote {
fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
let note_hash_for_nullify = compute_note_hash_for_consumption(self);
let secret = context.request_nullifier_secret_key(self.owner);
// TODO(#1205) Should use a non-zero generator index.
pedersen_hash([
note_hash_for_nullify,
secret.low,
secret.high,
],0)
}

fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_consumption(self);
let secret = get_nullifier_secret_key(self.owner);
pedersen_hash([
note_hash_for_nullify,
secret.low,
secret.high,
],0)
}

fn broadcast(self, context: &mut PrivateContext, slot: Field) {
let encryption_pub_key = get_public_key(self.owner);
emit_encrypted_log(
context,
(*context).this_address(),
slot,
Self::get_note_type_id(),
encryption_pub_key,
self.serialize_content(),
);
}
}
```

Automatic note (de)serialization implementation also means it is now easier to filter notes using `NoteGetterOptions.select` via the `::properties()` helper:

Before:

```rust
let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, owner.to_field(), Option::none()).set_limit(1);
```

After:

```rust
let options = NoteGetterOptions::new().select(ValueNote::properties().value, amount, Option::none()).select(ValueNote::properties().owner, owner.to_field(), Option::none()).set_limit(1);
```

The helper returns a metadata struct that looks like this (if autogenerated)

```rust
ValueNoteProperties {
value: PropertySelector { index: 0, offset: 0, length: 32 },
owner: PropertySelector { index: 1, offset: 0, length: 32 },
randomness: PropertySelector { index: 2, offset: 0, length: 32 },
}
```

It can also be used for the `.sort` method.

## 0.27.0

### `initializer` macro replaces `constructor`
Expand Down
33 changes: 1 addition & 32 deletions noir-projects/aztec-nr/address-note/src/address_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,14 @@ global ADDRESS_NOTE_LEN: Field = 3;

// docs:start:address_note_def
// Stores an address
#[aztec(note)]
struct AddressNote {
address: AztecAddress,
owner: AztecAddress,
randomness: Field,
header: NoteHeader,
}

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

fn deserialize_content(serialized_note: [Field; ADDRESS_NOTE_LEN]) -> Self {
AddressNote {
address: AztecAddress::from_field(serialized_note[0]),
owner: AztecAddress::from_field(serialized_note[1]),
randomness: serialized_note[2],
header: NoteHeader::empty(),
}
}

fn compute_note_content_hash(self) -> Field {
// TODO(#1205) Should use a non-zero generator index.
pedersen_hash(self.serialize_content(), 0)
}

fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
let note_hash_for_nullify = compute_note_hash_for_consumption(self);
Expand All @@ -60,14 +43,6 @@ impl NoteInterface<ADDRESS_NOTE_LEN> for AddressNote {
],0)
}

fn set_header(&mut self, header: NoteHeader) {
self.header = header;
}

fn get_header(note: Self) -> NoteHeader {
note.header
}

// Broadcasts the note as an encrypted log on L1.
fn broadcast(self, context: &mut PrivateContext, slot: Field) {
let encryption_pub_key = get_public_key(self.owner);
Expand All @@ -82,12 +57,6 @@ impl NoteInterface<ADDRESS_NOTE_LEN> for AddressNote {
);
// docs:end:encrypted
}

fn get_note_type_id() -> Field {
// TODO(#4519): autogenerate
// python -c "print(int(''.join(str(ord(c)) for c in 'AddressNote')))"
6510010011410111511578111116101
}
}

impl AddressNote {
Expand Down
Loading

0 comments on commit b2df979

Please sign in to comment.