Skip to content

Commit

Permalink
feat: 7 bit long note_type_id (#10951)
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan authored Jan 8, 2025
1 parent b5d51eb commit 6fc5673
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 22 deletions.
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();
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 @@ -43,6 +46,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

0 comments on commit 6fc5673

Please sign in to comment.