Skip to content

Commit b38ede6

Browse files
apollo_gateway: add GatewayFixedBlockStateReader instead of mempool state reader (#10486)
1 parent 1c0070a commit b38ede6

11 files changed

+193
-250
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use std::sync::Arc;
2+
3+
use apollo_gateway_types::deprecated_gateway_error::StarknetError;
4+
use apollo_state_sync_types::communication::{SharedStateSyncClient, StateSyncClient};
5+
use async_trait::async_trait;
6+
use starknet_api::block::{BlockInfo, BlockNumber, GasPriceVector, GasPrices};
7+
use starknet_api::data_availability::L1DataAvailabilityMode;
8+
use tokio::sync::OnceCell;
9+
10+
pub type StarknetResult<T> = Result<T, StarknetError>;
11+
12+
/// A reader to a fixed block in the synced state of Starknet.
13+
#[cfg_attr(test, mockall::automock)]
14+
#[async_trait]
15+
pub trait GatewayFixedBlockStateReader: Send + Sync {
16+
async fn get_block_info(&self) -> StarknetResult<BlockInfo>;
17+
}
18+
19+
pub struct GatewayFixedBlockSyncStateClient {
20+
state_sync_client: Arc<dyn StateSyncClient>,
21+
block_number: BlockNumber,
22+
block_info_cache: OnceCell<BlockInfo>,
23+
}
24+
25+
impl GatewayFixedBlockSyncStateClient {
26+
pub fn new(state_sync_client: SharedStateSyncClient, block_number: BlockNumber) -> Self {
27+
Self { state_sync_client, block_number, block_info_cache: OnceCell::new() }
28+
}
29+
30+
async fn get_block_info_from_sync_client(&self) -> StarknetResult<BlockInfo> {
31+
let block = self.state_sync_client.get_block(self.block_number).await.map_err(|e| {
32+
StarknetError::internal_with_logging("Failed to get latest block info", e)
33+
})?;
34+
35+
let block_header = block.block_header_without_hash;
36+
let block_info = BlockInfo {
37+
block_number: block_header.block_number,
38+
block_timestamp: block_header.timestamp,
39+
sequencer_address: block_header.sequencer.0,
40+
gas_prices: GasPrices {
41+
eth_gas_prices: GasPriceVector {
42+
l1_gas_price: block_header.l1_gas_price.price_in_wei.try_into()?,
43+
l1_data_gas_price: block_header.l1_data_gas_price.price_in_wei.try_into()?,
44+
l2_gas_price: block_header.l2_gas_price.price_in_wei.try_into()?,
45+
},
46+
strk_gas_prices: GasPriceVector {
47+
l1_gas_price: block_header.l1_gas_price.price_in_fri.try_into()?,
48+
l1_data_gas_price: block_header.l1_data_gas_price.price_in_fri.try_into()?,
49+
l2_gas_price: block_header.l2_gas_price.price_in_fri.try_into()?,
50+
},
51+
},
52+
use_kzg_da: match block_header.l1_da_mode {
53+
L1DataAvailabilityMode::Blob => true,
54+
L1DataAvailabilityMode::Calldata => false,
55+
},
56+
};
57+
58+
Ok(block_info)
59+
}
60+
}
61+
62+
#[async_trait]
63+
impl GatewayFixedBlockStateReader for GatewayFixedBlockSyncStateClient {
64+
async fn get_block_info(&self) -> StarknetResult<BlockInfo> {
65+
self.block_info_cache
66+
.get_or_try_init(|| self.get_block_info_from_sync_client())
67+
.await
68+
.cloned()
69+
}
70+
}

crates/apollo_gateway/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod communication;
22
pub mod errors;
33
pub mod gateway;
4+
pub mod gateway_fixed_block_state_reader;
45
pub mod metrics;
56
pub mod rpc_objects;
67
pub mod rpc_state_reader;

crates/apollo_gateway/src/rpc_state_reader.rs

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use apollo_gateway_config::config::RpcStateReaderConfig;
2+
use apollo_gateway_types::deprecated_gateway_error::StarknetError;
23
use apollo_rpc::CompiledContractClass;
34
use apollo_state_sync_types::communication::StateSyncClientResult;
45
use async_trait::async_trait;
@@ -21,6 +22,7 @@ use starknet_api::state::StorageKey;
2122
use starknet_types_core::felt::Felt;
2223

2324
use crate::errors::{serde_err_to_state_err, RPCStateReaderError, RPCStateReaderResult};
25+
use crate::gateway_fixed_block_state_reader::{GatewayFixedBlockStateReader, StarknetResult};
2426
use crate::rpc_objects::{
2527
BlockHeader,
2628
BlockId,
@@ -35,11 +37,7 @@ use crate::rpc_objects::{
3537
RPC_ERROR_CONTRACT_ADDRESS_NOT_FOUND,
3638
RPC_ERROR_INVALID_PARAMS,
3739
};
38-
use crate::state_reader::{
39-
GatewayStateReaderWithCompiledClasses,
40-
MempoolStateReader,
41-
StateReaderFactory,
42-
};
40+
use crate::state_reader::{GatewayStateReaderWithCompiledClasses, StateReaderFactory};
4341

4442
#[derive(Clone)]
4543
pub struct RpcStateReader {
@@ -100,25 +98,6 @@ impl RpcStateReader {
10098
}
10199
}
102100

103-
#[async_trait]
104-
impl MempoolStateReader for RpcStateReader {
105-
async fn get_block_info(&self) -> StateResult<BlockInfo> {
106-
let get_block_params = GetBlockWithTxHashesParams { block_id: self.block_id };
107-
let reader = self.clone();
108-
109-
let get_block_with_tx_hashes_result = tokio::task::spawn_blocking(move || {
110-
reader.send_rpc_request("starknet_getBlockWithTxHashes", get_block_params)
111-
})
112-
.await
113-
.map_err(|e| StateError::StateReadError(format!("JoinError: {e}")))??;
114-
115-
let block_header: BlockHeader = serde_json::from_value(get_block_with_tx_hashes_result)
116-
.map_err(serde_err_to_state_err)?;
117-
let block_info = block_header.try_into()?;
118-
Ok(block_info)
119-
}
120-
}
121-
122101
impl BlockifierStateReader for RpcStateReader {
123102
fn get_storage_at(
124103
&self,
@@ -209,11 +188,40 @@ impl FetchCompiledClasses for RpcStateReader {
209188

210189
impl GatewayStateReaderWithCompiledClasses for RpcStateReader {}
211190

191+
// An alternative impl to get the synced state, not via the state-sync component. To be removed in
192+
// the future.
193+
#[async_trait]
194+
impl GatewayFixedBlockStateReader for RpcStateReader {
195+
async fn get_block_info(&self) -> StarknetResult<BlockInfo> {
196+
let get_block_params = GetBlockWithTxHashesParams { block_id: self.block_id };
197+
let reader = self.clone();
198+
199+
let get_block_with_tx_hashes_result = tokio::task::spawn_blocking(move || {
200+
reader.send_rpc_request("starknet_getBlockWithTxHashes", get_block_params)
201+
})
202+
.await
203+
.map_err(|e| StarknetError::internal_with_logging("JoinError", e))?
204+
.map_err(|e| StarknetError::internal_with_logging("RPC error", e))?;
205+
206+
let block_header: BlockHeader = serde_json::from_value(get_block_with_tx_hashes_result)
207+
.map_err(|e| StarknetError::internal_with_logging("Failed to parse block header", e))?;
208+
let block_info = block_header.try_into().map_err(|e| {
209+
StarknetError::internal_with_logging("Failed to convert block header to BlockInfo", e)
210+
})?;
211+
Ok(block_info)
212+
}
213+
}
214+
212215
#[async_trait]
213216
impl StateReaderFactory for RpcStateReaderFactory {
214-
async fn get_state_reader_from_latest_block(
217+
async fn get_blockifier_state_reader_and_gateway_fixed_block_from_latest_block(
215218
&self,
216-
) -> StateSyncClientResult<Box<dyn GatewayStateReaderWithCompiledClasses>> {
217-
Ok(Box::new(RpcStateReader::from_latest(&self.config)))
219+
) -> StateSyncClientResult<(
220+
Box<dyn GatewayStateReaderWithCompiledClasses>,
221+
Box<dyn GatewayFixedBlockStateReader>,
222+
)> {
223+
let reader = RpcStateReader::from_latest(&self.config);
224+
let fixed_client = RpcStateReader::from_latest(&self.config);
225+
Ok((Box::new(reader), Box::new(fixed_client)))
218226
}
219227
}

crates/apollo_gateway/src/rpc_state_reader_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use starknet_api::block::{BlockInfo, BlockNumber, GasPricePerToken};
1010
use starknet_api::contract_class::SierraVersion;
1111
use starknet_api::{class_hash, contract_address, felt, nonce};
1212

13+
use crate::gateway_fixed_block_state_reader::GatewayFixedBlockStateReader;
1314
use crate::rpc_objects::{
1415
BlockHeader,
1516
BlockId,
@@ -22,7 +23,6 @@ use crate::rpc_objects::{
2223
RpcSuccessResponse,
2324
};
2425
use crate::rpc_state_reader::RpcStateReader;
25-
use crate::state_reader::MempoolStateReader;
2626

2727
async fn run_rpc_server() -> mockito::ServerGuard {
2828
mockito::Server::new_async().await

crates/apollo_gateway/src/state_reader.rs

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,28 @@ use async_trait::async_trait;
33
use blockifier::execution::contract_class::RunnableCompiledClass;
44
use blockifier::state::global_cache::CompiledClasses;
55
use blockifier::state::state_api::{StateReader as BlockifierStateReader, StateResult};
6-
use blockifier::state::state_reader_and_contract_manager::{
7-
FetchCompiledClasses,
8-
StateReaderAndContractManager,
9-
};
6+
use blockifier::state::state_reader_and_contract_manager::FetchCompiledClasses;
107
#[cfg(test)]
118
use mockall::automock;
12-
use starknet_api::block::BlockInfo;
139
use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce};
1410
use starknet_api::state::StorageKey;
1511
use starknet_types_core::felt::Felt;
1612

17-
#[async_trait]
18-
pub trait MempoolStateReader: BlockifierStateReader + Send + Sync {
19-
async fn get_block_info(&self) -> StateResult<BlockInfo>;
20-
}
21-
13+
use crate::gateway_fixed_block_state_reader::GatewayFixedBlockStateReader;
2214
#[cfg_attr(test, automock)]
2315
#[async_trait]
2416
pub trait StateReaderFactory: Send + Sync {
25-
async fn get_state_reader_from_latest_block(
17+
async fn get_blockifier_state_reader_and_gateway_fixed_block_from_latest_block(
2618
&self,
27-
) -> StateSyncClientResult<Box<dyn GatewayStateReaderWithCompiledClasses>>;
19+
) -> StateSyncClientResult<(
20+
Box<dyn GatewayStateReaderWithCompiledClasses>,
21+
Box<dyn GatewayFixedBlockStateReader>,
22+
)>;
2823
}
2924

30-
pub trait GatewayStateReaderWithCompiledClasses: MempoolStateReader + FetchCompiledClasses {}
25+
// TODO(Arni): Delete this trait, once we replace `dyn GatewayStateReaderWithCompiledClasses` with
26+
// generics.
27+
pub trait GatewayStateReaderWithCompiledClasses: FetchCompiledClasses + Send + Sync {}
3128

3229
impl BlockifierStateReader for Box<dyn GatewayStateReaderWithCompiledClasses> {
3330
fn get_storage_at(
@@ -64,25 +61,3 @@ impl FetchCompiledClasses for Box<dyn GatewayStateReaderWithCompiledClasses> {
6461
self.as_ref().is_declared(class_hash)
6562
}
6663
}
67-
68-
// By default, a Box<dyn Trait> does not implement the trait of the object it contains.
69-
// Therefore, for using the Box<dyn GatewayStateReaderWithCompiledClasses>, that the
70-
// StateReaderFactory creates, we need to implement the MempoolStateReader trait for
71-
// Box<dyn GatewayStateReaderWithCompiledClasses>.
72-
#[async_trait]
73-
impl MempoolStateReader for Box<dyn GatewayStateReaderWithCompiledClasses> {
74-
async fn get_block_info(&self) -> StateResult<BlockInfo> {
75-
self.as_ref().get_block_info().await
76-
}
77-
}
78-
79-
impl GatewayStateReaderWithCompiledClasses for Box<dyn GatewayStateReaderWithCompiledClasses> {}
80-
81-
#[async_trait]
82-
impl MempoolStateReader
83-
for StateReaderAndContractManager<Box<dyn GatewayStateReaderWithCompiledClasses>>
84-
{
85-
async fn get_block_info(&self) -> StateResult<BlockInfo> {
86-
self.state_reader.get_block_info().await
87-
}
88-
}

crates/apollo_gateway/src/state_reader_test.rs

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ use apollo_state_sync_types::communication::{
1111
SharedStateSyncClient,
1212
StateSyncClientResult,
1313
};
14-
use apollo_state_sync_types::state_sync_types::SyncBlock;
15-
use apollo_test_utils::{get_rng, GetTestInstance};
1614
use blockifier::blockifier::config::ContractClassManagerConfig;
1715
use blockifier::execution::contract_class::RunnableCompiledClass;
1816
use blockifier::state::contract_class_manager::ContractClassManager;
@@ -23,23 +21,13 @@ use blockifier::state::state_reader_and_contract_manager::StateReaderAndContract
2321
use blockifier::test_utils::initial_test_state::state_reader_and_contract_manager_for_testing;
2422
use mockall::predicate;
2523
use rstest::rstest;
26-
use starknet_api::block::{
27-
BlockHeaderWithoutHash,
28-
BlockInfo,
29-
BlockNumber,
30-
BlockTimestamp,
31-
GasPricePerToken,
32-
GasPriceVector,
33-
GasPrices,
34-
NonzeroGasPrice,
35-
};
24+
use starknet_api::block::BlockNumber;
3625
use starknet_api::contract_class::ContractClass;
37-
use starknet_api::core::{ClassHash, SequencerContractAddress};
38-
use starknet_api::data_availability::L1DataAvailabilityMode;
26+
use starknet_api::core::ClassHash;
3927
use starknet_api::state::SierraContractClass;
4028
use starknet_api::{class_hash, contract_address, felt, nonce, storage_key};
4129

42-
use crate::state_reader::{GatewayStateReaderWithCompiledClasses, MempoolStateReader};
30+
use crate::state_reader::GatewayStateReaderWithCompiledClasses;
4331
use crate::sync_state_reader::SyncStateReader;
4432

4533
static DUMMY_CLASS_HASH: LazyLock<ClassHash> = LazyLock::new(|| class_hash!(2_u32));
@@ -249,79 +237,6 @@ fn declared_but_not_in_manager_scenario() -> GetCompiledClassTestScenario {
249237
}
250238
}
251239

252-
#[tokio::test]
253-
async fn test_get_block_info() {
254-
let mut mock_state_sync_client = MockStateSyncClient::new();
255-
let mock_class_manager_client = MockClassManagerClient::new();
256-
let contract_class_manager = ContractClassManager::start(ContractClassManagerConfig::default());
257-
let block_number = BlockNumber(1);
258-
let block_timestamp = BlockTimestamp(2);
259-
let sequencer_address = contract_address!("0x3");
260-
let l1_gas_price = GasPricePerToken { price_in_wei: 4_u8.into(), price_in_fri: 5_u8.into() };
261-
let l1_data_gas_price =
262-
GasPricePerToken { price_in_wei: 6_u8.into(), price_in_fri: 7_u8.into() };
263-
let l2_gas_price = GasPricePerToken { price_in_wei: 8_u8.into(), price_in_fri: 9_u8.into() };
264-
let l1_da_mode = L1DataAvailabilityMode::get_test_instance(&mut get_rng());
265-
266-
mock_state_sync_client.expect_get_block().times(1).with(predicate::eq(block_number)).returning(
267-
move |_| {
268-
Ok(SyncBlock {
269-
state_diff: Default::default(),
270-
account_transaction_hashes: Default::default(),
271-
l1_transaction_hashes: Default::default(),
272-
block_header_without_hash: BlockHeaderWithoutHash {
273-
block_number,
274-
l1_gas_price,
275-
l1_data_gas_price,
276-
l2_gas_price,
277-
sequencer: SequencerContractAddress(sequencer_address),
278-
timestamp: block_timestamp,
279-
l1_da_mode,
280-
..Default::default()
281-
},
282-
})
283-
},
284-
);
285-
286-
let state_reader_and_contract_manager = sync_state_reader_and_contract_manager(
287-
Arc::new(mock_state_sync_client),
288-
Arc::new(mock_class_manager_client),
289-
contract_class_manager.clone(),
290-
block_number,
291-
tokio::runtime::Handle::current(),
292-
);
293-
let result = state_reader_and_contract_manager.get_block_info().await.unwrap();
294-
295-
assert_eq!(
296-
result,
297-
BlockInfo {
298-
block_number,
299-
block_timestamp,
300-
sequencer_address,
301-
gas_prices: GasPrices {
302-
eth_gas_prices: GasPriceVector {
303-
l1_gas_price: NonzeroGasPrice::new_unchecked(l1_gas_price.price_in_wei),
304-
l1_data_gas_price: NonzeroGasPrice::new_unchecked(
305-
l1_data_gas_price.price_in_wei
306-
),
307-
l2_gas_price: NonzeroGasPrice::new_unchecked(l2_gas_price.price_in_wei),
308-
},
309-
strk_gas_prices: GasPriceVector {
310-
l1_gas_price: NonzeroGasPrice::new_unchecked(l1_gas_price.price_in_fri),
311-
l1_data_gas_price: NonzeroGasPrice::new_unchecked(
312-
l1_data_gas_price.price_in_fri
313-
),
314-
l2_gas_price: NonzeroGasPrice::new_unchecked(l2_gas_price.price_in_fri),
315-
},
316-
},
317-
use_kzg_da: match l1_da_mode {
318-
L1DataAvailabilityMode::Blob => true,
319-
L1DataAvailabilityMode::Calldata => false,
320-
},
321-
}
322-
);
323-
}
324-
325240
#[tokio::test]
326241
async fn test_get_storage_at() {
327242
let mut mock_state_sync_client = MockStateSyncClient::new();

0 commit comments

Comments
 (0)