@@ -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,104 +84,70 @@ 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 . validate_state_preconditions ( executable_tx, account_nonce) ?;
154- runtime. block_on ( validate_by_mempool (
155- executable_tx,
156- account_nonce,
157- mempool_client. clone ( ) ,
158- ) ) ?;
159- self . run_validate_entry_point ( executable_tx, account_nonce, mempool_client, runtime) ?;
122+ let ( account_nonce, skip_validate) =
123+ self . run_pre_validation_checks ( executable_tx, mempool_client) . await ?;
124+ self . run_validate_entry_point ( executable_tx, skip_validate) . await ?;
160125 Ok ( account_nonce)
161126 }
162127}
163128
164- impl < B : BlockifierStatefulValidatorTrait > StatefulTransactionValidator < B > {
165- fn validate_state_preconditions (
129+ impl StatefulTransactionValidator {
130+ async fn validate_state_preconditions (
166131 & self ,
167132 executable_tx : & ExecutableTransaction ,
168133 account_nonce : Nonce ,
169134 ) -> StatefulTransactionValidatorResult < ( ) > {
170- self . validate_resource_bounds ( executable_tx) ?;
135+ self . validate_resource_bounds ( executable_tx) . await ?;
171136 self . validate_nonce ( executable_tx, account_nonce) ?;
172137 Ok ( ( ) )
173138 }
174139
175- fn validate_resource_bounds (
140+ async fn validate_resource_bounds (
176141 & self ,
177142 executable_tx : & ExecutableTransaction ,
178143 ) -> StatefulTransactionValidatorResult < ( ) > {
179144 // Skip this validation during the systems bootstrap phase.
180145 if self . config . validate_resource_bounds {
181146 // TODO(Arni): getnext_l2_gas_price from the block header.
182- // TODO(Itamar): Replace usage of `blockifier_stateful_tx_validator.block_info()` with
183- // the GW fixed-block provider and then remove `block_info()` from
184- // blockifier::{StatefulValidatorTrait, StatefulValidator}.
185147 let previous_block_l2_gas_price = self
186- . blockifier_stateful_tx_validator
187- . block_info ( )
148+ . gateway_fixed_block_state_reader
149+ . get_block_info ( )
150+ . await ?
188151 . gas_prices
189152 . strk_gas_prices
190153 . l2_gas_price ;
@@ -254,23 +217,52 @@ impl<B: BlockifierStatefulValidatorTrait> StatefulTransactionValidator<B> {
254217 }
255218
256219 #[ sequencer_latency_histogram( GATEWAY_VALIDATE_TX_LATENCY , true ) ]
257- fn run_validate_entry_point (
220+ async fn run_validate_entry_point (
258221 & mut self ,
259222 executable_tx : & ExecutableTransaction ,
260- account_nonce : Nonce ,
261- mempool_client : SharedMempoolClient ,
262- runtime : tokio:: runtime:: Handle ,
223+ skip_validate : bool ,
263224 ) -> StatefulTransactionValidatorResult < ( ) > {
264- let skip_validate =
265- skip_stateful_validations ( executable_tx, account_nonce, mempool_client, runtime) ?;
266225 let only_query = false ;
267226 let charge_fee = enforce_fee ( executable_tx, only_query) ;
268227 let strict_nonce_check = false ;
269228 let execution_flags =
270229 ExecutionFlags { only_query, charge_fee, validate : !skip_validate, strict_nonce_check } ;
271230
272231 let account_tx = AccountTransaction { tx : executable_tx. clone ( ) , execution_flags } ;
273- self . blockifier_stateful_tx_validator . validate ( account_tx) . map_err ( |e| StarknetError {
232+
233+ // Build block context here.
234+ let mut versioned_constants = VersionedConstants :: get_versioned_constants (
235+ self . config . versioned_constants_overrides . clone ( ) ,
236+ ) ;
237+ // The validation of a transaction is not affected by the casm hash migration.
238+ versioned_constants. enable_casm_hash_migration = false ;
239+
240+ let mut block_info = self . gateway_fixed_block_state_reader . get_block_info ( ) . await ?;
241+ block_info. block_number = block_info. block_number . unchecked_next ( ) ;
242+ let block_context = BlockContext :: new (
243+ block_info,
244+ self . chain_info . clone ( ) ,
245+ versioned_constants,
246+ BouncerConfig :: max ( ) ,
247+ ) ;
248+
249+ // Move state into the blocking task and run CPU-heavy validation.
250+ let state_reader_and_contract_manager =
251+ self . state_reader_and_contract_manager . take ( ) . expect ( "Validator was already consumed" ) ;
252+
253+ tokio:: task:: spawn_blocking ( move || {
254+ let state = CachedState :: new ( state_reader_and_contract_manager) ;
255+ let mut blockifier = BlockifierStatefulValidator :: create ( state, block_context) ;
256+ blockifier. validate ( account_tx)
257+ } )
258+ . await
259+ . map_err ( |e| StarknetError {
260+ code : StarknetErrorCode :: UnknownErrorCode (
261+ "StarknetErrorCode.InternalError" . to_string ( ) ,
262+ ) ,
263+ message : format ! ( "Blocking task join error: {e}" ) ,
264+ } ) ?
265+ . map_err ( |e| StarknetError {
274266 code : StarknetErrorCode :: KnownErrorCode ( KnownStarknetErrorCode :: ValidateFailure ) ,
275267 message : e. to_string ( ) ,
276268 } ) ?;
@@ -310,6 +302,28 @@ impl<B: BlockifierStatefulValidatorTrait> StatefulTransactionValidator<B> {
310302 }
311303 Ok ( ( ) )
312304 }
305+
306+ async fn run_pre_validation_checks (
307+ & self ,
308+ executable_tx : & ExecutableTransaction ,
309+ mempool_client : SharedMempoolClient ,
310+ ) -> StatefulTransactionValidatorResult < ( Nonce , bool ) > {
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+ // TODO(noamsp): Fix this. Need to map the errors better.
315+ StarknetError :: internal_with_signature_logging (
316+ format ! ( "Failed to get nonce for sender address {address}" ) ,
317+ & executable_tx. signature ( ) ,
318+ e,
319+ )
320+ } ) ?;
321+ self . validate_state_preconditions ( executable_tx, account_nonce) . await ?;
322+ validate_by_mempool ( executable_tx, account_nonce, mempool_client. clone ( ) ) . await ?;
323+ let skip_validate =
324+ skip_stateful_validations ( executable_tx, account_nonce, mempool_client. clone ( ) ) . await ?;
325+ Ok ( ( account_nonce, skip_validate) )
326+ }
313327}
314328
315329/// Perform transaction validation by the mempool.
@@ -328,11 +342,10 @@ async fn validate_by_mempool(
328342/// Check if validation of an invoke transaction should be skipped due to deploy_account not being
329343/// processed yet. This feature is used to improve UX for users sending deploy_account + invoke at
330344/// once.
331- fn skip_stateful_validations (
345+ async fn skip_stateful_validations (
332346 tx : & ExecutableTransaction ,
333347 account_nonce : Nonce ,
334348 mempool_client : SharedMempoolClient ,
335- runtime : tokio:: runtime:: Handle ,
336349) -> StatefulTransactionValidatorResult < bool > {
337350 if let ExecutableTransaction :: Invoke ( ExecutableInvokeTransaction { tx, .. } ) = tx {
338351 // check if the transaction nonce is 1, meaning it is post deploy_account, and the
@@ -344,8 +357,9 @@ fn skip_stateful_validations(
344357 // to check if the account exists in the mempool since it means that either it has a
345358 // deploy_account transaction or transactions with future nonces that passed
346359 // validations.
347- return runtime
348- . block_on ( mempool_client. account_tx_in_pool_or_recent_block ( tx. sender_address ( ) ) )
360+ return mempool_client
361+ . account_tx_in_pool_or_recent_block ( tx. sender_address ( ) )
362+ . await
349363 . map_err ( |err| mempool_client_err_to_deprecated_gw_err ( & tx. signature ( ) , err) )
350364 . inspect ( |exists| {
351365 if * exists {
0 commit comments