diff --git a/miden-lib/src/transaction/mod.rs b/miden-lib/src/transaction/mod.rs index aa8789894..02390e928 100644 --- a/miden-lib/src/transaction/mod.rs +++ b/miden-lib/src/transaction/mod.rs @@ -3,6 +3,7 @@ use alloc::{string::ToString, sync::Arc, vec::Vec}; use miden_objects::{ accounts::{AccountCode, AccountHeader, AccountId, AccountStorageHeader}, assembly::{Assembler, DefaultSourceManager, KernelLibrary}, + block::BlockNumber, crypto::merkle::{MerkleError, MerklePath}, transaction::{ OutputNote, OutputNotes, TransactionArgs, TransactionInputs, TransactionOutputs, @@ -220,7 +221,7 @@ impl TransactionKernel { pub fn build_output_stack( final_acct_hash: Digest, output_notes_hash: Digest, - expiration_block_num: u32, + expiration_block_num: BlockNumber, ) -> StackOutputs { let mut outputs: Vec = Vec::with_capacity(9); outputs.push(Felt::from(expiration_block_num)); @@ -252,7 +253,7 @@ impl TransactionKernel { /// - Overflow addresses are not empty. pub fn parse_output_stack( stack: &StackOutputs, - ) -> Result<(Digest, Digest, u32), TransactionOutputError> { + ) -> Result<(Digest, Digest, BlockNumber), TransactionOutputError> { let output_notes_hash = stack .get_stack_word(OUTPUT_NOTES_COMMITMENT_WORD_IDX * 4) .expect("first word missing") @@ -267,11 +268,13 @@ impl TransactionKernel { .get_stack_item(EXPIRATION_BLOCK_ELEMENT_IDX) .expect("element on index 8 missing"); - let expiration_block_num = u32::try_from(expiration_block_num.as_int()).map_err(|_| { - TransactionOutputError::OutputStackInvalid( - "Expiration block number should be smaller than u32::MAX".into(), - ) - })?; + let expiration_block_num = u32::try_from(expiration_block_num.as_int()) + .map_err(|_| { + TransactionOutputError::OutputStackInvalid( + "Expiration block number should be smaller than u32::MAX".into(), + ) + })? + .into(); if stack.get_stack_word(12).expect("fourth word missing") != EMPTY_WORD { return Err(TransactionOutputError::OutputStackInvalid( diff --git a/miden-tx/src/tests/kernel_tests/test_note.rs b/miden-tx/src/tests/kernel_tests/test_note.rs index 4f9b38b5a..19e47c2a3 100644 --- a/miden-tx/src/tests/kernel_tests/test_note.rs +++ b/miden-tx/src/tests/kernel_tests/test_note.rs @@ -562,7 +562,7 @@ fn test_build_note_metadata() { sender, NoteType::Private, NoteTag::from_account_id(receiver, NoteExecutionMode::Local).unwrap(), - NoteExecutionHint::after_block(500).unwrap(), + NoteExecutionHint::after_block(500.into()).unwrap(), Felt::try_from(1u64 << 63).unwrap(), ) .unwrap(); diff --git a/miden-tx/src/tests/kernel_tests/test_tx.rs b/miden-tx/src/tests/kernel_tests/test_tx.rs index dfc7afdf0..9435a4f47 100644 --- a/miden-tx/src/tests/kernel_tests/test_tx.rs +++ b/miden-tx/src/tests/kernel_tests/test_tx.rs @@ -82,7 +82,7 @@ fn test_create_note() { ", recipient = prepare_word(&recipient), PUBLIC_NOTE = NoteType::Public as u8, - note_execution_hint = Felt::from(NoteExecutionHint::after_block(23).unwrap()), + note_execution_hint = Felt::from(NoteExecutionHint::after_block(23.into()).unwrap()), tag = tag, ); @@ -104,7 +104,7 @@ fn test_create_note() { account_id, NoteType::Public, tag, - NoteExecutionHint::after_block(23).unwrap(), + NoteExecutionHint::after_block(23.into()).unwrap(), Felt::new(27), ) .unwrap() @@ -242,7 +242,7 @@ fn test_get_output_notes_commitment() { tx_context.tx_inputs().account().id(), NoteType::Public, output_tag_2, - NoteExecutionHint::after_block(123).unwrap(), + NoteExecutionHint::after_block(123.into()).unwrap(), ZERO, ) .unwrap(); @@ -631,7 +631,7 @@ fn test_build_recipient_hash() { output_serial_no = prepare_word(&output_serial_no), PUBLIC_NOTE = NoteType::Public as u8, tag = tag, - execution_hint = Felt::from(NoteExecutionHint::after_block(2).unwrap()), + execution_hint = Felt::from(NoteExecutionHint::after_block(2.into()).unwrap()), aux = aux, ); diff --git a/objects/src/block/header.rs b/objects/src/block/header.rs index 4a0f57ed3..c51422fba 100644 --- a/objects/src/block/header.rs +++ b/objects/src/block/header.rs @@ -69,7 +69,7 @@ impl BlockHeader { kernel_root, proof_hash, timestamp, - block_num.as_u32(), + block_num, ); // The sub hash is merged with the note_root - hash(sub_hash, note_root) to produce the @@ -204,7 +204,7 @@ impl BlockHeader { kernel_root: Digest, proof_hash: Digest, timestamp: u32, - block_num: u32, + block_num: BlockNumber, ) -> Digest { let mut elements: Vec = Vec::with_capacity(32); elements.extend_from_slice(prev_hash.as_elements()); diff --git a/objects/src/notes/execution_hint.rs b/objects/src/notes/execution_hint.rs index fe2f88e89..7f14dd4e8 100644 --- a/objects/src/notes/execution_hint.rs +++ b/objects/src/notes/execution_hint.rs @@ -3,7 +3,7 @@ use vm_core::Felt; -use crate::NoteError; +use crate::{block::BlockNumber, NoteError}; /// Specifies the conditions under which a note is ready to be consumed. /// These conditions are meant to be encoded in the note script as well. @@ -79,7 +79,7 @@ impl NoteExecutionHint { /// # Errors /// /// Returns an error if `block_num` is equal to [`u32::MAX`]. - pub fn after_block(block_num: u32) -> Result { + pub fn after_block(block_num: BlockNumber) -> Result { AfterBlockNumber::new(block_num) .map(|block_number| NoteExecutionHint::AfterBlock { block_num: block_number }) } @@ -104,7 +104,7 @@ impl NoteExecutionHint { } Ok(NoteExecutionHint::Always) }, - Self::AFTER_BLOCK_TAG => NoteExecutionHint::after_block(payload), + Self::AFTER_BLOCK_TAG => NoteExecutionHint::after_block(payload.into()), Self::ON_BLOCK_SLOT_TAG => { let remainder = (payload >> 24 & 0xff) as u8; if remainder != 0 { @@ -128,7 +128,8 @@ impl NoteExecutionHint { /// - `None` if we don't know whether the note can be consumed. /// - `Some(true)` if the note is consumable for the given `block_num` /// - `Some(false)` if the note is not consumable for the given `block_num` - pub fn can_be_consumed(&self, block_num: u32) -> Option { + pub fn can_be_consumed(&self, block_num: BlockNumber) -> Option { + let block_num = block_num.as_u32(); match self { NoteExecutionHint::None => None, NoteExecutionHint::Always => Some(true), @@ -213,7 +214,7 @@ impl From for u64 { /// /// Used for the [`NoteExecutionHint::AfterBlock`] variant where this constraint is needed. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct AfterBlockNumber(u32); +pub struct AfterBlockNumber(BlockNumber); impl AfterBlockNumber { /// Creates a new [`AfterBlockNumber`] from the given `block_number`. @@ -222,8 +223,8 @@ impl AfterBlockNumber { /// /// Returns an error if: /// - `block_number` is equal to `u32::MAX`. - pub fn new(block_number: u32) -> Result { - if block_number == u32::MAX { + pub fn new(block_number: BlockNumber) -> Result { + if block_number.as_u32() == u32::MAX { Err(NoteError::NoteExecutionHintAfterBlockCannotBeU32Max) } else { Ok(Self(block_number)) @@ -232,13 +233,13 @@ impl AfterBlockNumber { /// Returns the block number as a `u32`. pub fn as_u32(&self) -> u32 { - self.0 + self.0.as_u32() } } impl From for u32 { fn from(block_number: AfterBlockNumber) -> Self { - block_number.0 + block_number.0.as_u32() } } @@ -246,7 +247,7 @@ impl TryFrom for AfterBlockNumber { type Error = NoteError; fn try_from(block_number: u32) -> Result { - Self::new(block_number) + Self::new(block_number.into()) } } @@ -269,7 +270,7 @@ mod tests { fn test_serialization_round_trip() { assert_hint_serde(NoteExecutionHint::None); assert_hint_serde(NoteExecutionHint::Always); - assert_hint_serde(NoteExecutionHint::after_block(15).unwrap()); + assert_hint_serde(NoteExecutionHint::after_block(15.into()).unwrap()); assert_hint_serde(NoteExecutionHint::OnBlockSlot { round_len: 9, slot_len: 12, @@ -279,7 +280,7 @@ mod tests { #[test] fn test_encode_round_trip() { - let hint = NoteExecutionHint::after_block(15).unwrap(); + let hint = NoteExecutionHint::after_block(15.into()).unwrap(); let hint_int: u64 = hint.into(); let decoded_hint: NoteExecutionHint = hint_int.try_into().unwrap(); assert_eq!(hint, decoded_hint); @@ -300,25 +301,25 @@ mod tests { #[test] fn test_can_be_consumed() { let none = NoteExecutionHint::none(); - assert!(none.can_be_consumed(100).is_none()); + assert!(none.can_be_consumed(100.into()).is_none()); let always = NoteExecutionHint::always(); - assert!(always.can_be_consumed(100).unwrap()); + assert!(always.can_be_consumed(100.into()).unwrap()); - let after_block = NoteExecutionHint::after_block(12345).unwrap(); - assert!(!after_block.can_be_consumed(12344).unwrap()); - assert!(after_block.can_be_consumed(12345).unwrap()); + let after_block = NoteExecutionHint::after_block(12345.into()).unwrap(); + assert!(!after_block.can_be_consumed(12344.into()).unwrap()); + assert!(after_block.can_be_consumed(12345.into()).unwrap()); let on_block_slot = NoteExecutionHint::on_block_slot(10, 7, 1); - assert!(!on_block_slot.can_be_consumed(127).unwrap()); // Block 127 is not in the slot 128..255 - assert!(on_block_slot.can_be_consumed(128).unwrap()); // Block 128 is in the slot 128..255 - assert!(on_block_slot.can_be_consumed(255).unwrap()); // Block 255 is in the slot 128..255 - assert!(!on_block_slot.can_be_consumed(256).unwrap()); // Block 256 is not in the slot 128..255 - assert!(on_block_slot.can_be_consumed(1152).unwrap()); // Block 1152 is in the slot 1152..1279 - assert!(on_block_slot.can_be_consumed(1279).unwrap()); // Block 1279 is in the slot 1152..1279 - assert!(on_block_slot.can_be_consumed(2176).unwrap()); // Block 2176 is in the slot 2176..2303 - assert!(!on_block_slot.can_be_consumed(2175).unwrap()); // Block 1279 is in the slot - // 2176..2303 + assert!(!on_block_slot.can_be_consumed(127.into()).unwrap()); // Block 127 is not in the slot 128..255 + assert!(on_block_slot.can_be_consumed(128.into()).unwrap()); // Block 128 is in the slot 128..255 + assert!(on_block_slot.can_be_consumed(255.into()).unwrap()); // Block 255 is in the slot 128..255 + assert!(!on_block_slot.can_be_consumed(256.into()).unwrap()); // Block 256 is not in the slot 128..255 + assert!(on_block_slot.can_be_consumed(1152.into()).unwrap()); // Block 1152 is in the slot 1152..1279 + assert!(on_block_slot.can_be_consumed(1279.into()).unwrap()); // Block 1279 is in the slot 1152..1279 + assert!(on_block_slot.can_be_consumed(2176.into()).unwrap()); // Block 2176 is in the slot 2176..2303 + assert!(!on_block_slot.can_be_consumed(2175.into()).unwrap()); // Block 1279 is in the slot + // 2176..2303 } #[test] @@ -335,7 +336,7 @@ mod tests { #[test] fn test_after_block_fails_on_u32_max() { assert_matches!( - NoteExecutionHint::after_block(u32::MAX).unwrap_err(), + NoteExecutionHint::after_block(u32::MAX.into()).unwrap_err(), NoteError::NoteExecutionHintAfterBlockCannotBeU32Max ); } diff --git a/objects/src/notes/file.rs b/objects/src/notes/file.rs index 39899de75..2663f6736 100644 --- a/objects/src/notes/file.rs +++ b/objects/src/notes/file.rs @@ -2,6 +2,7 @@ use vm_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; use vm_processor::DeserializationError; use super::{Note, NoteDetails, NoteId, NoteInclusionProof, NoteTag}; +use crate::block::BlockNumber; // NOTE FILE // ================================================================================================ @@ -20,7 +21,7 @@ pub enum NoteFile { /// treated as a hint. NoteDetails { details: NoteDetails, - after_block_num: u32, + after_block_num: BlockNumber, tag: Option, }, /// The note has been recorded on chain. @@ -29,7 +30,11 @@ pub enum NoteFile { impl From for NoteFile { fn from(details: NoteDetails) -> Self { - NoteFile::NoteDetails { details, after_block_num: 0, tag: None } + NoteFile::NoteDetails { + details, + after_block_num: 0.into(), + tag: None, + } } } @@ -77,7 +82,7 @@ impl Deserializable for NoteFile { 0 => Ok(NoteFile::NoteId(NoteId::read_from(source)?)), 1 => { let details = NoteDetails::read_from(source)?; - let after_block_num = u32::read_from(source)?; + let after_block_num = BlockNumber::read_from(source)?; let tag = Option::::read_from(source)?; Ok(NoteFile::NoteDetails { details, after_block_num, tag }) }, @@ -175,7 +180,7 @@ mod tests { let note = create_example_note(); let file = NoteFile::NoteDetails { details: note.details.clone(), - after_block_num: 456, + after_block_num: 456.into(), tag: Some(NoteTag::from(123)), }; let mut buffer = Vec::new(); @@ -186,7 +191,7 @@ mod tests { match file_copy { NoteFile::NoteDetails { details, after_block_num, tag } => { assert_eq!(details, note.details); - assert_eq!(after_block_num, 456); + assert_eq!(after_block_num, 456.into()); assert_eq!(tag, Some(NoteTag::from(123))); }, _ => panic!("Invalid note file variant"), diff --git a/objects/src/notes/metadata.rs b/objects/src/notes/metadata.rs index 50547f762..dd7caa711 100644 --- a/objects/src/notes/metadata.rs +++ b/objects/src/notes/metadata.rs @@ -306,7 +306,7 @@ mod tests { NoteExecutionHint::always(), NoteExecutionHint::none(), NoteExecutionHint::on_block_slot(10, 11, 12), - NoteExecutionHint::after_block(u32::MAX - 1).unwrap(), + NoteExecutionHint::after_block((u32::MAX - 1).into()).unwrap(), ] { let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux).unwrap(); NoteMetadata::read_from_bytes(&metadata.to_bytes()) diff --git a/objects/src/transaction/outputs.rs b/objects/src/transaction/outputs.rs index fc93e47ea..775ecb32d 100644 --- a/objects/src/transaction/outputs.rs +++ b/objects/src/transaction/outputs.rs @@ -6,6 +6,7 @@ use vm_processor::DeserializationError; use crate::{ accounts::AccountHeader, + block::BlockNumber, notes::{compute_note_hash, Note, NoteAssets, NoteHeader, NoteId, NoteMetadata, PartialNote}, Digest, Felt, Hasher, TransactionOutputError, Word, MAX_OUTPUT_NOTES_PER_TX, }; @@ -20,7 +21,7 @@ pub struct TransactionOutputs { /// Set of output notes created by the transaction. pub output_notes: OutputNotes, /// Defines up to which block the transaction is considered valid. - pub expiration_block_num: u32, + pub expiration_block_num: BlockNumber, } // OUTPUT NOTES diff --git a/objects/src/transaction/proven_tx.rs b/objects/src/transaction/proven_tx.rs index 492ecf5f0..ed29ababf 100644 --- a/objects/src/transaction/proven_tx.rs +++ b/objects/src/transaction/proven_tx.rs @@ -5,6 +5,7 @@ use miden_verifier::ExecutionProof; use super::{InputNote, ToInputNoteCommitments}; use crate::{ accounts::delta::AccountUpdateDetails, + block::BlockNumber, notes::NoteHeader, transaction::{ AccountId, Digest, InputNotes, Nullifier, OutputNote, OutputNotes, TransactionId, @@ -37,7 +38,7 @@ pub struct ProvenTransaction { block_ref: Digest, /// The block number by which the transaction will expire, as defined by the executed scripts. - expiration_block_num: u32, + expiration_block_num: BlockNumber, /// A STARK proof that attests to the correct execution of the transaction. proof: ExecutionProof, @@ -85,7 +86,7 @@ impl ProvenTransaction { } /// Returns the block number at which the transaction will expire. - pub fn expiration_block_num(&self) -> u32 { + pub fn expiration_block_num(&self) -> BlockNumber { self.expiration_block_num } @@ -166,7 +167,7 @@ impl Deserializable for ProvenTransaction { let output_notes = OutputNotes::read_from(source)?; let block_ref = Digest::read_from(source)?; - let expiration_block_num = u32::read_from(source)?; + let expiration_block_num = BlockNumber::read_from(source)?; let proof = ExecutionProof::read_from(source)?; let id = TransactionId::new( @@ -220,7 +221,7 @@ pub struct ProvenTransactionBuilder { block_ref: Digest, /// The block number by which the transaction will expire, as defined by the executed scripts. - expiration_block_num: u32, + expiration_block_num: BlockNumber, /// A STARK proof that attests to the correct execution of the transaction. proof: ExecutionProof, @@ -236,7 +237,7 @@ impl ProvenTransactionBuilder { initial_account_hash: Digest, final_account_hash: Digest, block_ref: Digest, - expiration_block_num: u32, + expiration_block_num: BlockNumber, proof: ExecutionProof, ) -> Self { Self {