@@ -14,6 +14,12 @@ use crate::Hasher;
1414use crate :: errors:: NoteError ;
1515use crate :: note:: { NoteAttachment , NoteAttachmentKind , NoteAttachmentScheme } ;
1616
17+ // CONSTANTS
18+ // ================================================================================================
19+
20+ /// The number of bits by which the note type is offset in the first felt of the note metadata.
21+ const NOTE_TYPE_SHIFT : u64 = 4 ;
22+
1723// NOTE METADATA
1824// ================================================================================================
1925
@@ -34,7 +40,7 @@ use crate::note::{NoteAttachment, NoteAttachmentKind, NoteAttachmentScheme};
3440/// The header word has the following layout:
3541///
3642/// ```text
37- /// 0th felt: [sender_id_suffix (56 bits) | 6 zero bits | note_type (2 bit)]
43+ /// 0th felt: [sender_id_suffix (56 bits) | reserved (3 bits) | note_type (1 bit) | version (4 bits )]
3844/// 1st felt: [sender_id_prefix (64 bits)]
3945/// 2nd felt: [32 zero bits | note_tag (32 bits)]
4046/// 3rd felt: [30 zero bits | attachment_kind (2 bits) | attachment_scheme (32 bits)]
@@ -44,11 +50,13 @@ use crate::note::{NoteAttachment, NoteAttachmentKind, NoteAttachmentScheme};
4450/// - 1st felt: The lower 8 bits of the account ID suffix are `0` by construction, so that they can
4551/// be overwritten with other data. The suffix' most significant bit must be zero such that the
4652/// entire felt retains its validity even if all of its lower 8 bits are set to `1`. So the note
47- /// type can be comfortably encoded.
53+ /// type and version can be comfortably encoded.
4854/// - 2nd felt: Is equivalent to the prefix of the account ID so it inherits its validity.
4955/// - 3rd felt: The upper 32 bits are always zero.
5056/// - 4th felt: The upper 30 bits are always zero.
5157///
58+ /// The version is hardcoded to 0 and is reserved to make it easier to introduce another version.
59+ ///
5260/// The value of the attachment word depends on the
5361/// [`NoteAttachmentKind`](crate::note::NoteAttachmentKind):
5462/// - [`NoteAttachmentKind::None`](crate::note::NoteAttachmentKind::None): Empty word.
@@ -73,6 +81,12 @@ pub struct NoteMetadata {
7381}
7482
7583impl NoteMetadata {
84+ /// Version 0 of the note metadata encoding.
85+ ///
86+ /// If we make this public, we may want to instead consider introducing a `NoteMetadataVersion`
87+ /// struct, similar to `AccountIdVersion`.
88+ const VERSION_0 : u8 = 0 ;
89+
7690 // CONSTRUCTORS
7791 // --------------------------------------------------------------------------------------------
7892
@@ -337,12 +351,12 @@ impl TryFrom<Word> for NoteMetadataHeader {
337351// HELPER FUNCTIONS
338352// ================================================================================================
339353
340- /// Merges the suffix of an [`AccountId`] and the [`NoteType`] into a single [`Felt`].
354+ /// Merges the suffix of an [`AccountId`] and note metadata into a single [`Felt`].
341355///
342356/// The layout is as follows:
343357///
344358/// ```text
345- /// [sender_id_suffix (56 bits) | 6 zero bits | note_type (2 bits)]
359+ /// [sender_id_suffix (56 bits) | reserved (3 bits) | note_type (1 bit) | version (4 bits)]
346360/// ```
347361///
348362/// The most significant bit of the suffix is guaranteed to be zero, so the felt retains its
@@ -353,28 +367,44 @@ fn merge_sender_suffix_and_note_type(sender_id_suffix: Felt, note_type: NoteType
353367 let mut merged = sender_id_suffix. as_canonical_u64 ( ) ;
354368
355369 let note_type_byte = note_type as u8 ;
356- debug_assert ! ( note_type_byte < 4 , "note type must not contain values >= 4" ) ;
357- merged |= note_type_byte as u64 ;
370+ debug_assert ! ( note_type_byte < 2 , "note type must not contain values >= 2" ) ;
371+ // note_type at bit 4, version at bits 0..=3 (hardcoded to NoteMetadata::VERSION_0_NUMBER)
372+ merged |= ( note_type_byte as u64 ) << NOTE_TYPE_SHIFT ;
373+ merged |= NoteMetadata :: VERSION_0 as u64 ;
358374
359375 // SAFETY: The most significant bit of the suffix is zero by construction so the u64 will be a
360376 // valid felt.
361377 Felt :: try_from ( merged) . expect ( "encoded value should be a valid felt" )
362378}
363379
364- /// Unmerges the sender ID suffix and note type.
380+ /// Unmerges the sender ID suffix and note metadata (note type and version) .
365381fn unmerge_sender_suffix_and_note_type ( element : Felt ) -> Result < ( Felt , NoteType ) , NoteError > {
366- const NOTE_TYPE_MASK : u8 = 0b11 ;
367- // Inverts the note type mask.
368- const SENDER_SUFFIX_MASK : u64 = !( NOTE_TYPE_MASK as u64 ) ;
382+ // The mask that clears out the lower 8 bits to recover the sender suffix.
383+ const SENDER_SUFFIX_MASK : u64 = 0xffff_ffff_ffff_ff00 ;
384+
385+ let raw = element. as_canonical_u64 ( ) ;
386+ let version = ( raw & 0b1111 ) as u8 ;
387+ let note_type_bit = ( ( raw >> NOTE_TYPE_SHIFT ) & 0b1 ) as u8 ;
388+ let reserved = ( ( raw >> 5 ) & 0b111 ) as u8 ;
389+
390+ if reserved != 0 {
391+ return Err ( NoteError :: other ( "reserved bits in note metadata header must be zero" ) ) ;
392+ }
393+
394+ if version != NoteMetadata :: VERSION_0 {
395+ return Err ( NoteError :: other ( format ! (
396+ "unsupported note metadata version {version}, expected {}" ,
397+ NoteMetadata :: VERSION_0
398+ ) ) ) ;
399+ }
369400
370- let note_type_byte = element. as_canonical_u64 ( ) as u8 & NOTE_TYPE_MASK ;
371- let note_type = NoteType :: try_from ( note_type_byte) . map_err ( |source| {
401+ let note_type = NoteType :: try_from ( note_type_bit) . map_err ( |source| {
372402 NoteError :: other_with_source ( "failed to decode note type from metadata header" , source)
373403 } ) ?;
374404
375405 // No bits were set so felt should still be valid.
376- let sender_suffix = Felt :: try_from ( element . as_canonical_u64 ( ) & SENDER_SUFFIX_MASK )
377- . expect ( "felt should still be valid" ) ;
406+ let sender_suffix =
407+ Felt :: try_from ( raw & SENDER_SUFFIX_MASK ) . expect ( "felt should still be valid" ) ;
378408
379409 Ok ( ( sender_suffix, note_type) )
380410}
0 commit comments