-
Notifications
You must be signed in to change notification settings - Fork 56
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
Extract notes via events issue #539 #572
Conversation
ebea437
to
e1b7693
Compare
@@ -9,7 +9,7 @@ members = [ | |||
|
|||
[workspace.package] | |||
edition = "2021" | |||
rust-version = "1.75" | |||
rust-version = "1.77" |
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.
Bumped because split_first_chunk
was stabilized on 1.77
. This API is used together with u32::from_le_bytes
to encode the NoteScript
to be used with the advice_map.
} | ||
|
||
impl fmt::Display for TransactionKernelError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::InvalidStorageSlotIndex(index) => { | ||
TransactionKernelError::InvalidStorageSlotIndex(index) => { |
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.
Self::
confused the rust-analyzer and broke the suggested actions. I have done similar changes in other places, where use TransactionKernelError::*
caused issues (namely, autocompletion is gone, actions don't work)
miden-tx/src/host/state_tracker.rs
Outdated
|
||
/// Extracts information from the process state about the storage slot being updated and | ||
/// records the latest value of this storage slot. | ||
pub(super) fn on_account_storage_set_item<S: ProcessState>( |
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 put the handlers together, it makes it easier to follow the code.
@@ -53,17 +51,13 @@ pub fn mock_inputs_with_account_seed( | |||
}, | |||
}; | |||
|
|||
// mock notes |
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.
These comments didn't add context, the function being called is already named mock_notes
.
/// The metadata consists of: | ||
/// - sender is the ID of the account which created the note. | ||
/// - note_type defines how the note is to be stored (e.g., on-chain or off-chain). | ||
/// - tag is a value which can be used by the recipient(s) to identify notes intended for them. | ||
/// - aux is arbitrary user-defined value. |
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.
Moved the documentation to the fields. Having the comments along side thing they refer to makes it easier to update when changes are done.
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.
There is one downside to this: the generated documentation will no longer include this info because the fields are private. So, one would be forced to look at the source code to understand what the metadata consists of. That was one of the main reasons why we originally put the description of fields into struct comments.
We could still keep the comments for each specific filed, but I think we also need similar detail in the struct comment.
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 don't think that is a problem. If the attribute is private, the docs should not be shown because it is not part of the public API. The user has to rely on the public API, in this case the accessor methods (they have documentation).
Hasher::merge(&[merge_script, inputs.commitment()]) | ||
} | ||
|
||
impl Note { |
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.
Renamed to NoteConsumable
and moved to the consumable
module. For our current usage, Note
is too generic, the new name should make a bit cleared that this structure is used to hold the details necessary to consume a note as an input.
serial_num: Word, | ||
script: NoteScript, |
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.
This should probably be put into a NoteNullifier
, but this PR was too big already.
/// When a note is produced in a transaction, the note's recipient, assets, and metadata must be | ||
/// known. However, other information about the note may or may not be know to the note's producer. | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
pub struct OutputNote { |
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.
Remove in favor of an enum of NoteConsumable
and NoteEnvelope
, which correspond to public and private notes.
output_notes: OutputNotes<NoteEnvelope>, | ||
|
||
/// Optionally the output note's data, used to share the note with the network. | ||
output_note_details: BTreeMap<NoteId, Note>, |
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.
This is no longer necessary, the enum gives better type safety since the data is not split into two places.
.filter(|&k| !known_output_ids.contains(k)) | ||
.cloned() | ||
.collect(); | ||
if !unknown_ids.is_empty() { |
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.
This is an example of improved type safety, the check is no longer necessary, because the data is no longer split into two places.
e1b7693
to
4b4ec49
Compare
miden-lib/src/notes/mod.rs
Outdated
@@ -31,17 +34,19 @@ pub fn create_p2id_note<R: FeltRng>( | |||
assets: Vec<Asset>, | |||
note_type: NoteType, | |||
mut rng: R, | |||
) -> Result<Note, NoteError> { | |||
) -> Result<NoteConsumable, NoteError> { |
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 I disagree slightly with this name change. I feel like Note
/InputNote
/NoteMetadata
are good matches for what they represent already, and maybe Consumable
collides a little bit semantically with Input
? Don't feel strongly and this was likely discussed somewhere else, so I wouldn't mind either way but just my two cents.
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.
If you look at Note
/InputNote
/NoteMetadata
alone, yes. But we also have NoteEnvelope
and NoteOutput
. In comparison to these, it is not clear at all how do they differ from plain Note
.
I'm happy to change this, but I have no better suffix, any suggestions?
objects/src/transaction/outputs.rs
Outdated
/// For the purposes of this struct, anything that can be reduced to a note envelope can be an | ||
/// output note. However, [ToEnvelope] trait is currently implemented only for [OutputNote] and | ||
/// [NoteEnvelope], and so these are the only two allowed output note types. |
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.
Looks like this doc comment might be outdated.
miden-tx/src/host/mod.rs
Outdated
if data.len() != 12 { | ||
return Err(TransactionKernelError::MalformedRecipientData(data.to_vec())); | ||
} | ||
let inputs_hash = Digest::new([data[0], data[1], data[2], data[3]]); | ||
let script_root = Digest::new([data[4], data[5], data[6], data[7]]); | ||
let serial_num = [data[8], data[9], data[10], data[11]]; | ||
let input_els = self.adv_provider.get_mapped_values(&inputs_hash); | ||
let script_data = self.adv_provider.get_mapped_values(&script_root).unwrap_or(&[]); | ||
|
||
let inputs = NoteInputs::new(input_els.map(|e| e.to_vec()).unwrap_or_default()) | ||
.map_err(TransactionKernelError::MalformedNoteInputs)?; | ||
let script = NoteScript::try_from(script_data) | ||
.map_err(|_| TransactionKernelError::MalformedNoteScript(script_data.to_vec()))?; | ||
let recipient = NoteRecipient::new(serial_num, script, inputs); | ||
NoteOutput::Public(NoteConsumable::new(vault, metadata, recipient)) |
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.
Should these if branches validate the note type (or maybe the if should be based on note_type
)? Feels like if there is some corruption up to this point, you could get a NoteOutput::Public()
out of this function that contains a NoteType::OffChain
on its metadata.
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.
added check for public note
objects/src/notes/output.rs
Outdated
pub enum NoteOutput { | ||
Public(NoteConsumable), | ||
Private(NoteEnvelope), | ||
} |
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.
Similarly (or alternatively) to the other note type validation comment, it might be a good idea to validate on creation that NoteOutput::Private(envelope)
cannot contain an envelope with NoteType::Onchain
and the reverse for NoteOutput::Public
(unless there's some use-case I'm missing)
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.
Added validation
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.
Looks great! Thank you! This is not a full review, but I reviewed probably 90% of the code (the only thing left are some part os the changes to the transaction host).
I left some comments inline - but they are mostly related not naming/code organization.
/// The metadata consists of: | ||
/// - sender is the ID of the account which created the note. | ||
/// - note_type defines how the note is to be stored (e.g., on-chain or off-chain). | ||
/// - tag is a value which can be used by the recipient(s) to identify notes intended for them. | ||
/// - aux is arbitrary user-defined value. |
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.
There is one downside to this: the generated documentation will no longer include this info because the fields are private. So, one would be forced to look at the source code to understand what the metadata consists of. That was one of the main reasons why we originally put the description of fields into struct comments.
We could still keep the comments for each specific filed, but I think we also need similar detail in the struct comment.
objects/src/notes/vault.rs
Outdated
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
pub struct NoteVault { |
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 would probably roll-back this change (would it be a simple find/replace?). One of the reasons is that we have a generic AssetVault
(which is used by accounts, but also in other contexts), and having NoteVault
while not having AccountVault
is a bit confusing, IMO.
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.
changed
objects/src/notes/output.rs
Outdated
/// The types of note outputs supported by the transaction kernel. | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
pub enum NoteOutput { | ||
Public(NoteConsumable), | ||
Private(NoteEnvelope), | ||
} |
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 like the idea of using an enum instead of ToEnvelope
a lot! One comment: why not just transform the existing OutputNote
into the enum? Generally, I have two comments here:
- I think
NoteOutput
name is a bit confusing (it may be interpreted as something that is an output of a note) - so, I would preferOutputNote
. - I would move this enum to the
transactions
module as it is related to transaction execution.
So, basically, I would rename this enum into OutputNote
and move it into src/transaction/outputs.rs
.
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.
Another thing to consider in a future PR: I'm not sure the variant names here is what we want to have long term. Specifically, we may know full details of a private note, and it would make sense to be able to keep track of that. So, maybe the variant names should be something like:
pub enum OutputNote {
Full(Note),
Minimal(NoteEnvelope), // not a great name but couldn't come up with anything better
}
Let's create an issue to come back to this 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.
Let's create an issue for refactoring the OutputNote
along the lines of what I described above.
objects/src/notes/consumable.rs
Outdated
/// A note with all the data required for it to be consumed by executing it against the transaction | ||
/// kernel. | ||
/// | ||
/// Notes are created with a script, inputs, assets, and a serial number. Fungible and non-fungible | ||
/// asset transfers are done by moving assets to the note's vault. The note's script determines the | ||
/// conditions required for the note consumpution, i.e. the target account of a P2ID or conditions | ||
/// of a SWAP, and the effects of the note. The serial number has a double duty of preventing double | ||
/// spend, and providing unlikability to the consumer of a note. The note's inputs allow for | ||
/// customization of its script. | ||
/// | ||
/// To create a note, the kernel does not require all the information above, a user can create a | ||
/// note only with the commitment to the script, inputs, the serial number, and the kernel only | ||
/// verifies the source account has the assets necessary for the note creation. See [NoteRecipient] | ||
/// for more details. | ||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
pub struct NoteConsumable { | ||
vault: NoteVault, | ||
metadata: NoteMetadata, | ||
recipient: NoteRecipient, | ||
|
||
id: NoteId, | ||
nullifier: Nullifier, | ||
} |
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 like the structural changes here - but I'm not so sure about the naming. First, I think ConsumableNote
would probably be a better name than NoteConsumable
, but more importantly, I'm not sure we need to make this renaming at this point in time.
There are a couple of reasons for this:
- I think we need to have a note object which is independent of the transaction context.
- We need to break out note metadata out of here, and I'd like to revisit note object naming more comprehensively as a part of that change (mostly because we have too many variations of "note" objects floating around across miden base and miden node).
So, my preference here would be to keep the structural changes but rename this back into Note
and move the code into src/notes/mod.rs
.
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.
IMO we should stop putting stuff in the mod.rs
, specially when we have sub modules. There is no need to put everything in the mod.rs
, and having sub mods makes it easier to navigate the source code.
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.
renamed and moved to mod.rs
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.
IMO we should stop putting stuff in the
mod.rs
, specially when we have sub modules. There is no need to put everything in themod.rs
, and having sub mods makes it easier to navigate the source code.
I'm not opposed to this. Sometimes, when the inner and outer module names are the same, clippy may give an "inception" warning - but I don't think that's the case here (because notes
and note
would be different).
miden-tx/src/host/mod.rs
Outdated
pub struct TransactionHost<A> { | ||
adv_provider: A, | ||
account_delta: AccountDeltaTracker, | ||
state_tracker: StateTracker, | ||
acct_procedure_index_map: AccountProcedureIndexMap, | ||
notes: Vec<NoteOutput>, | ||
} |
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.
The struct doc comments need to be updated.
085b12f
to
1162f16
Compare
|
||
assert_eq!(executed_transaction.final_account().hash(), tx_outputs.account.hash()); | ||
assert_eq!(executed_transaction.output_notes(), &tx_outputs.output_notes); | ||
} | ||
|
||
#[test] | ||
#[ignore] |
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.
We decided to skip the tests for the moment. This will be fixed in a follow up.
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.
Looks good! Thank you! I left more comments inline - but pretty much all of them are nits. After these are addressed, we are good to merge.
let script = NoteScript::try_from(script_data) | ||
.map_err(|_| TransactionKernelError::MalformedNoteScript(script_data.to_vec()))?; |
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.
This is fine for now, but in the future, we'll also need to validate that the script data is consistent with script hash. This will become simpler once we move to MAST-based script serialization (i.e., we will not need to compile the script). Let's add a TODO
about it here and also create an issue to come back to this later.
objects/src/notes/output.rs
Outdated
/// The types of note outputs supported by the transaction kernel. | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
pub enum NoteOutput { | ||
Public(NoteConsumable), | ||
Private(NoteEnvelope), | ||
} |
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.
Let's create an issue for refactoring the OutputNote
along the lines of what I described above.
Nice, merge! |
1162f16
to
252f225
Compare
This PR is a little big, but mostly because of renaming of some of the existing structs.
ToEnvelope
trait in favor of a simpler enum.Note
toNoteConsumable
. The goal is to make it a bit more clear how this differs from the other note typesNoteRecipient
. The goal was to use this type to parse data from the advice map, in the event handler. Currently there isn't enough infrastructure to do that with traits, and because of time constraints I didn't add it. Nevertheless the separation aligns the struct type more closely to their semantics, and should make the code easier to follow, so I kept it around.NoteAssets
toNoteVault
because we have been mixing the terms. I kinda regret this change, but rolling back seems like twice the work for no gain.