Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8c59e66
refactor: Change way of providing code and inputs to executor
igamigo Mar 19, 2025
0a576c0
test: Fix 3 tests
igamigo Mar 19, 2025
f0888df
chore: Merge next
igamigo Mar 19, 2025
4052596
chore: Merge next
igamigo Mar 31, 2025
017d8fd
Checkpoint
igamigo Apr 2, 2025
7df20d7
feat: Add ForeignTransactionInputs
igamigo Apr 7, 2025
1edbfe7
chore: Merge next
igamigo Apr 7, 2025
0ac3111
chore: Remove TODO
igamigo Apr 7, 2025
d88556f
chore: CHANGELOG
igamigo Apr 7, 2025
327fc3f
reviews: Address comment
igamigo Apr 10, 2025
4040442
Revert change
igamigo Apr 10, 2025
fc60697
refactor: Remove unused DataStore error variant
igamigo Apr 10, 2025
a34b940
chore: Clippy
igamigo Apr 10, 2025
2f7e499
refactor: TransactionArgs mutators
igamigo Apr 11, 2025
20c9d25
chore: Clippy
igamigo Apr 11, 2025
32a5296
refactor: MAST store
igamigo Apr 11, 2025
5965b5c
refactor: FPI inputs
igamigo Apr 11, 2025
0a680cf
reviews: propagate errors, add validations and tests
igamigo Apr 14, 2025
75e1fdc
test: Fix assembles
igamigo Apr 14, 2025
96c28ef
reviews: Move mast_store.rs
igamigo Apr 14, 2025
eee32d8
Merge branch 'next' into igamigo-refactor-executor
igamigo Apr 14, 2025
853ca80
fix: Remove accidentall std addition
igamigo Apr 14, 2025
26ae3a5
chore: Merge
igamigo Apr 14, 2025
124d03f
chore: Lints
igamigo Apr 14, 2025
e7a88e2
test: Fix doc test build
igamigo Apr 14, 2025
beb71f1
reviews: Change name, add execute_code variant and make function private
igamigo Apr 15, 2025
b4b2e1c
reviews: Box string in error
igamigo Apr 15, 2025
cd15d1f
reviews: Typo
igamigo Apr 15, 2025
9e3b4e9
chore: Merge next
igamigo Apr 21, 2025
635cf7f
chore: Merge conflicts
igamigo Apr 21, 2025
7b959d1
tests: refactor test to work around the new validations
igamigo Apr 21, 2025
bf97884
feat: Add is_valid()
igamigo Apr 21, 2025
f352972
chore: Clippy
igamigo Apr 21, 2025
5757967
style: CHANGELOG extra newline
igamigo Apr 21, 2025
6503c8b
chore: Renames and imports
igamigo Apr 21, 2025
65ea187
reviews: Remove unnecessary (crate) and add note
igamigo Apr 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [BREAKING] Hash keys in storage maps before insertion into the SMT (#1250).
- Added getter for proof security level in `ProvenBatch` and `ProvenBlock` (#1259).
- [BREAKING] Replaced the `ProvenBatch::new_unchecked` with the `ProvenBatch::new` method to initialize the struct with validations (#1260).
- Added a retry strategy for worker's health check (#1255).
- [BREAKING] Refactored how foreign account inputs are passed to `TransactionExecutor`, and upgraded Rust version to 1.86 (#1229).

## 0.8.1 (2025-03-26) - `miden-objects` and `miden-tx` crates only.
Expand Down
2 changes: 0 additions & 2 deletions bin/bench-tx/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ fn main() -> Result<(), String> {
// ================================================================================================

/// Runs the default transaction with empty transaction script and two default notes.
#[allow(clippy::arc_with_non_send_sync)]
pub fn benchmark_default_tx() -> Result<TransactionMeasurements, String> {
let tx_context = TransactionContextBuilder::with_standard_account(ONE)
.with_mock_notes_preserved()
Expand All @@ -81,7 +80,6 @@ pub fn benchmark_default_tx() -> Result<TransactionMeasurements, String> {
}

/// Runs the transaction which consumes a P2ID note into a basic wallet.
#[allow(clippy::arc_with_non_send_sync)]
pub fn benchmark_p2id() -> Result<TransactionMeasurements, String> {
// Create assets
let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap();
Expand Down
22 changes: 14 additions & 8 deletions crates/miden-lib/src/transaction/inputs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloc::vec::Vec;

use miden_objects::{
Digest, EMPTY_WORD, Felt, FieldElement, WORD_SIZE, Word, ZERO,
Digest, EMPTY_WORD, Felt, FieldElement, TransactionInputError, WORD_SIZE, Word, ZERO,
account::{Account, StorageSlot},
transaction::{ChainMmr, InputNote, TransactionArgs, TransactionInputs, TransactionScript},
vm::AdviceInputs,
Expand All @@ -22,7 +22,7 @@ pub(super) fn extend_advice_inputs(
tx_inputs: &TransactionInputs,
tx_args: &TransactionArgs,
advice_inputs: &mut AdviceInputs,
) {
) -> Result<(), TransactionInputError> {
// TODO: remove this value and use a user input instead
let kernel_version = 0;

Expand All @@ -32,13 +32,13 @@ pub(super) fn extend_advice_inputs(
add_kernel_commitments_to_advice_inputs(advice_inputs, kernel_version);
add_chain_mmr_to_advice_inputs(tx_inputs.block_chain(), advice_inputs);
add_account_to_advice_inputs(tx_inputs.account(), tx_inputs.account_seed(), advice_inputs);
add_input_notes_to_advice_inputs(tx_inputs, tx_args, advice_inputs);
add_input_notes_to_advice_inputs(tx_inputs, tx_args, advice_inputs)?;
for foreign_account in tx_args.foreign_accounts() {
TransactionKernel::extend_advice_inputs_for_account(advice_inputs, foreign_account)
.unwrap();
TransactionKernel::extend_advice_inputs_for_account(advice_inputs, foreign_account)?;
}

advice_inputs.extend(tx_args.advice_inputs().clone());
Ok(())
}

// ADVICE STACK BUILDER
Expand Down Expand Up @@ -228,10 +228,10 @@ fn add_input_notes_to_advice_inputs(
tx_inputs: &TransactionInputs,
tx_args: &TransactionArgs,
inputs: &mut AdviceInputs,
) {
) -> Result<(), TransactionInputError> {
// if there are no input notes, nothing is added to the advice inputs
if tx_inputs.input_notes().is_empty() {
return;
return Ok(());
}

let mut note_data = Vec::new();
Expand Down Expand Up @@ -288,7 +288,12 @@ fn add_input_notes_to_advice_inputs(
proof.location().node_index_in_block().into(),
note.commitment(),
)
.unwrap(),
.map_err(|err| {
TransactionInputError::InvalidMerklePath(
format!("input note ID {}", note.id()),
err,
)
})?,
);
note_data.push(proof.location().block_num().into());
note_data.extend(note_block_header.sub_commitment());
Expand All @@ -305,6 +310,7 @@ fn add_input_notes_to_advice_inputs(

// NOTE: keep map in sync with the `prologue::process_input_notes_data` kernel procedure
inputs.extend_map([(tx_inputs.input_notes().commitment(), note_data)]);
Ok(())
}

// KERNEL COMMITMENTS INJECTOR
Expand Down
32 changes: 23 additions & 9 deletions crates/miden-lib/src/transaction/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use alloc::{string::ToString, sync::Arc, vec::Vec};

use miden_objects::{
Digest, EMPTY_WORD, Felt, TransactionOutputError, ZERO,
Digest, EMPTY_WORD, Felt, TransactionInputError, TransactionOutputError, ZERO,
account::{AccountCode, AccountId},
assembly::{Assembler, DefaultSourceManager, KernelLibrary},
block::BlockNumber,
crypto::merkle::MerkleError,
transaction::{
ForeignAccountInputs, OutputNote, OutputNotes, TransactionArgs, TransactionInputs,
TransactionOutputs,
Expand Down Expand Up @@ -115,7 +114,7 @@ impl TransactionKernel {
tx_inputs: &TransactionInputs,
tx_args: &TransactionArgs,
init_advice_inputs: Option<AdviceInputs>,
) -> (StackInputs, AdviceInputs) {
) -> Result<(StackInputs, AdviceInputs), TransactionInputError> {
let account = tx_inputs.account();

let stack_inputs = TransactionKernel::build_input_stack(
Expand All @@ -127,9 +126,9 @@ impl TransactionKernel {
);

let mut advice_inputs = init_advice_inputs.unwrap_or_default();
inputs::extend_advice_inputs(tx_inputs, tx_args, &mut advice_inputs);
inputs::extend_advice_inputs(tx_inputs, tx_args, &mut advice_inputs)?;

(stack_inputs, advice_inputs)
Ok((stack_inputs, advice_inputs))
}

// ASSEMBLER CONSTRUCTOR
Expand Down Expand Up @@ -201,11 +200,11 @@ impl TransactionKernel {
pub fn extend_advice_inputs_for_account(
advice_inputs: &mut AdviceInputs,
foreign_account_inputs: &ForeignAccountInputs,
) -> Result<(), MerkleError> {
) -> Result<(), TransactionInputError> {
let account_header = foreign_account_inputs.account_header();
let storage_header = foreign_account_inputs.storage_header();
let account_code = foreign_account_inputs.account_code();
let merkle_path = foreign_account_inputs.merkle_proof();
let merkle_path = foreign_account_inputs.account_witness();
let storage_proofs = foreign_account_inputs.storage_map_proofs();

let account_id = account_header.id();
Expand All @@ -228,14 +227,29 @@ impl TransactionKernel {
// Extend the advice inputs with Merkle store data
advice_inputs.extend_merkle_store(
// The prefix is the index in the account tree.
merkle_path.inner_nodes(account_id.prefix().as_u64(), account_header.commitment())?,
merkle_path
.inner_nodes(account_id.prefix().as_u64(), account_header.commitment())
.map_err(|err| {
TransactionInputError::InvalidMerklePath(
format!("foreign account ID {}", account_id),
err,
)
})?,
);

// Load merkle nodes for storage maps
for proof in storage_proofs {
// Extend the merkle store and map with the storage maps
advice_inputs.extend_merkle_store(
proof.path().inner_nodes(proof.leaf().index().value(), proof.leaf().hash())?,
proof
.path()
.inner_nodes(proof.leaf().index().value(), proof.leaf().hash())
.map_err(|err| {
TransactionInputError::InvalidMerklePath(
format!("foreign account ID {} storage proof", account_id),
err,
)
})?,
);
// Populate advice map with Sparse Merkle Tree leaf nodes
advice_inputs
Expand Down
2 changes: 2 additions & 0 deletions crates/miden-objects/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ pub enum TransactionInputError {
InputNoteNotInBlock(NoteId, BlockNumber),
#[error("account ID computed from seed is invalid")]
InvalidAccountIdSeed(#[source] AccountIdError),
#[error("merkle path for {0} is invalid")]
InvalidMerklePath(String, #[source] MerkleError),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
InvalidMerklePath(String, #[source] MerkleError),
InvalidMerklePath(Box<str>, #[source] MerkleError),

Nit: We could box the string to save 8 bytes. This is the largest variant in the enum now and dictates its stack size. Just requires adding an .into() after the format! where it is used.

#[error(
"total number of input notes is {0} which exceeds the maximum of {MAX_INPUT_NOTES_PER_TX}"
)]
Expand Down
64 changes: 56 additions & 8 deletions crates/miden-objects/src/transaction/inputs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloc::{collections::BTreeSet, vec::Vec};
use core::fmt::Debug;

use miden_crypto::merkle::{MerklePath, SmtProof};
use miden_crypto::merkle::{MerkleError, MerklePath, SmtProof};

use super::{BlockHeader, ChainMmr, Digest, Felt, Hasher, Word};
use crate::{
Expand Down Expand Up @@ -549,7 +549,7 @@ pub struct ForeignAccountInputs {
/// Code associated with the account.
account_code: AccountCode,
/// Merkle proof of the account's inclusion in the account tree.
merkle_proof: MerklePath,
account_witness: MerklePath,
/// Storage SMT proof for storage map values that the transaction will access.
storage_map_proofs: Vec<SmtProof>,
}
Expand All @@ -567,7 +567,7 @@ impl ForeignAccountInputs {
account_header,
storage_header,
account_code,
merkle_proof,
account_witness: merkle_proof,
storage_map_proofs,
}
}
Expand Down Expand Up @@ -597,9 +597,15 @@ impl ForeignAccountInputs {
&self.account_code
}

/// Returns the account's proof.
pub fn merkle_proof(&self) -> &MerklePath {
&self.merkle_proof
/// Returns the account witness.
pub fn account_witness(&self) -> &MerklePath {
&self.account_witness
}

/// Computes account root based on the account witness.
pub fn compute_account_root(&self) -> Result<Digest, MerkleError> {
self.account_witness()
.compute_root(self.id().prefix().into(), self.account_header().commitment())
}

/// Extends the storage proofs with the input `smt_proofs` and returns the new structure
Expand All @@ -620,7 +626,7 @@ impl ForeignAccountInputs {
self.account_header,
self.storage_header,
self.account_code,
self.merkle_proof,
self.account_witness,
self.storage_map_proofs,
)
}
Expand All @@ -631,7 +637,7 @@ impl Serializable for ForeignAccountInputs {
self.account_header.write_into(target);
self.storage_header.write_into(target);
self.account_code.write_into(target);
self.merkle_proof.write_into(target);
self.account_witness.write_into(target);
self.storage_map_proofs.write_into(target);
}
}
Expand All @@ -654,3 +660,45 @@ impl Deserializable for ForeignAccountInputs {
))
}
}

#[cfg(test)]
mod tests {

use miden_crypto::merkle::MerklePath;
use vm_core::{
Felt,
utils::{Deserializable, Serializable},
};

use super::ForeignAccountInputs;
use crate::{
account::{Account, AccountCode, AccountHeader, AccountId, AccountStorage},
asset::AssetVault,
testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
};

#[test]
fn serde_roundtrip() {
let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
let code = AccountCode::mock();
let vault = AssetVault::new(&[]).unwrap();
let storage = AccountStorage::new(vec![]).unwrap();
let account = Account::from_parts(id, vault, storage, code, Felt::new(10));

let commitment = account.commitment();
let header: AccountHeader = account.clone().into();
let (_, _, storage, code, _) = account.into_parts();

let fpi_inputs = ForeignAccountInputs::new(
header,
storage.get_header(),
code,
MerklePath::new(vec![commitment]),
vec![],
);

let serialized = fpi_inputs.to_bytes();
let deserialized = ForeignAccountInputs::read_from_bytes(&serialized).unwrap();
assert_eq!(deserialized, fpi_inputs);
}
}
16 changes: 14 additions & 2 deletions crates/miden-objects/src/transaction/tx_args.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use alloc::{
collections::{BTreeMap, BTreeSet},
sync::Arc,
vec::Vec,
};

use assembly::{Assembler, Compile};
use miden_crypto::merkle::InnerNodeInfo;
Expand Down Expand Up @@ -107,6 +111,14 @@ impl TransactionArgs {
&self.foreign_accounts
}

/// Collects and returns a set containing all code commitments from foreign accounts.
pub fn foreign_account_code_commitments(&self) -> BTreeSet<Digest> {
self.foreign_accounts()
.iter()
.map(|acc| acc.account_code().commitment())
.collect()
}

// STATE MUTATORS
// --------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -149,7 +161,7 @@ impl TransactionArgs {
}
}

/// Extends the provided [TransactionArgs] with the passed-in `advice_inputs`.
/// Extends the advice inputs in self with the provided ones.
pub fn extend_advice_inputs(&mut self, advice_inputs: AdviceInputs) {
self.advice_inputs.extend(advice_inputs);
}
Expand Down
5 changes: 5 additions & 0 deletions crates/miden-objects/src/transaction/tx_witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ use crate::utils::serde::{ByteReader, Deserializable, DeserializationError, Seri
/// additional advice data to initialize the advice provide with prior to transaction execution.
/// - Advice witness which contains all data requested by the VM from the advice provider while
/// executing the transaction program.
///
/// TODO: currently, the advice witness contains redundant and irrelevant data (e.g., tx inputs
/// and tx outputs; account codes and a subset of that data in advice inputs).
/// We should optimize it to contain only the minimum data required for executing/proving the
/// transaction.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransactionWitness {
pub tx_inputs: TransactionInputs,
Expand Down
22 changes: 16 additions & 6 deletions crates/miden-tx/src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ use core::error::Error;

use miden_objects::{
AccountError, Felt, ProvenTransactionError, TransactionInputError, TransactionOutputError,
account::AccountId, block::BlockNumber,
account::AccountId, block::BlockNumber, note::NoteId,
};
use miden_verifier::VerificationError;
use thiserror::Error;
use vm_processor::ExecutionError;
use vm_processor::{ExecutionError, crypto::MerkleError};

// TRANSACTION EXECUTOR ERROR
// ================================================================================================

#[derive(Debug, Error)]
pub enum TransactionExecutorError {
#[error("failed to execute transaction kernel program")]
TransactionProgramExecutionFailed(#[source] ExecutionError),
#[error("failed to fetch transaction inputs from the data store")]
FetchTransactionInputsFailed(#[source] DataStoreError),
#[error("foreign account inputs for ID {0} are not anchored on reference block")]
ForeignAccountNotAnchoredInReference(AccountId),
#[error("failed to create transaction inputs")]
InvalidTransactionInputs(#[source] TransactionInputError),
#[error("input account ID {input_id} does not match output account ID {output_id}")]
Expand All @@ -30,10 +30,18 @@ pub enum TransactionExecutorError {
expected: Option<Felt>,
actual: Option<Felt>,
},
#[error("failed to construct transaction outputs")]
TransactionOutputConstructionFailed(#[source] TransactionOutputError),
#[error("account witness provided for account ID {0} is invalid")]
InvalidAccountWitness(AccountId, #[source] MerkleError),
#[error(
"input note {0} was created in a block past the transaction reference block number ({1})"
)]
NoteBlockPastReferenceBlock(NoteId, BlockNumber),
#[error("failed to create transaction host")]
TransactionHostCreationFailed(#[source] TransactionHostError),
#[error("failed to construct transaction outputs")]
TransactionOutputConstructionFailed(#[source] TransactionOutputError),
#[error("failed to execute transaction kernel program")]
TransactionProgramExecutionFailed(#[source] ExecutionError),
}

// TRANSACTION PROVER ERROR
Expand All @@ -43,6 +51,8 @@ pub enum TransactionExecutorError {
pub enum TransactionProverError {
#[error("failed to apply account delta")]
AccountDeltaApplyFailed(#[source] AccountError),
#[error("transaction inputs are not valid")]
InvalidTransactionInputs(#[source] TransactionInputError),
#[error("failed to construct transaction outputs")]
TransactionOutputConstructionFailed(#[source] TransactionOutputError),
#[error("failed to build proven transaction")]
Expand Down
Loading