Skip to content

Commit cdec7b2

Browse files
committed
feat(katana): limit number of proof keys (#2814)
1 parent bd94e32 commit cdec7b2

File tree

11 files changed

+142
-11
lines changed

11 files changed

+142
-11
lines changed

crates/dojo/test-utils/src/sequencer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ pub fn get_default_test_config(sequencing: SequencingConfig) -> Config {
125125
max_connections: DEFAULT_RPC_MAX_CONNECTIONS,
126126
apis: HashSet::from([ApiKind::Starknet, ApiKind::Dev, ApiKind::Saya, ApiKind::Torii]),
127127
max_event_page_size: Some(100),
128+
max_proof_keys: Some(100),
128129
};
129130

130131
Config { sequencing, rpc, dev, chain: chain.into(), ..Default::default() }

crates/katana/cli/src/args.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ impl NodeArgs {
213213
max_connections: self.server.max_connections,
214214
cors_origins: self.server.http_cors_origins.clone(),
215215
max_event_page_size: Some(self.server.max_event_page_size),
216+
max_proof_keys: Some(self.server.max_proof_keys),
216217
}
217218
}
218219

crates/katana/cli/src/options.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::net::IpAddr;
1212
use clap::Args;
1313
use katana_node::config::execution::{DEFAULT_INVOCATION_MAX_STEPS, DEFAULT_VALIDATION_MAX_STEPS};
1414
use katana_node::config::metrics::{DEFAULT_METRICS_ADDR, DEFAULT_METRICS_PORT};
15+
use katana_node::config::rpc::DEFAULT_RPC_MAX_PROOF_KEYS;
1516
#[cfg(feature = "server")]
1617
use katana_node::config::rpc::{
1718
DEFAULT_RPC_ADDR, DEFAULT_RPC_MAX_CONNECTIONS, DEFAULT_RPC_MAX_EVENT_PAGE_SIZE,
@@ -107,6 +108,12 @@ pub struct ServerOptions {
107108
#[arg(default_value_t = DEFAULT_RPC_MAX_EVENT_PAGE_SIZE)]
108109
#[serde(default = "default_page_size")]
109110
pub max_event_page_size: u64,
111+
112+
/// Maximum keys for requesting storage proofs.
113+
#[arg(long = "rpc.max-proof-keys", value_name = "SIZE")]
114+
#[arg(default_value_t = DEFAULT_RPC_MAX_PROOF_KEYS)]
115+
#[serde(default = "default_proof_keys")]
116+
pub max_proof_keys: u64,
110117
}
111118

112119
#[cfg(feature = "server")]
@@ -118,6 +125,7 @@ impl Default for ServerOptions {
118125
max_connections: DEFAULT_RPC_MAX_CONNECTIONS,
119126
http_cors_origins: Vec::new(),
120127
max_event_page_size: DEFAULT_RPC_MAX_EVENT_PAGE_SIZE,
128+
max_proof_keys: DEFAULT_RPC_MAX_PROOF_KEYS,
121129
}
122130
}
123131
}
@@ -380,6 +388,11 @@ fn default_page_size() -> u64 {
380388
DEFAULT_RPC_MAX_EVENT_PAGE_SIZE
381389
}
382390

391+
#[cfg(feature = "server")]
392+
fn default_proof_keys() -> u64 {
393+
katana_node::config::rpc::DEFAULT_RPC_MAX_PROOF_KEYS
394+
}
395+
383396
#[cfg(feature = "server")]
384397
fn default_metrics_addr() -> IpAddr {
385398
DEFAULT_METRICS_ADDR

crates/katana/core/src/backend/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ impl<'a, P: TrieWriter> UncommittedBlock<'a, P> {
252252

253253
// state_commitment = hPos("STARKNET_STATE_V0", contract_trie_root, class_trie_root)
254254
fn compute_new_state_root(&self) -> Felt {
255-
println!("ohayo im committing now");
256255
let class_trie_root = self
257256
.provider
258257
.trie_insert_declared_classes(self.header.number, &self.state_updates.declared_classes)

crates/katana/node/src/config/rpc.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub const DEFAULT_RPC_PORT: u16 = 5050;
1010

1111
/// Default maximmum page size for the `starknet_getEvents` RPC method.
1212
pub const DEFAULT_RPC_MAX_EVENT_PAGE_SIZE: u64 = 1024;
13+
/// Default maximmum number of keys for the `starknet_getStorageProof` RPC method.
14+
pub const DEFAULT_RPC_MAX_PROOF_KEYS: u64 = 100;
1315

1416
/// List of APIs supported by Katana.
1517
#[derive(
@@ -29,8 +31,9 @@ pub struct RpcConfig {
2931
pub port: u16,
3032
pub max_connections: u32,
3133
pub apis: HashSet<ApiKind>,
32-
pub max_event_page_size: Option<u64>,
3334
pub cors_origins: Vec<HeaderValue>,
35+
pub max_event_page_size: Option<u64>,
36+
pub max_proof_keys: Option<u64>,
3437
}
3538

3639
impl RpcConfig {
@@ -49,6 +52,7 @@ impl Default for RpcConfig {
4952
max_connections: DEFAULT_RPC_MAX_CONNECTIONS,
5053
apis: HashSet::from([ApiKind::Starknet]),
5154
max_event_page_size: Some(DEFAULT_RPC_MAX_EVENT_PAGE_SIZE),
55+
max_proof_keys: Some(DEFAULT_RPC_MAX_PROOF_KEYS),
5256
}
5357
}
5458
}

crates/katana/node/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,10 @@ pub async fn build(mut config: Config) -> Result<Node> {
253253
.allow_headers([hyper::header::CONTENT_TYPE, "argent-client".parse().unwrap(), "argent-version".parse().unwrap()]);
254254

255255
if config.rpc.apis.contains(&ApiKind::Starknet) {
256-
let cfg = StarknetApiConfig { max_event_page_size: config.rpc.max_event_page_size };
256+
let cfg = StarknetApiConfig {
257+
max_event_page_size: config.rpc.max_event_page_size,
258+
max_proof_keys: config.rpc.max_proof_keys,
259+
};
257260

258261
let api = if let Some(client) = forked_client {
259262
StarknetApi::new_forked(

crates/katana/rpc/rpc-types/src/error/starknet.rs

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use jsonrpsee::types::error::CallError;
33
use jsonrpsee::types::ErrorObject;
44
use katana_pool::validation::error::InvalidTransactionError;
55
use katana_pool::PoolError;
6+
use katana_primitives::block::BlockNumber;
67
use katana_primitives::event::ContinuationTokenError;
78
use katana_provider::error::ProviderError;
89
use serde::Serialize;
@@ -77,12 +78,24 @@ pub enum StarknetApiError {
7778
UnsupportedContractClassVersion,
7879
#[error("An unexpected error occured")]
7980
UnexpectedError { reason: String },
80-
#[error("Too many storage keys requested")]
81-
ProofLimitExceeded,
8281
#[error("Too many keys provided in a filter")]
8382
TooManyKeysInFilter,
8483
#[error("Failed to fetch pending transactions")]
8584
FailedToFetchPendingTransactions,
85+
#[error("The node doesn't support storage proofs for blocks that are too far in the past")]
86+
StorageProofNotSupported {
87+
/// The oldest block whose storage proof can be obtained.
88+
oldest_block: BlockNumber,
89+
/// The block of the storage proof that is being requested.
90+
requested_block: BlockNumber,
91+
},
92+
#[error("Proof limit exceeded")]
93+
ProofLimitExceeded {
94+
/// The limit for the total number of keys that can be specified in a single request.
95+
limit: u64,
96+
/// The total number of keys that is being requested.
97+
total: u64,
98+
},
8699
}
87100

88101
impl StarknetApiError {
@@ -103,6 +116,7 @@ impl StarknetApiError {
103116
StarknetApiError::FailedToFetchPendingTransactions => 38,
104117
StarknetApiError::ContractError { .. } => 40,
105118
StarknetApiError::TransactionExecutionError { .. } => 41,
119+
StarknetApiError::StorageProofNotSupported { .. } => 42,
106120
StarknetApiError::InvalidContractClass => 50,
107121
StarknetApiError::ClassAlreadyDeclared => 51,
108122
StarknetApiError::InvalidTransactionNonce { .. } => 52,
@@ -117,7 +131,7 @@ impl StarknetApiError {
117131
StarknetApiError::UnsupportedTransactionVersion => 61,
118132
StarknetApiError::UnsupportedContractClassVersion => 62,
119133
StarknetApiError::UnexpectedError { .. } => 63,
120-
StarknetApiError::ProofLimitExceeded => 10000,
134+
StarknetApiError::ProofLimitExceeded { .. } => 1000,
121135
}
122136
}
123137

@@ -128,8 +142,10 @@ impl StarknetApiError {
128142
pub fn data(&self) -> Option<serde_json::Value> {
129143
match self {
130144
StarknetApiError::ContractError { .. }
131-
| StarknetApiError::UnexpectedError { .. }
132145
| StarknetApiError::PageSizeTooBig { .. }
146+
| StarknetApiError::UnexpectedError { .. }
147+
| StarknetApiError::ProofLimitExceeded { .. }
148+
| StarknetApiError::StorageProofNotSupported { .. }
133149
| StarknetApiError::TransactionExecutionError { .. } => Some(serde_json::json!(self)),
134150

135151
StarknetApiError::InvalidTransactionNonce { reason }
@@ -284,7 +300,6 @@ mod tests {
284300
#[case(StarknetApiError::InvalidMessageSelector, 21, "Invalid message selector")]
285301
#[case(StarknetApiError::NonAccount, 58, "Sender address in not an account contract")]
286302
#[case(StarknetApiError::InvalidTxnIndex, 27, "Invalid transaction index in a block")]
287-
#[case(StarknetApiError::ProofLimitExceeded, 10000, "Too many storage keys requested")]
288303
#[case(StarknetApiError::TooManyKeysInFilter, 34, "Too many keys provided in a filter")]
289304
#[case(StarknetApiError::ContractClassSizeIsTooLarge, 57, "Contract class size is too large")]
290305
#[case(StarknetApiError::FailedToFetchPendingTransactions, 38, "Failed to fetch pending transactions")]
@@ -372,6 +387,30 @@ mod tests {
372387
"max_allowed": 500
373388
}),
374389
)]
390+
#[case(
391+
StarknetApiError::StorageProofNotSupported {
392+
oldest_block: 10,
393+
requested_block: 9
394+
},
395+
42,
396+
"The node doesn't support storage proofs for blocks that are too far in the past",
397+
json!({
398+
"oldest_block": 10,
399+
"requested_block": 9
400+
}),
401+
)]
402+
#[case(
403+
StarknetApiError::ProofLimitExceeded {
404+
limit: 5,
405+
total: 10
406+
},
407+
1000,
408+
"Proof limit exceeded",
409+
json!({
410+
"limit": 5,
411+
"total": 10
412+
}),
413+
)]
375414
fn test_starknet_api_error_to_error_conversion_data_some(
376415
#[case] starknet_error: StarknetApiError,
377416
#[case] expected_code: i32,

crates/katana/rpc/rpc-types/src/trie.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use katana_trie::bonsai::BitSlice;
66
use katana_trie::{MultiProof, Path, ProofNode};
77
use serde::{Deserialize, Serialize};
88

9-
#[derive(Debug, Serialize, Deserialize)]
9+
#[derive(Debug, Default, Serialize, Deserialize)]
1010
pub struct ContractStorageKeys {
1111
#[serde(rename = "contract_address")]
1212
pub address: ContractAddress,

crates/katana/rpc/rpc/src/starknet/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,9 @@ pub struct StarknetApiConfig {
44
///
55
/// If `None`, the maximum chunk size is bounded by [`u64::MAX`].
66
pub max_event_page_size: Option<u64>,
7+
8+
/// The max keys whose proofs can be requested for from the `getStorageProof` method.
9+
///
10+
/// If `None`, the maximum keys size is bounded by [`u64::MAX`].
11+
pub max_proof_keys: Option<u64>,
712
}

crates/katana/rpc/rpc/src/starknet/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,18 @@ where
11391139
self.on_io_blocking_task(move |this| {
11401140
let provider = this.inner.backend.blockchain.provider();
11411141

1142+
// Check if the total number of keys requested exceeds the RPC limit.
1143+
if let Some(limit) = this.inner.config.max_proof_keys {
1144+
let total_keys = class_hashes.as_ref().map(|v| v.len()).unwrap_or(0)
1145+
+ contract_addresses.as_ref().map(|v| v.len()).unwrap_or(0)
1146+
+ contracts_storage_keys.as_ref().map(|v| v.len()).unwrap_or(0);
1147+
1148+
let total_keys = total_keys as u64;
1149+
if total_keys > limit {
1150+
return Err(StarknetApiError::ProofLimitExceeded { limit, total: total_keys });
1151+
}
1152+
}
1153+
11421154
let state = this.state(&block_id)?;
11431155
let block_hash = provider.latest_hash()?;
11441156

@@ -1185,7 +1197,6 @@ where
11851197

11861198
let classes_tree_root = state.classes_root()?;
11871199
let contracts_tree_root = state.contracts_root()?;
1188-
11891200
let global_roots = GlobalRoots { block_hash, classes_tree_root, contracts_tree_root };
11901201

11911202
Ok(GetStorageProofResponse {

0 commit comments

Comments
 (0)