From e27e00b1044895ac1dc588231778faaffa371122 Mon Sep 17 00:00:00 2001 From: FabijanC Date: Fri, 6 Dec 2024 16:32:43 +0100 Subject: [PATCH 1/3] Add getStorageProof RPC method --- .../src/api/json_rpc/endpoints.rs | 13 +++++++- .../src/api/json_rpc/error.rs | 8 +++++ .../src/api/json_rpc/mod.rs | 9 ++++- .../src/api/json_rpc/models.rs | 18 +++++++++- .../tests/general_rpc_tests.rs | 33 +++++++++++++++---- 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs b/crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs index 57e052d8b..7846185ef 100644 --- a/crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs +++ b/crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs @@ -14,7 +14,9 @@ use starknet_types::rpc::transactions::{ use starknet_types::starknet_api::block::BlockStatus; use super::error::{ApiError, StrictRpcResult}; -use super::models::{BlockHashAndNumberOutput, L1TransactionHashInput, SyncingOutput}; +use super::models::{ + BlockHashAndNumberOutput, GetStorageProofInput, L1TransactionHashInput, SyncingOutput, +}; use super::{DevnetResponse, JsonRpcHandler, JsonRpcResponse, StarknetResponse, RPC_SPEC_VERSION}; use crate::api::http::endpoints::accounts::{ get_account_balance_impl, get_predeployed_accounts_impl, BalanceQuery, PredeployedAccountsQuery, @@ -136,6 +138,15 @@ impl JsonRpcHandler { Ok(StarknetResponse::Felt(felt).into()) } + /// starknet_getStorageProof + pub async fn get_storage_proof(&self, data: GetStorageProofInput) -> StrictRpcResult { + match self.api.starknet.lock().await.get_block(data.block_id.as_ref()) { + Ok(_) => Err(ApiError::StorageProofNotSupported), + Err(Error::NoBlock) => Err(ApiError::BlockNotFound), + Err(unknown_error) => Err(ApiError::StarknetDevnetError(unknown_error)), + } + } + /// starknet_getTransactionByHash pub async fn get_transaction_by_hash( &self, diff --git a/crates/starknet-devnet-server/src/api/json_rpc/error.rs b/crates/starknet-devnet-server/src/api/json_rpc/error.rs index ffe9da12f..7eccdc38a 100644 --- a/crates/starknet-devnet-server/src/api/json_rpc/error.rs +++ b/crates/starknet-devnet-server/src/api/json_rpc/error.rs @@ -68,6 +68,8 @@ pub enum ApiError { CallOnPending, #[error("Invalid subscription id")] InvalidSubscriptionId, + #[error("Devnet doesn't support storage proofs")] + StorageProofNotSupported, } impl ApiError { @@ -226,6 +228,11 @@ impl ApiError { message: error_message.into(), data: None, }, + ApiError::StorageProofNotSupported => RpcError { + code: crate::rpc_core::error::ErrorCode::ServerError(42), + message: error_message.into(), + data: None, + }, } } @@ -260,6 +267,7 @@ impl ApiError { | Self::TooManyBlocksBack | Self::InvalidSubscriptionId | Self::InsufficientResourcesForValidate + | Self::StorageProofNotSupported | Self::CompiledClassHashMismatch => false, } } diff --git a/crates/starknet-devnet-server/src/api/json_rpc/mod.rs b/crates/starknet-devnet-server/src/api/json_rpc/mod.rs index 6844e8607..fb9d70b2e 100644 --- a/crates/starknet-devnet-server/src/api/json_rpc/mod.rs +++ b/crates/starknet-devnet-server/src/api/json_rpc/mod.rs @@ -17,7 +17,7 @@ use futures::stream::SplitSink; use futures::{SinkExt, StreamExt}; use models::{ BlockAndClassHashInput, BlockAndContractAddressInput, BlockAndIndexInput, CallInput, - EstimateFeeInput, EventsInput, EventsSubscriptionInput, GetStorageInput, + EstimateFeeInput, EventsInput, EventsSubscriptionInput, GetStorageInput, GetStorageProofInput, L1TransactionHashInput, PendingTransactionsSubscriptionInput, SubscriptionIdInput, TransactionBlockInput, TransactionHashInput, TransactionHashOutput, }; @@ -569,6 +569,7 @@ impl JsonRpcHandler { JsonRpcRequest::Mint(data) => self.mint(data).await, JsonRpcRequest::DevnetConfig => self.get_devnet_config().await, JsonRpcRequest::MessagesStatusByL1Hash(data) => self.get_messages_status(data).await, + JsonRpcRequest::StorageProof(data) => self.get_storage_proof(data).await, } } @@ -667,6 +668,8 @@ pub enum JsonRpcRequest { StateUpdate(BlockIdInput), #[serde(rename = "starknet_getStorageAt")] StorageAt(GetStorageInput), + #[serde(rename = "starknet_getStorageProof")] + StorageProof(GetStorageProofInput), #[serde(rename = "starknet_getTransactionByHash")] TransactionByHash(TransactionHashInput), #[serde(rename = "starknet_getTransactionByBlockIdAndIndex")] @@ -813,6 +816,7 @@ impl JsonRpcRequest { | Self::Restart(_) | Self::PredeployedAccounts(_) | Self::AccountBalance(_) + | Self::StorageProof(_) | Self::DevnetConfig => false, } } @@ -845,6 +849,7 @@ impl JsonRpcRequest { | Self::TraceTransaction(_) | Self::MessagesStatusByL1Hash(_) | Self::CompiledCasmByClassHash(_) + | Self::StorageProof(_) | Self::BlockTransactionTraces(_) => true, Self::SpecVersion | Self::ChainId @@ -929,6 +934,7 @@ impl JsonRpcRequest { | Self::AccountBalance(_) | Self::MessagesStatusByL1Hash(_) | Self::CompiledCasmByClassHash(_) + | Self::StorageProof(_) | Self::DevnetConfig => false, } } @@ -1031,6 +1037,7 @@ pub enum StarknetResponse { TraceTransaction(TransactionTrace), BlockTransactionTraces(Vec), MessagesStatusByL1Hash(Vec), + StorageProofs(serde_json::Value), // dummy, the corresponding RPC method always errors } #[derive(Serialize)] diff --git a/crates/starknet-devnet-server/src/api/json_rpc/models.rs b/crates/starknet-devnet-server/src/api/json_rpc/models.rs index 41a8ce526..bc361f036 100644 --- a/crates/starknet-devnet-server/src/api/json_rpc/models.rs +++ b/crates/starknet-devnet-server/src/api/json_rpc/models.rs @@ -13,6 +13,7 @@ use starknet_types::rpc::transactions::{ }; use starknet_types::starknet_api::block::BlockNumber; +// TODO remove Serialize here and in other places #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct BlockIdInput { @@ -25,7 +26,7 @@ pub struct TransactionHashInput { pub transaction_hash: TransactionHash, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] #[cfg_attr(test, derive(PartialEq, Eq))] pub struct GetStorageInput { @@ -34,6 +35,21 @@ pub struct GetStorageInput { pub block_id: BlockId, } +#[derive(Deserialize, Clone, Debug)] +pub struct ContractStorage { + pub contract_address: ContractAddress, + pub storage_keys: Vec, +} + +#[derive(Deserialize, Clone, Debug)] +#[serde(deny_unknown_fields)] +pub struct GetStorageProofInput { + pub block_id: BlockId, + pub class_hashes: Option>, + pub contract_addresses: Option>, + pub contract_storage_keys: Option, +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct BlockAndIndexInput { diff --git a/crates/starknet-devnet/tests/general_rpc_tests.rs b/crates/starknet-devnet/tests/general_rpc_tests.rs index 0001f3344..ea811addf 100644 --- a/crates/starknet-devnet/tests/general_rpc_tests.rs +++ b/crates/starknet-devnet/tests/general_rpc_tests.rs @@ -5,6 +5,8 @@ pub mod common; mod general_rpc_tests { use serde_json::json; use server::api::json_rpc::RPC_SPEC_VERSION; + use server::rpc_core::error::RpcError; + use starknet_rs_core::types::BlockId; use crate::common::background_devnet::BackgroundDevnet; use crate::common::constants::RPC_PATH; @@ -46,15 +48,34 @@ mod general_rpc_tests { async fn rpc_returns_invalid_params() { let devnet = BackgroundDevnet::spawn().await.unwrap(); let rpc_error = devnet - .send_custom_rpc( - "starknet_specVersion", - json!({ - "invalid_param": "unneeded_value", - }), - ) + .send_custom_rpc("starknet_specVersion", json!({ "invalid_param": "unneeded_value" })) .await .unwrap_err(); assert_eq!(rpc_error.code, server::rpc_core::error::ErrorCode::InvalidParams); } + + #[tokio::test] + async fn storage_proof_request_should_always_return_error() { + let devnet = BackgroundDevnet::spawn().await.unwrap(); + devnet.create_block().await.unwrap(); + + for (req_params, expected_code, expected_msg) in [ + (json!({}), -32602, "missing field `block_id`"), + ( + json!({ "block_id": BlockId::Number(0) }), + 42, + "Devnet doesn't support storage proofs", + ), + (json!({ "block_id": "latest" }), 42, "Devnet doesn't support storage proofs"), + (json!({ "block_id": BlockId::Number(5) }), 24, "Block not found"), + ] { + let error = + devnet.send_custom_rpc("starknet_getStorageProof", req_params).await.unwrap_err(); + assert_eq!( + error, + RpcError { code: expected_code.into(), message: expected_msg.into(), data: None } + ); + } + } } From 7bba64303349d94532f2771e7682449f12de21de Mon Sep 17 00:00:00 2001 From: FabijanC Date: Fri, 6 Dec 2024 16:50:33 +0100 Subject: [PATCH 2/3] Remove unused Serialize derivations on RPC input structs --- .../src/api/json_rpc/endpoints.rs | 1 + .../src/api/json_rpc/error.rs | 2 +- .../src/api/json_rpc/models.rs | 17 ++++++++--------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs b/crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs index 7846185ef..c240d4eba 100644 --- a/crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs +++ b/crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs @@ -141,6 +141,7 @@ impl JsonRpcHandler { /// starknet_getStorageProof pub async fn get_storage_proof(&self, data: GetStorageProofInput) -> StrictRpcResult { match self.api.starknet.lock().await.get_block(data.block_id.as_ref()) { + // storage proofs not applicable to Devnet Ok(_) => Err(ApiError::StorageProofNotSupported), Err(Error::NoBlock) => Err(ApiError::BlockNotFound), Err(unknown_error) => Err(ApiError::StarknetDevnetError(unknown_error)), diff --git a/crates/starknet-devnet-server/src/api/json_rpc/error.rs b/crates/starknet-devnet-server/src/api/json_rpc/error.rs index 7eccdc38a..4959abfac 100644 --- a/crates/starknet-devnet-server/src/api/json_rpc/error.rs +++ b/crates/starknet-devnet-server/src/api/json_rpc/error.rs @@ -68,7 +68,7 @@ pub enum ApiError { CallOnPending, #[error("Invalid subscription id")] InvalidSubscriptionId, - #[error("Devnet doesn't support storage proofs")] + #[error("Devnet doesn't support storage proofs")] // slightly modified spec message StorageProofNotSupported, } diff --git a/crates/starknet-devnet-server/src/api/json_rpc/models.rs b/crates/starknet-devnet-server/src/api/json_rpc/models.rs index bc361f036..a0206cd1b 100644 --- a/crates/starknet-devnet-server/src/api/json_rpc/models.rs +++ b/crates/starknet-devnet-server/src/api/json_rpc/models.rs @@ -13,14 +13,13 @@ use starknet_types::rpc::transactions::{ }; use starknet_types::starknet_api::block::BlockNumber; -// TODO remove Serialize here and in other places -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct BlockIdInput { pub block_id: BlockId, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct TransactionHashInput { pub transaction_hash: TransactionHash, @@ -50,34 +49,34 @@ pub struct GetStorageProofInput { pub contract_storage_keys: Option, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct BlockAndIndexInput { pub block_id: BlockId, pub index: u64, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct BlockAndClassHashInput { pub block_id: BlockId, pub class_hash: ClassHash, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct BlockAndContractAddressInput { pub block_id: BlockId, pub contract_address: ContractAddress, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct AccountAddressInput { pub account_address: ContractAddress, } -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize)] #[cfg_attr(test, derive(PartialEq, Eq))] #[serde(deny_unknown_fields)] pub struct CallInput { @@ -108,7 +107,7 @@ pub enum SyncingOutput { False(bool), } -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize)] pub struct EventsInput { pub filter: EventFilter, } From 44da25f042e9062c61e3220c3c0706bb25c1f22f Mon Sep 17 00:00:00 2001 From: FabijanC Date: Fri, 6 Dec 2024 17:01:50 +0100 Subject: [PATCH 3/3] docs: Add storage proof disclaimer [skip ci] --- website/docs/intro.md | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/intro.md b/website/docs/intro.md index 9cb33f012..cbab18156 100644 --- a/website/docs/intro.md +++ b/website/docs/intro.md @@ -7,6 +7,7 @@ sidebar_position: 1 :::danger Difference disclaimer - Devnet should not be used as a replacement for official testnets. After testing on Devnet, be sure to test on a testnet (alpha-sepolia)! +- Devnet does not organize state data into Merkle-Patricia trees, so calling the `starknet_getStorageProof` RPC method shall always result in `STORAGE_PROOF_NOT_SUPPORTED`. - The semantics of `REJECTED` and `REVERTED` status of a transaction is not the same as on the official testnet: | Tx status | Official testnet | Devnet |