-
Notifications
You must be signed in to change notification settings - Fork 598
feat: add note metadata #12240
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
Merged
Merged
feat: add note metadata #12240
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
603c5d8
Add note metadata
nventuro 70f483d
Some docs
nventuro e999f39
Apply renamings
nventuro 622e89b
Fix test
nventuro 7045db6
Remove note hash counter
nventuro 8aea1a3
Fix from trait
nventuro e8ee039
Add comment
nventuro fe2298a
Fix private exec test
nventuro a8bbad4
apply review comments
nventuro ae7cfb6
Merge branch 'master' into nv/note-metadata
nventuro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| use protocol_types::traits::Serialize; | ||
|
|
||
| // There's temporarily quite a bit of boilerplate here because Noir does not yet support enums. This file will | ||
| // eventually be simplified into something closer to: | ||
| // | ||
| // pub enum NoteMetadata { | ||
| // PendingSamePhase{ note_hash_counter: u32 }, | ||
| // PendingOtherPhase{ note_hash_counter: u32, nonce: Field }, | ||
| // Settled{ nonce: Field }, | ||
| // } | ||
| // | ||
| // For now, we have `NoteMetadata` acting as a sort of tagged union. | ||
|
|
||
| struct NoteStageEnum { | ||
| /// A note that was created in the transaction that is currently being executed, during the current execution phase, | ||
| /// i.e. non-revertible or revertible. | ||
| /// | ||
| /// These notes are not yet in the note hash tree, though they will be inserted unless nullified in this transaction | ||
| /// (becoming a transient note). | ||
| PENDING_SAME_PHASE: u8, | ||
| /// A note that was created in the transaction that is currently being executed, during the previous execution | ||
| /// phase. Because there are only two phases and their order is always the same (first non-revertible and then | ||
| /// revertible) this implies that the note was created in the non-revertible phase, and that the current phase is | ||
| /// the revertible phase. | ||
| /// | ||
| /// These notes are not yet in the note hash tree, though they will be inserted **even if nullified in this | ||
| /// transaction**. This means that they must be nullified as if they were settled (i.e. using the unique note hash) | ||
| /// in order to avoid double spends once they become settled. | ||
| PENDING_PREVIOUS_PHASE: u8, | ||
| /// A note that was created in a prior transaction and is therefore already in the note hash tree. | ||
| SETTLED: u8, | ||
| } | ||
|
|
||
| global NoteStage: NoteStageEnum = | ||
| NoteStageEnum { PENDING_SAME_PHASE: 1, PENDING_PREVIOUS_PHASE: 2, SETTLED: 3 }; | ||
|
|
||
| /// The metadata required to both prove a note's existence and destroy it, by computing the correct note hash for kernel | ||
| /// read requests, as well as the correct nullifier to avoid double-spends. | ||
| /// | ||
| /// This represents a note in any of the three valid stages (pending same phase, pending previous phase, or settled). In | ||
| /// order to access the underlying fields callers must first find the appropriate stage (e.g. via `is_settled()`) and | ||
| /// then convert this into the appropriate type (e.g. via `to_settled()`). | ||
| #[derive(Eq, Serialize)] | ||
| pub struct NoteMetadata { | ||
| stage: u8, | ||
| maybe_nonce: Field, | ||
| } | ||
|
|
||
| impl NoteMetadata { | ||
| /// Constructs a `NoteMetadata` object from optional note hash counter and nonce. Both a zero note hash counter and | ||
| /// a zero nonce are invalid, so those are used to signal non-existent values. | ||
| pub fn from_raw_data(nonzero_note_hash_counter: bool, maybe_nonce: Field) -> Self { | ||
| if nonzero_note_hash_counter { | ||
| if maybe_nonce == 0 { | ||
| Self { stage: NoteStage.PENDING_SAME_PHASE, maybe_nonce } | ||
| } else { | ||
| Self { stage: NoteStage.PENDING_PREVIOUS_PHASE, maybe_nonce } | ||
| } | ||
| } else if maybe_nonce != 0 { | ||
| Self { stage: NoteStage.SETTLED, maybe_nonce } | ||
| } else { | ||
| panic( | ||
| f"Note has a zero note hash counter and no nonce - existence cannot be proven", | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /// Returns true if the note is pending **and** from the same phase, i.e. if it's been created in the current | ||
| /// transaction during the current execution phase (either non-revertible or revertible). | ||
| pub fn is_pending_same_phase(self) -> bool { | ||
| self.stage == NoteStage.PENDING_SAME_PHASE | ||
| } | ||
|
|
||
| /// Returns true if the note is pending **and** from the previous phase, i.e. if it's been created in the current | ||
| /// transaction during an execution phase prior to the current one. Because private execution only has two phases | ||
| /// with strict ordering, this implies that the note was created in the non-revertible phase, and that the current | ||
| /// phase is the revertible phase. | ||
| pub fn is_pending_previous_phase(self) -> bool { | ||
| self.stage == NoteStage.PENDING_PREVIOUS_PHASE | ||
| } | ||
|
|
||
| /// Returns true if the note is settled, i.e. if it's been created in a prior transaction and is therefore already | ||
| /// in the note hash tree. | ||
| pub fn is_settled(self) -> bool { | ||
| self.stage == NoteStage.SETTLED | ||
| } | ||
|
|
||
| /// Asserts that the metadata is that of a pending note from the same phase and converts it accordingly. | ||
| pub fn to_pending_same_phase(self) -> PendingSamePhaseNoteMetadata { | ||
| assert_eq(self.stage, NoteStage.PENDING_SAME_PHASE); | ||
| PendingSamePhaseNoteMetadata::new() | ||
| } | ||
|
|
||
| /// Asserts that the metadata is that of a pending note from a previous phase and converts it accordingly. | ||
| pub fn to_pending_previous_phase(self) -> PendingPreviousPhaseNoteMetadata { | ||
| assert_eq(self.stage, NoteStage.PENDING_PREVIOUS_PHASE); | ||
| PendingPreviousPhaseNoteMetadata::new(self.maybe_nonce) | ||
| } | ||
|
|
||
| /// Asserts that the metadata is that of a settled note and converts it accordingly. | ||
| pub fn to_settled(self) -> SettledNoteMetadata { | ||
| assert_eq(self.stage, NoteStage.SETTLED); | ||
| SettledNoteMetadata::new(self.maybe_nonce) | ||
| } | ||
| } | ||
|
|
||
| impl From<PendingSamePhaseNoteMetadata> for NoteMetadata { | ||
| fn from(_value: PendingSamePhaseNoteMetadata) -> Self { | ||
| NoteMetadata::from_raw_data(true, std::mem::zeroed()) | ||
| } | ||
| } | ||
|
|
||
| impl From<PendingPreviousPhaseNoteMetadata> for NoteMetadata { | ||
| fn from(value: PendingPreviousPhaseNoteMetadata) -> Self { | ||
| NoteMetadata::from_raw_data(true, value.nonce()) | ||
| } | ||
| } | ||
|
|
||
| impl From<SettledNoteMetadata> for NoteMetadata { | ||
| fn from(value: SettledNoteMetadata) -> Self { | ||
| NoteMetadata::from_raw_data(false, value.nonce()) | ||
| } | ||
| } | ||
|
|
||
| /// The metadata required to both prove a note's existence and destroy it, by computing the correct note hash for kernel | ||
| /// read requests, as well as the correct nullifier to avoid double-spends. | ||
| /// | ||
| /// This represents a pending same phase note, i.e. a note that was created in the transaction that is currently being | ||
| /// executed during the current execution phase (either non-revertible or revertible). | ||
| pub struct PendingSamePhaseNoteMetadata { | ||
| // This struct contains no fields since there is no metadata associated with a pending same phase note: it has no | ||
| // nonce (since it may get squashed by a nullifier emitted in the same phase), and while it does have a note hash | ||
| // counter we cannot constrain its value (and don't need to - only that it is non-zero). | ||
| } | ||
|
|
||
| impl PendingSamePhaseNoteMetadata { | ||
| pub fn new() -> Self { | ||
| Self {} | ||
| } | ||
| } | ||
|
|
||
| /// The metadata required to both prove a note's existence and destroy it, by computing the correct note hash for kernel | ||
| /// read requests, as well as the correct nullifier to avoid double-spends. | ||
| /// | ||
| /// This represents a pending previous phase note, i.e. a note that was created in the transaction that is currently | ||
| /// being executed, during the previous execution phase. Because there are only two phases and their order is always the | ||
| /// same (first non-revertible and then revertible) this implies that the note was created in the non-revertible phase, | ||
| /// and that the current phase is the revertible phase. | ||
| pub struct PendingPreviousPhaseNoteMetadata { | ||
| nonce: Field, | ||
| // This struct does not contain a note hash counter, even though one exists for this note, because we cannot | ||
| // constrain its value (and don't need to - only that it is non-zero). | ||
| } | ||
|
|
||
| impl PendingPreviousPhaseNoteMetadata { | ||
| pub fn new(nonce: Field) -> Self { | ||
| Self { nonce } | ||
| } | ||
|
|
||
| pub fn nonce(self) -> Field { | ||
| self.nonce | ||
| } | ||
| } | ||
|
|
||
| /// The metadata required to both prove a note's existence and destroy it, by computing the correct note hash for kernel | ||
| /// read requests, as well as the correct nullifier to avoid double-spends. | ||
| /// | ||
| /// This represents a settled note, i.e. a note that was created in a prior transaction and is therefore already in the | ||
| /// note hash tree. | ||
| pub struct SettledNoteMetadata { | ||
| nonce: Field, | ||
| } | ||
|
|
||
| impl SettledNoteMetadata { | ||
| pub fn new(nonce: Field) -> Self { | ||
| Self { nonce } | ||
| } | ||
|
|
||
| pub fn nonce(self) -> Field { | ||
| self.nonce | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,34 +1,27 @@ | ||
| use protocol_types::{address::AztecAddress, traits::Serialize, utils::arrays::array_concat}; | ||
| use crate::note::note_metadata::NoteMetadata; | ||
| use protocol_types::{ | ||
| address::AztecAddress, | ||
| traits::{Serialize, ToField}, | ||
| utils::arrays::array_concat, | ||
| }; | ||
|
|
||
| /// A container of a note and the metadata required to constrain its existence, regardless of whether the note is | ||
| /// historical (created in a previous transaction) or transient (created in the current transaction). | ||
| /// A container of a note and the metadata required to prove its existence, regardless of whether the note is | ||
| /// pending (created in the current transaction) or settled (created in a previous transaction). | ||
| #[derive(Eq)] | ||
| pub struct RetrievedNote<NOTE> { | ||
| pub note: NOTE, | ||
| pub contract_address: AztecAddress, | ||
| pub nonce: Field, | ||
| pub note_hash_counter: u32, // a note_hash_counter of 0 means non-transient | ||
| pub metadata: NoteMetadata, | ||
| } | ||
|
|
||
| impl<NOTE> Eq for RetrievedNote<NOTE> | ||
| where | ||
| NOTE: Eq, | ||
| { | ||
| fn eq(self, other: Self) -> bool { | ||
| (self.note == other.note) | ||
| & (self.contract_address == other.contract_address) | ||
| & (self.nonce == other.nonce) | ||
| & (self.note_hash_counter == other.note_hash_counter) | ||
| } | ||
| } | ||
|
|
||
| impl<NOTE, let N: u32> Serialize<N + 3> for RetrievedNote<NOTE> | ||
| impl<NOTE, let N: u32> Serialize<N + 1 + 2> for RetrievedNote<NOTE> | ||
| where | ||
| NOTE: Serialize<N>, | ||
| { | ||
| fn serialize(self) -> [Field; N + 3] { | ||
| fn serialize(self) -> [Field; N + 1 + 2] { | ||
| array_concat( | ||
| self.note.serialize(), | ||
| [self.contract_address.to_field(), self.nonce, self.note_hash_counter as Field], | ||
| array_concat(self.note.serialize(), [self.contract_address.to_field()]), | ||
| self.metadata.serialize(), | ||
| ) | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think readers would generally have no clue why this implies that it has a nonce.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some reflection I think we just simplify this fn so that it also takes a note hash. I'll do that once both our PRs are in.