Skip to content

Commit

Permalink
feat: Set transaction recency conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
igamigo committed Sep 27, 2024
1 parent c146bfd commit 5676e7b
Show file tree
Hide file tree
Showing 16 changed files with 252 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
16 changes: 16 additions & 0 deletions miden-lib/asm/kernels/transaction/api.masm
Original file line number Diff line number Diff line change
Expand Up @@ -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, <procedure_inputs>, <pad>]
Expand Down
17 changes: 12 additions & 5 deletions miden-lib/asm/kernels/transaction/lib/epilogue.masm
Original file line number Diff line number Diff line change
Expand Up @@ -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
# =================================================================================================

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
20 changes: 20 additions & 0 deletions miden-lib/asm/kernels/transaction/lib/memory.masm
Original file line number Diff line number Diff line change
Expand Up @@ -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
# -------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -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: []
Expand Down
8 changes: 8 additions & 0 deletions miden-lib/asm/kernels/transaction/lib/prologue.masm
Original file line number Diff line number Diff line change
Expand Up @@ -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
# =================================================================================================

Expand Down Expand Up @@ -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
43 changes: 42 additions & 1 deletion miden-lib/asm/kernels/transaction/lib/tx.masm
Original file line number Diff line number Diff line change
Expand Up @@ -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
# =================================================================================================

Expand Down Expand Up @@ -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
# =================================================================================================

Expand All @@ -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: []
Expand Down Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion miden-lib/asm/miden/kernel_proc_offsets.masm
Original file line number Diff line number Diff line change
Expand Up @@ -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
# -------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: []
Expand Down
5 changes: 4 additions & 1 deletion miden-lib/src/transaction/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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? |
Expand Down Expand Up @@ -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
// ------------------------------------------------------------------------------------------------

Expand Down
41 changes: 29 additions & 12 deletions miden-lib/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -174,35 +175,43 @@ 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:
/// - 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, 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(),
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -238,7 +250,8 @@ impl TransactionKernel {
adv_map: &AdviceMap,
output_notes: Vec<OutputNote>,
) -> Result<TransactionOutputs, TransactionOutputError> {
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(
Expand All @@ -258,7 +271,11 @@ impl TransactionKernel {
));
}

Ok(TransactionOutputs { account, output_notes })
Ok(TransactionOutputs {
account,
output_notes,
expiration_block_num,
})
}
}

Expand Down
3 changes: 3 additions & 0 deletions miden-lib/src/transaction/outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// ================================================================================================

Expand Down
4 changes: 3 additions & 1 deletion miden-lib/src/transaction/procedures/kernel_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
];
Loading

0 comments on commit 5676e7b

Please sign in to comment.