diff --git a/miden-lib/src/tests/test_note.rs b/miden-lib/src/tests/test_note.rs index 79b810569..5e114083d 100644 --- a/miden-lib/src/tests/test_note.rs +++ b/miden-lib/src/tests/test_note.rs @@ -63,7 +63,7 @@ fn test_get_sender() { let transaction = prepare_transaction(tx_inputs, None, code, None); let process = run_tx(&transaction).unwrap(); - let sender = transaction.input_notes().get_note(0).note().metadata().sender().into(); + let sender = transaction.input_notes().get_note(0).metadata().sender().into(); assert_eq!(process.stack.get(0), sender); } @@ -107,10 +107,10 @@ fn test_get_vault_data() { push.{note_1_num_assets} assert_eq end ", - note_0_asset_hash = prepare_word(¬es.get_note(0).note().assets().commitment()), - note_0_num_assets = notes.get_note(0).note().assets().num_assets(), - note_1_asset_hash = prepare_word(¬es.get_note(1).note().assets().commitment()), - note_1_num_assets = notes.get_note(1).note().assets().num_assets(), + note_0_asset_hash = prepare_word(¬es.get_note(0).assets().commitment()), + note_0_num_assets = notes.get_note(0).assets().num_assets(), + note_1_asset_hash = prepare_word(¬es.get_note(1).assets().commitment()), + note_1_num_assets = notes.get_note(1).assets().num_assets(), ); let transaction = prepare_transaction(tx_inputs, None, &code, None); @@ -213,10 +213,10 @@ fn test_get_assets() { call.process_note_1 end ", - note_0_num_assets = notes.get_note(0).note().assets().num_assets(), - note_1_num_assets = notes.get_note(1).note().assets().num_assets(), - NOTE_0_ASSET_ASSERTIONS = construct_asset_assertions(notes.get_note(0).note()), - NOTE_1_ASSET_ASSERTIONS = construct_asset_assertions(notes.get_note(1).note()), + note_0_num_assets = notes.get_note(0).assets().num_assets(), + note_1_num_assets = notes.get_note(1).assets().num_assets(), + NOTE_0_ASSET_ASSERTIONS = construct_asset_assertions(notes.get_note(0).inner()), + NOTE_1_ASSET_ASSERTIONS = construct_asset_assertions(notes.get_note(1).inner()), ); let transaction = prepare_transaction(tx_inputs, None, &code, None); @@ -248,7 +248,7 @@ fn test_get_inputs() { code } - let note1 = notes.get_note(0).note(); + let note1 = notes.get_note(0).inner(); // calling get_assets should return assets at the specified address let code = format!( @@ -326,7 +326,7 @@ fn note_setup_stack_assertions(process: &Process, inputs: &PreparedTra let mut expected_stack = [ZERO; 16]; // replace the top four elements with the tx script root - let mut note_script_root = *inputs.input_notes().get_note(0).note().script().hash(); + let mut note_script_root = *inputs.input_notes().get_note(0).script().hash(); note_script_root.reverse(); expected_stack[..4].copy_from_slice(¬e_script_root); diff --git a/miden-lib/src/tests/test_prologue.rs b/miden-lib/src/tests/test_prologue.rs index 6c923137f..018ad40f5 100644 --- a/miden-lib/src/tests/test_prologue.rs +++ b/miden-lib/src/tests/test_prologue.rs @@ -242,7 +242,7 @@ fn consumed_notes_memory_assertions(process: &Process, inputs: &Prepar ); for (note, note_idx) in inputs.input_notes().iter().zip(0_u32..) { - let note = note.note(); + let note = note.inner(); // The note nullifier should be computer and stored at the correct offset assert_eq!( diff --git a/miden-lib/src/tests/test_tx.rs b/miden-lib/src/tests/test_tx.rs index 049cc5235..f5dd264e2 100644 --- a/miden-lib/src/tests/test_tx.rs +++ b/miden-lib/src/tests/test_tx.rs @@ -130,9 +130,9 @@ fn test_get_output_notes_hash() { mock_inputs(MockAccountType::StandardExisting, AssetPreservationStatus::Preserved); // extract input note data - let input_note_1 = tx_inputs.input_notes().get_note(0).note(); + let input_note_1 = tx_inputs.input_notes().get_note(0).inner(); let input_asset_1 = **input_note_1.assets().iter().take(1).collect::>().first().unwrap(); - let input_note_2 = tx_inputs.input_notes().get_note(1).note(); + let input_note_2 = tx_inputs.input_notes().get_note(1).inner(); let input_asset_2 = **input_note_2.assets().iter().take(1).collect::>().first().unwrap(); // create output note 1 diff --git a/miden-lib/src/transaction/inputs.rs b/miden-lib/src/transaction/inputs.rs index 339a80d8b..1ce8d100e 100644 --- a/miden-lib/src/transaction/inputs.rs +++ b/miden-lib/src/transaction/inputs.rs @@ -1,8 +1,8 @@ use miden_objects::{ accounts::Account, transaction::{ - ChainMmr, ExecutedTransaction, InputNotes, PreparedTransaction, TransactionInputs, - TransactionScript, TransactionWitness, + ChainMmr, ExecutedTransaction, PreparedTransaction, TransactionInputs, TransactionScript, + TransactionWitness, }, utils::{collections::Vec, vec, IntoBytes}, vm::{AdviceInputs, StackInputs}, @@ -91,7 +91,7 @@ fn extend_advice_inputs( // build the advice map and Merkle store for relevant components add_chain_mmr_to_advice_inputs(tx_inputs.block_chain(), advice_inputs); add_account_to_advice_inputs(tx_inputs.account(), tx_inputs.account_seed(), advice_inputs); - add_input_notes_to_advice_inputs(tx_inputs.input_notes(), advice_inputs); + add_input_notes_to_advice_inputs(tx_inputs, advice_inputs); add_tx_script_inputs_to_advice_map(tx_script, advice_inputs); } @@ -276,27 +276,25 @@ fn add_account_to_advice_inputs( /// - inputs_hash |-> inputs /// - asset_hash |-> assets /// - notes_hash |-> combined note data -fn add_input_notes_to_advice_inputs(notes: &InputNotes, inputs: &mut AdviceInputs) { +fn add_input_notes_to_advice_inputs(tx_inputs: &TransactionInputs, inputs: &mut AdviceInputs) { + let notes = tx_inputs.input_notes(); + // if there are no input notes, nothing is added to the advice inputs if notes.is_empty() { return; } let mut note_data = Vec::new(); - for input_note in notes.iter() { - let note = input_note.note(); - let proof = input_note.proof(); - + for note in notes.iter() { // insert note inputs and assets into the advice map inputs.extend_map([(note.inputs().commitment().into(), note.inputs().to_padded_values())]); inputs.extend_map([(note.assets().commitment().into(), note.assets().to_padded_assets())]); // insert note authentication path nodes into the Merkle store inputs.extend_merkle_store( - proof - .note_path() - .inner_nodes(proof.origin().node_index.value(), note.authentication_hash()) - .unwrap(), + note.auth_path() + .inner_nodes(note.location().note_index() as u64, note.authentication_hash()) + .expect("failed to compute inner nodes for Merkle path"), ); // add the note elements to the combined vector of note data @@ -311,10 +309,21 @@ fn add_input_notes_to_advice_inputs(notes: &InputNotes, inputs: &mut AdviceInput note_data.push((note.assets().num_assets() as u32).into()); note_data.extend(note.assets().to_padded_assets()); - note_data.push(proof.origin().block_num.into()); - note_data.extend(*proof.sub_hash()); - note_data.extend(*proof.note_root()); - note_data.push(proof.origin().node_index.value().into()); + // determine which block header is associated with the note + let block_num = note.location().block_num(); + let block_header = if block_num == tx_inputs.block_header().block_num() { + tx_inputs.block_header() + } else { + tx_inputs + .block_chain() + .get_block(block_num) + .expect("missing block header for note") + }; + + note_data.push(block_num.into()); + note_data.extend(*block_header.sub_hash()); + note_data.extend(*block_header.note_root()); + note_data.push(note.location().note_index().into()); } // insert the combined note data into the advice map diff --git a/miden-tx/src/compiler/mod.rs b/miden-tx/src/compiler/mod.rs index 52616127d..7f615b998 100644 --- a/miden-tx/src/compiler/mod.rs +++ b/miden-tx/src/compiler/mod.rs @@ -216,7 +216,7 @@ impl TransactionCompiler { for recorded_note in notes.iter() { let note_program = self .assembler - .compile_in_context(recorded_note.note().script().code(), assembly_context) + .compile_in_context(recorded_note.script().code(), assembly_context) .map_err(TransactionCompilerError::CompileNoteScriptFailed)?; verify_program_account_compatibility( ¬e_program, diff --git a/miden-tx/src/compiler/tests.rs b/miden-tx/src/compiler/tests.rs index 0841675e6..3c33bc53d 100644 --- a/miden-tx/src/compiler/tests.rs +++ b/miden-tx/src/compiler/tests.rs @@ -1,7 +1,7 @@ use miden_objects::{ accounts::ACCOUNT_ID_REGULAR_ACCOUNT_IMMUTABLE_CODE_ON_CHAIN, assets::{Asset, FungibleAsset}, - notes::{Note, NoteInclusionProof}, + notes::Note, transaction::{InputNote, InputNotes}, Felt, FieldElement, Word, }; @@ -187,17 +187,9 @@ fn test_transaction_compilation_succeeds() { let _account_code = tx_compiler.load_account(account_id, account_code_ast).unwrap(); let notes = mock_consumed_notes(&mut tx_compiler, account_id); - let mock_inclusion_proof = NoteInclusionProof::new( - Default::default(), - Default::default(), - Default::default(), - 0, - Default::default(), - ) - .unwrap(); let notes = notes .into_iter() - .map(|note| InputNote::new(note, mock_inclusion_proof.clone())) + .map(|note| InputNote::new(note, Default::default(), Default::default())) .collect::>(); let notes = InputNotes::new(notes).unwrap(); diff --git a/miden-tx/src/tests.rs b/miden-tx/src/tests.rs index 83cf00e0f..f0f1ba156 100644 --- a/miden-tx/src/tests.rs +++ b/miden-tx/src/tests.rs @@ -227,15 +227,8 @@ fn executed_transaction_account_delta() { // vault delta // -------------------------------------------------------------------------------------------- // assert that added assets are tracked - let added_assets = data_store - .notes - .last() - .unwrap() - .note() - .assets() - .iter() - .cloned() - .collect::>(); + let added_assets = + data_store.notes.last().unwrap().assets().iter().cloned().collect::>(); assert!(executed_transaction .account_delta() .vault() diff --git a/mock/src/builders/note.rs b/mock/src/builders/note.rs index 58f2dba5e..8d581f798 100644 --- a/mock/src/builders/note.rs +++ b/mock/src/builders/note.rs @@ -2,7 +2,7 @@ use miden_objects::{ accounts::AccountId, assembly::ProgramAst, assets::Asset, - notes::{Note, NoteInclusionProof, NoteInputs, NoteScript}, + notes::{Note, NoteInputs, NoteScript}, utils::{ collections::Vec, string::{String, ToString}, @@ -27,7 +27,6 @@ pub struct NoteBuilder { serial_num: Word, tag: Felt, code: String, - proof: Option, } impl NoteBuilder { @@ -46,7 +45,6 @@ impl NoteBuilder { serial_num, tag: Felt::default(), code: DEFAULT_NOTE_CODE.to_string(), - proof: None, } } @@ -71,11 +69,6 @@ impl NoteBuilder { self } - pub fn proof(mut self, proof: NoteInclusionProof) -> Self { - self.proof = Some(proof); - self - } - pub fn build(self) -> Result { let assembler = TransactionKernel::assembler(); let note_ast = ProgramAst::parse(&self.code).unwrap(); diff --git a/mock/src/mock/chain.rs b/mock/src/mock/chain.rs index 021f085ec..2dd6b492a 100644 --- a/mock/src/mock/chain.rs +++ b/mock/src/mock/chain.rs @@ -4,7 +4,7 @@ use miden_objects::{ accounts::{Account, AccountId, AccountType, SlotItem}, assets::Asset, crypto::merkle::{LeafIndex, Mmr, PartialMmr, SimpleSmt, Smt}, - notes::{Note, NoteInclusionProof}, + notes::{Note, NoteLocation}, transaction::{ChainMmr, InputNote}, utils::collections::Vec, BlockHeader, Digest, Felt, FieldElement, Word, ACCOUNT_TREE_DEPTH, NOTE_TREE_DEPTH, @@ -104,17 +104,8 @@ impl Objects { .enumerate() .map(|(index, note)| { let auth_index = LeafIndex::new(index as u64).expect("index bigger than 2**20"); - InputNote::new( - note.clone(), - NoteInclusionProof::new( - header.block_num(), - header.sub_hash(), - header.note_root(), - index as u64, - notes.open(&auth_index).path, - ) - .expect("Invalid data provided to proof constructor"), - ) + let location = NoteLocation::new(header.block_num(), auth_index.value() as u32); + InputNote::new(note.clone(), location, notes.open(&auth_index).path) }) .collect::>() } @@ -633,17 +624,8 @@ pub fn mock_chain_data(consumed_notes: Vec) -> (ChainMmr, Vec) .map(|(index, note)| { let block_header = &block_chain[index]; let auth_index = LeafIndex::new(index as u64).unwrap(); - InputNote::new( - note, - NoteInclusionProof::new( - block_header.block_num(), - block_header.sub_hash(), - block_header.note_root(), - index as u64, - note_trees[index].open(&auth_index).path, - ) - .unwrap(), - ) + let location = NoteLocation::new(block_header.block_num(), auth_index.value() as u32); + InputNote::new(note, location, note_trees[index].open(&auth_index).path) }) .collect::>(); diff --git a/objects/src/notes/location.rs b/objects/src/notes/location.rs new file mode 100644 index 000000000..11458516e --- /dev/null +++ b/objects/src/notes/location.rs @@ -0,0 +1,47 @@ +use super::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; + +// NOTE LOCATION +// ================================================================================================ + +/// Location at which the note is recorded in the chain. +/// +/// The location consists of two elements: +/// - The number of the block at which the note was recorded in the chain. +/// - The index of the note in the block's note tree. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct NoteLocation { + block_num: u32, + note_index: u32, // TODO: change to u16 +} + +impl NoteLocation { + pub fn new(block_num: u32, note_index: u32) -> Self { + Self { block_num, note_index } + } + + /// Returns the number of the block at which the note was recorded in the chain. + pub fn block_num(&self) -> u32 { + self.block_num + } + + /// Return the index of thn note in the block's note tree. + pub fn note_index(&self) -> u32 { + self.note_index + } +} + +impl Serializable for NoteLocation { + fn write_into(&self, target: &mut W) { + target.write_u32(self.block_num); + self.note_index.write_into(target); + } +} + +impl Deserializable for NoteLocation { + fn read_from(source: &mut R) -> Result { + let block_num = source.read_u32()?; + let note_index = source.read_u32()?; + Ok(Self { block_num, note_index }) + } +} diff --git a/objects/src/notes/mod.rs b/objects/src/notes/mod.rs index 63704d9d1..1047f9161 100644 --- a/objects/src/notes/mod.rs +++ b/objects/src/notes/mod.rs @@ -7,7 +7,7 @@ use super::{ utils::{ collections::Vec, serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, - string::{String, ToString}, + string::String, }, vm::CodeBlock, Digest, Felt, Hasher, NoteError, Word, NOTE_TREE_DEPTH, WORD_SIZE, ZERO, @@ -28,8 +28,8 @@ pub use note_id::NoteId; mod nullifier; pub use nullifier::Nullifier; -mod origin; -pub use origin::{NoteInclusionProof, NoteOrigin}; +mod location; +pub use location::NoteLocation; mod script; pub use script::NoteScript; diff --git a/objects/src/notes/origin.rs b/objects/src/notes/origin.rs deleted file mode 100644 index 255b9c795..000000000 --- a/objects/src/notes/origin.rs +++ /dev/null @@ -1,114 +0,0 @@ -use super::{ - ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, NoteError, Serializable, - ToString, NOTE_TREE_DEPTH, -}; -use crate::crypto::merkle::{MerklePath, NodeIndex}; - -/// Contains information about the origin of a note. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct NoteOrigin { - pub block_num: u32, - pub node_index: NodeIndex, // TODO: should be a u32 because the depth is always the same -} - -/// Contains the data required to prove inclusion of a note in the canonical chain. -/// -/// block_num - the block number the note was created in. -/// sub_hash - the sub hash of the block the note was created in. -/// note_root - the note root of the block the note was created in. -/// note_index - the index of the note in the note Merkle tree of the block the note was created -/// in. -/// note_path - the Merkle path to the note in the note Merkle tree of the block the note was -/// created in. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct NoteInclusionProof { - origin: NoteOrigin, - sub_hash: Digest, - note_root: Digest, - note_path: MerklePath, -} - -impl NoteInclusionProof { - /// Creates a new note origin. - pub fn new( - block_num: u32, - sub_hash: Digest, - note_root: Digest, - index: u64, - note_path: MerklePath, - ) -> Result { - let node_index = NodeIndex::new(NOTE_TREE_DEPTH, index) - .map_err(|e| NoteError::invalid_origin_index(e.to_string()))?; - Ok(Self { - origin: NoteOrigin { block_num, node_index }, - sub_hash, - note_root, - note_path, - }) - } - - // ACCESSORS - // -------------------------------------------------------------------------------------------- - - /// Returns the sub hash of the block header the note was created in. - pub fn sub_hash(&self) -> Digest { - self.sub_hash - } - - /// Returns the note root of the block header the note was created in. - pub fn note_root(&self) -> Digest { - self.note_root - } - - /// Returns the origin of the note. - pub fn origin(&self) -> &NoteOrigin { - &self.origin - } - - /// Returns the Merkle path to the note in the note Merkle tree of the block the note was - /// created in. - pub fn note_path(&self) -> &MerklePath { - &self.note_path - } -} - -// SERIALIZATION -// ================================================================================================ - -impl Serializable for NoteOrigin { - fn write_into(&self, target: &mut W) { - target.write_u32(self.block_num); - self.node_index.write_into(target); - } -} - -impl Deserializable for NoteOrigin { - fn read_from(source: &mut R) -> Result { - let block_num = source.read_u32()?; - let node_index = NodeIndex::read_from(source)?; - - Ok(Self { block_num, node_index }) - } -} - -impl Serializable for NoteInclusionProof { - fn write_into(&self, target: &mut W) { - self.origin.write_into(target); - self.sub_hash.write_into(target); - self.note_root.write_into(target); - self.note_path.write_into(target); - } -} - -impl Deserializable for NoteInclusionProof { - fn read_from(source: &mut R) -> Result { - let origin = NoteOrigin::read_from(source)?; - let sub_hash = Digest::read_from(source)?; - let note_root = Digest::read_from(source)?; - let note_path = MerklePath::read_from(source)?; - - Ok(Self { origin, sub_hash, note_root, note_path }) - } -} diff --git a/objects/src/transaction/inputs.rs b/objects/src/transaction/inputs.rs index 0b737bc0f..cbb015dd7 100644 --- a/objects/src/transaction/inputs.rs +++ b/objects/src/transaction/inputs.rs @@ -3,7 +3,10 @@ use core::fmt::Debug; use super::{BlockHeader, ChainMmr, Digest, Felt, Hasher, Word}; use crate::{ accounts::{validate_account_seed, Account}, - notes::{Note, NoteId, NoteInclusionProof, NoteOrigin, Nullifier}, + crypto::merkle::MerklePath, + notes::{ + Note, NoteAssets, NoteId, NoteInputs, NoteLocation, NoteMetadata, NoteScript, Nullifier, + }, utils::{ collections::{self, BTreeSet, Vec}, serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, @@ -70,7 +73,7 @@ impl TransactionInputs { // which were created in the current block we skip this check because their authentication // paths are derived implicitly for note in input_notes.iter() { - let note_block_num = note.origin().block_num; + let note_block_num = note.location().block_num(); let block_header = if note_block_num == block_num { &block_header @@ -342,13 +345,14 @@ pub fn build_input_notes_commitment(notes: &[T]) -> Digest { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct InputNote { note: Note, - proof: NoteInclusionProof, + location: NoteLocation, + auth_path: MerklePath, } impl InputNote { /// Returns a new instance of an [InputNote] with the specified note and proof. - pub fn new(note: Note, proof: NoteInclusionProof) -> Self { - Self { note, proof } + pub fn new(note: Note, location: NoteLocation, auth_path: MerklePath) -> Self { + Self { note, location, auth_path } } /// Returns the ID of the note. @@ -356,26 +360,60 @@ impl InputNote { self.note.id() } - /// Returns a reference to the underlying note. - pub fn note(&self) -> &Note { - &self.note + /// Returns a reference script which locks the assets of this note. + pub fn script(&self) -> &NoteScript { + self.note.script() + } + + /// Returns a reference to the note inputs. + pub fn inputs(&self) -> &NoteInputs { + self.note.inputs() + } + + /// Returns a reference to the asset of this note. + pub fn assets(&self) -> &NoteAssets { + self.note.assets() + } + + /// Returns a serial number of this note. + pub fn serial_num(&self) -> Word { + self.note.serial_num() } - /// Returns a reference to the inclusion proof of the note. - pub fn proof(&self) -> &NoteInclusionProof { - &self.proof + /// Returns the metadata associated with this note. + pub fn metadata(&self) -> &NoteMetadata { + self.note.metadata() + } + + /// Returns the note's Merkle authentication path in the note tree of the block in which + /// this note was included into the chain. + pub fn auth_path(&self) -> &MerklePath { + &self.auth_path + } + + /// Returns the value used to authenticate a notes existence in the note tree. + /// + /// This is computed as a 2-to-1 hash of the note hash and note metadata + /// [hash(note_id, note_metadata)] + pub fn authentication_hash(&self) -> Digest { + self.note.authentication_hash() + } + + /// Returns a reference to the underlying note. + pub fn inner(&self) -> &Note { + &self.note } - /// Returns a reference to the origin of the note. - pub fn origin(&self) -> &NoteOrigin { - self.proof.origin() + /// Returns info about the location of this note in the chain. + pub fn location(&self) -> &NoteLocation { + &self.location } /// Returns true if this note belongs to the note tree of the specified block. fn is_in_block(&self, block_header: &BlockHeader) -> bool { - let note_index = self.origin().node_index.value(); + let note_index = self.location().note_index() as u64; let note_hash = self.note.authentication_hash(); - self.proof.note_path().verify(note_index, note_hash, &block_header.note_root()) + self.auth_path.verify(note_index, note_hash, &block_header.note_root()) } } @@ -385,15 +423,17 @@ impl InputNote { impl Serializable for InputNote { fn write_into(&self, target: &mut W) { self.note.write_into(target); - self.proof.write_into(target); + self.location.write_into(target); + self.auth_path.write_into(target); } } impl Deserializable for InputNote { fn read_from(source: &mut R) -> Result { let note = Note::read_from(source)?; - let proof = NoteInclusionProof::read_from(source)?; + let location = NoteLocation::read_from(source)?; + let auth_path = MerklePath::read_from(source)?; - Ok(Self { note, proof }) + Ok(Self { note, location, auth_path }) } }