@@ -11,10 +11,7 @@ use apollo_mempool_types::communication::SharedMempoolClient;
1111use apollo_mempool_types:: mempool_types:: ValidationArgs ;
1212use apollo_proc_macros:: sequencer_latency_histogram;
1313use async_trait:: async_trait;
14- use blockifier:: blockifier:: stateful_validator:: {
15- StatefulValidator ,
16- StatefulValidatorTrait as BlockifierStatefulValidatorTrait ,
17- } ;
14+ use blockifier:: blockifier:: stateful_validator:: { StatefulValidator , StatefulValidatorTrait } ;
1815use blockifier:: blockifier_versioned_constants:: VersionedConstants ;
1916use blockifier:: bouncer:: BouncerConfig ;
2017use blockifier:: context:: { BlockContext , ChainInfo } ;
@@ -87,115 +84,71 @@ impl StatefulTransactionValidatorFactoryTrait for StatefulTransactionValidatorFa
8784 self . contract_class_manager . clone ( ) ,
8885 Some ( GATEWAY_CLASS_CACHE_METRICS ) ,
8986 ) ;
90-
91- let state = CachedState :: new ( state_reader_and_contract_manager) ;
92- let mut versioned_constants = VersionedConstants :: get_versioned_constants (
93- self . config . versioned_constants_overrides . clone ( ) ,
94- ) ;
95- // The validation of a transaction is not affected by the casm hash migration.
96- versioned_constants. enable_casm_hash_migration = false ;
97-
98- let mut block_info = gateway_fixed_block_state_reader. get_block_info ( ) . await ?;
99- block_info. block_number = block_info. block_number . unchecked_next ( ) ;
100- let block_context = BlockContext :: new (
101- block_info,
102- self . chain_info . clone ( ) ,
103- versioned_constants,
104- BouncerConfig :: max ( ) ,
105- ) ;
106-
107- let blockifier_stateful_tx_validator =
108- BlockifierStatefulValidator :: create ( state, block_context) ;
10987 Ok ( Box :: new ( StatefulTransactionValidator {
11088 config : self . config . clone ( ) ,
111- blockifier_stateful_tx_validator,
89+ chain_info : self . chain_info . clone ( ) ,
90+ state_reader_and_contract_manager : Some ( state_reader_and_contract_manager) ,
11291 gateway_fixed_block_state_reader,
11392 } ) )
11493 }
11594}
11695
11796#[ cfg_attr( test, mockall:: automock) ]
97+ #[ async_trait]
11898pub trait StatefulTransactionValidatorTrait : Send {
119- fn extract_state_nonce_and_run_validations (
99+ async fn extract_state_nonce_and_run_validations (
120100 & mut self ,
121101 executable_tx : & ExecutableTransaction ,
122102 mempool_client : SharedMempoolClient ,
123- runtime : tokio:: runtime:: Handle ,
124103 ) -> StatefulTransactionValidatorResult < Nonce > ;
125104}
126105
127- pub struct StatefulTransactionValidator < B : BlockifierStatefulValidatorTrait > {
106+ pub struct StatefulTransactionValidator {
128107 config : StatefulTransactionValidatorConfig ,
129- blockifier_stateful_tx_validator : B ,
108+ chain_info : ChainInfo ,
109+ // Consumed when running the CPU-heavy blockifier validation.
110+ state_reader_and_contract_manager :
111+ Option < StateReaderAndContractManager < Box < dyn GatewayStateReaderWithCompiledClasses > > > ,
130112 gateway_fixed_block_state_reader : Box < dyn GatewayFixedBlockStateReader > ,
131113}
132114
133- impl < B : BlockifierStatefulValidatorTrait + Send > StatefulTransactionValidatorTrait
134- for StatefulTransactionValidator < B >
135- {
136- fn extract_state_nonce_and_run_validations (
115+ #[ async_trait]
116+ impl StatefulTransactionValidatorTrait for StatefulTransactionValidator {
117+ async fn extract_state_nonce_and_run_validations (
137118 & mut self ,
138119 executable_tx : & ExecutableTransaction ,
139120 mempool_client : SharedMempoolClient ,
140- runtime : tokio:: runtime:: Handle ,
141121 ) -> StatefulTransactionValidatorResult < Nonce > {
142- let address = executable_tx. contract_address ( ) ;
143- let account_nonce = runtime
144- . block_on ( self . gateway_fixed_block_state_reader . get_nonce ( address) )
145- . map_err ( |e| {
146- // TODO(noamsp): Fix this. Need to map the errors better.
147- StarknetError :: internal_with_signature_logging (
148- format ! ( "Failed to get nonce for sender address {address}" ) ,
149- & executable_tx. signature ( ) ,
150- e,
151- )
152- } ) ?;
153- self . run_transaction_validation ( executable_tx, account_nonce, mempool_client, runtime) ?;
122+ let account_nonce = self . extract_nonce ( executable_tx) . await ?;
123+ let skip_validate =
124+ self . run_pre_validation_checks ( executable_tx, account_nonce, mempool_client) . await ?;
125+ self . run_validate_entry_point ( executable_tx, skip_validate) . await ?;
154126 Ok ( account_nonce)
155127 }
156128}
157129
158- impl < B : BlockifierStatefulValidatorTrait > StatefulTransactionValidator < B > {
159- fn run_transaction_validation (
160- & mut self ,
161- executable_tx : & ExecutableTransaction ,
162- account_nonce : Nonce ,
163- mempool_client : SharedMempoolClient ,
164- runtime : tokio:: runtime:: Handle ,
165- ) -> StatefulTransactionValidatorResult < ( ) > {
166- self . validate_state_preconditions ( executable_tx, account_nonce) ?;
167- runtime. block_on ( validate_by_mempool (
168- executable_tx,
169- account_nonce,
170- mempool_client. clone ( ) ,
171- ) ) ?;
172- self . run_validate_entry_point ( executable_tx, account_nonce, mempool_client, runtime) ?;
173- Ok ( ( ) )
174- }
175-
176- fn validate_state_preconditions (
130+ impl StatefulTransactionValidator {
131+ async fn validate_state_preconditions (
177132 & self ,
178133 executable_tx : & ExecutableTransaction ,
179134 account_nonce : Nonce ,
180135 ) -> StatefulTransactionValidatorResult < ( ) > {
181- self . validate_resource_bounds ( executable_tx) ?;
136+ self . validate_resource_bounds ( executable_tx) . await ?;
182137 self . validate_nonce ( executable_tx, account_nonce) ?;
183138 Ok ( ( ) )
184139 }
185140
186- fn validate_resource_bounds (
141+ async fn validate_resource_bounds (
187142 & self ,
188143 executable_tx : & ExecutableTransaction ,
189144 ) -> StatefulTransactionValidatorResult < ( ) > {
190145 // Skip this validation during the systems bootstrap phase.
191146 if self . config . validate_resource_bounds {
192147 // TODO(Arni): getnext_l2_gas_price from the block header.
193- // TODO(Itamar): Replace usage of `blockifier_stateful_tx_validator.block_info()` with
194- // the GW fixed-block provider and then remove `block_info()` from
195- // blockifier::{StatefulValidatorTrait, StatefulValidator}.
196148 let previous_block_l2_gas_price = self
197- . blockifier_stateful_tx_validator
198- . block_info ( )
149+ . gateway_fixed_block_state_reader
150+ . get_block_info ( )
151+ . await ?
199152 . gas_prices
200153 . strk_gas_prices
201154 . l2_gas_price ;
@@ -265,23 +218,52 @@ impl<B: BlockifierStatefulValidatorTrait> StatefulTransactionValidator<B> {
265218 }
266219
267220 #[ sequencer_latency_histogram( GATEWAY_VALIDATE_TX_LATENCY , true ) ]
268- fn run_validate_entry_point (
221+ async fn run_validate_entry_point (
269222 & mut self ,
270223 executable_tx : & ExecutableTransaction ,
271- account_nonce : Nonce ,
272- mempool_client : SharedMempoolClient ,
273- runtime : tokio:: runtime:: Handle ,
224+ skip_validate : bool ,
274225 ) -> StatefulTransactionValidatorResult < ( ) > {
275- let skip_validate =
276- skip_stateful_validations ( executable_tx, account_nonce, mempool_client, runtime) ?;
277226 let only_query = false ;
278227 let charge_fee = enforce_fee ( executable_tx, only_query) ;
279228 let strict_nonce_check = false ;
280229 let execution_flags =
281230 ExecutionFlags { only_query, charge_fee, validate : !skip_validate, strict_nonce_check } ;
282231
283232 let account_tx = AccountTransaction { tx : executable_tx. clone ( ) , execution_flags } ;
284- self . blockifier_stateful_tx_validator . validate ( account_tx) . map_err ( |e| StarknetError {
233+
234+ // Build block context here.
235+ let mut versioned_constants = VersionedConstants :: get_versioned_constants (
236+ self . config . versioned_constants_overrides . clone ( ) ,
237+ ) ;
238+ // The validation of a transaction is not affected by the casm hash migration.
239+ versioned_constants. enable_casm_hash_migration = false ;
240+
241+ let mut block_info = self . gateway_fixed_block_state_reader . get_block_info ( ) . await ?;
242+ block_info. block_number = block_info. block_number . unchecked_next ( ) ;
243+ let block_context = BlockContext :: new (
244+ block_info,
245+ self . chain_info . clone ( ) ,
246+ versioned_constants,
247+ BouncerConfig :: max ( ) ,
248+ ) ;
249+
250+ // Move state into the blocking task and run CPU-heavy validation.
251+ let state_reader_and_contract_manager =
252+ self . state_reader_and_contract_manager . take ( ) . expect ( "Validator was already consumed" ) ;
253+
254+ tokio:: task:: spawn_blocking ( move || {
255+ let state = CachedState :: new ( state_reader_and_contract_manager) ;
256+ let mut blockifier = BlockifierStatefulValidator :: create ( state, block_context) ;
257+ blockifier. validate ( account_tx)
258+ } )
259+ . await
260+ . map_err ( |e| StarknetError {
261+ code : StarknetErrorCode :: UnknownErrorCode (
262+ "StarknetErrorCode.InternalError" . to_string ( ) ,
263+ ) ,
264+ message : format ! ( "Blocking task join error: {e}" ) ,
265+ } ) ?
266+ . map_err ( |e| StarknetError {
285267 code : StarknetErrorCode :: KnownErrorCode ( KnownStarknetErrorCode :: ValidateFailure ) ,
286268 message : e. to_string ( ) ,
287269 } ) ?;
@@ -321,6 +303,35 @@ impl<B: BlockifierStatefulValidatorTrait> StatefulTransactionValidator<B> {
321303 }
322304 Ok ( ( ) )
323305 }
306+
307+ async fn extract_nonce (
308+ & self ,
309+ executable_tx : & ExecutableTransaction ,
310+ ) -> StatefulTransactionValidatorResult < Nonce > {
311+ let address = executable_tx. contract_address ( ) ;
312+ let account_nonce =
313+ self . gateway_fixed_block_state_reader . get_nonce ( address) . await . map_err ( |e| {
314+ StarknetError :: internal_with_signature_logging (
315+ format ! ( "Failed to get nonce for sender address {address}" ) ,
316+ & executable_tx. signature ( ) ,
317+ e,
318+ )
319+ } ) ?;
320+ Ok ( account_nonce)
321+ }
322+
323+ async fn run_pre_validation_checks (
324+ & self ,
325+ executable_tx : & ExecutableTransaction ,
326+ account_nonce : Nonce ,
327+ mempool_client : SharedMempoolClient ,
328+ ) -> StatefulTransactionValidatorResult < bool > {
329+ self . validate_state_preconditions ( executable_tx, account_nonce) . await ?;
330+ validate_by_mempool ( executable_tx, account_nonce, mempool_client. clone ( ) ) . await ?;
331+ let skip_validate =
332+ skip_stateful_validations ( executable_tx, account_nonce, mempool_client. clone ( ) ) . await ?;
333+ Ok ( skip_validate)
334+ }
324335}
325336
326337/// Perform transaction validation by the mempool.
@@ -339,11 +350,10 @@ async fn validate_by_mempool(
339350/// Check if validation of an invoke transaction should be skipped due to deploy_account not being
340351/// processed yet. This feature is used to improve UX for users sending deploy_account + invoke at
341352/// once.
342- fn skip_stateful_validations (
353+ async fn skip_stateful_validations (
343354 tx : & ExecutableTransaction ,
344355 account_nonce : Nonce ,
345356 mempool_client : SharedMempoolClient ,
346- runtime : tokio:: runtime:: Handle ,
347357) -> StatefulTransactionValidatorResult < bool > {
348358 if let ExecutableTransaction :: Invoke ( ExecutableInvokeTransaction { tx, .. } ) = tx {
349359 // check if the transaction nonce is 1, meaning it is post deploy_account, and the
@@ -355,8 +365,9 @@ fn skip_stateful_validations(
355365 // to check if the account exists in the mempool since it means that either it has a
356366 // deploy_account transaction or transactions with future nonces that passed
357367 // validations.
358- return runtime
359- . block_on ( mempool_client. account_tx_in_pool_or_recent_block ( tx. sender_address ( ) ) )
368+ return mempool_client
369+ . account_tx_in_pool_or_recent_block ( tx. sender_address ( ) )
370+ . await
360371 . map_err ( |err| mempool_client_err_to_deprecated_gw_err ( & tx. signature ( ) , err) )
361372 . inspect ( |exists| {
362373 if * exists {
0 commit comments