Skip to content

Commit 677edc2

Browse files
apollo_gateway: use get_nonce outside extract_state_nonce_and_run_validations
1 parent bf0dd16 commit 677edc2

File tree

8 files changed

+127
-54
lines changed

8 files changed

+127
-54
lines changed

crates/apollo_gateway/src/gateway.rs

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use crate::errors::{
4141
GatewayResult,
4242
};
4343
use crate::metrics::{register_metrics, GatewayMetricHandle, GATEWAY_ADD_TX_LATENCY};
44-
use crate::state_reader::StateReaderFactory;
44+
use crate::state_reader::{MempoolStateReader, StateReaderFactory};
4545
use crate::stateful_transaction_validator::{
4646
StatefulTransactionValidatorFactory,
4747
StatefulTransactionValidatorFactoryTrait,
@@ -151,7 +151,7 @@ impl Gateway {
151151
transaction_converter_err_to_deprecated_gw_err(&tx_signature, e)
152152
})?;
153153

154-
let blocking_task =
154+
let mut blocking_task =
155155
ProcessTxBlockingTask::new(self, executable_tx, tokio::runtime::Handle::current())
156156
.await
157157
.map_err(|e| {
@@ -162,10 +162,22 @@ impl Gateway {
162162
metric_counters.record_add_tx_failure(&e);
163163
e
164164
})?;
165+
166+
let state_reader = blocking_task.get_state_reader().await?;
167+
168+
let account_address = blocking_task.executable_tx.contract_address();
169+
let account_nonce =
170+
state_reader.get_account_nonce(account_address).await.map_err(|e| {
171+
StarknetError::internal_with_logging("Failed to get account nonce", e)
172+
})?;
173+
174+
let stateful_tx_validator = blocking_task.create_stateful_validator(state_reader).await?;
175+
165176
// Run the blocking task in the current span.
166177
let curr_span = Span::current();
167-
let handle =
168-
tokio::task::spawn_blocking(move || curr_span.in_scope(|| blocking_task.process_tx()));
178+
let handle = tokio::task::spawn_blocking(move || {
179+
curr_span.in_scope(|| blocking_task.process_tx(account_nonce, stateful_tx_validator))
180+
});
169181
let handle_result = handle.await;
170182
let nonce = match handle_result {
171183
Ok(Ok(nonce)) => nonce,
@@ -240,7 +252,8 @@ impl Gateway {
240252
/// CPU-intensive transaction processing, spawned in a blocking thread to avoid blocking other tasks
241253
/// from running.
242254
struct ProcessTxBlockingTask {
243-
stateful_tx_validator: Box<dyn StatefulTransactionValidatorTrait + Send>,
255+
state_reader_factory: Arc<dyn StateReaderFactory>,
256+
stateful_tx_validator_factory: Arc<dyn StatefulTransactionValidatorFactoryTrait>,
244257
mempool_client: SharedMempoolClient,
245258
executable_tx: AccountTransaction,
246259
runtime: tokio::runtime::Handle,
@@ -252,21 +265,52 @@ impl ProcessTxBlockingTask {
252265
executable_tx: AccountTransaction,
253266
runtime: tokio::runtime::Handle,
254267
) -> GatewayResult<Self> {
255-
let stateful_tx_validator = gateway
256-
.stateful_tx_validator_factory
257-
.instantiate_validator(gateway.state_reader_factory.as_ref())
258-
.await?;
259268
Ok(Self {
260-
stateful_tx_validator,
269+
state_reader_factory: gateway.state_reader_factory.clone(),
270+
stateful_tx_validator_factory: gateway.stateful_tx_validator_factory.clone(),
261271
mempool_client: gateway.mempool_client.clone(),
262272
executable_tx,
263273
runtime,
264274
})
265275
}
266276

267-
fn process_tx(mut self) -> GatewayResult<Nonce> {
268-
let nonce = self.stateful_tx_validator.extract_state_nonce_and_run_validations(
277+
pub async fn get_state_reader(&self) -> GatewayResult<Box<dyn MempoolStateReader>> {
278+
let state_reader = self
279+
.stateful_tx_validator_factory
280+
.get_state_reader_for_validation(self.state_reader_factory.as_ref())
281+
.await
282+
.map_err(|e| {
283+
StarknetError::internal_with_logging(
284+
"Failed to get state reader from latest block",
285+
e,
286+
)
287+
})?;
288+
Ok(state_reader)
289+
}
290+
291+
pub async fn create_stateful_validator(
292+
&mut self,
293+
state_reader: Box<dyn MempoolStateReader>,
294+
) -> GatewayResult<Box<dyn StatefulTransactionValidatorTrait>> {
295+
self.stateful_tx_validator_factory
296+
.create_validator_from_state_reader(state_reader)
297+
.await
298+
.map_err(|e| {
299+
StarknetError::internal_with_logging(
300+
"Failed to create stateful validator from state reader",
301+
e,
302+
)
303+
})
304+
}
305+
306+
fn process_tx(
307+
self,
308+
account_nonce: Nonce,
309+
mut stateful_tx_validator: Box<dyn StatefulTransactionValidatorTrait>,
310+
) -> GatewayResult<Nonce> {
311+
let nonce = stateful_tx_validator.extract_state_nonce_and_run_validations(
269312
&self.executable_tx,
313+
account_nonce,
270314
self.mempool_client,
271315
self.runtime,
272316
)?;

crates/apollo_gateway/src/gateway_test.rs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,6 @@ use crate::state_reader_test_utils::{local_test_state_reader_factory, TestStateR
9797
use crate::stateful_transaction_validator::{
9898
MockStatefulTransactionValidatorFactoryTrait,
9999
MockStatefulTransactionValidatorTrait,
100-
StatefulTransactionValidatorFactoryTrait,
101-
StatefulTransactionValidatorTrait,
102100
};
103101
use crate::stateless_transaction_validator::MockStatelessTransactionValidatorTrait;
104102

@@ -316,17 +314,12 @@ async fn run_add_tx_and_extract_metrics(
316314
}
317315

318316
async fn process_tx_task(
319-
stateful_transaction_validator_factory: MockStatefulTransactionValidatorFactoryTrait,
317+
stateful_tx_validator_factory: MockStatefulTransactionValidatorFactoryTrait,
320318
) -> ProcessTxBlockingTask {
321319
let state_reader_factory = Arc::new(MockStateReaderFactory::new());
322-
let stateful_tx_validator = StatefulTransactionValidatorFactoryTrait::instantiate_validator(
323-
&stateful_transaction_validator_factory,
324-
state_reader_factory.as_ref(),
325-
)
326-
.await
327-
.expect("instantiate_validator should be mocked in tests");
328320
ProcessTxBlockingTask {
329-
stateful_tx_validator,
321+
state_reader_factory,
322+
stateful_tx_validator_factory: Arc::new(stateful_tx_validator_factory),
330323
mempool_client: Arc::new(MockMempoolClient::new()),
331324
executable_tx: executable_invoke_tx(invoke_args()),
332325
runtime: tokio::runtime::Handle::current(),
@@ -558,7 +551,7 @@ fn test_full_cycle_dump_deserialize_authorized_declarer_accounts(
558551
async fn process_tx_returns_error_when_extract_state_nonce_and_run_validations_fails(
559552
#[case] error_code: StarknetErrorCode,
560553
mut mock_stateful_transaction_validator: MockStatefulTransactionValidatorTrait,
561-
mut mock_stateful_transaction_validator_factory: MockStatefulTransactionValidatorFactoryTrait,
554+
mock_stateful_transaction_validator_factory: MockStatefulTransactionValidatorFactoryTrait,
562555
) {
563556
let expected_error = StarknetError {
564557
code: error_code.clone(),
@@ -567,19 +560,15 @@ async fn process_tx_returns_error_when_extract_state_nonce_and_run_validations_f
567560

568561
mock_stateful_transaction_validator
569562
.expect_extract_state_nonce_and_run_validations()
570-
.return_once(|_, _, _| Err(expected_error));
571-
572-
mock_stateful_transaction_validator_factory.expect_instantiate_validator().return_once(|_| {
573-
Box::pin(async {
574-
Ok::<Box<dyn StatefulTransactionValidatorTrait>, _>(Box::new(
575-
mock_stateful_transaction_validator,
576-
))
577-
})
578-
});
563+
.return_once(|_, _, _, _| Err(expected_error));
579564

580565
let process_tx_task = process_tx_task(mock_stateful_transaction_validator_factory).await;
581-
582-
let result = tokio::task::spawn_blocking(move || process_tx_task.process_tx()).await.unwrap();
566+
let account_nonce = nonce!(0);
567+
let result = tokio::task::spawn_blocking(move || {
568+
process_tx_task.process_tx(account_nonce, Box::new(mock_stateful_transaction_validator))
569+
})
570+
.await
571+
.unwrap();
583572

584573
assert!(result.is_err());
585574
assert_eq!(result.unwrap_err().code, error_code);
@@ -620,7 +609,7 @@ async fn add_tx_returns_error_when_instantiating_validator_fails(
620609
let error_code = StarknetErrorCode::UnknownErrorCode("StarknetErrorCode.InternalError".into());
621610
let expected_error = StarknetError { code: error_code.clone(), message: "placeholder".into() };
622611
mock_stateful_transaction_validator_factory
623-
.expect_instantiate_validator()
612+
.expect_get_state_reader_for_validation()
624613
.return_once(|_| Box::pin(async { Err::<_, _>(expected_error) }));
625614

626615
// Build gateway and inject the failing factory.

crates/apollo_gateway/src/rpc_state_reader.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,22 @@ impl MempoolStateReader for RpcStateReader {
111111
let block_info = block_header.try_into()?;
112112
Ok(block_info)
113113
}
114+
115+
async fn get_account_nonce(
116+
&self,
117+
contract_address: ContractAddress,
118+
) -> StateResult<Nonce> {
119+
let get_nonce_params = GetNonceParams { block_id: self.block_id, contract_address };
120+
let result = self.send_rpc_request("starknet_getNonce", get_nonce_params);
121+
match result {
122+
Ok(value) => {
123+
let nonce: Nonce = serde_json::from_value(value).map_err(serde_err_to_state_err)?;
124+
Ok(nonce)
125+
}
126+
Err(RPCStateReaderError::ContractAddressNotFound(_)) => Ok(Nonce::default()),
127+
Err(e) => Err(e)?,
128+
}
129+
}
114130
}
115131

116132
impl BlockifierStateReader for RpcStateReader {

crates/apollo_gateway/src/state_reader.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ use starknet_types_core::felt::Felt;
1313
#[async_trait]
1414
pub trait MempoolStateReader: BlockifierStateReader + Send + Sync {
1515
async fn get_block_info(&self) -> Result<BlockInfo, StateError>;
16+
async fn get_account_nonce(
17+
&self,
18+
contract_address: ContractAddress,
19+
) -> Result<Nonce, StateError>;
1620
}
1721

1822
#[async_trait]
@@ -32,6 +36,13 @@ impl MempoolStateReader for Box<dyn MempoolStateReader> {
3236
async fn get_block_info(&self) -> Result<BlockInfo, StateError> {
3337
self.as_ref().get_block_info().await
3438
}
39+
async fn get_account_nonce(
40+
&self,
41+
contract_address: ContractAddress,
42+
) -> Result<Nonce, StateError> {
43+
crate::state_reader::MempoolStateReader::get_account_nonce(self.as_ref(), contract_address)
44+
.await
45+
}
3546
}
3647

3748
impl BlockifierStateReader for Box<dyn MempoolStateReader> {

crates/apollo_gateway/src/state_reader_test_utils.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ impl MempoolStateReader for TestStateReader {
2828
async fn get_block_info(&self) -> Result<BlockInfo, StateError> {
2929
Ok(self.block_info.clone())
3030
}
31+
32+
async fn get_account_nonce(
33+
&self,
34+
contract_address: ContractAddress,
35+
) -> Result<Nonce, StateError> {
36+
self.blockifier_state_reader.get_nonce_at(contract_address)
37+
}
3138
}
3239

3340
impl BlockifierStateReader for TestStateReader {

crates/apollo_gateway/src/stateful_transaction_validator.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ pub trait StatefulTransactionValidatorFactoryTrait: Send + Sync {
4747
state_reader_factory: &dyn StateReaderFactory,
4848
) -> StatefulTransactionValidatorResult<Box<dyn StatefulTransactionValidatorTrait>>;
4949

50+
// 1) Get a state reader for validation (async, uses the factory).
5051
async fn get_state_reader_for_validation(
5152
&self,
5253
state_reader_factory: &dyn StateReaderFactory,
5354
) -> StatefulTransactionValidatorResult<Box<dyn MempoolStateReader>>;
54-
55+
// 2) Create the validator from a provided state reader (async, uses ChainInfo/config).
5556
async fn create_validator_from_state_reader(
5657
&self,
5758
state_reader: Box<dyn MempoolStateReader>,
@@ -132,6 +133,7 @@ pub trait StatefulTransactionValidatorTrait: Send {
132133
fn extract_state_nonce_and_run_validations(
133134
&mut self,
134135
executable_tx: &ExecutableTransaction,
136+
account_nonce: Nonce,
135137
mempool_client: SharedMempoolClient,
136138
runtime: tokio::runtime::Handle,
137139
) -> StatefulTransactionValidatorResult<Nonce>;
@@ -153,11 +155,10 @@ impl<B: BlockifierStatefulValidatorTrait + Send> StatefulTransactionValidatorTra
153155
fn extract_state_nonce_and_run_validations(
154156
&mut self,
155157
executable_tx: &ExecutableTransaction,
158+
account_nonce: Nonce,
156159
mempool_client: SharedMempoolClient,
157160
runtime: tokio::runtime::Handle,
158161
) -> StatefulTransactionValidatorResult<Nonce> {
159-
let address = executable_tx.contract_address();
160-
let account_nonce = self.get_nonce(address)?;
161162
self.run_transaction_validations(executable_tx, account_nonce, mempool_client, runtime)?;
162163
Ok(account_nonce)
163164
}

crates/apollo_gateway/src/stateful_transaction_validator_test.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,32 +64,24 @@ async fn test_get_nonce_fail_on_extract_state_nonce_and_run_validations() {
6464
// The mempool does not have any transactions from the sender.
6565
Ok(false)
6666
});
67-
let mempool_client = Arc::new(mock_mempool_client);
68-
let runtime = tokio::runtime::Handle::current();
67+
// Removed unused mempool client and runtime in this test since nonce is fetched outside now.
6968

7069
let mut stateful_validator = StatefulTransactionValidator {
7170
config: StatefulTransactionValidatorConfig::default(),
7271
blockifier_stateful_tx_validator: mock_blockifier_validator,
7372
};
7473

7574
let executable_tx = create_executable_invoke_tx(CairoVersion::Cairo1(RunnableCairo1::Casm));
76-
let result = tokio::task::spawn_blocking(move || {
77-
stateful_validator.extract_state_nonce_and_run_validations(
78-
&executable_tx,
79-
mempool_client,
80-
runtime,
81-
)
75+
// get_nonce should fail and return an InternalError.
76+
let err = tokio::task::spawn_blocking(move || {
77+
stateful_validator.get_nonce(executable_tx.contract_address())
8278
})
8379
.await
84-
.unwrap();
80+
.unwrap()
81+
.unwrap_err();
8582
assert_eq!(
86-
result,
87-
Err(StarknetError {
88-
code: StarknetErrorCode::UnknownErrorCode(
89-
"StarknetErrorCode.InternalError".to_string()
90-
),
91-
message: "Internal error".to_string(),
92-
})
83+
err.code,
84+
StarknetErrorCode::UnknownErrorCode("StarknetErrorCode.InternalError".to_string())
9385
);
9486
}
9587

@@ -147,6 +139,7 @@ async fn test_extract_state_nonce_and_run_validations(
147139
let result = tokio::task::spawn_blocking(move || {
148140
stateful_validator.extract_state_nonce_and_run_validations(
149141
&executable_tx,
142+
account_nonce,
150143
mempool_client,
151144
runtime,
152145
)

crates/apollo_gateway/src/sync_state_reader.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ impl MempoolStateReader for SyncStateReader {
8888

8989
Ok(block_info)
9090
}
91+
92+
async fn get_account_nonce(&self, contract_address: ContractAddress) -> StateResult<Nonce> {
93+
let res = self.state_sync_client.get_nonce_at(self.block_number, contract_address).await;
94+
95+
match res {
96+
Ok(value) => Ok(value),
97+
Err(StateSyncClientError::StateSyncError(StateSyncError::ContractNotFound(_))) => {
98+
Ok(Nonce::default())
99+
}
100+
Err(e) => Err(StateError::StateReadError(e.to_string())),
101+
}
102+
}
91103
}
92104

93105
impl BlockifierStateReader for SyncStateReader {

0 commit comments

Comments
 (0)