From ce820fef5945ced5b4c94442dcad8763202e78ca Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Wed, 25 Sep 2024 16:05:32 -0300 Subject: [PATCH] feat: Set transaction recency conditions --- miden-lib/asm/kernels/transaction/api.masm | 16 +++++ .../asm/kernels/transaction/lib/epilogue.masm | 30 +++++++++ .../asm/kernels/transaction/lib/memory.masm | 22 ++++++ miden-lib/asm/kernels/transaction/lib/tx.masm | 47 ++++++++++++- miden-lib/asm/miden/kernel_proc_offsets.masm | 7 +- miden-lib/build.rs | 2 +- miden-lib/src/transaction/mod.rs | 44 ++++++++---- 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 | 67 ++++++++++++++++++- objects/src/transaction/outputs.rs | 4 ++ objects/src/transaction/proven_tx.rs | 16 +++++ 14 files changed, 245 insertions(+), 22 deletions(-) diff --git a/miden-lib/asm/kernels/transaction/api.masm b/miden-lib/asm/kernels/transaction/api.masm index 26327d0c2..de12c1ec3 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 leter 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_delta +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..e26cd52d8 100644 --- a/miden-lib/asm/kernels/transaction/lib/epilogue.masm +++ b/miden-lib/asm/kernels/transaction/lib/epilogue.masm @@ -3,6 +3,7 @@ use.kernel::asset_vault use.kernel::constants use.kernel::memory use.kernel::note +use.kernel::tx use.std::crypto::hashes::native @@ -213,6 +214,31 @@ proc.update_account_storage_commitment end end +# EXPIRATION BLOCK HEIGHT +# ================================================================================================= + +#! Calculates the block number by which the transaction will not be valid anymore, given by the +#! addition of the reference block number and the transaction expiration delta. +#! +#! Stack: [] +#! Output: [expiration_block_num] +#! +#! Where: +#! - expiration_block_num: If the expiration block delta was set, it will be added to the block +#! ref number. Otherwise, it will be equal to 0. +proc.get_absolute_expiration_block + exec.memory::get_expiration_delta + # => [expiration_block_delta] + + dup neq.0 + if.true + # get block number to add to transaction expiration delta + exec.tx::get_block_number add + # => [expiration_block_num] + end +end + + # TRANSACTION EPILOGUE PROCEDURE # ================================================================================================= @@ -308,4 +334,8 @@ export.finalize_transaction # 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] + + exec.get_absolute_expiration_block movdn.8 + + exec.::std::sys::truncate_stack end diff --git a/miden-lib/asm/kernels/transaction/lib/memory.masm b/miden-lib/asm/kernels/transaction/lib/memory.masm index 5abfcc947..dd1ce7aac 100644 --- a/miden-lib/asm/kernels/transaction/lib/memory.masm +++ b/miden-lib/asm/kernels/transaction/lib/memory.masm @@ -32,6 +32,11 @@ 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 +# Memory address at which the transaction expiration delta is stored. +# The maximum block number for which the transaction is valid will be given by the transaction's +# block number, plus the expiration height delta. +const.TX_EXPIRATION_DELTA_PTR=7 + # GLOBAL INPUTS # ------------------------------------------------------------------------------------------------- @@ -829,6 +834,23 @@ export.set_new_acct_code_commitment mem_storew end +#! Sets the transaction expiration time delta, in blocks. +#! +#! Inputs: [block_height_delta, ...] +#! Output: [...] +export.set_expiration_delta + push.TX_EXPIRATION_DELTA_PTR mem_store +end + +#! Gets the transaction expiration time delta, in blocks. +#! +#! Inputs: [] +#! Output: [block_height_delta] +export.get_expiration_delta + push.TX_EXPIRATION_DELTA_PTR mem_load +end + + #! Returns the number of procedures contained in the account code. #! #! Stack: [] diff --git a/miden-lib/asm/kernels/transaction/lib/tx.masm b/miden-lib/asm/kernels/transaction/lib/tx.masm index 19805aaa5..482286fe4 100644 --- a/miden-lib/asm/kernels/transaction/lib/tx.masm +++ b/miden-lib/asm/kernels/transaction/lib/tx.masm @@ -61,6 +61,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 +76,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 +181,48 @@ 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_delta + # Ensure block_height_delta is between 1 and 255 (inclusive) + dup neq.0 assert.err=ERR_INVALID_TX_EXPIRATION_DELTA + dup push.256 lt assert.err=ERR_INVALID_TX_EXPIRATION_DELTA + # => [block_height_delta] + + # Load the current stored delta from memory + exec.memory::get_expiration_delta dup + # => [stored_delta, stored_delta, block_height_delta] + + # if stored_delta is 0, it was not yet set + eq.0 + if.true + drop + # => [block_height_delta] + + exec.memory::set_expiration_delta + else + # Expiration delta had been set, check if new one should be stored + dup.1 + # => [block_height_delta, stored_delta, block_height_delta] + gt + if.true + exec.memory::set_expiration_delta + else + drop + end + 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..d9818bc2e 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,10 @@ export.get_block_number_offset push.GET_BLOCK_NUMBER_OFFSET end +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/build.rs b/miden-lib/build.rs index 7cadfd1d8..b5fb06ae4 100644 --- a/miden-lib/build.rs +++ b/miden-lib/build.rs @@ -208,7 +208,7 @@ fn generate_kernel_proc_hash_file(kernel: KernelLibrary) -> Result<()> { let proc_count = generated_procs.len(); let generated_procs: String = generated_procs.into_iter().enumerate().map(|(index, (offset, txt))| { if index != offset { - panic!("Offset constants in the file `{offsets_filename:?}` are not contiguous (missing offset: {index})"); + panic!("Offset constants in the file `{offsets_filename:?}` are not contiguous (missing offset: {index} - {txt})"); } txt diff --git a/miden-lib/src/transaction/mod.rs b/miden-lib/src/transaction/mod.rs index 623d6858d..928fc14da 100644 --- a/miden-lib/src/transaction/mod.rs +++ b/miden-lib/src/transaction/mod.rs @@ -10,9 +10,10 @@ use miden_objects::{ }, utils::{group_slice_elements, serde::Deserializable}, vm::{AdviceInputs, AdviceMap, Program, ProgramInfo, StackInputs, StackOutputs}, - Digest, Felt, TransactionOutputError, Word, EMPTY_WORD, + Digest, Felt, FieldElement, TransactionOutputError, Word, EMPTY_WORD, }; use miden_stdlib::StdLibrary; +use outputs::EXPIRATION_BLOCK_ELEMENT_IDX; use super::MidenLib; @@ -174,35 +175,42 @@ impl TransactionKernel { /// /// The data on the stack is expected to be arranged as follows: /// - /// Stack: [CNC, FAH] + /// Stack: [CNC, FAH, block_expiration_height] /// /// 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. - /// + /// - block_expiration_height 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: /// - Words 3 and 4 on the stack are not 0. /// - Overflow addresses are not empty. pub fn parse_output_stack( stack: &StackOutputs, - ) -> Result<(Digest, Digest), TransactionOutputError> { + ) -> Result<(Digest, Digest, Option), TransactionOutputError> { + // # => [OUTPUT_NOTES_COMMITMENT, FINAL_ACCOUNT_HASH] 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: Felt = stack + .get_stack_item(EXPIRATION_BLOCK_ELEMENT_IDX) + .expect("element on index 8 missing") + .into(); + + let expiration_block_num = + (expiration_block_num != Felt::ZERO).then_some(expiration_block_num.as_int() as u32); + 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 +222,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 +232,15 @@ impl TransactionKernel { /// /// The output stack is expected to be arrange as follows: /// - /// Stack: [CNC, FAH] + /// Stack: [CNC, FAH, block_expiration_height] /// /// 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. + /// - block_expiration_height 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 +249,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 +270,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 715fa4379..4f71ae2ce 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!(0x56505ecb517b3a7f, 0x8c531dc547913e86, 0x20ef1cdcb370f308, 0xad7f1097f1b8038d), ]; diff --git a/miden-tx/src/errors/tx_kernel_errors.rs b/miden-tx/src/errors/tx_kernel_errors.rs index fa8bad3cb..4acd18245 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..63a2c8b81 100644 --- a/miden-tx/src/tests/kernel_tests/test_epilogue.rs +++ b/miden-tx/src/tests/kernel_tests/test_epilogue.rs @@ -1,4 +1,5 @@ use alloc::vec::Vec; +use std::string::ToString; use miden_lib::transaction::{ memory::{NOTE_MEM_SIZE, OUTPUT_NOTE_ASSET_HASH_OFFSET, OUTPUT_NOTE_SECTION_OFFSET}, @@ -8,12 +9,15 @@ use miden_objects::{ accounts::Account, transaction::{OutputNote, OutputNotes}, }; -use vm_processor::ONE; +use vm_processor::{Felt, 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, }; @@ -67,9 +71,10 @@ fn test_epilogue() { .unwrap(); let mut expected_stack = Vec::with_capacity(16); + expected_stack.push(Felt::new(0)); // Block expiration height 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.extend((9..16).map(|_| ZERO)); assert_eq!( process.stack.build_stack_outputs().stack(), @@ -191,6 +196,62 @@ 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_delta + push.{value_2} + exec.tx::set_expiration_delta + 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 = [0, 256, 1000]; + let code_template = " + use.kernel::tx + + begin + push.{value_1} + exec.tx::set_expiration_delta + 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_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 4fc4563d8..f2c0bd172 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: Option, } // OUTPUT NOTES diff --git a/objects/src/transaction/proven_tx.rs b/objects/src/transaction/proven_tx.rs index 461121cb6..40a3bbd64 100644 --- a/objects/src/transaction/proven_tx.rs +++ b/objects/src/transaction/proven_tx.rs @@ -36,6 +36,11 @@ 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. + /// When `expiration_block_num` is `None`, it means the executed scripts did not set any + /// expiration block delta. + expiration_block_num: Option, + /// A STARK proof that attests to the correct execution of the transaction. proof: ExecutionProof, } @@ -143,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); } } @@ -155,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 = >::read_from(source)?; let proof = ExecutionProof::read_from(source)?; let id = TransactionId::new( @@ -170,6 +177,7 @@ impl Deserializable for ProvenTransaction { input_notes, output_notes, block_ref, + expiration_block_num, proof, }; @@ -206,6 +214,11 @@ 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. + /// When `expiration_block_num` is `None`, it means the executed scripts did not set any + /// expiration block delta. + expiration_block_num: Option, + /// A STARK proof that attests to the correct execution of the transaction. proof: ExecutionProof, } @@ -220,6 +233,7 @@ impl ProvenTransactionBuilder { initial_account_hash: Digest, final_account_hash: Digest, block_ref: Digest, + expiration_block_num: Option, proof: ExecutionProof, ) -> Self { Self { @@ -230,6 +244,7 @@ impl ProvenTransactionBuilder { input_notes: Vec::new(), output_notes: Vec::new(), block_ref, + expiration_block_num, proof, } } @@ -292,6 +307,7 @@ impl ProvenTransactionBuilder { input_notes, output_notes, block_ref: self.block_ref, + expiration_block_num: self.expiration_block_num, proof: self.proof, };