diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f563ff43..5229560516 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - [BREAKING] Added cycle counts to notes returned by `NoteConsumptionInfo` and removed public fields from related types ([#2772](https://github.com/0xMiden/miden-base/issues/2772)). - [BREAKING] Removed unused `payback_attachment` from `SwapNoteStorage` and `attachment` from `MintNoteStorage` ([#2789](https://github.com/0xMiden/protocol/pull/2789)). - Automatically enable `concurrent` feature in `miden-tx` for `std` context ([#2791](https://github.com/0xMiden/protocol/pull/2791)). +- Added `tx::get_tx_script_root` kernel procedure returning the root of the executed transaction script (zero word if no script was executed) ([#2816](https://github.com/0xMiden/protocol/pull/2816)). - Added `TransactionScript::from_package()` method to create `TransactionScript` from `miden-mast-package::Package` ([#2779](https://github.com/0xMiden/protocol/pull/2779)). ### Fixes diff --git a/crates/miden-protocol/asm/kernels/transaction/api.masm b/crates/miden-protocol/asm/kernels/transaction/api.masm index 137f1321b7..e35aebcdb1 100644 --- a/crates/miden-protocol/asm/kernels/transaction/api.masm +++ b/crates/miden-protocol/asm/kernels/transaction/api.masm @@ -1330,6 +1330,26 @@ pub proc tx_get_num_input_notes # => [num_input_notes, pad(15)] end +#! Returns the transaction script root. +#! +#! Inputs: [pad(16)] +#! Outputs: [TX_SCRIPT_ROOT, pad(12)] +#! +#! Where: +#! - TX_SCRIPT_ROOT is the transaction script root, or the zero word if no transaction script was +#! executed. +#! +#! Invocation: dynexec +pub proc tx_get_tx_script_root + # get the tx script root + exec.tx::get_tx_script_root + # => [TX_SCRIPT_ROOT, pad(16)] + + # truncate the stack + swapw dropw + # => [TX_SCRIPT_ROOT, pad(12)] +end + #! Returns the current number of output notes created in this transaction. #! #! Inputs: [pad(16)] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm index 25f533bddd..7b394afde4 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/memory.masm @@ -660,6 +660,18 @@ pub proc set_tx_script_root mem_storew_le.TX_SCRIPT_ROOT_PTR end +#! Returns the transaction script root. +#! +#! Inputs: [] +#! Outputs: [TX_SCRIPT_ROOT] +#! +#! Where: +#! - TX_SCRIPT_ROOT is the transaction script root, or the zero word if no transaction script was +#! executed. +pub proc get_tx_script_root + padw mem_loadw_le.TX_SCRIPT_ROOT_PTR +end + #! Returns the transaction script arguments. #! #! Inputs: [] diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/tx.masm b/crates/miden-protocol/asm/kernels/transaction/lib/tx.masm index aaed2d01fc..ba6145764a 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/tx.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/tx.masm @@ -85,6 +85,16 @@ pub use memory::get_num_input_notes #! - num_output_notes is the number of output notes created in this transaction so far. pub use memory::get_num_output_notes +#! Returns the transaction script root. +#! +#! Inputs: [] +#! Outputs: [TX_SCRIPT_ROOT] +#! +#! Where: +#! - TX_SCRIPT_ROOT is the transaction script root, or the zero word if no transaction script was +#! executed. +pub use memory::get_tx_script_root + #! Updates the transaction expiration block delta. #! #! The input block_height_delta is added to the block reference number in order to output an upper diff --git a/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm b/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm index eeb370179c..cf3e6098ea 100644 --- a/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm +++ b/crates/miden-protocol/asm/protocol/kernel_proc_offsets.masm @@ -89,3 +89,6 @@ pub const TX_EXEC_FOREIGN_PROC_OFFSET = 48 # expiration data pub const TX_GET_EXPIRATION_DELTA_OFFSET=49 # accessor pub const TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET=50 # mutator + +# tx script +pub const TX_GET_TX_SCRIPT_ROOT_OFFSET=51 diff --git a/crates/miden-protocol/asm/protocol/tx.masm b/crates/miden-protocol/asm/protocol/tx.masm index 8dfe18d13d..60fc2996c6 100644 --- a/crates/miden-protocol/asm/protocol/tx.masm +++ b/crates/miden-protocol/asm/protocol/tx.masm @@ -9,6 +9,7 @@ use miden::protocol::kernel_proc_offsets::TX_PREPARE_FPI_OFFSET use miden::protocol::kernel_proc_offsets::TX_EXEC_FOREIGN_PROC_OFFSET use miden::protocol::kernel_proc_offsets::TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET use miden::protocol::kernel_proc_offsets::TX_GET_EXPIRATION_DELTA_OFFSET +use miden::protocol::kernel_proc_offsets::TX_GET_TX_SCRIPT_ROOT_OFFSET #! Returns the block number of the transaction reference block. #! @@ -319,3 +320,29 @@ pub proc get_expiration_block_delta swapdw dropw dropw swapw dropw movdn.3 drop drop drop # => [expiration_delta] end + +#! Returns the transaction script root, or the zero word if no transaction script was executed. +#! +#! Inputs: [] +#! Outputs: [TX_SCRIPT_ROOT] +#! +#! Where: +#! - TX_SCRIPT_ROOT is the root of the transaction script executed in this transaction, or the zero +#! word if no transaction script was executed. +#! +#! Invocation: exec +pub proc get_tx_script_root + # pad the stack + padw padw padw push.0.0.0 + # => [pad(15)] + + push.TX_GET_TX_SCRIPT_ROOT_OFFSET + # => [offset, pad(15)] + + syscall.exec_kernel_proc + # => [TX_SCRIPT_ROOT, pad(12)] + + # clean the stack + swapdw dropw dropw swapw dropw + # => [TX_SCRIPT_ROOT] +end diff --git a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs index d0e3ab8ffd..5c662b9c3d 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_tx.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_tx.rs @@ -752,6 +752,62 @@ async fn test_tx_script_args() -> anyhow::Result<()> { Ok(()) } +/// Tests that `tx::get_tx_script_root` returns the root of the executed transaction script. +#[tokio::test] +async fn test_get_script_root_with_script() -> anyhow::Result<()> { + let tx_script = CodeBuilder::default().compile_tx_script("begin nop end")?; + let expected_root = tx_script.root(); + + let code = format!( + r#" + use miden::protocol::tx + use $kernel::prologue + + begin + exec.prologue::prepare_transaction + + exec.tx::get_tx_script_root + # => [TX_SCRIPT_ROOT] + + push.{expected_root} assert_eqw.err="tx script root mismatch" + end + "# + ); + + let tx_context = TransactionContextBuilder::with_existing_mock_account() + .tx_script(tx_script) + .build()?; + + tx_context.execute_code(&code).await?; + + Ok(()) +} + +/// Tests that `tx::get_tx_script_root` returns the zero word when no transaction script is +/// executed. +#[tokio::test] +async fn test_get_script_root_without_script() -> anyhow::Result<()> { + let code = r#" + use miden::protocol::tx + use $kernel::prologue + + begin + exec.prologue::prepare_transaction + + exec.tx::get_tx_script_root + # => [TX_SCRIPT_ROOT] + + padw assert_eqw.err="tx script root must be zero when no script is executed" + end + "#; + + let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?; + + tx_context.execute_code(code).await?; + + Ok(()) +} + // Tests that advice map from the account code and transaction script gets correctly passed as // part of the transaction advice inputs #[tokio::test]