Skip to content

Commit

Permalink
refactor: simplify NoteInclusionProof
Browse files Browse the repository at this point in the history
  • Loading branch information
polydez committed Jul 31, 2024
1 parent af42659 commit 8ce0a31
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 77 deletions.
32 changes: 18 additions & 14 deletions miden-lib/src/transaction/inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use alloc::vec::Vec;
use miden_objects::{
accounts::Account,
transaction::{
ChainMmr, ExecutedTransaction, InputNote, InputNotes, PreparedTransaction, TransactionArgs,
ChainMmr, ExecutedTransaction, InputNote, PreparedTransaction, TransactionArgs,
TransactionInputs, TransactionScript, TransactionWitness,
},
vm::{AdviceInputs, StackInputs},
Expand Down Expand Up @@ -92,7 +92,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(), tx_args, advice_inputs);
add_input_notes_to_advice_inputs(tx_inputs, tx_args, advice_inputs);
advice_inputs.extend(tx_args.advice_inputs().clone());
}

Expand Down Expand Up @@ -265,29 +265,29 @@ fn add_account_to_advice_inputs(
/// The advice provider is populated with:
///
/// - For each note:
/// - The note's details (serial number, script root, and its' input / assets hash).
/// - The note's details (serial number, script root, and its input / assets hash).
/// - The note's private arguments.
/// - The note's public metadata.
/// - The note's public inputs data. Prefixed by its length and padded to an even word length.
/// - The note's asset padded. Prefixed by its length and padded to an even word length.
/// - For autheticated notes (determined by the `is_authenticated` flag):
/// - For authenticated notes (determined by the `is_authenticated` flag):
/// - The note's authentication path against its block's note tree.
/// - The block number, sub hash, note root.
/// - The note's position in the note tree
///
/// The data above is processed by `prologue::process_input_notes_data`.
fn add_input_notes_to_advice_inputs(
notes: &InputNotes<InputNote>,
tx_inputs: &TransactionInputs,
tx_args: &TransactionArgs,
inputs: &mut AdviceInputs,
) {
// if there are no input notes, nothing is added to the advice inputs
if notes.is_empty() {
if tx_inputs.input_notes().is_empty() {
return;
}

let mut note_data = Vec::new();
for input_note in notes.iter() {
for input_note in tx_inputs.input_notes().iter() {
let note = input_note.note();
let assets = note.assets();
let recipient = note.recipient();
Expand Down Expand Up @@ -318,6 +318,11 @@ fn add_input_notes_to_advice_inputs(
// insert note authentication path nodes into the Merkle store
match input_note {
InputNote::Authenticated { note, proof } => {
let note_block_header = tx_inputs
.block_chain()
.get_block(proof.location().block_num())
.unwrap_or(tx_inputs.block_header());

// NOTE: keep in sync with the `prologue::process_input_note` kernel procedure
// Push the `is_authenticated` flag
note_data.push(Felt::ONE);
Expand All @@ -326,17 +331,16 @@ fn add_input_notes_to_advice_inputs(
inputs.extend_merkle_store(
proof
.note_path()
.inner_nodes(proof.location().node_index.value(), note.hash())
.inner_nodes(proof.location().node_index(), note.hash())
.unwrap(),
);
note_data.push(proof.location().block_num.into());
note_data.extend(*proof.sub_hash());
note_data.extend(*proof.note_root());
note_data.push(proof.location().block_num().into());
note_data.extend(note_block_header.sub_hash());
note_data.extend(note_block_header.note_root());
note_data.push(
proof
.location()
.node_index
.value()
.node_index()
.try_into()
.expect("value is greater than or equal to the field modulus"),
);
Expand All @@ -350,5 +354,5 @@ fn add_input_notes_to_advice_inputs(
}

// NOTE: keep map in sync with the `prologue::process_input_notes_data` kernel procedure
inputs.extend_map([(notes.commitment(), note_data)]);
inputs.extend_map([(tx_inputs.input_notes().commitment(), note_data)]);
}
9 changes: 1 addition & 8 deletions miden-tx/src/compiler/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,7 @@ fn test_transaction_compilation_succeeds() {
let _account_code = tx_compiler.load_account(account_id, account_code_ast).unwrap();

let notes = mock_input_notes(&mut tx_compiler, account_id);
let mock_inclusion_proof = NoteInclusionProof::new(
Default::default(),
Default::default(),
Default::default(),
0,
Default::default(),
)
.unwrap();
let mock_inclusion_proof = NoteInclusionProof::new(0, 0, Default::default()).unwrap();
let notes = notes
.into_iter()
.map(|note| InputNote::authenticated(note, mock_inclusion_proof.clone()))
Expand Down
1 change: 1 addition & 0 deletions objects/src/block/note_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ impl BlockNoteIndex {
(self.batch_idx() * MAX_NOTES_PER_BATCH + self.note_idx_in_batch()) as u64
}

/// Returns an index of the leaf containing the note.
fn leaf_index(&self) -> u64 {
self.to_absolute_index() * 2
}
Expand Down
9 changes: 1 addition & 8 deletions objects/src/notes/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,7 @@ mod tests {
#[test]
fn serialize_with_proof() {
let note = create_example_note();
let mock_inclusion_proof = NoteInclusionProof::new(
Default::default(),
Default::default(),
Default::default(),
0,
Default::default(),
)
.unwrap();
let mock_inclusion_proof = NoteInclusionProof::new(0, 0, Default::default()).unwrap();
let file = NoteFile::NoteWithProof(note.clone(), mock_inclusion_proof.clone());
let mut buffer = Vec::new();
file.write_into(&mut buffer);
Expand Down
67 changes: 26 additions & 41 deletions objects/src/notes/location.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
use alloc::string::ToString;
use core::num::TryFromIntError;

use super::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, NoteError, Serializable,
NOTE_TREE_DEPTH,
ByteReader, ByteWriter, Deserializable, DeserializationError, NoteError, Serializable,
};
use crate::crypto::merkle::{MerklePath, NodeIndex};
use crate::crypto::merkle::MerklePath;

/// Contains information about the location of a note.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct NoteLocation {
/// The block number the note was created in.
pub block_num: u32,
block_num: u32,

/// The index of the note in the note Merkle tree of the block the note was created in.
pub node_index: NodeIndex, // TODO: should be a u32 because the depth is always the same
node_index_in_block: u32,
}

impl NoteLocation {
pub fn block_num(&self) -> u32 {
self.block_num
}

pub fn node_index(&self) -> u64 {
self.node_index_in_block as u64
}
}

/// Contains the data required to prove inclusion of a note in the canonical chain.
Expand All @@ -24,48 +34,27 @@ pub struct NoteInclusionProof {
/// Details about the note's location.
location: NoteLocation,

/// The sub hash of the block the note was created in.
sub_hash: Digest,

/// The note root of the block the note was created in.
note_root: Digest,

/// The note's authentication Merkle path its block's the note root.
note_path: MerklePath,
}

impl NoteInclusionProof {
/// Returns a new [NoteInclusionProof].
pub fn new(
block_num: u32,
sub_hash: Digest,
note_root: Digest,
index: u64,
note_path: MerklePath,
) -> Result<Self, NoteError> {
let node_index = NodeIndex::new(NOTE_TREE_DEPTH, index)
.map_err(|e| NoteError::invalid_location_index(e.to_string()))?;
pub fn new(block_num: u32, index: u64, note_path: MerklePath) -> Result<Self, NoteError> {
Ok(Self {
location: NoteLocation { block_num, node_index },
sub_hash,
note_root,
location: NoteLocation {
block_num,
node_index_in_block: index.try_into().map_err(|err: TryFromIntError| {
NoteError::InvalidLocationIndex(err.to_string())
})?,
},
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 location of the note.
pub fn location(&self) -> &NoteLocation {
&self.location
Expand All @@ -84,35 +73,31 @@ impl NoteInclusionProof {
impl Serializable for NoteLocation {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u32(self.block_num);
self.node_index.write_into(target);
target.write_u32(self.node_index_in_block);
}
}

impl Deserializable for NoteLocation {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let block_num = source.read_u32()?;
let node_index = NodeIndex::read_from(source)?;
let node_index_in_block = source.read_u32()?;

Ok(Self { block_num, node_index })
Ok(Self { block_num, node_index_in_block })
}
}

impl Serializable for NoteInclusionProof {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.location.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<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let location = NoteLocation::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 { location, sub_hash, note_root, note_path })
Ok(Self { location, note_path })
}
}
6 changes: 2 additions & 4 deletions objects/src/testing/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ impl MockChain {
for note in notes {
let input_note = self.available_notes.get(note).unwrap().clone();
block_headers_map.insert(
input_note.location().unwrap().block_num,
input_note.location().unwrap().block_num(),
self.blocks
.get(input_note.location().unwrap().block_num as usize)
.get(input_note.location().unwrap().block_num() as usize)
.unwrap()
.header(),
);
Expand Down Expand Up @@ -291,8 +291,6 @@ impl MockChain {
let note_path = notes_tree.get_note_path(block_note_index).unwrap();
let note_inclusion_proof = NoteInclusionProof::new(
block.header().block_num(),
block.header().sub_hash(),
block.header().note_root(),
block_note_index.to_absolute_index(),
note_path,
)
Expand Down
4 changes: 2 additions & 2 deletions objects/src/transaction/inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl TransactionInputs {
// check the authentication paths of the input notes.
for note in input_notes.iter() {
if let InputNote::Authenticated { note, proof } = note {
let note_block_num = proof.location().block_num;
let note_block_num = proof.location().block_num();

let block_header = if note_block_num == block_num {
&block_header
Expand Down Expand Up @@ -377,7 +377,7 @@ impl InputNote {

/// Returns true if this note belongs to the note tree of the specified block.
fn is_in_block(note: &Note, proof: &NoteInclusionProof, block_header: &BlockHeader) -> bool {
let note_index = proof.location().node_index.value();
let note_index = proof.location().node_index();
let note_hash = note.hash();
proof.note_path().verify(note_index, note_hash, &block_header.note_root())
}
Expand Down

0 comments on commit 8ce0a31

Please sign in to comment.