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

feat: 7 bit long note_type_id #10951

Merged
merged 8 commits into from
Jan 8, 2025
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
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
"noirc",
"noirup",
"nullifer",
"Nullifiable",
"offchain",
"onchain",
"opentelemetry",
Expand Down
33 changes: 17 additions & 16 deletions noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ comptime global NOTE_HEADER_TYPE: Type = type_of(NoteHeader::empty());
pub comptime mut global NOTES: UHashMap<Type, (StructDefinition, u32, Field, [(Quoted, u32, bool)]), BuildHasherDefault<Poseidon2Hasher>> =
UHashMap::default();

/// Computes a note type id by hashing a note name (e.g. `TokenNote`), getting the first 4 bytes of the hash
/// and returning it as a `Field`.
comptime fn compute_note_type_id(name: Quoted) -> Field {
let (name_as_str_quote, _) = name.as_str_quote();
pub comptime mut global NOTE_TYPE_ID_COUNTER: u32 = 0;

/// The note type id is set by enumerating the note types.
comptime fn get_next_note_type_id() -> Field {
// We assert that the note type id fits within 7 bits
assert(
NOTE_TYPE_ID_COUNTER < 128 as u32,
"A contract can contain at most 128 different note types",
);

unquote!(
quote {
let bytes = $name_as_str_quote.as_bytes();
let hash = protocol_types::hash::poseidon2_hash_bytes(bytes);
let hash_bytes = hash.to_be_bytes::<4>();
protocol_types::utils::field::field_from_bytes(hash_bytes, true)
},
)
let note_type_id = NOTE_TYPE_ID_COUNTER as Field;
NOTE_TYPE_ID_COUNTER += 1;
note_type_id
}

/// Generates default `NoteInterface` implementation for a given note struct `s` and returns it as quote along with
Expand Down Expand Up @@ -123,6 +123,7 @@ comptime fn generate_note_interface(
let mut buffer: [u8; $content_len * 32 + 64] = [0; $content_len * 32 + 64];

let storage_slot_bytes: [u8; 32] = storage_slot.to_be_bytes();
// TODO(#10952): The following can be reduced to 7 bits
let note_type_id_bytes: [u8; 32] = $name::get_note_type_id().to_be_bytes();

for i in 0..32 {
Expand Down Expand Up @@ -265,7 +266,7 @@ pub(crate) comptime fn generate_note_export(
let mut hasher = Poseidon2Hasher::default();
s.as_type().hash(&mut hasher);
let hash = hasher.finish() as u32;
let global_export_name = f"{name}_{hash}_EXPORTS".quoted_contents();
let global_export_name = f"{name}_EXPORTS_{hash}".quoted_contents();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sneaked in this change as it did not follow the docs on line 249.

let note_fields_name = f"{name}Fields_{hash}".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() });
Expand Down Expand Up @@ -871,7 +872,7 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) ->
inject_note_header(s);

let note_properties = generate_note_properties(s);
let note_type_id = compute_note_type_id(s.name());
let note_type_id = get_next_note_type_id();
let (setup_payload_impl, setup_payload_name) =
generate_setup_payload(s, indexed_fixed_fields, indexed_nullable_fields);
let (finalization_payload_impl, finalization_payload_name) =
Expand Down Expand Up @@ -914,7 +915,7 @@ pub comptime fn note(s: StructDefinition) -> Quoted {
inject_note_header(s);

let note_properties = generate_note_properties(s);
let note_type_id = compute_note_type_id(s.name());
let note_type_id = get_next_note_type_id();
let (note_interface_impl, note_serialized_len) = generate_note_interface(
s,
note_type_id,
Expand Down Expand Up @@ -944,7 +945,7 @@ pub comptime fn note_custom_interface(s: StructDefinition) -> Quoted {
inject_note_header(s);

let note_properties = generate_note_properties(s);
let note_type_id = compute_note_type_id(s.name());
let note_type_id = get_next_note_type_id();
let serialized_len_type = fresh_type_variable();
let note_interface_impl = s.as_type().get_trait_impl(
quote { crate::note::note_interface::NoteInterface<$serialized_len_type> }
Expand Down
2 changes: 2 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 @@ -3,6 +3,8 @@ use std::{collections::umap::UHashMap, hash::{BuildHasherDefault, poseidon2::Pos
use super::utils::get_serialized_size;
use super::utils::is_note;

/// Stores a map from a module to the name of the struct that describes its storage layout.
/// This is then used when generating a `storage_layout()` getter on the contract struct.
pub comptime mut global STORAGE_LAYOUT_NAME: UHashMap<Module, Quoted, BuildHasherDefault<Poseidon2Hasher>> =
UHashMap::default();

Expand Down
1 change: 0 additions & 1 deletion noir-projects/aztec-nr/aztec/src/note/note_type_id.nr

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ type = "contract"
[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
value_note = { path = "../../../aztec-nr/value-note" }
uint_note = { path = "../../../aztec-nr/uint-note" }
token_portal_content_hash_lib = { path = "../token_portal_content_hash_lib" }
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
mod test;
mod test_note;

// A contract used for testing a random hodgepodge of small features from simulator and end-to-end tests.
use dep::aztec::macros::aztec;

#[aztec]
contract Test {
// Note: If you import a new kind of note you will most likely need to update the test_note_type_id test
// as the note type ids of current notes might have changed.

use dep::aztec::encrypted_logs::encrypted_event_emission::encode_and_encrypt_event_unconstrained;
use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note;
Expand Down Expand Up @@ -42,6 +45,10 @@ contract Test {
use std::meta::derive;

use crate::test_note::TestNote;

// Imported just to test note type ids
use dep::uint_note::uint_note::UintNote;

#[derive(Serialize)]
#[event]
struct ExampleEvent {
Expand Down
12 changes: 12 additions & 0 deletions noir-projects/noir-contracts/contracts/test_contract/src/test.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::test_note::TestNote;
use dep::uint_note::uint_note::UintNote;
use dep::value_note::value_note::ValueNote;

#[test]
unconstrained fn test_note_type_id() {
// The order in which the note types are sorted seems arbitrary and an implementation detail of Noir,
// but the important thing is that they are sequential and start from 0.
assert_eq(UintNote::get_note_type_id(), 0, "UintNote type id should be 0");
assert_eq(ValueNote::get_note_type_id(), 1, "ValueNote type id should be 1");
assert_eq(TestNote::get_note_type_id(), 2, "TestNote type id should be 2");
}
2 changes: 1 addition & 1 deletion yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ export interface PXE {
isContractInitialized(address: AztecAddress): Promise<boolean>;

/**
* Returns the enctypred events given search parameters.
* Returns the encrypted events given search parameters.
* @param eventMetadata - Metadata of the event. This should be the class generated from the contract. e.g. Contract.events.Event
* @param from - The block number to search from.
* @param limit - The amount of blocks to search.
Expand Down
15 changes: 11 additions & 4 deletions yarn-project/foundation/src/abi/note_selector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { toBigIntBE } from '../bigint-buffer/index.js';
import { randomBytes } from '../crypto/index.js';
import { type Fr } from '../fields/fields.js';
import { randomInt } from '../crypto/index.js';
import { Fr } from '../fields/fields.js';
import { hexSchemaFor } from '../schemas/utils.js';
import { BufferReader } from '../serialize/buffer_reader.js';
import { TypeRegistry } from '../serialize/type_registry.js';
Expand All @@ -14,7 +14,10 @@ export interface NoteSelector {
_branding: 'NoteSelector';
}

/** A note selector is the first 4 bytes of the hash of a note signature. */
/**
* A note selector is a 7 bit long value that identifies a note type within a contract.
* TODO(#10952): Encoding of note type id can be reduced to 7 bits.
*/
export class NoteSelector extends Selector {
/**
* Deserializes from a buffer or reader, corresponding to a write in cpp.
Expand All @@ -24,6 +27,9 @@ export class NoteSelector extends Selector {
static fromBuffer(buffer: Buffer | BufferReader) {
const reader = BufferReader.asReader(buffer);
const value = Number(toBigIntBE(reader.readBytes(Selector.SIZE)));
if (value >= 1 << 7) {
throw new Error('Invalid note selector');
}
return new NoteSelector(value);
}

Expand Down Expand Up @@ -55,7 +61,8 @@ export class NoteSelector extends Selector {
* @returns A random selector.
*/
static random() {
return NoteSelector.fromBuffer(randomBytes(Selector.SIZE));
const value = randomInt(1 << 7);
return NoteSelector.fromField(new Fr(value));
}

toJSON() {
Expand Down
Loading