Skip to content

Commit 8c59e66

Browse files
committed
refactor: Change way of providing code and inputs to executor
1 parent 60a581e commit 8c59e66

File tree

21 files changed

+243
-287
lines changed

21 files changed

+243
-287
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
- [BREAKING] Enable timestamp customization on `MockChain::seal_block` (#1208).
3333
- [BREAKING] Renamed constants and comments: `OnChain` -> `Public` and `OffChain` -> `Private` (#1218).
3434
- [BREAKING] Replace "hash" with "commitment" in `BlockHeader::{prev_hash, chain_root, kernel_root, tx_hash, proof_hash, sub_hash, hash}` (#1209, #1221, #1226).
35+
- [BREAKING] Split `DataStore` API and refactored `TransactionExecutor` API (#1229).
3536

3637
## 0.7.2 (2025-01-28) - `miden-objects` crate only
3738

bin/bench-tx/src/main.rs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{
33
fs::{read_to_string, write, File},
44
io::Write,
55
path::Path,
6+
sync::Arc,
67
};
78

89
use miden_lib::{note::create_p2id_note, transaction::TransactionKernel};
@@ -59,6 +60,7 @@ fn main() -> Result<(), String> {
5960
// ================================================================================================
6061

6162
/// Runs the default transaction with empty transaction script and two default notes.
63+
#[allow(clippy::arc_with_non_send_sync)]
6264
pub fn benchmark_default_tx() -> Result<TransactionMeasurements, String> {
6365
let tx_context = TransactionContextBuilder::with_standard_account(ONE)
6466
.with_mock_notes_preserved()
@@ -67,23 +69,20 @@ pub fn benchmark_default_tx() -> Result<TransactionMeasurements, String> {
6769
let account_id = tx_context.account().id();
6870

6971
let block_ref = tx_context.tx_inputs().block_header().block_num();
70-
let note_ids = tx_context
71-
.tx_inputs()
72-
.input_notes()
73-
.iter()
74-
.map(|note| note.id())
75-
.collect::<Vec<_>>();
76-
72+
let tx_args = tx_context.tx_args().clone();
73+
let notes = tx_context.tx_inputs().input_notes().clone();
74+
let mast_store = tx_context.get_mast_store();
7775
let executor: TransactionExecutor =
78-
TransactionExecutor::new(tx_context.get_data_store(), None).with_tracing();
76+
TransactionExecutor::new(Arc::new(tx_context), mast_store, None).with_tracing();
7977
let executed_transaction = executor
80-
.execute_transaction(account_id, block_ref, &note_ids, tx_context.tx_args().clone())
78+
.execute_transaction(account_id, block_ref, notes, tx_args)
8179
.map_err(|e| e.to_string())?;
8280

8381
Ok(executed_transaction.into())
8482
}
8583

8684
/// Runs the transaction which consumes a P2ID note into a basic wallet.
85+
#[allow(clippy::arc_with_non_send_sync)]
8786
pub fn benchmark_p2id() -> Result<TransactionMeasurements, String> {
8887
// Create assets
8988
let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap();
@@ -116,17 +115,14 @@ pub fn benchmark_p2id() -> Result<TransactionMeasurements, String> {
116115
let tx_context = TransactionContextBuilder::new(target_account.clone())
117116
.input_notes(vec![note.clone()])
118117
.build();
118+
let block_ref = tx_context.tx_inputs().block_header().block_num();
119119

120-
let executor = TransactionExecutor::new(tx_context.get_data_store(), Some(falcon_auth.clone()))
121-
.with_tracing();
120+
let notes = tx_context.tx_inputs().input_notes().clone();
121+
let mast_store = tx_context.get_mast_store();
122122

123-
let block_ref = tx_context.tx_inputs().block_header().block_num();
124-
let note_ids = tx_context
125-
.tx_inputs()
126-
.input_notes()
127-
.iter()
128-
.map(|note| note.id())
129-
.collect::<Vec<_>>();
123+
let executor =
124+
TransactionExecutor::new(Arc::new(tx_context), mast_store, Some(falcon_auth.clone()))
125+
.with_tracing();
130126

131127
let tx_script_target =
132128
TransactionScript::compile(DEFAULT_AUTH_SCRIPT, [], TransactionKernel::assembler())
@@ -135,7 +131,7 @@ pub fn benchmark_p2id() -> Result<TransactionMeasurements, String> {
135131

136132
// execute transaction
137133
let executed_transaction = executor
138-
.execute_transaction(target_account.id(), block_ref, &note_ids, tx_args_target)
134+
.execute_transaction(target_account.id(), block_ref, notes, tx_args_target)
139135
.unwrap();
140136

141137
Ok(executed_transaction.into())

crates/miden-objects/src/note/note_tag.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ mod tests {
324324
ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
325325
ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2,
326326
ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE,
327-
ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE_ON_2, ACCOUNT_ID_SENDER,
327+
ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE_ON_CHAIN_2, ACCOUNT_ID_SENDER,
328328
},
329329
NoteError,
330330
};
@@ -342,7 +342,8 @@ mod tests {
342342
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(),
343343
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2).unwrap(),
344344
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE).unwrap(),
345-
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE_ON_2).unwrap(),
345+
AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE_ON_CHAIN_2)
346+
.unwrap(),
346347
AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap(),
347348
AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1).unwrap(),
348349
AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2).unwrap(),

crates/miden-objects/src/testing/account_id.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub const ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE: u128 = account_id(
3737
AccountStorageMode::Public,
3838
0xacdd_eefc,
3939
);
40-
pub const ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE_ON_2: u128 = account_id(
40+
pub const ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE_ON_CHAIN_2: u128 = account_id(
4141
AccountType::RegularAccountUpdatableCode,
4242
AccountStorageMode::Public,
4343
0xeeff_ccdd,

crates/miden-tx/src/errors/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub enum TransactionExecutorError {
1818
TransactionProgramExecutionFailed(#[source] ExecutionError),
1919
#[error("failed to fetch transaction inputs from the data store")]
2020
FetchTransactionInputsFailed(#[source] DataStoreError),
21+
#[error("failed to create transaction inputs")]
22+
InvalidTransactionInputs(#[source] TransactionInputError),
2123
#[error("input account ID {input_id} does not match output account ID {output_id}")]
2224
InconsistentAccountId {
2325
input_id: AccountId,
@@ -113,8 +115,6 @@ pub enum DataStoreError {
113115
AccountNotFound(AccountId),
114116
#[error("block with number {0} not found in data store")]
115117
BlockNotFound(BlockNumber),
116-
#[error("failed to create transaction inputs")]
117-
InvalidTransactionInput(#[source] TransactionInputError),
118118
#[error("note with id {0} is already consumed")]
119119
NoteAlreadyConsumed(NoteId),
120120
#[error("not with id {0} not found in data store")]
Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
#[cfg(feature = "async")]
22
use alloc::boxed::Box;
3+
use alloc::collections::BTreeSet;
34

45
use miden_objects::{
5-
account::AccountId, block::BlockNumber, note::NoteId, transaction::TransactionInputs,
6+
account::{Account, AccountId},
7+
block::{BlockHeader, BlockNumber},
8+
transaction::ChainMmr,
69
};
10+
use vm_processor::Word;
711
use winter_maybe_async::*;
812

913
use crate::DataStoreError;
@@ -15,26 +19,36 @@ use crate::DataStoreError;
1519
/// required for transaction execution.
1620
#[maybe_async_trait]
1721
pub trait DataStore {
18-
/// Returns account, chain, and input note data required to execute a transaction against
19-
/// the account with the specified ID and consuming the set of specified input notes.
22+
/// Returns blockchain-related data required to execute a transaction against a specific
23+
/// account, that consumes specific notes.
2024
///
21-
/// block_ref must be the block number of the block by which all of the input notes have been
22-
/// recorded in the chain. In general, it is recommended that bock_ref corresponds to the
23-
/// latest block available in the data store.
25+
/// The returned [`ChainMmr`] is expected to contain the complete set of requested
26+
/// block numbers (`ref_blocks`).
2427
///
2528
/// # Errors
2629
/// Returns an error if:
27-
/// - The account with the specified ID could not be found in the data store.
2830
/// - The block with the specified number could not be found in the data store.
29-
/// - Any of the notes with the specified IDs could not be found in the data store.
30-
/// - Any of the notes with the specified IDs were already consumed.
3131
/// - The combination of specified inputs resulted in a transaction input error.
3232
/// - The data store encountered some internal error
3333
#[maybe_async]
34-
fn get_transaction_inputs(
34+
fn get_chain_inputs(
35+
&self,
36+
ref_blocks: BTreeSet<BlockNumber>,
37+
block_header: BlockNumber,
38+
) -> Result<(ChainMmr, BlockHeader), DataStoreError>;
39+
40+
/// Returns account data required to execute a transaction.
41+
///
42+
/// For a new [`Account`], the corresponding seed should be returned as the second element
43+
/// of the return tuple.
44+
///
45+
/// # Errors
46+
/// Returns an error if:
47+
/// - The account with the specified ID could not be found in the data store.
48+
/// - The data store encountered some internal error.
49+
#[maybe_async]
50+
fn get_account_inputs(
3551
&self,
3652
account_id: AccountId,
37-
block_ref: BlockNumber,
38-
notes: &[NoteId],
39-
) -> Result<TransactionInputs, DataStoreError>;
53+
) -> Result<(Account, Option<Word>), DataStoreError>;
4054
}

crates/miden-tx/src/executor/mod.rs

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ use alloc::{collections::BTreeSet, sync::Arc, vec::Vec};
33
use miden_lib::transaction::TransactionKernel;
44
use miden_objects::{
55
account::{AccountCode, AccountId},
6-
assembly::Library,
76
block::BlockNumber,
8-
note::NoteId,
9-
transaction::{ExecutedTransaction, TransactionArgs, TransactionInputs, TransactionScript},
7+
note::NoteLocation,
8+
transaction::{
9+
ExecutedTransaction, InputNote, InputNotes, TransactionArgs, TransactionInputs,
10+
TransactionScript,
11+
},
1012
vm::StackOutputs,
1113
Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES, ZERO,
1214
};
13-
use vm_processor::{AdviceInputs, ExecutionOptions, Process, RecAdviceProvider};
15+
use vm_processor::{AdviceInputs, ExecutionOptions, MastForestStore, Process, RecAdviceProvider};
1416
use winter_maybe_async::{maybe_async, maybe_await};
1517

1618
use super::{TransactionExecutorError, TransactionHost};
@@ -36,7 +38,7 @@ pub use mast_store::TransactionMastStore;
3638
/// [TransactionAuthenticator], allowing it to be used with different backend implementations.
3739
pub struct TransactionExecutor {
3840
data_store: Arc<dyn DataStore>,
39-
mast_store: Arc<TransactionMastStore>,
41+
mast_forest_store: Arc<dyn MastForestStore>,
4042
authenticator: Option<Arc<dyn TransactionAuthenticator>>,
4143
/// Holds the code of all accounts loaded into this transaction executor via the
4244
/// [Self::load_account_code()] method.
@@ -52,13 +54,14 @@ impl TransactionExecutor {
5254
/// [TransactionAuthenticator].
5355
pub fn new(
5456
data_store: Arc<dyn DataStore>,
57+
mast_forest_store: Arc<dyn MastForestStore>,
5558
authenticator: Option<Arc<dyn TransactionAuthenticator>>,
5659
) -> Self {
5760
const _: () = assert!(MIN_TX_EXECUTION_CYCLES <= MAX_TX_EXECUTION_CYCLES);
5861

5962
Self {
6063
data_store,
61-
mast_store: Arc::new(TransactionMastStore::new()),
64+
mast_forest_store,
6265
authenticator,
6366
exec_options: ExecutionOptions::new(
6467
Some(MAX_TX_EXECUTION_CYCLES),
@@ -91,27 +94,6 @@ impl TransactionExecutor {
9194
self
9295
}
9396

94-
// STATE MUTATORS
95-
// --------------------------------------------------------------------------------------------
96-
97-
/// Loads the provided account code into the internal MAST forest store and adds the commitment
98-
/// of the provided code to the commitments set.
99-
pub fn load_account_code(&mut self, code: &AccountCode) {
100-
// load the code mast forest to the mast store
101-
self.mast_store.load_account_code(code);
102-
103-
// store the commitment of the foreign account code in the set
104-
self.account_codes.insert(code.clone());
105-
}
106-
107-
/// Loads the provided library code into the internal MAST forest store.
108-
///
109-
/// TODO: this is a work-around to support accounts which were complied with user-defined
110-
/// libraries. Once Miden Assembler supports library vendoring, this should go away.
111-
pub fn load_library(&mut self, library: &Library) {
112-
self.mast_store.insert(library.mast_forest().clone());
113-
}
114-
11597
// TRANSACTION EXECUTION
11698
// --------------------------------------------------------------------------------------------
11799

@@ -130,24 +112,36 @@ impl TransactionExecutor {
130112
&self,
131113
account_id: AccountId,
132114
block_ref: BlockNumber,
133-
notes: &[NoteId],
115+
notes: InputNotes<InputNote>,
134116
tx_args: TransactionArgs,
135117
) -> Result<ExecutedTransaction, TransactionExecutorError> {
136-
let tx_inputs =
137-
maybe_await!(self.data_store.get_transaction_inputs(account_id, block_ref, notes))
138-
.map_err(TransactionExecutorError::FetchTransactionInputsFailed)?;
118+
// TODO: Check that the reference block is not listed here, or otherwise
119+
// change the DataStore/executor API so that returning a ChainMmr with the reference
120+
// block works anyway.
121+
let ref_blocks: BTreeSet<BlockNumber> = notes
122+
.iter()
123+
.filter_map(InputNote::location)
124+
.map(NoteLocation::block_num)
125+
.collect();
126+
127+
let (account, seed) = maybe_await!(self.data_store.get_account_inputs(account_id))
128+
.map_err(TransactionExecutorError::FetchTransactionInputsFailed)?;
129+
130+
let (mmr, header) = maybe_await!(self.data_store.get_chain_inputs(ref_blocks, block_ref))
131+
.map_err(TransactionExecutorError::FetchTransactionInputsFailed)?;
132+
133+
let tx_inputs = TransactionInputs::new(account, seed, header, mmr, notes)
134+
.map_err(TransactionExecutorError::InvalidTransactionInputs)?;
139135

140136
let (stack_inputs, advice_inputs) =
141137
TransactionKernel::prepare_inputs(&tx_inputs, &tx_args, None);
142-
let advice_recorder: RecAdviceProvider = advice_inputs.into();
143138

144-
// load note script MAST into the MAST store
145-
self.mast_store.load_transaction_code(&tx_inputs, &tx_args);
139+
let advice_recorder: RecAdviceProvider = advice_inputs.into();
146140

147141
let mut host = TransactionHost::new(
148142
tx_inputs.account().into(),
149143
advice_recorder,
150-
self.mast_store.clone(),
144+
self.mast_forest_store.clone(),
151145
self.authenticator.clone(),
152146
self.account_codes.iter().map(|code| code.commitment()).collect(),
153147
)
@@ -202,23 +196,26 @@ impl TransactionExecutor {
202196
tx_script: TransactionScript,
203197
advice_inputs: AdviceInputs,
204198
) -> Result<[Felt; 16], TransactionExecutorError> {
205-
let tx_inputs =
206-
maybe_await!(self.data_store.get_transaction_inputs(account_id, block_ref, &[]))
199+
let (account, seed) = maybe_await!(self.data_store.get_account_inputs(account_id))
200+
.map_err(TransactionExecutorError::FetchTransactionInputsFailed)?;
201+
202+
let (mmr, header) =
203+
maybe_await!(self.data_store.get_chain_inputs(Default::default(), block_ref))
207204
.map_err(TransactionExecutorError::FetchTransactionInputsFailed)?;
208205

206+
let tx_inputs = TransactionInputs::new(account, seed, header, mmr, Default::default())
207+
.map_err(TransactionExecutorError::InvalidTransactionInputs)?;
208+
209209
let tx_args = TransactionArgs::new(Some(tx_script.clone()), None, Default::default());
210210

211211
let (stack_inputs, advice_inputs) =
212212
TransactionKernel::prepare_inputs(&tx_inputs, &tx_args, Some(advice_inputs));
213213
let advice_recorder: RecAdviceProvider = advice_inputs.into();
214214

215-
// load transaction script MAST into the MAST store
216-
self.mast_store.load_transaction_code(&tx_inputs, &tx_args);
217-
218215
let mut host = TransactionHost::new(
219216
tx_inputs.account().into(),
220217
advice_recorder,
221-
self.mast_store.clone(),
218+
self.mast_forest_store.clone(),
222219
self.authenticator.clone(),
223220
self.account_codes.iter().map(|code| code.commitment()).collect(),
224221
)

crates/miden-tx/src/host/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ use note_builder::OutputNoteBuilder;
3838
mod tx_progress;
3939
pub use tx_progress::TransactionProgress;
4040

41-
use crate::{
42-
auth::TransactionAuthenticator, errors::TransactionHostError, executor::TransactionMastStore,
43-
};
41+
use crate::{auth::TransactionAuthenticator, errors::TransactionHostError};
4442

4543
// TRANSACTION HOST
4644
// ================================================================================================
@@ -56,7 +54,7 @@ pub struct TransactionHost<A> {
5654
adv_provider: A,
5755

5856
/// MAST store which contains the code required to execute the transaction.
59-
mast_store: Arc<TransactionMastStore>,
57+
mast_store: Arc<dyn MastForestStore>,
6058

6159
/// Account state changes accumulated during transaction execution.
6260
///
@@ -98,7 +96,7 @@ impl<A: AdviceProvider> TransactionHost<A> {
9896
pub fn new(
9997
account: AccountHeader,
10098
adv_provider: A,
101-
mast_store: Arc<TransactionMastStore>,
99+
mast_store: Arc<dyn MastForestStore>,
102100
authenticator: Option<Arc<dyn TransactionAuthenticator>>,
103101
mut account_code_commitments: BTreeSet<Digest>,
104102
) -> Result<Self, TransactionHostError> {

crates/miden-tx/src/testing/tx_context/builder.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use alloc::{collections::BTreeMap, vec::Vec};
55

6-
use miden_lib::transaction::TransactionKernel;
6+
use miden_lib::{transaction::TransactionKernel, utils::word_to_masm_push_string};
77
use miden_objects::{
88
account::{Account, AccountCode, AccountId},
99
assembly::Assembler,
@@ -23,7 +23,6 @@ use miden_objects::{
2323
storage::prepare_assets,
2424
},
2525
transaction::{OutputNote, TransactionArgs, TransactionInputs, TransactionScript},
26-
utils::word_to_masm_push_string,
2726
vm::AdviceMap,
2827
FieldElement,
2928
};

0 commit comments

Comments
 (0)