@@ -6,12 +6,15 @@ use blockifier::blockifier::transaction_executor::{
66use blockifier:: context:: BlockContext ;
77use blockifier:: state:: cached_state:: { CachedState , StateMaps } ;
88use blockifier:: state:: contract_class_manager:: ContractClassManager ;
9- use blockifier:: state:: state_reader_and_contract_manager:: StateReaderAndContractManager ;
9+ use blockifier:: state:: state_reader_and_contract_manager:: {
10+ FetchCompiledClasses ,
11+ StateReaderAndContractManager ,
12+ } ;
1013use blockifier:: transaction:: account_transaction:: ExecutionFlags ;
1114use blockifier:: transaction:: transaction_execution:: Transaction as BlockifierTransaction ;
1215use blockifier_reexecution:: state_reader:: rpc_state_reader:: RpcStateReader ;
1316use starknet_api:: block:: BlockNumber ;
14- use starknet_api:: core :: ChainId ;
17+ use starknet_api:: transaction :: fields :: Fee ;
1518use starknet_api:: transaction:: { Transaction , TransactionHash } ;
1619
1720use crate :: errors:: VirtualBlockExecutorError ;
@@ -32,8 +35,9 @@ pub struct VirtualBlockExecutionData {
3235
3336/// Executes a virtual block of transactions.
3437///
35- /// A virtual block executor runs transactions without block preprocessing
36- /// (`pre_process_block`), which is useful for simulating execution or generating
38+ /// A virtual block executor runs transactions on top of a given, finalized block.
39+ /// This means that some parts, like block preprocessing
40+ /// (`pre_process_block`), are skipped. Useful for simulating execution or generating
3741/// OS input for proving.
3842///
3943/// Implementations can fetch state from different sources (RPC nodes, local state,
@@ -47,13 +51,13 @@ pub struct VirtualBlockExecutionData {
4751/// # Examples
4852///
4953/// ```text
50- /// let executor = RpcVirtualBlockExecutor::new (
54+ /// let executor = RpcStateReader::new_with_config_from_url (
5155/// "http://localhost:9545".to_string(),
52- /// ChainId::Mainnet,
53- /// contract_class_manager ,
56+ /// ChainId::Mainnet,
57+ /// BlockNumber(1000) ,
5458/// );
5559///
56- /// let execution_data = executor.execute(block_number, transactions)?;
60+ /// let execution_data = executor.execute(block_number, contract_class_manager, transactions)?;
5761/// // Use execution_data to build OS input for proving...
5862/// ```
5963pub trait VirtualBlockExecutor {
@@ -62,6 +66,7 @@ pub trait VirtualBlockExecutor {
6266 /// # Arguments
6367 ///
6468 /// * `block_number` - The block number to use for state and context
69+ /// * `contract_class_manager` - Manager for compiled contract classes
6570 /// * `txs` - Invoke transactions to execute (with their hashes)
6671 ///
6772 /// # Returns
@@ -71,105 +76,16 @@ pub trait VirtualBlockExecutor {
7176 fn execute (
7277 & self ,
7378 block_number : BlockNumber ,
79+ contract_class_manager : ContractClassManager ,
7480 txs : Vec < ( Transaction , TransactionHash ) > ,
7581 ) -> Result < VirtualBlockExecutionData , VirtualBlockExecutorError > {
7682 let blockifier_txs = Self :: convert_invoke_txs ( txs) ?;
77- self . execute_inner ( block_number, blockifier_txs)
78- }
79-
80- fn execute_inner (
81- & self ,
82- block_number : BlockNumber ,
83- txs : Vec < BlockifierTransaction > ,
84- ) -> Result < VirtualBlockExecutionData , VirtualBlockExecutorError > ;
85-
86- /// Converts Invoke transactions to blockifier transactions.
87- ///
88- /// Uses execution flags that skip fee charging and nonce check.
89- /// Returns an error if any transaction is not an Invoke.
90- fn convert_invoke_txs (
91- txs : Vec < ( Transaction , TransactionHash ) > ,
92- ) -> Result < Vec < BlockifierTransaction > , VirtualBlockExecutorError > {
93- // Skip validation, fee charging, and nonce check for virtual block execution.
94- let execution_flags = ExecutionFlags {
95- validate : true ,
96- charge_fee : false ,
97- strict_nonce_check : false ,
98- only_query : false ,
99- } ;
100-
101- txs. into_iter ( )
102- . map ( |( tx, tx_hash) | {
103- if !matches ! ( tx, Transaction :: Invoke ( _) ) {
104- return Err ( VirtualBlockExecutorError :: UnsupportedTransactionType ) ;
105- }
106-
107- BlockifierTransaction :: from_api (
108- tx,
109- tx_hash,
110- None , // class_info - not needed for Invoke.
111- None , // paid_fee_on_l1 - not needed for Invoke.
112- None , // deployed_contract_address - not needed for Invoke.
113- execution_flags. clone ( ) ,
114- )
115- . map_err ( |e| VirtualBlockExecutorError :: TransactionExecutionError ( e. to_string ( ) ) )
116- } )
117- . collect ( )
118- }
119- }
120-
121- /// RPC-based virtual block executor.
122- ///
123- /// This executor fetches historical state from an RPC node and executes transactions
124- /// without block preprocessing. Validation and fee charging are always skipped,
125- /// making it suitable for simulation and OS input generation.
126- pub struct RpcVirtualBlockExecutor {
127- node_url : String ,
128- chain_id : ChainId ,
129- contract_class_manager : ContractClassManager ,
130- }
131-
132- impl RpcVirtualBlockExecutor {
133- /// Creates a new RPC-based virtual block executor.
134- ///
135- /// # Arguments
136- ///
137- /// * `node_url` - URL of the RPC node to fetch state from
138- /// * `chain_id` - The chain ID for transaction hash computation
139- /// * `contract_class_manager` - Manager for compiled contract classes
140- pub fn new (
141- node_url : String ,
142- chain_id : ChainId ,
143- contract_class_manager : ContractClassManager ,
144- ) -> Self {
145- Self { node_url, chain_id, contract_class_manager }
146- }
147- }
148-
149- impl VirtualBlockExecutor for RpcVirtualBlockExecutor {
150- fn execute_inner (
151- & self ,
152- block_number : BlockNumber ,
153- txs : Vec < BlockifierTransaction > ,
154- ) -> Result < VirtualBlockExecutionData , VirtualBlockExecutorError > {
155- // Create RPC state reader for the given block.
156- let rpc_state_reader = RpcStateReader :: new_with_config_from_url (
157- self . node_url . clone ( ) ,
158- self . chain_id . clone ( ) ,
159- block_number,
160- ) ;
161-
162- // Get block context from RPC.
163- let block_context = rpc_state_reader
164- . get_block_context ( )
165- . map_err ( |e| VirtualBlockExecutorError :: ReexecutionError ( Box :: new ( e) ) ) ?;
83+ let block_context = self . block_context ( block_number) ?;
84+ let state_reader = self . state_reader ( block_number) ?;
16685
16786 // Create state reader with contract manager.
168- let state_reader_and_contract_manager = StateReaderAndContractManager :: new (
169- rpc_state_reader,
170- self . contract_class_manager . clone ( ) ,
171- None ,
172- ) ;
87+ let state_reader_and_contract_manager =
88+ StateReaderAndContractManager :: new ( state_reader, contract_class_manager, None ) ;
17389
17490 let block_state = CachedState :: new ( state_reader_and_contract_manager) ;
17591
@@ -181,7 +97,7 @@ impl VirtualBlockExecutor for RpcVirtualBlockExecutor {
18197 ) ;
18298
18399 // Execute all transactions.
184- let execution_results = transaction_executor. execute_txs ( & txs , None ) ;
100+ let execution_results = transaction_executor. execute_txs ( & blockifier_txs , None ) ;
185101
186102 // Collect results, returning error if any transaction fails.
187103 let execution_outputs: Vec < TransactionExecutionOutput > = execution_results
@@ -203,4 +119,77 @@ impl VirtualBlockExecutor for RpcVirtualBlockExecutor {
203119
204120 Ok ( VirtualBlockExecutionData { execution_outputs, block_context, initial_reads } )
205121 }
122+
123+ /// Converts Invoke transactions to blockifier transactions.
124+ ///
125+ /// Uses execution flags that skip fee charging and nonce check.
126+ /// Returns an error if any transaction is not an Invoke.
127+ fn convert_invoke_txs (
128+ txs : Vec < ( Transaction , TransactionHash ) > ,
129+ ) -> Result < Vec < BlockifierTransaction > , VirtualBlockExecutorError > {
130+ txs. into_iter ( )
131+ . map ( |( tx, tx_hash) | {
132+ if let Transaction :: Invoke ( invoke_tx) = tx {
133+ // Execute with validation, conditional fee charging based on resource bounds,
134+ // but skip strict nonce check for virtual block execution.
135+ let execution_flags = ExecutionFlags {
136+ only_query : false ,
137+ charge_fee : invoke_tx. resource_bounds ( ) . max_possible_fee ( invoke_tx. tip ( ) )
138+ > Fee ( 0 ) ,
139+ validate : true ,
140+ strict_nonce_check : false ,
141+ } ;
142+
143+ BlockifierTransaction :: from_api (
144+ Transaction :: Invoke ( invoke_tx) ,
145+ tx_hash,
146+ None , // class_info - not needed for Invoke.
147+ None , // paid_fee_on_l1 - not needed for Invoke.
148+ None , // deployed_contract_address - not needed for Invoke.
149+ execution_flags,
150+ )
151+ . map_err ( |e| {
152+ VirtualBlockExecutorError :: TransactionExecutionError ( e. to_string ( ) )
153+ } )
154+ } else {
155+ Err ( VirtualBlockExecutorError :: UnsupportedTransactionType )
156+ }
157+ } )
158+ . collect ( )
159+ }
160+ /// Returns the block context for the given block number.
161+ fn block_context (
162+ & self ,
163+ block_number : BlockNumber ,
164+ ) -> Result < BlockContext , VirtualBlockExecutorError > ;
165+
166+ /// Returns a state reader that implements `FetchCompiledClasses` for the given block number.
167+ /// Must be `Send + Sync + 'static` to be used in the transaction executor.
168+ fn state_reader (
169+ & self ,
170+ block_number : BlockNumber ,
171+ ) -> Result < impl FetchCompiledClasses + Send + Sync + ' static , VirtualBlockExecutorError > ;
172+ }
173+
174+ /// RPC-based virtual block executor.
175+ ///
176+ /// This executor fetches historical state from an RPC node and executes transactions
177+ /// without block preprocessing. Validation and fee charging are always skipped,
178+ /// making it suitable for simulation and OS input generation.
179+ impl VirtualBlockExecutor for RpcStateReader {
180+ fn block_context (
181+ & self ,
182+ _block_number : BlockNumber ,
183+ ) -> Result < BlockContext , VirtualBlockExecutorError > {
184+ self . get_block_context ( )
185+ . map_err ( |e| VirtualBlockExecutorError :: ReexecutionError ( Box :: new ( e) ) )
186+ }
187+
188+ fn state_reader (
189+ & self ,
190+ _block_number : BlockNumber ,
191+ ) -> Result < impl FetchCompiledClasses + Send + Sync + ' static , VirtualBlockExecutorError > {
192+ // RpcStateReader itself is the state reader
193+ Ok ( self . clone ( ) )
194+ }
206195}
0 commit comments