From 5676e7b3ae41fd9c9a0f8d24314edd03f08882e1 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Fri, 27 Sep 2024 16:01:11 -0300 Subject: [PATCH 01/12] feat: Set transaction recency conditions --- CHANGELOG.md | 1 + miden-lib/asm/kernels/transaction/api.masm | 16 ++++ .../asm/kernels/transaction/lib/epilogue.masm | 17 ++-- .../asm/kernels/transaction/lib/memory.masm | 20 +++++ .../asm/kernels/transaction/lib/prologue.masm | 8 ++ miden-lib/asm/kernels/transaction/lib/tx.masm | 43 +++++++++- miden-lib/asm/miden/kernel_proc_offsets.masm | 15 +++- miden-lib/src/transaction/memory.rs | 5 +- miden-lib/src/transaction/mod.rs | 41 ++++++--- miden-lib/src/transaction/outputs.rs | 3 + .../src/transaction/procedures/kernel_v0.rs | 4 +- miden-tx/src/errors/tx_kernel_errors.rs | 4 +- miden-tx/src/prover/mod.rs | 1 + .../src/tests/kernel_tests/test_epilogue.rs | 83 ++++++++++++++++++- objects/src/transaction/outputs.rs | 4 + objects/src/transaction/proven_tx.rs | 12 +++ 16 files changed, 252 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ee7be277..a29326653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.6.0 (TBD) +- Implemented kernel procedure to set transaction expiration block delta (#897). - Created a proving service that receives `TransactionWitness` and returns the proof using gRPC (#881). - Made note scripts public (#880). - Implemented serialization for `TransactionWitness`, `ChainMmr`, `TransactionInputs` and `TransactionArgs` (#888). diff --git a/miden-lib/asm/kernels/transaction/api.masm b/miden-lib/asm/kernels/transaction/api.masm index 26327d0c2..b0db97f24 100644 --- a/miden-lib/asm/kernels/transaction/api.masm +++ b/miden-lib/asm/kernels/transaction/api.masm @@ -893,6 +893,22 @@ export.end_foreign_context dropw end +#! Sets the transaction expiration time delta, in blocks. +#! Once set, the delta can be later decreased but not increased. +#! +#! The input block height delta is added to the reference block in order to output an upper limit +#! up until which the transaction will be considered valid (not expired). +#! +#! Inputs: [block_height_delta, ...] +#! Output: [tx_block_height_delta, ...] +#! +#! Where: +#! - block_height_delta is the desired expiration time delta (1 to 256). +#! - tx_block_height_delta is the updated (or unchanged) transaction expiration time delta. +export.set_tx_expiration_delta + exec.tx::set_expiration_block_num +end + #! Executes a kernel procedure specified by its offset. #! #! Inputs: [procedure_offset, , ] diff --git a/miden-lib/asm/kernels/transaction/lib/epilogue.masm b/miden-lib/asm/kernels/transaction/lib/epilogue.masm index 5db8f4681..2f578e623 100644 --- a/miden-lib/asm/kernels/transaction/lib/epilogue.masm +++ b/miden-lib/asm/kernels/transaction/lib/epilogue.masm @@ -3,9 +3,16 @@ use.kernel::asset_vault use.kernel::constants use.kernel::memory use.kernel::note +use.kernel::tx use.std::crypto::hashes::native +# CONSTS +# ================================================================================================= + +# Max U32 value. This is the default value for transaction expiration delta +const.U32_MAX=0xFFFFFFFF + # ERRORS # ================================================================================================= @@ -224,7 +231,7 @@ end #! - asserts that the input and output vault roots are equal #! #! Stack: [] -#! Output: [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH] +#! Output: [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH, tx_expiration_block_num] #! #! - OUTPUT_NOTES_COMMITMENT is the commitment of the output notes #! - FINAL_ACCOUNT_HASH is the final account hash @@ -301,11 +308,11 @@ export.finalize_transaction exec.copy_output_notes_to_advice_map # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH] - # truncate stack - swapdw dropw dropw - # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH] - # assert no net creation or destruction of assets over the transaction exec.memory::get_input_vault_root exec.memory::get_output_vault_root assert_eqw.err=ERR_EPILOGUE_ASSETS_DONT_ADD_UP # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH] + + swapdw + exec.memory::get_expiration_block_num + swap drop swapdw end diff --git a/miden-lib/asm/kernels/transaction/lib/memory.masm b/miden-lib/asm/kernels/transaction/lib/memory.masm index 5abfcc947..b20345512 100644 --- a/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/miden-lib/asm/kernels/transaction/lib/memory.masm @@ -32,6 +32,9 @@ const.CURRENT_ACCOUNT_DATA_PTR=5 # The memory address at which the native account's new code commitment is stored. const.NEW_CODE_ROOT_PTR=6 +# The memory address at which the absolute expiration block number is stored. +const.TX_EXPIRATION_BLOCK_NUM_PTR=7 + # GLOBAL INPUTS # ------------------------------------------------------------------------------------------------- @@ -829,6 +832,23 @@ export.set_new_acct_code_commitment mem_storew end +#! Sets the transaction expiration block number. +#! +#! Inputs: [tx_expiration_block_num, ...] +#! Output: [...] +export.set_expiration_block_num + push.TX_EXPIRATION_BLOCK_NUM_PTR mem_store +end + +#! Gets the transaction expiration block number. +#! +#! Inputs: [] +#! Output: [tx_expiration_block_num] +export.get_expiration_block_num + push.TX_EXPIRATION_BLOCK_NUM_PTR mem_load +end + + #! Returns the number of procedures contained in the account code. #! #! Stack: [] diff --git a/miden-lib/asm/kernels/transaction/lib/prologue.masm b/miden-lib/asm/kernels/transaction/lib/prologue.masm index dd13f42cc..bc9072918 100644 --- a/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -9,6 +9,12 @@ use.kernel::constants use.kernel::memory use.kernel::utils +# CONSTS +# ================================================================================================= + +# Max U32 value, used for initializing the expiration block delta +const.U32_MAX=0xFFFFFFFF + # ERRORS # ================================================================================================= @@ -1233,4 +1239,6 @@ export.prepare_transaction exec.process_input_notes_data exec.process_tx_script_root # => [] + + push.U32_MAX exec.memory::set_expiration_block_num end diff --git a/miden-lib/asm/kernels/transaction/lib/tx.masm b/miden-lib/asm/kernels/transaction/lib/tx.masm index 53799a10d..e67219de1 100644 --- a/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -15,6 +15,9 @@ const.ENCRYPTED_NOTE=3 # 0b11 # Two raised to the power of 38 (2^38), used for shifting the note type value const.TWO_POW_38=274877906944 +# Max value for U16, used as the upper limit for expiration block delta +const.EXPIRY_UPPER_LIMIT=0xFFFF+1 + # ERRORS # ================================================================================================= @@ -61,6 +64,9 @@ const.ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS=0x00020051 # Note idx must be within [0, num_of_notes] const.ERR_INVALID_NOTE_IDX=0x00020052 +# Input transaction expiration block delta is not within 1 and 255. +const.ERR_INVALID_TX_EXPIRATION_DELTA=0x00020055 + # EVENTS # ================================================================================================= @@ -73,7 +79,7 @@ const.NOTE_AFTER_CREATED_EVENT=131084 const.NOTE_BEFORE_ADD_ASSET_EVENT=131085 # Event emitted after an ASSET is added to a note const.NOTE_AFTER_ADD_ASSET_EVENT=131086 - + #! Returns the block hash of the reference block to memory. #! #! Stack: [] @@ -178,6 +184,41 @@ proc.add_non_fungible_asset_to_note # => [note_ptr, note_idx] end +#! Sets the transaction expiration delta, in blocks. +#! Once set, the delta can be later decreased but not increased. +#! +#! The input block height delta added to the reference block in order to output an upper limit +#! at which the transaction will be considered valid (not expired). +#! +#! Inputs: [block_height_delta, ...] +#! Output: [...] +#! +#! Where: +#! - block_height_delta is the desired expiration time delta (1 to 256). +#! - tx_block_height_delta is the updated (or unchanged) transaction expiration time delta. +export.set_expiration_block_num + # Ensure block_height_delta is between 1 and 255 (inclusive) + dup neq.0 assert.err=ERR_INVALID_TX_EXPIRATION_DELTA + dup push.EXPIRY_UPPER_LIMIT lt assert.err=ERR_INVALID_TX_EXPIRATION_DELTA + # => [block_height_delta] + + exec.get_block_number add + # => [absolute_expiration_num] + + # Load the current stored delta from memory + dup exec.memory::get_expiration_block_num + # => [block_height_delta, absolute_expiration_num, absolute_expiration_num] + + # Check if block_height_delta is greater + lt + if.true + # Set new expiration delta + exec.memory::set_expiration_block_num + else + drop + end +end + #! Adds a fungible asset to a note. If the note already holds an asset issued by the #! same faucet id the two quantities are summed up and the new quantity is stored at the #! old position in the note. In the other case, the asset is stored at the next available diff --git a/miden-lib/asm/miden/kernel_proc_offsets.masm b/miden-lib/asm/miden/kernel_proc_offsets.masm index 2f48ec6f2..da2053fdd 100644 --- a/miden-lib/asm/miden/kernel_proc_offsets.masm +++ b/miden-lib/asm/miden/kernel_proc_offsets.masm @@ -38,6 +38,7 @@ const.GET_BLOCK_HASH_OFFSET=26 const.GET_BLOCK_NUMBER_OFFSET=27 const.START_FOREIGN_CONTEXT_OFFSET=28 const.END_FOREIGN_CONTEXT_OFFSET=29 +const.SET_TX_EXPIRATION_DELTA_OFFSET=30 # ACCESSORS # ------------------------------------------------------------------------------------------------- @@ -105,7 +106,7 @@ end #! Returns an offset of the `get_account_item` kernel procedure. #! #! Stack: [] -#! Output: [proc_offset] +#! Output: [proc_offset] #! #! Where: #! - proc_offset is the offset of the `get_account_item` kernel procedure required to get the @@ -258,6 +259,18 @@ export.get_block_number_offset push.GET_BLOCK_NUMBER_OFFSET end +#! Returns an offset of the `set_tx_expiration_delta` kernel procedure. +#! +#! Stack: [] +#! Output: [proc_offset] +#! +#! Where: +#! - proc_offset is the offset of the `set_tx_expiration_delta` kernel procedure required to get the +#! address where this procedure is stored. +export.set_tx_expiration_delta + push.SET_TX_EXPIRATION_DELTA_OFFSET +end + #! Returns an offset of the `get_block_hash` kernel procedure. #! #! Stack: [] diff --git a/miden-lib/src/transaction/memory.rs b/miden-lib/src/transaction/memory.rs index b5b33cce5..f8ee290de 100644 --- a/miden-lib/src/transaction/memory.rs +++ b/miden-lib/src/transaction/memory.rs @@ -14,7 +14,7 @@ pub type StorageSlot = u8; // // | Section | Start address | End address | // | ------------- | :------------:| :-----------:| -// | Bookkeeping | 0 | 6 | +// | Bookkeeping | 0 | 7 | // | Global inputs | 100 | 105 | // | Block header | 200 | 208 | // | Chain MMR | 300 | 332? | @@ -73,6 +73,9 @@ pub const CURRENT_ACCOUNT_DATA_PTR: MemoryAddress = 5; /// The memory address at which the native account's new code commitment is stored. pub const NEW_CODE_ROOT_PTR: MemoryAddress = 6; +/// The memory address at which the transaction expiration block number is stored. +pub const TX_EXPIRATION_BLOCK_NUM_PTR: MemoryAddress = 7; + // GLOBAL INPUTS // ------------------------------------------------------------------------------------------------ diff --git a/miden-lib/src/transaction/mod.rs b/miden-lib/src/transaction/mod.rs index 623d6858d..7be9025e8 100644 --- a/miden-lib/src/transaction/mod.rs +++ b/miden-lib/src/transaction/mod.rs @@ -13,6 +13,7 @@ use miden_objects::{ Digest, Felt, TransactionOutputError, Word, EMPTY_WORD, }; use miden_stdlib::StdLibrary; +use outputs::EXPIRATION_BLOCK_ELEMENT_IDX; use super::MidenLib; @@ -174,12 +175,15 @@ impl TransactionKernel { /// /// The data on the stack is expected to be arranged as follows: /// - /// Stack: [CNC, FAH] + /// Stack: [CNC, FAH, tx_expiration_block_num] /// /// Where: /// - CNC is the commitment to the notes created by the transaction. /// - FAH is the final account hash of the account that the transaction is being executed /// against. + /// - tx_expiration_block_num is the block height at which the transaction will become expired, + /// defined by the sum of the execution block ref and the transaction's block expiration delta + /// (if set during transaction execution). /// /// # Errors /// Returns an error if: @@ -187,22 +191,27 @@ impl TransactionKernel { /// - Overflow addresses are not empty. pub fn parse_output_stack( stack: &StackOutputs, - ) -> Result<(Digest, Digest), TransactionOutputError> { + ) -> Result<(Digest, Digest, u32), TransactionOutputError> { let output_notes_hash = stack .get_stack_word(OUTPUT_NOTES_COMMITMENT_WORD_IDX * 4) .expect("first word missing") .into(); + let final_account_hash = stack .get_stack_word(FINAL_ACCOUNT_HASH_WORD_IDX * 4) .expect("second word missing") .into(); - // make sure that the stack has been properly cleaned - if stack.get_stack_word(8).expect("third word missing") != EMPTY_WORD { - return Err(TransactionOutputError::OutputStackInvalid( - "Third word on output stack should consist only of ZEROs".into(), - )); - } + let expiration_block_num = stack + .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 lower than u32::MAX".into(), + ) + })?; + if stack.get_stack_word(12).expect("fourth word missing") != EMPTY_WORD { return Err(TransactionOutputError::OutputStackInvalid( "Fourth word on output stack should consist only of ZEROs".into(), @@ -214,7 +223,7 @@ impl TransactionKernel { )); } - Ok((final_account_hash, output_notes_hash)) + Ok((final_account_hash, output_notes_hash, expiration_block_num)) } // TRANSACTION OUTPUT PARSER @@ -224,12 +233,15 @@ impl TransactionKernel { /// /// The output stack is expected to be arrange as follows: /// - /// Stack: [CNC, FAH] + /// Stack: [CNC, FAH, tx_expiration_block_num] /// /// Where: /// - CNC is the commitment to the notes created by the transaction. /// - FAH is the final account hash of the account that the transaction is being executed /// against. + /// - tx_expiration_block_num is the block height at which the transaction will become expired, + /// defined by the sum of the execution block ref and the transaction's block expiration delta + /// (if set during transaction execution). /// /// The actual data describing the new account state and output notes is expected to be located /// in the provided advice map under keys CNC and FAH. @@ -238,7 +250,8 @@ impl TransactionKernel { adv_map: &AdviceMap, output_notes: Vec, ) -> Result { - let (final_acct_hash, output_notes_hash) = Self::parse_output_stack(stack)?; + let (final_acct_hash, output_notes_hash, expiration_block_num) = + Self::parse_output_stack(stack)?; // parse final account state let final_account_data: &[Word] = group_slice_elements( @@ -258,7 +271,11 @@ impl TransactionKernel { )); } - Ok(TransactionOutputs { account, output_notes }) + Ok(TransactionOutputs { + account, + output_notes, + expiration_block_num, + }) } } diff --git a/miden-lib/src/transaction/outputs.rs b/miden-lib/src/transaction/outputs.rs index 3cf55a071..da3cfba80 100644 --- a/miden-lib/src/transaction/outputs.rs +++ b/miden-lib/src/transaction/outputs.rs @@ -17,6 +17,9 @@ pub const OUTPUT_NOTES_COMMITMENT_WORD_IDX: usize = 0; /// The index of the word at which the final account hash is stored on the output stack. pub const FINAL_ACCOUNT_HASH_WORD_IDX: usize = 1; +/// The index of the item at which the expiration block height is stored on the output stack. +pub const EXPIRATION_BLOCK_ELEMENT_IDX: usize = 8; + // ACCOUNT HEADER EXTRACTOR // ================================================================================================ diff --git a/miden-lib/src/transaction/procedures/kernel_v0.rs b/miden-lib/src/transaction/procedures/kernel_v0.rs index f318b41e5..391cd8e5f 100644 --- a/miden-lib/src/transaction/procedures/kernel_v0.rs +++ b/miden-lib/src/transaction/procedures/kernel_v0.rs @@ -6,7 +6,7 @@ use miden_objects::{digest, Digest, Felt}; // ================================================================================================ /// Hashes of all dynamically executed procedures from the kernel 0. -pub const KERNEL0_PROCEDURES: [Digest; 30] = [ +pub const KERNEL0_PROCEDURES: [Digest; 31] = [ // account_vault_add_asset digest!(0xb8815bfacbdcb4c2, 0x6c7e694cf4f6a517, 0xf6233da2865ca264, 0xe51463cd0df6e896), // account_vault_get_balance @@ -67,4 +67,6 @@ pub const KERNEL0_PROCEDURES: [Digest; 30] = [ digest!(0x9d231f21bd27ff27, 0x5cc4476fad12b66d, 0x82f40fd18e7abb0a, 0xc09c240f2a1d82af), // end_foreign_context digest!(0x3770db711ce9aaf1, 0xb6f3c929151a5d52, 0x3ed145ec5dbee85f, 0xf979d975d7951bf6), + // set_tx_expiration_delta + digest!(0xc9582b7ff8edbd9f, 0xfa71377979938e5, 0x28d7c073f4cb7693, 0xa23523e23145949e), ]; diff --git a/miden-tx/src/errors/tx_kernel_errors.rs b/miden-tx/src/errors/tx_kernel_errors.rs index 96e831117..5e46aa7e9 100644 --- a/miden-tx/src/errors/tx_kernel_errors.rs +++ b/miden-tx/src/errors/tx_kernel_errors.rs @@ -86,8 +86,9 @@ pub const ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS: u32 = 131153; pub const ERR_INVALID_NOTE_IDX: u32 = 131154; pub const ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS: u32 = 131155; pub const ERR_CURRENT_ACCOUNT_IS_NOT_NATIVE: u32 = 131156; +pub const ERR_INVALID_TX_EXPIRATION_DELTA: u32 = 131157; -pub const KERNEL_ERRORS: [(u32, &str); 85] = [ +pub const KERNEL_ERRORS: [(u32, &str); 86] = [ (ERR_FAUCET_RESERVED_DATA_SLOT, "For faucets, storage slot 254 is reserved and can not be used with set_account_item procedure"), (ERR_ACCT_MUST_BE_A_FAUCET, "Procedure can only be called from faucet accounts"), (ERR_P2ID_WRONG_NUMBER_OF_INPUTS, "P2ID scripts expect exactly 1 note input"), @@ -173,4 +174,5 @@ pub const KERNEL_ERRORS: [(u32, &str); 85] = [ (ERR_INVALID_FAUCET_STORAGE_OFFSET, "Storage offset is invalid for a faucet account (0 is prohibited being the reserved faucet data slot)"), (ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS, "Provided kernel procedure offset is out of bounds"), (ERR_CURRENT_ACCOUNT_IS_NOT_NATIVE, "Procedure can be called only for the native account"), + (ERR_INVALID_TX_EXPIRATION_DELTA, "Invalid transaction expiration block delta was set."), ]; diff --git a/miden-tx/src/prover/mod.rs b/miden-tx/src/prover/mod.rs index 3452895d0..098ff3999 100644 --- a/miden-tx/src/prover/mod.rs +++ b/miden-tx/src/prover/mod.rs @@ -108,6 +108,7 @@ impl TransactionProver for LocalTransactionProver { account.init_hash(), tx_outputs.account.hash(), block_hash, + tx_outputs.expiration_block_num, proof, ) .add_input_notes(input_notes) diff --git a/miden-tx/src/tests/kernel_tests/test_epilogue.rs b/miden-tx/src/tests/kernel_tests/test_epilogue.rs index 10256ed21..78e57af7c 100644 --- a/miden-tx/src/tests/kernel_tests/test_epilogue.rs +++ b/miden-tx/src/tests/kernel_tests/test_epilogue.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{string::ToString, vec::Vec}; use miden_lib::transaction::{ memory::{NOTE_MEM_SIZE, OUTPUT_NOTE_ASSET_HASH_OFFSET, OUTPUT_NOTE_SECTION_OFFSET}, @@ -8,12 +8,15 @@ use miden_objects::{ accounts::Account, transaction::{OutputNote, OutputNotes}, }; -use vm_processor::ONE; +use vm_processor::{ProcessState, ONE}; use super::{output_notes_data_procedure, ZERO}; use crate::{ assert_execution_error, - errors::tx_kernel_errors::{ERR_EPILOGUE_ASSETS_DONT_ADD_UP, ERR_NONCE_DID_NOT_INCREASE}, + errors::tx_kernel_errors::{ + ERR_EPILOGUE_ASSETS_DONT_ADD_UP, ERR_INVALID_TX_EXPIRATION_DELTA, + ERR_NONCE_DID_NOT_INCREASE, + }, testing::TransactionContextBuilder, tests::kernel_tests::read_root_mem_value, }; @@ -191,6 +194,80 @@ fn test_epilogue_asset_preservation_violation_too_many_fungible_input() { assert_execution_error!(process, ERR_EPILOGUE_ASSETS_DONT_ADD_UP); } +#[test] +fn test_block_expiration_height_monotonically_decreases() { + let tx_context = TransactionContextBuilder::with_standard_account(ONE).build(); + + let test_pairs: [(u64, u64); 3] = [(9, 12), (18, 3), (20, 20)]; + let code_template = " + use.kernel::prologue + use.kernel::tx + use.kernel::epilogue + + begin + exec.prologue::prepare_transaction + push.{value_1} + exec.tx::set_expiration_block_num + push.{value_2} + exec.tx::set_expiration_block_num + exec.epilogue::finalize_transaction + end + "; + + for (v1, v2) in test_pairs { + let code = &code_template + .replace("{value_1}", &v1.to_string()) + .replace("{value_2}", &v2.to_string()); + + let process = tx_context.execute_code(code).unwrap(); + + // Expiry block should be set to transaction's block + the stored expiration delta + // (which can only decrease, not increase) + let expected_expiry = v1.min(v2) + tx_context.tx_inputs().block_header().block_num() as u64; + assert_eq!(process.get_stack_item(8).as_int(), expected_expiry); + } +} + +#[test] +fn test_invalid_expiration_deltas() { + let tx_context = TransactionContextBuilder::with_standard_account(ONE).build(); + + let test_values = [0u64, u16::MAX as u64 + 1, u32::MAX as u64]; + let code_template = " + use.kernel::tx + + begin + push.{value_1} + exec.tx::set_expiration_block_num + end + "; + + for value in test_values { + let code = &code_template.replace("{value_1}", &value.to_string()); + let process = tx_context.execute_code(code); + + assert_execution_error!(process, ERR_INVALID_TX_EXPIRATION_DELTA); + } +} + +#[test] +fn test_no_expiration_delta_set() { + let tx_context = TransactionContextBuilder::with_standard_account(ONE).build(); + + let code_template = " + use.kernel::prologue + use.kernel::epilogue + + begin + exec.prologue::prepare_transaction + exec.epilogue::finalize_transaction + end + "; + let process = tx_context.execute_code(code_template).unwrap(); + // Default value should be equal to u32::max, set in the prologue + assert_eq!(process.get_stack_item(8).as_int() as u32, u32::MAX); +} + #[test] fn test_epilogue_increment_nonce_success() { let tx_context = TransactionContextBuilder::with_standard_account(ONE) diff --git a/objects/src/transaction/outputs.rs b/objects/src/transaction/outputs.rs index 3d38a3ae8..8de8e2db1 100644 --- a/objects/src/transaction/outputs.rs +++ b/objects/src/transaction/outputs.rs @@ -15,8 +15,12 @@ use crate::{ /// Describes the result of executing a transaction. #[derive(Debug, Clone, PartialEq, Eq)] pub struct TransactionOutputs { + /// Information related to the account's final state. pub account: AccountHeader, + /// 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, } // OUTPUT NOTES diff --git a/objects/src/transaction/proven_tx.rs b/objects/src/transaction/proven_tx.rs index 1ba8faa19..6646487ac 100644 --- a/objects/src/transaction/proven_tx.rs +++ b/objects/src/transaction/proven_tx.rs @@ -36,6 +36,9 @@ pub struct ProvenTransaction { /// The block hash of the last known block at the time the transaction was executed. block_ref: Digest, + /// The block number by which the transaction will expire, as defined by the executed scripts. + expiration_block_num: u32, + /// A STARK proof that attests to the correct execution of the transaction. proof: ExecutionProof, } @@ -145,6 +148,7 @@ impl Serializable for ProvenTransaction { self.input_notes.write_into(target); self.output_notes.write_into(target); self.block_ref.write_into(target); + self.expiration_block_num.write_into(target); self.proof.write_into(target); } } @@ -157,6 +161,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 proof = ExecutionProof::read_from(source)?; let id = TransactionId::new( @@ -172,6 +177,7 @@ impl Deserializable for ProvenTransaction { input_notes, output_notes, block_ref, + expiration_block_num, proof, }; @@ -208,6 +214,9 @@ pub struct ProvenTransactionBuilder { /// Block [Digest] of the transaction's reference block. block_ref: Digest, + /// The block number by which the transaction will expire, as defined by the executed scripts. + expiration_block_num: u32, + /// A STARK proof that attests to the correct execution of the transaction. proof: ExecutionProof, } @@ -222,6 +231,7 @@ impl ProvenTransactionBuilder { initial_account_hash: Digest, final_account_hash: Digest, block_ref: Digest, + expiration_block_num: u32, proof: ExecutionProof, ) -> Self { Self { @@ -232,6 +242,7 @@ impl ProvenTransactionBuilder { input_notes: Vec::new(), output_notes: Vec::new(), block_ref, + expiration_block_num, proof, } } @@ -294,6 +305,7 @@ impl ProvenTransactionBuilder { input_notes, output_notes, block_ref: self.block_ref, + expiration_block_num: self.expiration_block_num, proof: self.proof, }; From 8a9029a68bc4de232711d4fcad1edbb45edad1e3 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Fri, 27 Sep 2024 16:02:45 -0300 Subject: [PATCH 02/12] Remove unused const --- miden-lib/asm/kernels/transaction/lib/epilogue.masm | 6 ------ 1 file changed, 6 deletions(-) diff --git a/miden-lib/asm/kernels/transaction/lib/epilogue.masm b/miden-lib/asm/kernels/transaction/lib/epilogue.masm index 2f578e623..578cfe64a 100644 --- a/miden-lib/asm/kernels/transaction/lib/epilogue.masm +++ b/miden-lib/asm/kernels/transaction/lib/epilogue.masm @@ -7,12 +7,6 @@ use.kernel::tx use.std::crypto::hashes::native -# CONSTS -# ================================================================================================= - -# Max U32 value. This is the default value for transaction expiration delta -const.U32_MAX=0xFFFFFFFF - # ERRORS # ================================================================================================= From 9700fae6620cff1b62ccb8ef525093651bab439f Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Fri, 27 Sep 2024 16:05:47 -0300 Subject: [PATCH 03/12] Fix comment --- miden-lib/asm/kernels/transaction/lib/tx.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miden-lib/asm/kernels/transaction/lib/tx.masm b/miden-lib/asm/kernels/transaction/lib/tx.masm index e67219de1..a1fcc1b68 100644 --- a/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -64,7 +64,7 @@ const.ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS=0x00020051 # Note idx must be within [0, num_of_notes] const.ERR_INVALID_NOTE_IDX=0x00020052 -# Input transaction expiration block delta is not within 1 and 255. +# Input transaction expiration block delta is not within 0x1 and 0xFFFF. const.ERR_INVALID_TX_EXPIRATION_DELTA=0x00020055 # EVENTS From 70a734c87b2ec49352bfa1776ee79a353cb45fc7 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Fri, 27 Sep 2024 16:06:09 -0300 Subject: [PATCH 04/12] Fix other commeent --- miden-lib/asm/kernels/transaction/lib/tx.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miden-lib/asm/kernels/transaction/lib/tx.masm b/miden-lib/asm/kernels/transaction/lib/tx.masm index a1fcc1b68..45e5867f6 100644 --- a/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -197,7 +197,7 @@ end #! - block_height_delta is the desired expiration time delta (1 to 256). #! - tx_block_height_delta is the updated (or unchanged) transaction expiration time delta. export.set_expiration_block_num - # Ensure block_height_delta is between 1 and 255 (inclusive) + # Ensure block_height_delta is between 1 and 0xFFFF (inclusive) dup neq.0 assert.err=ERR_INVALID_TX_EXPIRATION_DELTA dup push.EXPIRY_UPPER_LIMIT lt assert.err=ERR_INVALID_TX_EXPIRATION_DELTA # => [block_height_delta] From c910ec79aedfff532066ecd9bd4f68e770081d30 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Fri, 27 Sep 2024 16:06:46 -0300 Subject: [PATCH 05/12] Fix other comment --- miden-lib/asm/kernels/transaction/api.masm | 2 +- miden-lib/asm/kernels/transaction/lib/tx.masm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/miden-lib/asm/kernels/transaction/api.masm b/miden-lib/asm/kernels/transaction/api.masm index b0db97f24..dce978574 100644 --- a/miden-lib/asm/kernels/transaction/api.masm +++ b/miden-lib/asm/kernels/transaction/api.masm @@ -903,7 +903,7 @@ end #! Output: [tx_block_height_delta, ...] #! #! Where: -#! - block_height_delta is the desired expiration time delta (1 to 256). +#! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). #! - tx_block_height_delta is the updated (or unchanged) transaction expiration time delta. export.set_tx_expiration_delta exec.tx::set_expiration_block_num diff --git a/miden-lib/asm/kernels/transaction/lib/tx.masm b/miden-lib/asm/kernels/transaction/lib/tx.masm index 45e5867f6..0d5f7663e 100644 --- a/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -194,7 +194,7 @@ end #! Output: [...] #! #! Where: -#! - block_height_delta is the desired expiration time delta (1 to 256). +#! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). #! - tx_block_height_delta is the updated (or unchanged) transaction expiration time delta. export.set_expiration_block_num # Ensure block_height_delta is between 1 and 0xFFFF (inclusive) From a9d2db6ee0133e0eb94232af7253312429d36aba Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Fri, 27 Sep 2024 16:50:13 -0300 Subject: [PATCH 06/12] Fix tests --- bin/tx-prover/src/server/generated/api.rs | 83 ++++++++++++------- .../asm/kernels/transaction/lib/epilogue.masm | 4 + .../src/tests/kernel_tests/test_epilogue.rs | 5 +- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/bin/tx-prover/src/server/generated/api.rs b/bin/tx-prover/src/server/generated/api.rs index 9015ff32f..082a941d1 100644 --- a/bin/tx-prover/src/server/generated/api.rs +++ b/bin/tx-prover/src/server/generated/api.rs @@ -14,7 +14,8 @@ pub struct ProveTransactionResponse { /// Generated client implementations. pub mod api_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::{http::Uri, *}; + use tonic::codegen::*; + use tonic::codegen::http::Uri; #[derive(Debug, Clone)] pub struct ApiClient { inner: tonic::client::Grpc, @@ -45,7 +46,10 @@ pub mod api_client { let inner = tonic::client::Grpc::with_origin(inner, origin); Self { inner } } - pub fn with_interceptor(inner: T, interceptor: F) -> ApiClient> + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ApiClient> where F: tonic::service::Interceptor, T::ResponseBody: Default, @@ -55,8 +59,9 @@ pub mod api_client { >::ResponseBody, >, >, - >>::Error: - Into + Send + Sync, + , + >>::Error: Into + Send + Sync, { ApiClient::new(InterceptedService::new(inner, interceptor)) } @@ -94,14 +99,19 @@ pub mod api_client { pub async fn prove_transaction( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> - { - self.inner.ready().await.map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/api.Api/ProveTransaction"); let mut req = request.into_request(); @@ -120,7 +130,10 @@ pub mod api_server { async fn prove_transaction( &self, request: tonic::Request, - ) -> std::result::Result, tonic::Status>; + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct ApiServer { @@ -145,7 +158,10 @@ pub mod api_server { max_encoding_message_size: None, } } - pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService where F: tonic::service::Interceptor, { @@ -201,18 +217,23 @@ pub mod api_server { "/api.Api/ProveTransaction" => { #[allow(non_camel_case_types)] struct ProveTransactionSvc(pub Arc); - impl tonic::server::UnaryService - for ProveTransactionSvc - { + impl< + T: Api, + > tonic::server::UnaryService + for ProveTransactionSvc { type Response = super::ProveTransactionResponse; - type Future = BoxFuture, tonic::Status>; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = - async move { ::prove_transaction(&inner, request).await }; + let fut = async move { + ::prove_transaction(&inner, request).await + }; Box::pin(fut) } } @@ -238,15 +259,19 @@ pub mod api_server { Ok(res) }; Box::pin(fut) - }, - _ => Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap()) - }), + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } } } } diff --git a/miden-lib/asm/kernels/transaction/lib/epilogue.masm b/miden-lib/asm/kernels/transaction/lib/epilogue.masm index 578cfe64a..1d31ea52e 100644 --- a/miden-lib/asm/kernels/transaction/lib/epilogue.masm +++ b/miden-lib/asm/kernels/transaction/lib/epilogue.masm @@ -302,6 +302,10 @@ export.finalize_transaction exec.copy_output_notes_to_advice_map # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH] + # truncate stack + swapdw dropw dropw + # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH] + # assert no net creation or destruction of assets over the transaction exec.memory::get_input_vault_root exec.memory::get_output_vault_root assert_eqw.err=ERR_EPILOGUE_ASSETS_DONT_ADD_UP # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH] diff --git a/miden-tx/src/tests/kernel_tests/test_epilogue.rs b/miden-tx/src/tests/kernel_tests/test_epilogue.rs index 78e57af7c..a318e05e6 100644 --- a/miden-tx/src/tests/kernel_tests/test_epilogue.rs +++ b/miden-tx/src/tests/kernel_tests/test_epilogue.rs @@ -8,7 +8,7 @@ use miden_objects::{ accounts::Account, transaction::{OutputNote, OutputNotes}, }; -use vm_processor::{ProcessState, ONE}; +use vm_processor::{Felt, ProcessState, ONE}; use super::{output_notes_data_procedure, ZERO}; use crate::{ @@ -72,7 +72,8 @@ fn test_epilogue() { let mut expected_stack = Vec::with_capacity(16); expected_stack.extend(output_notes.commitment().as_elements().iter().rev()); expected_stack.extend(final_account.hash().as_elements().iter().rev()); - expected_stack.extend((8..16).map(|_| ZERO)); + expected_stack.push(Felt::from(u32::MAX)); // Value for tx expiration block number + expected_stack.extend((9..16).map(|_| ZERO)); assert_eq!( process.stack.build_stack_outputs().stack(), From 9918bd6474d9e5c7c11562b3a06fbaa5ea6480ad Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Fri, 27 Sep 2024 16:52:45 -0300 Subject: [PATCH 07/12] Lints --- bin/tx-prover/src/server/generated/api.rs | 83 ++++++++--------------- 1 file changed, 29 insertions(+), 54 deletions(-) diff --git a/bin/tx-prover/src/server/generated/api.rs b/bin/tx-prover/src/server/generated/api.rs index 082a941d1..9015ff32f 100644 --- a/bin/tx-prover/src/server/generated/api.rs +++ b/bin/tx-prover/src/server/generated/api.rs @@ -14,8 +14,7 @@ pub struct ProveTransactionResponse { /// Generated client implementations. pub mod api_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - use tonic::codegen::http::Uri; + use tonic::codegen::{http::Uri, *}; #[derive(Debug, Clone)] pub struct ApiClient { inner: tonic::client::Grpc, @@ -46,10 +45,7 @@ pub mod api_client { let inner = tonic::client::Grpc::with_origin(inner, origin); Self { inner } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> ApiClient> + pub fn with_interceptor(inner: T, interceptor: F) -> ApiClient> where F: tonic::service::Interceptor, T::ResponseBody: Default, @@ -59,9 +55,8 @@ pub mod api_client { >::ResponseBody, >, >, - , - >>::Error: Into + Send + Sync, + >>::Error: + Into + Send + Sync, { ApiClient::new(InterceptedService::new(inner, interceptor)) } @@ -99,19 +94,14 @@ pub mod api_client { pub async fn prove_transaction( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/api.Api/ProveTransaction"); let mut req = request.into_request(); @@ -130,10 +120,7 @@ pub mod api_server { async fn prove_transaction( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; } #[derive(Debug)] pub struct ApiServer { @@ -158,10 +145,7 @@ pub mod api_server { max_encoding_message_size: None, } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService + pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService where F: tonic::service::Interceptor, { @@ -217,23 +201,18 @@ pub mod api_server { "/api.Api/ProveTransaction" => { #[allow(non_camel_case_types)] struct ProveTransactionSvc(pub Arc); - impl< - T: Api, - > tonic::server::UnaryService - for ProveTransactionSvc { + impl tonic::server::UnaryService + for ProveTransactionSvc + { type Response = super::ProveTransactionResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::prove_transaction(&inner, request).await - }; + let fut = + async move { ::prove_transaction(&inner, request).await }; Box::pin(fut) } } @@ -259,19 +238,15 @@ pub mod api_server { Ok(res) }; Box::pin(fut) - } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + }, + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } From afdf9175c3e94ec53ce936a6e011a3650baf87ef Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Fri, 27 Sep 2024 18:44:41 -0300 Subject: [PATCH 08/12] Reviews --- miden-lib/asm/kernels/transaction/api.masm | 22 +++++++++++++----- .../asm/kernels/transaction/lib/memory.masm | 1 - .../asm/kernels/transaction/lib/prologue.masm | 6 ++--- miden-lib/asm/kernels/transaction/lib/tx.masm | 23 +++++++++++++++---- miden-lib/asm/miden/kernel_proc_offsets.masm | 21 +++++++++++++---- .../src/transaction/procedures/kernel_v0.rs | 6 +++-- .../src/tests/kernel_tests/test_epilogue.rs | 12 ++++++---- 7 files changed, 66 insertions(+), 25 deletions(-) diff --git a/miden-lib/asm/kernels/transaction/api.masm b/miden-lib/asm/kernels/transaction/api.masm index dce978574..49fd7fac9 100644 --- a/miden-lib/asm/kernels/transaction/api.masm +++ b/miden-lib/asm/kernels/transaction/api.masm @@ -893,20 +893,30 @@ export.end_foreign_context dropw end -#! Sets the transaction expiration time delta, in blocks. -#! Once set, the delta can be later decreased but not increased. +#! Updates the transaction expiration time delta. +#! Once set, the delta can be decreased but not increased. #! #! The input block height delta is added to the reference block in order to output an upper limit #! up until which the transaction will be considered valid (not expired). #! #! Inputs: [block_height_delta, ...] -#! Output: [tx_block_height_delta, ...] +#! Output: [...] #! #! Where: #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). -#! - tx_block_height_delta is the updated (or unchanged) transaction expiration time delta. -export.set_tx_expiration_delta - exec.tx::set_expiration_block_num +export.update_expiration_block_num + exec.tx::update_expiration_block_num +end + +#! Gets the transaction expiration block number. +#! +#! Inputs: [...] +#! Output: [block_height_delta, ...] +#! +#! Where: +#! - block_height_delta is the stored expiration time delta (1 to 0xFFFF). +export.get_expiration_delta + exec.tx::get_expiration_delta end #! Executes a kernel procedure specified by its offset. diff --git a/miden-lib/asm/kernels/transaction/lib/memory.masm b/miden-lib/asm/kernels/transaction/lib/memory.masm index b20345512..4e51b9064 100644 --- a/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/miden-lib/asm/kernels/transaction/lib/memory.masm @@ -848,7 +848,6 @@ export.get_expiration_block_num push.TX_EXPIRATION_BLOCK_NUM_PTR mem_load end - #! Returns the number of procedures contained in the account code. #! #! Stack: [] diff --git a/miden-lib/asm/kernels/transaction/lib/prologue.masm b/miden-lib/asm/kernels/transaction/lib/prologue.masm index bc9072918..812f39fd9 100644 --- a/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -12,8 +12,8 @@ use.kernel::utils # CONSTS # ================================================================================================= -# Max U32 value, used for initializing the expiration block delta -const.U32_MAX=0xFFFFFFFF +# Max U32 value, used for initializing the expiration block number +const.MAX_BLOCK_NUM=0xFFFFFFFF # ERRORS # ================================================================================================= @@ -1240,5 +1240,5 @@ export.prepare_transaction exec.process_tx_script_root # => [] - push.U32_MAX exec.memory::set_expiration_block_num + push.MAX_BLOCK_NUM exec.memory::set_expiration_block_num end diff --git a/miden-lib/asm/kernels/transaction/lib/tx.masm b/miden-lib/asm/kernels/transaction/lib/tx.masm index 0d5f7663e..262dbb8ce 100644 --- a/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -184,11 +184,11 @@ proc.add_non_fungible_asset_to_note # => [note_ptr, note_idx] end -#! Sets the transaction expiration delta, in blocks. -#! Once set, the delta can be later decreased but not increased. +#! Updates the transaction expiration block number. #! -#! The input block height delta added to the reference block in order to output an upper limit -#! at which the transaction will be considered valid (not expired). +#! The input block_height_delta is added to the block reference number in order to output an upper +#! limit at which the transaction will be considered valid (not expired). +#! This value can be later decreased, but not increased. #! #! Inputs: [block_height_delta, ...] #! Output: [...] @@ -196,7 +196,7 @@ end #! Where: #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). #! - tx_block_height_delta is the updated (or unchanged) transaction expiration time delta. -export.set_expiration_block_num +export.update_expiration_block_num # Ensure block_height_delta is between 1 and 0xFFFF (inclusive) dup neq.0 assert.err=ERR_INVALID_TX_EXPIRATION_DELTA dup push.EXPIRY_UPPER_LIMIT lt assert.err=ERR_INVALID_TX_EXPIRATION_DELTA @@ -219,6 +219,19 @@ export.set_expiration_block_num end end +#! Gets the transaction expiration block number. +#! +#! Inputs: [...] +#! Output: [block_height_delta, ...] +#! +#! Where: +#! - block_height_delta is the stored expiration time delta (1 to 0xFFFF). +export.get_expiration_delta + exec.memory::get_expiration_block_num exec.get_block_number + sub +end + + #! Adds a fungible asset to a note. If the note already holds an asset issued by the #! same faucet id the two quantities are summed up and the new quantity is stored at the #! old position in the note. In the other case, the asset is stored at the next available diff --git a/miden-lib/asm/miden/kernel_proc_offsets.masm b/miden-lib/asm/miden/kernel_proc_offsets.masm index da2053fdd..d5a5028d9 100644 --- a/miden-lib/asm/miden/kernel_proc_offsets.masm +++ b/miden-lib/asm/miden/kernel_proc_offsets.masm @@ -38,7 +38,8 @@ const.GET_BLOCK_HASH_OFFSET=26 const.GET_BLOCK_NUMBER_OFFSET=27 const.START_FOREIGN_CONTEXT_OFFSET=28 const.END_FOREIGN_CONTEXT_OFFSET=29 -const.SET_TX_EXPIRATION_DELTA_OFFSET=30 +const.UPDATE_EXPIRATION_BLOCK_NUM_OFFSET=30 +const.GET_EXPIRATION_DELTA_OFFSET=31 # ACCESSORS # ------------------------------------------------------------------------------------------------- @@ -259,7 +260,7 @@ export.get_block_number_offset push.GET_BLOCK_NUMBER_OFFSET end -#! Returns an offset of the `set_tx_expiration_delta` kernel procedure. +#! Returns an offset of the `update_expiration_block_num` kernel procedure. #! #! Stack: [] #! Output: [proc_offset] @@ -267,8 +268,20 @@ end #! Where: #! - proc_offset is the offset of the `set_tx_expiration_delta` kernel procedure required to get the #! address where this procedure is stored. -export.set_tx_expiration_delta - push.SET_TX_EXPIRATION_DELTA_OFFSET +export.update_expiration_block_num_offset + push.UPDATE_EXPIRATION_BLOCK_NUM_OFFSET +end + +#! Returns an offset of the `get_expiration_delta` kernel procedure. +#! +#! Stack: [] +#! Output: [proc_offset] +#! +#! Where: +#! - proc_offset is the offset of the `set_tx_expiration_delta` kernel procedure required to get the +#! address where this procedure is stored. +export.get_expiration_block_delta_offset + push.GET_EXPIRATION_DELTA_OFFSET end #! Returns an offset of the `get_block_hash` kernel procedure. diff --git a/miden-lib/src/transaction/procedures/kernel_v0.rs b/miden-lib/src/transaction/procedures/kernel_v0.rs index 391cd8e5f..4c196cc6e 100644 --- a/miden-lib/src/transaction/procedures/kernel_v0.rs +++ b/miden-lib/src/transaction/procedures/kernel_v0.rs @@ -6,7 +6,7 @@ use miden_objects::{digest, Digest, Felt}; // ================================================================================================ /// Hashes of all dynamically executed procedures from the kernel 0. -pub const KERNEL0_PROCEDURES: [Digest; 31] = [ +pub const KERNEL0_PROCEDURES: [Digest; 32] = [ // account_vault_add_asset digest!(0xb8815bfacbdcb4c2, 0x6c7e694cf4f6a517, 0xf6233da2865ca264, 0xe51463cd0df6e896), // account_vault_get_balance @@ -67,6 +67,8 @@ pub const KERNEL0_PROCEDURES: [Digest; 31] = [ digest!(0x9d231f21bd27ff27, 0x5cc4476fad12b66d, 0x82f40fd18e7abb0a, 0xc09c240f2a1d82af), // end_foreign_context digest!(0x3770db711ce9aaf1, 0xb6f3c929151a5d52, 0x3ed145ec5dbee85f, 0xf979d975d7951bf6), - // set_tx_expiration_delta + // update_expiration_block_num digest!(0xc9582b7ff8edbd9f, 0xfa71377979938e5, 0x28d7c073f4cb7693, 0xa23523e23145949e), + // get_expiration_delta + digest!(0x2d93af519fa32359, 0x14275beadcb2ab9c, 0x68f9336f45c32c86, 0x75ee8ba0f3c11c83), ]; diff --git a/miden-tx/src/tests/kernel_tests/test_epilogue.rs b/miden-tx/src/tests/kernel_tests/test_epilogue.rs index a318e05e6..bd160b1d5 100644 --- a/miden-tx/src/tests/kernel_tests/test_epilogue.rs +++ b/miden-tx/src/tests/kernel_tests/test_epilogue.rs @@ -208,9 +208,12 @@ fn test_block_expiration_height_monotonically_decreases() { begin exec.prologue::prepare_transaction push.{value_1} - exec.tx::set_expiration_block_num + exec.tx::update_expiration_block_num push.{value_2} - exec.tx::set_expiration_block_num + exec.tx::update_expiration_block_num + + push.{min_value} exec.tx::get_expiration_delta assert_eq + exec.epilogue::finalize_transaction end "; @@ -218,7 +221,8 @@ fn test_block_expiration_height_monotonically_decreases() { for (v1, v2) in test_pairs { let code = &code_template .replace("{value_1}", &v1.to_string()) - .replace("{value_2}", &v2.to_string()); + .replace("{value_2}", &v2.to_string()) + .replace("{min_value}", &v2.min(v1).to_string()); let process = tx_context.execute_code(code).unwrap(); @@ -239,7 +243,7 @@ fn test_invalid_expiration_deltas() { begin push.{value_1} - exec.tx::set_expiration_block_num + exec.tx::update_expiration_block_num end "; From e81e5993109268dd79defc85f78c7773505adac1 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Sun, 29 Sep 2024 12:24:59 -0300 Subject: [PATCH 09/12] Reviews and fixes --- bin/tx-prover/src/server/generated/api.rs | 83 ++++++++++++------- miden-lib/asm/kernels/transaction/api.masm | 2 +- .../asm/kernels/transaction/lib/prologue.masm | 2 +- miden-lib/asm/kernels/transaction/lib/tx.masm | 30 ++++--- .../src/transaction/procedures/kernel_v0.rs | 2 +- miden-tx/tests/integration/scripts/faucet.rs | 2 +- miden-tx/tests/integration/wallet/mod.rs | 2 +- 7 files changed, 76 insertions(+), 47 deletions(-) diff --git a/bin/tx-prover/src/server/generated/api.rs b/bin/tx-prover/src/server/generated/api.rs index 9015ff32f..082a941d1 100644 --- a/bin/tx-prover/src/server/generated/api.rs +++ b/bin/tx-prover/src/server/generated/api.rs @@ -14,7 +14,8 @@ pub struct ProveTransactionResponse { /// Generated client implementations. pub mod api_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::{http::Uri, *}; + use tonic::codegen::*; + use tonic::codegen::http::Uri; #[derive(Debug, Clone)] pub struct ApiClient { inner: tonic::client::Grpc, @@ -45,7 +46,10 @@ pub mod api_client { let inner = tonic::client::Grpc::with_origin(inner, origin); Self { inner } } - pub fn with_interceptor(inner: T, interceptor: F) -> ApiClient> + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ApiClient> where F: tonic::service::Interceptor, T::ResponseBody: Default, @@ -55,8 +59,9 @@ pub mod api_client { >::ResponseBody, >, >, - >>::Error: - Into + Send + Sync, + , + >>::Error: Into + Send + Sync, { ApiClient::new(InterceptedService::new(inner, interceptor)) } @@ -94,14 +99,19 @@ pub mod api_client { pub async fn prove_transaction( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> - { - self.inner.ready().await.map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/api.Api/ProveTransaction"); let mut req = request.into_request(); @@ -120,7 +130,10 @@ pub mod api_server { async fn prove_transaction( &self, request: tonic::Request, - ) -> std::result::Result, tonic::Status>; + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct ApiServer { @@ -145,7 +158,10 @@ pub mod api_server { max_encoding_message_size: None, } } - pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService where F: tonic::service::Interceptor, { @@ -201,18 +217,23 @@ pub mod api_server { "/api.Api/ProveTransaction" => { #[allow(non_camel_case_types)] struct ProveTransactionSvc(pub Arc); - impl tonic::server::UnaryService - for ProveTransactionSvc - { + impl< + T: Api, + > tonic::server::UnaryService + for ProveTransactionSvc { type Response = super::ProveTransactionResponse; - type Future = BoxFuture, tonic::Status>; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = - async move { ::prove_transaction(&inner, request).await }; + let fut = async move { + ::prove_transaction(&inner, request).await + }; Box::pin(fut) } } @@ -238,15 +259,19 @@ pub mod api_server { Ok(res) }; Box::pin(fut) - }, - _ => Box::pin(async move { - Ok(http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap()) - }), + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } } } } diff --git a/miden-lib/asm/kernels/transaction/api.masm b/miden-lib/asm/kernels/transaction/api.masm index 49fd7fac9..7fee6b195 100644 --- a/miden-lib/asm/kernels/transaction/api.masm +++ b/miden-lib/asm/kernels/transaction/api.masm @@ -908,7 +908,7 @@ export.update_expiration_block_num exec.tx::update_expiration_block_num end -#! Gets the transaction expiration block number. +#! Gets the transaction expiration delta. #! #! Inputs: [...] #! Output: [block_height_delta, ...] diff --git a/miden-lib/asm/kernels/transaction/lib/prologue.masm b/miden-lib/asm/kernels/transaction/lib/prologue.masm index 812f39fd9..d783faa09 100644 --- a/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -1239,6 +1239,6 @@ export.prepare_transaction exec.process_input_notes_data exec.process_tx_script_root # => [] - + push.MAX_BLOCK_NUM exec.memory::set_expiration_block_num end diff --git a/miden-lib/asm/kernels/transaction/lib/tx.masm b/miden-lib/asm/kernels/transaction/lib/tx.masm index 262dbb8ce..7280cb7d6 100644 --- a/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -195,31 +195,35 @@ end #! #! Where: #! - block_height_delta is the desired expiration time delta (1 to 0xFFFF). -#! - tx_block_height_delta is the updated (or unchanged) transaction expiration time delta. export.update_expiration_block_num # Ensure block_height_delta is between 1 and 0xFFFF (inclusive) dup neq.0 assert.err=ERR_INVALID_TX_EXPIRATION_DELTA dup push.EXPIRY_UPPER_LIMIT lt assert.err=ERR_INVALID_TX_EXPIRATION_DELTA # => [block_height_delta] - exec.get_block_number add - # => [absolute_expiration_num] + exec.get_block_number u32overflowing_add + # => [is_higher_than_u32max, absolute_expiration_num] - # Load the current stored delta from memory - dup exec.memory::get_expiration_block_num - # => [block_height_delta, absolute_expiration_num, absolute_expiration_num] + if.false + # Load the current stored delta from memory + dup exec.memory::get_expiration_block_num + # => [stored_expiration_block_num, absolute_expiration_num, absolute_expiration_num] - # Check if block_height_delta is greater - lt - if.true - # Set new expiration delta - exec.memory::set_expiration_block_num + # Check if block_height_delta is greater + u32lt + if.true + # Set new expiration delta + exec.memory::set_expiration_block_num + else + drop + end else - drop + # If the addition is higher than the highest block number, no need to compare + drop end end -#! Gets the transaction expiration block number. +#! Gets the transaction expiration delta. #! #! Inputs: [...] #! Output: [block_height_delta, ...] diff --git a/miden-lib/src/transaction/procedures/kernel_v0.rs b/miden-lib/src/transaction/procedures/kernel_v0.rs index 4c196cc6e..54205f1b8 100644 --- a/miden-lib/src/transaction/procedures/kernel_v0.rs +++ b/miden-lib/src/transaction/procedures/kernel_v0.rs @@ -68,7 +68,7 @@ pub const KERNEL0_PROCEDURES: [Digest; 32] = [ // end_foreign_context digest!(0x3770db711ce9aaf1, 0xb6f3c929151a5d52, 0x3ed145ec5dbee85f, 0xf979d975d7951bf6), // update_expiration_block_num - digest!(0xc9582b7ff8edbd9f, 0xfa71377979938e5, 0x28d7c073f4cb7693, 0xa23523e23145949e), + digest!(0xb111da64a8792f2d, 0x5fb267857eb9b657, 0xcb78aa9423268e71, 0x1bff8a6aa33af94e), // get_expiration_delta digest!(0x2d93af519fa32359, 0x14275beadcb2ab9c, 0x68f9336f45c32c86, 0x75ee8ba0f3c11c83), ]; diff --git a/miden-tx/tests/integration/scripts/faucet.rs b/miden-tx/tests/integration/scripts/faucet.rs index 4147c2dc1..bc13e3a9d 100644 --- a/miden-tx/tests/integration/scripts/faucet.rs +++ b/miden-tx/tests/integration/scripts/faucet.rs @@ -86,7 +86,7 @@ fn prove_faucet_contract_mint_fungible_asset_succeeds() { .execute_transaction(faucet_account.id(), block_ref, ¬e_ids, tx_args) .unwrap(); - assert!(prove_and_verify_transaction(executed_transaction.clone()).is_ok()); + prove_and_verify_transaction(executed_transaction.clone()).unwrap(); let fungible_asset: Asset = FungibleAsset::new(faucet_account.id(), amount.into()).unwrap().into(); diff --git a/miden-tx/tests/integration/wallet/mod.rs b/miden-tx/tests/integration/wallet/mod.rs index 46335cdec..c9905b561 100644 --- a/miden-tx/tests/integration/wallet/mod.rs +++ b/miden-tx/tests/integration/wallet/mod.rs @@ -152,7 +152,7 @@ fn prove_send_note_without_asset_via_wallet() { .execute_transaction(sender_account.id(), block_ref, ¬e_ids, tx_args) .unwrap(); - assert!(prove_and_verify_transaction(executed_transaction.clone()).is_ok()); + prove_and_verify_transaction(executed_transaction.clone()).unwrap(); // clones account info let sender_account_storage = AccountStorage::new(vec![ From cdd5fae334fcac0b73a04cafef711064f390488e Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Sun, 29 Sep 2024 17:49:27 -0300 Subject: [PATCH 10/12] Correctly build stack outputs for verifier --- miden-lib/asm/kernels/transaction/lib/tx.masm | 27 ++++++++----------- miden-lib/src/transaction/mod.rs | 7 ++++- .../src/transaction/procedures/kernel_v0.rs | 2 +- miden-tx/src/verifier/mod.rs | 1 + objects/src/transaction/proven_tx.rs | 5 ++++ 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/miden-lib/asm/kernels/transaction/lib/tx.masm b/miden-lib/asm/kernels/transaction/lib/tx.masm index 7280cb7d6..972612fc6 100644 --- a/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -201,25 +201,20 @@ export.update_expiration_block_num dup push.EXPIRY_UPPER_LIMIT lt assert.err=ERR_INVALID_TX_EXPIRATION_DELTA # => [block_height_delta] - exec.get_block_number u32overflowing_add - # => [is_higher_than_u32max, absolute_expiration_num] + exec.get_block_number add + # => [absolute_expiration_num] - if.false - # Load the current stored delta from memory - dup exec.memory::get_expiration_block_num - # => [stored_expiration_block_num, absolute_expiration_num, absolute_expiration_num] + # Load the current stored delta from memory + dup exec.memory::get_expiration_block_num + # => [stored_expiration_block_num, absolute_expiration_num, absolute_expiration_num] - # Check if block_height_delta is greater - u32lt - if.true - # Set new expiration delta - exec.memory::set_expiration_block_num - else - drop - end + # Check if block_height_delta is greater + u32lt + if.true + # Set new expiration delta + exec.memory::set_expiration_block_num else - # If the addition is higher than the highest block number, no need to compare - drop + drop end end diff --git a/miden-lib/src/transaction/mod.rs b/miden-lib/src/transaction/mod.rs index 7be9025e8..34da9a155 100644 --- a/miden-lib/src/transaction/mod.rs +++ b/miden-lib/src/transaction/mod.rs @@ -161,8 +161,13 @@ impl TransactionKernel { .expect("Invalid stack input") } - pub fn build_output_stack(final_acct_hash: Digest, output_notes_hash: Digest) -> StackOutputs { + pub fn build_output_stack( + final_acct_hash: Digest, + output_notes_hash: Digest, + expiration_block_num: u32, + ) -> StackOutputs { let mut outputs: Vec = Vec::with_capacity(9); + outputs.push(Felt::new(expiration_block_num as u64)); outputs.extend(final_acct_hash); outputs.extend(output_notes_hash); outputs.reverse(); diff --git a/miden-lib/src/transaction/procedures/kernel_v0.rs b/miden-lib/src/transaction/procedures/kernel_v0.rs index 54205f1b8..76e21aad8 100644 --- a/miden-lib/src/transaction/procedures/kernel_v0.rs +++ b/miden-lib/src/transaction/procedures/kernel_v0.rs @@ -68,7 +68,7 @@ pub const KERNEL0_PROCEDURES: [Digest; 32] = [ // end_foreign_context digest!(0x3770db711ce9aaf1, 0xb6f3c929151a5d52, 0x3ed145ec5dbee85f, 0xf979d975d7951bf6), // update_expiration_block_num - digest!(0xb111da64a8792f2d, 0x5fb267857eb9b657, 0xcb78aa9423268e71, 0x1bff8a6aa33af94e), + digest!(0xb5b796c8143e57de, 0x43d6914fb889f3ba, 0xf65308f85c7c73b7, 0xe86bfcaccebe6b49), // get_expiration_delta digest!(0x2d93af519fa32359, 0x14275beadcb2ab9c, 0x68f9336f45c32c86, 0x75ee8ba0f3c11c83), ]; diff --git a/miden-tx/src/verifier/mod.rs b/miden-tx/src/verifier/mod.rs index af69ecbe9..284d1d9ac 100644 --- a/miden-tx/src/verifier/mod.rs +++ b/miden-tx/src/verifier/mod.rs @@ -41,6 +41,7 @@ impl TransactionVerifier { let stack_outputs = TransactionKernel::build_output_stack( transaction.account_update().final_state_hash(), transaction.output_notes().commitment(), + transaction.expiration_block_num(), ); // verify transaction proof diff --git a/objects/src/transaction/proven_tx.rs b/objects/src/transaction/proven_tx.rs index 6646487ac..4b31e985d 100644 --- a/objects/src/transaction/proven_tx.rs +++ b/objects/src/transaction/proven_tx.rs @@ -84,6 +84,11 @@ impl ProvenTransaction { self.input_notes.iter().filter_map(|note| note.header()) } + /// Returns the block number at which the transaction will expire. + pub fn expiration_block_num(&self) -> u32 { + self.expiration_block_num + } + /// Returns an iterator over the nullifiers of all input notes in this transaction. /// /// This includes both authenticated and unauthenticated notes. From 7497b985f54999e2264179b826b70407151e6aa5 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Sun, 29 Sep 2024 17:58:53 -0300 Subject: [PATCH 11/12] Format correctly --- bin/tx-prover/src/server/generated/api.rs | 83 ++++++++--------------- 1 file changed, 29 insertions(+), 54 deletions(-) diff --git a/bin/tx-prover/src/server/generated/api.rs b/bin/tx-prover/src/server/generated/api.rs index 082a941d1..9015ff32f 100644 --- a/bin/tx-prover/src/server/generated/api.rs +++ b/bin/tx-prover/src/server/generated/api.rs @@ -14,8 +14,7 @@ pub struct ProveTransactionResponse { /// Generated client implementations. pub mod api_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - use tonic::codegen::http::Uri; + use tonic::codegen::{http::Uri, *}; #[derive(Debug, Clone)] pub struct ApiClient { inner: tonic::client::Grpc, @@ -46,10 +45,7 @@ pub mod api_client { let inner = tonic::client::Grpc::with_origin(inner, origin); Self { inner } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> ApiClient> + pub fn with_interceptor(inner: T, interceptor: F) -> ApiClient> where F: tonic::service::Interceptor, T::ResponseBody: Default, @@ -59,9 +55,8 @@ pub mod api_client { >::ResponseBody, >, >, - , - >>::Error: Into + Send + Sync, + >>::Error: + Into + Send + Sync, { ApiClient::new(InterceptedService::new(inner, interceptor)) } @@ -99,19 +94,14 @@ pub mod api_client { pub async fn prove_transaction( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/api.Api/ProveTransaction"); let mut req = request.into_request(); @@ -130,10 +120,7 @@ pub mod api_server { async fn prove_transaction( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; } #[derive(Debug)] pub struct ApiServer { @@ -158,10 +145,7 @@ pub mod api_server { max_encoding_message_size: None, } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService + pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService where F: tonic::service::Interceptor, { @@ -217,23 +201,18 @@ pub mod api_server { "/api.Api/ProveTransaction" => { #[allow(non_camel_case_types)] struct ProveTransactionSvc(pub Arc); - impl< - T: Api, - > tonic::server::UnaryService - for ProveTransactionSvc { + impl tonic::server::UnaryService + for ProveTransactionSvc + { type Response = super::ProveTransactionResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::prove_transaction(&inner, request).await - }; + let fut = + async move { ::prove_transaction(&inner, request).await }; Box::pin(fut) } } @@ -259,19 +238,15 @@ pub mod api_server { Ok(res) }; Box::pin(fut) - } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + }, + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } From dd64387d86f81b6a6245736f61df444734720ff3 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Mon, 30 Sep 2024 11:25:25 -0300 Subject: [PATCH 12/12] Address final reviews --- .../asm/kernels/transaction/lib/epilogue.masm | 1 + miden-lib/src/transaction/mod.rs | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/miden-lib/asm/kernels/transaction/lib/epilogue.masm b/miden-lib/asm/kernels/transaction/lib/epilogue.masm index 1d31ea52e..ecb58d1ce 100644 --- a/miden-lib/asm/kernels/transaction/lib/epilogue.masm +++ b/miden-lib/asm/kernels/transaction/lib/epilogue.masm @@ -313,4 +313,5 @@ export.finalize_transaction swapdw exec.memory::get_expiration_block_num swap drop swapdw + # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH, tx_expiration_block_num] end diff --git a/miden-lib/src/transaction/mod.rs b/miden-lib/src/transaction/mod.rs index 34da9a155..78a5cc519 100644 --- a/miden-lib/src/transaction/mod.rs +++ b/miden-lib/src/transaction/mod.rs @@ -161,13 +161,28 @@ impl TransactionKernel { .expect("Invalid stack input") } + /// Builds the stack for expected transaction execution outputs. + /// The transaction kernel's output stack is formed like so: + /// + /// ```text + /// [ + /// expiration_block_num, + /// OUTPUT_NOTES_COMMITMENT, + /// FINAL_ACCOUNT_HASH, + /// ] + /// ``` + /// + /// Where: + /// - OUTPUT_NOTES_COMMITMENT is a commitment to the output notes. + /// - FINAL_ACCOUNT_HASH is a hash of the account's final state. + /// - expiration_block_num is the block number at which the transaction will expire. pub fn build_output_stack( final_acct_hash: Digest, output_notes_hash: Digest, expiration_block_num: u32, ) -> StackOutputs { let mut outputs: Vec = Vec::with_capacity(9); - outputs.push(Felt::new(expiration_block_num as u64)); + outputs.push(Felt::from(expiration_block_num)); outputs.extend(final_acct_hash); outputs.extend(output_notes_hash); outputs.reverse(); @@ -213,7 +228,7 @@ impl TransactionKernel { let expiration_block_num = u32::try_from(expiration_block_num.as_int()).map_err(|_| { TransactionOutputError::OutputStackInvalid( - "Expiration block number should be lower than u32::MAX".into(), + "Expiration block number should be smaller than u32::MAX".into(), ) })?;