diff --git a/starknet-providers/src/any.rs b/starknet-providers/src/any.rs index beff9c47..5a10711f 100644 --- a/starknet-providers/src/any.rs +++ b/starknet-providers/src/any.rs @@ -26,7 +26,9 @@ use crate::{ /// it's still needed anymore. #[derive(Debug)] pub enum AnyProvider { + /// JSON-RPC provider. JsonRpcHttp(JsonRpcClient), + /// Sequencer gateway provider. SequencerGateway(SequencerGatewayProvider), } diff --git a/starknet-providers/src/jsonrpc/mod.rs b/starknet-providers/src/jsonrpc/mod.rs index be9b9571..2d6013ed 100644 --- a/starknet-providers/src/jsonrpc/mod.rs +++ b/starknet-providers/src/jsonrpc/mod.rs @@ -24,144 +24,242 @@ use crate::{provider::ProviderImplError, Provider, ProviderError}; mod transports; pub use transports::{HttpTransport, HttpTransportError, JsonRpcTransport}; +/// A generic JSON-RPC client with any transport. +/// +/// A "transport" is any implementation that can send JSON-RPC requests and receive responses. This +/// most commonly happens over a network via HTTP connections, as with [`HttpTransport`]. #[derive(Debug)] pub struct JsonRpcClient { transport: T, } +/// All JSON-RPC methods as listed by the official specification. #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum JsonRpcMethod { + /// The `starknet_specVersion` method. #[serde(rename = "starknet_specVersion")] SpecVersion, + /// The `starknet_getBlockWithTxHashes` method. #[serde(rename = "starknet_getBlockWithTxHashes")] GetBlockWithTxHashes, + /// The `starknet_getBlockWithTxs` method. #[serde(rename = "starknet_getBlockWithTxs")] GetBlockWithTxs, + /// The `starknet_getBlockWithReceipts` method. #[serde(rename = "starknet_getBlockWithReceipts")] GetBlockWithReceipts, + /// The `starknet_getStateUpdate` method. #[serde(rename = "starknet_getStateUpdate")] GetStateUpdate, + /// The `starknet_getStorageAt` method. #[serde(rename = "starknet_getStorageAt")] GetStorageAt, + /// The `starknet_getTransactionStatus` method. #[serde(rename = "starknet_getTransactionStatus")] GetTransactionStatus, + /// The `starknet_getTransactionByHash` method. #[serde(rename = "starknet_getTransactionByHash")] GetTransactionByHash, + /// The `starknet_getTransactionByBlockIdAndIndex` method. #[serde(rename = "starknet_getTransactionByBlockIdAndIndex")] GetTransactionByBlockIdAndIndex, + /// The `starknet_getTransactionReceipt` method. #[serde(rename = "starknet_getTransactionReceipt")] GetTransactionReceipt, + /// The `starknet_getClass` method. #[serde(rename = "starknet_getClass")] GetClass, + /// The `starknet_getClassHashAt` method. #[serde(rename = "starknet_getClassHashAt")] GetClassHashAt, + /// The `starknet_getClassAt` method. #[serde(rename = "starknet_getClassAt")] GetClassAt, + /// The `starknet_getBlockTransactionCount` method. #[serde(rename = "starknet_getBlockTransactionCount")] GetBlockTransactionCount, + /// The `starknet_call` method. #[serde(rename = "starknet_call")] Call, + /// The `starknet_estimateFee` method. #[serde(rename = "starknet_estimateFee")] EstimateFee, + /// The `starknet_estimateMessageFee` method. #[serde(rename = "starknet_estimateMessageFee")] EstimateMessageFee, + /// The `starknet_blockNumber` method. #[serde(rename = "starknet_blockNumber")] BlockNumber, + /// The `starknet_blockHashAndNumber` method. #[serde(rename = "starknet_blockHashAndNumber")] BlockHashAndNumber, + /// The `starknet_chainId` method. #[serde(rename = "starknet_chainId")] ChainId, + /// The `starknet_syncing` method. #[serde(rename = "starknet_syncing")] Syncing, + /// The `starknet_getEvents` method. #[serde(rename = "starknet_getEvents")] GetEvents, + /// The `starknet_getNonce` method. #[serde(rename = "starknet_getNonce")] GetNonce, + /// The `starknet_addInvokeTransaction` method. #[serde(rename = "starknet_addInvokeTransaction")] AddInvokeTransaction, + /// The `starknet_addDeclareTransaction` method. #[serde(rename = "starknet_addDeclareTransaction")] AddDeclareTransaction, + /// The `starknet_addDeployAccountTransaction` method. #[serde(rename = "starknet_addDeployAccountTransaction")] AddDeployAccountTransaction, + /// The `starknet_traceTransaction` method. #[serde(rename = "starknet_traceTransaction")] TraceTransaction, + /// The `starknet_simulateTransactions` method. #[serde(rename = "starknet_simulateTransactions")] SimulateTransactions, + /// The `starknet_traceBlockTransactions` method. #[serde(rename = "starknet_traceBlockTransactions")] TraceBlockTransactions, } +/// JSON-RPC request. #[derive(Debug, Clone)] pub struct JsonRpcRequest { + /// ID of the request. Useful for identifying responses in certain transports like `WebSocket`. pub id: u64, + /// Data of the requeest. pub data: JsonRpcRequestData, } +/// Typed request data for Starknet JSON-RPC requests. #[derive(Debug, Clone)] pub enum JsonRpcRequestData { + /// Request data for `starknet_specVersion`. SpecVersion(SpecVersionRequest), + /// Request data for `starknet_getBlockWithTxHashes`. GetBlockWithTxHashes(GetBlockWithTxHashesRequest), + /// Request data for `starknet_getBlockWithTxs`. GetBlockWithTxs(GetBlockWithTxsRequest), + /// Request data for `starknet_getBlockWithReceipts`. GetBlockWithReceipts(GetBlockWithReceiptsRequest), + /// Request data for `starknet_getStateUpdate`. GetStateUpdate(GetStateUpdateRequest), + /// Request data for `starknet_getStorageAt`. GetStorageAt(GetStorageAtRequest), + /// Request data for `starknet_getTransactionStatus`. GetTransactionStatus(GetTransactionStatusRequest), + /// Request data for `starknet_getTransactionByHash`. GetTransactionByHash(GetTransactionByHashRequest), + /// Request data for `starknet_getTransactionByBlockIdAndIndex`. GetTransactionByBlockIdAndIndex(GetTransactionByBlockIdAndIndexRequest), + /// Request data for `starknet_getTransactionReceipt`. GetTransactionReceipt(GetTransactionReceiptRequest), + /// Request data for `starknet_getClass`. GetClass(GetClassRequest), + /// Request data for `starknet_getClassHashAt`. GetClassHashAt(GetClassHashAtRequest), + /// Request data for `starknet_getClassAt`. GetClassAt(GetClassAtRequest), + /// Request data for `starknet_getBlockTransactionCount`. GetBlockTransactionCount(GetBlockTransactionCountRequest), + /// Request data for `starknet_call`. Call(CallRequest), + /// Request data for `starknet_estimateFee`. EstimateFee(EstimateFeeRequest), + /// Request data for `starknet_estimateMessageFee`. EstimateMessageFee(EstimateMessageFeeRequest), + /// Request data for `starknet_blockNumber`. BlockNumber(BlockNumberRequest), + /// Request data for `starknet_blockHashAndNumber`. BlockHashAndNumber(BlockHashAndNumberRequest), + /// Request data for `starknet_chainId`. ChainId(ChainIdRequest), + /// Request data for `starknet_syncing`. Syncing(SyncingRequest), + /// Request data for `starknet_getEvents`. GetEvents(GetEventsRequest), + /// Request data for `starknet_getNonce`. GetNonce(GetNonceRequest), + /// Request data for `starknet_addInvokeTransaction`. AddInvokeTransaction(AddInvokeTransactionRequest), + /// Request data for `starknet_addDeclareTransaction`. AddDeclareTransaction(AddDeclareTransactionRequest), + /// Request data for `starknet_addDeployAccountTransaction`. AddDeployAccountTransaction(AddDeployAccountTransactionRequest), + /// Request data for `starknet_traceTransaction`. TraceTransaction(TraceTransactionRequest), + /// Request data for `starknet_simulateTransactions`. SimulateTransactions(SimulateTransactionsRequest), + /// Request data for `starknet_traceBlockTransactions`. TraceBlockTransactions(TraceBlockTransactionsRequest), } +/// Errors from JSON-RPC client. #[derive(Debug, thiserror::Error)] pub enum JsonRpcClientError { + /// JSON serialization/deserialization erors. #[error(transparent)] JsonError(serde_json::Error), + /// Transport-specific errors. #[error(transparent)] TransportError(T), + /// An unsuccessful response returned from the server is encountered. #[error(transparent)] JsonRpcError(JsonRpcError), } +/// An unsuccessful response returned from the server. #[derive(Debug, Deserialize)] pub struct JsonRpcError { + /// Error code. pub code: i64, + /// Error message. pub message: String, + /// Additional error data if any. #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, } +/// JSON-RPC response returned from a server. #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum JsonRpcResponse { - Success { id: u64, result: T }, - Error { id: u64, error: JsonRpcError }, + /// Successful response. + Success { + /// Same ID as the corresponding request. + id: u64, + /// Response data. + result: T, + }, + /// Unsuccessful response. + Error { + /// Same ID as the corresponding request. + id: u64, + /// Error details. + error: JsonRpcError, + }, } /// Failures trying to parse a [`JsonRpcError`] into [`StarknetError`]. +/// +/// [`StarknetError`] is the standard, provider-agnostic error type that all [`Provider`] +/// implementations should strive to return in an error case, in a best-effort basis. This allows +/// for unified error handling logic. +/// +/// However, not all error cases can be properly converted, and this error type represents the cases +/// when such failure happens. #[derive(Debug, thiserror::Error)] pub enum JsonRpcErrorConversionError { + /// The error code is outside of the range specified by the specification. #[error("unknown error code")] UnknownCode, + /// Error data is expected but missing. #[error("missing data field")] MissingData, + /// Error data is malformed. #[error("unable to parse the data field")] DataParsingFailure, } @@ -175,6 +273,7 @@ struct Felt(#[serde_as(as = "UfeHex")] pub FeltPrimitive); struct FeltArray(#[serde_as(as = "Vec")] pub Vec); impl JsonRpcClient { + /// Constructs a new [`JsonRpcClient`] from a transport. pub const fn new(transport: T) -> Self { Self { transport } } diff --git a/starknet-providers/src/jsonrpc/transports/http.rs b/starknet-providers/src/jsonrpc/transports/http.rs index d8cd0576..4663b859 100644 --- a/starknet-providers/src/jsonrpc/transports/http.rs +++ b/starknet-providers/src/jsonrpc/transports/http.rs @@ -5,6 +5,7 @@ use serde::{de::DeserializeOwned, Serialize}; use crate::jsonrpc::{transports::JsonRpcTransport, JsonRpcMethod, JsonRpcResponse}; +/// A [`JsonRpcTransport`] implementation that uses HTTP connections. #[derive(Debug)] pub struct HttpTransport { client: Client, @@ -12,10 +13,13 @@ pub struct HttpTransport { headers: Vec<(String, String)>, } +/// Errors using [`HttpTransport`]. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub enum HttpTransportError { + /// HTTP-related errors. Reqwest(reqwest::Error), + /// JSON serialization/deserialization errors. Json(serde_json::Error), } @@ -28,10 +32,15 @@ struct JsonRpcRequest { } impl HttpTransport { + /// Constructs [`HttpTransport`] from a JSON-RPC server URL, using default HTTP client settings. + /// + /// To use custom HTTP settings (e.g. proxy, timeout), use + /// [`new_with_client`](fn.new_with_client) instead. pub fn new(url: impl Into) -> Self { Self::new_with_client(url, Client::new()) } + /// Constructs [`HttpTransport`] from a JSON-RPC server URL and a custom `reqwest` client. pub fn new_with_client(url: impl Into, client: Client) -> Self { Self { client, diff --git a/starknet-providers/src/jsonrpc/transports/mod.rs b/starknet-providers/src/jsonrpc/transports/mod.rs index 7b119f74..c7602c98 100644 --- a/starknet-providers/src/jsonrpc/transports/mod.rs +++ b/starknet-providers/src/jsonrpc/transports/mod.rs @@ -8,12 +8,16 @@ use crate::jsonrpc::{JsonRpcMethod, JsonRpcResponse}; mod http; pub use http::{HttpTransport, HttpTransportError}; +/// Any type that is capable of producing JSON-RPC responses when given JSON-RPC requests. An +/// implementation does not necessarily use the network, but typically does. #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[auto_impl(&, Box, Arc)] pub trait JsonRpcTransport { + /// Possible errors processing requests. type Error: Error + Send + Sync; + /// Sends a JSON-RPC request to retrieve a response. async fn send_request( &self, method: JsonRpcMethod, diff --git a/starknet-providers/src/lib.rs b/starknet-providers/src/lib.rs index 31dde2b2..0954c3d2 100644 --- a/starknet-providers/src/lib.rs +++ b/starknet-providers/src/lib.rs @@ -1,13 +1,23 @@ -#![doc = include_str!("../README.md")] +//! Clients for interacting with Starknet nodes and sequencers. +//! +//! This crate provides the [`Provider`] trait for abstraction over means of accessing the Starknet +//! network. The most commonly used implementation is [`JsonRpcClient`] with +//! [`HttpTransport`](jsonrpc::HttpTransport). + +#![deny(missing_docs)] mod provider; pub use provider::{Provider, ProviderError}; +// Sequencer-related functionalities are mostly deprecated so we skip the docs. +/// Module containing types related to the (now deprecated) sequencer gateway client. +#[allow(missing_docs)] pub mod sequencer; pub use sequencer::{ GatewayClientError as SequencerGatewayProviderError, SequencerGatewayProvider, }; +/// Module containing types related to JSON-RPC clients and servers. pub mod jsonrpc; pub use jsonrpc::JsonRpcClient; diff --git a/starknet-providers/src/provider.rs b/starknet-providers/src/provider.rs index 9259c855..94d853c3 100644 --- a/starknet-providers/src/provider.rs +++ b/starknet-providers/src/provider.rs @@ -12,14 +12,24 @@ use starknet_core::types::{ }; use std::{any::Any, error::Error, fmt::Debug}; +/// A generic interface for any type allowing communication with a Starknet network. +/// +/// Historically, the only official way to access the network is through the sequencer gateway, +/// implemented by [`SequencerGatewayProvider`](crate::sequencer::SequencerGatewayProvider), which +/// has since been deprecated. Currently, the recommended way of accessing the network is via the +/// JSON-RPC specification, implemented with [`JsonRpcClient`](crate::jsonrpc::JsonRpcClient). +/// +/// The legacy [`SequencerGatewayProvider`](crate::sequencer::SequencerGatewayProvider) still +/// implements this trait for backward compatibility reasons, but most of its methods no longer work +/// in practice, as public sequencer servers have generally block access to most methods. #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[auto_impl(&, Box, Arc)] pub trait Provider { - /// Returns the version of the Starknet JSON-RPC specification being used + /// Returns the version of the Starknet JSON-RPC specification being used. async fn spec_version(&self) -> Result; - /// Get block information with transaction hashes given the block id + /// Gets block information with transaction hashes given the block id. async fn get_block_with_tx_hashes( &self, block_id: B, @@ -27,7 +37,7 @@ pub trait Provider { where B: AsRef + Send + Sync; - /// Get block information with full transactions given the block id + /// Gets block information with full transactions given the block id. async fn get_block_with_txs( &self, block_id: B, @@ -35,7 +45,7 @@ pub trait Provider { where B: AsRef + Send + Sync; - /// Get block information with full transactions and receipts given the block id + /// Gets block information with full transactions and receipts given the block id. async fn get_block_with_receipts( &self, block_id: B, @@ -43,7 +53,7 @@ pub trait Provider { where B: AsRef + Send + Sync; - /// Get the information about the result of executing the requested block + /// Gets the information about the result of executing the requested block. async fn get_state_update( &self, block_id: B, @@ -51,7 +61,7 @@ pub trait Provider { where B: AsRef + Send + Sync; - /// Get the value of the storage at the given address and key + /// Gets the value of the storage at the given address and key. async fn get_storage_at( &self, contract_address: A, @@ -63,8 +73,8 @@ pub trait Provider { K: AsRef + Send + Sync, B: AsRef + Send + Sync; - /// Gets the transaction status (possibly reflecting that the tx is still in - /// the mempool, or dropped from it) + /// Gets the transaction status (possibly reflecting that the tx is still in the mempool, or + /// dropped from it). async fn get_transaction_status( &self, transaction_hash: H, @@ -72,7 +82,7 @@ pub trait Provider { where H: AsRef + Send + Sync; - /// Get the details and status of a submitted transaction + /// Gets the details and status of a submitted transaction. async fn get_transaction_by_hash( &self, transaction_hash: H, @@ -80,7 +90,7 @@ pub trait Provider { where H: AsRef + Send + Sync; - /// Get the details of a transaction by a given block id and index + /// Gets the details of a transaction by a given block id and index. async fn get_transaction_by_block_id_and_index( &self, block_id: B, @@ -89,7 +99,7 @@ pub trait Provider { where B: AsRef + Send + Sync; - /// Get the details of a transaction by a given block number and index + /// Gets the details of a transaction by a given block number and index. async fn get_transaction_receipt( &self, transaction_hash: H, @@ -97,7 +107,7 @@ pub trait Provider { where H: AsRef + Send + Sync; - /// Get the contract class definition in the given block associated with the given hash + /// Gets the contract class definition in the given block associated with the given hash. async fn get_class( &self, block_id: B, @@ -107,7 +117,8 @@ pub trait Provider { B: AsRef + Send + Sync, H: AsRef + Send + Sync; - /// Get the contract class hash in the given block for the contract deployed at the given address + /// Gets the contract class hash in the given block for the contract deployed at the given + /// address. async fn get_class_hash_at( &self, block_id: B, @@ -117,7 +128,7 @@ pub trait Provider { B: AsRef + Send + Sync, A: AsRef + Send + Sync; - /// Get the contract class definition in the given block at the given address + /// Gets the contract class definition in the given block at the given address. async fn get_class_at( &self, block_id: B, @@ -127,18 +138,18 @@ pub trait Provider { B: AsRef + Send + Sync, A: AsRef + Send + Sync; - /// Get the number of transactions in a block given a block id + /// Gets the number of transactions in a block given a block id. async fn get_block_transaction_count(&self, block_id: B) -> Result where B: AsRef + Send + Sync; - /// Call a starknet function without creating a Starknet transaction + /// Calls a starknet function without creating a Starknet transaction. async fn call(&self, request: R, block_id: B) -> Result, ProviderError> where R: AsRef + Send + Sync, B: AsRef + Send + Sync; - /// Estimate the fee for a given Starknet transaction + /// Estimates the fee for a given Starknet transaction. async fn estimate_fee( &self, request: R, @@ -150,6 +161,7 @@ pub trait Provider { S: AsRef<[SimulationFlagForEstimateFee]> + Send + Sync, B: AsRef + Send + Sync; + /// Estimates the fee for sending an L1-to-L2 message. async fn estimate_message_fee( &self, message: M, @@ -159,19 +171,19 @@ pub trait Provider { M: AsRef + Send + Sync, B: AsRef + Send + Sync; - /// Get the most recent accepted block number + /// Gets the most recent accepted block number. async fn block_number(&self) -> Result; - /// Get the most recent accepted block hash and number + /// Gets the most recent accepted block hash and number. async fn block_hash_and_number(&self) -> Result; - /// Return the currently configured Starknet chain id + /// Returns the currently configured Starknet chain id. async fn chain_id(&self) -> Result; - /// Returns an object about the sync status, or false if the node is not synching + /// Returns an object about the sync status, or false if the node is not synching. async fn syncing(&self) -> Result; - /// Returns all events matching the given filter + /// Returns all events matching the given filter. async fn get_events( &self, filter: EventFilter, @@ -179,7 +191,7 @@ pub trait Provider { chunk_size: u64, ) -> Result; - /// Get the nonce associated with the given address in the given block + /// Gets the nonce associated with the given address in the given block. async fn get_nonce( &self, block_id: B, @@ -189,7 +201,7 @@ pub trait Provider { B: AsRef + Send + Sync, A: AsRef + Send + Sync; - /// Submit a new transaction to be added to the chain + /// Submits a new transaction to be added to the chain. async fn add_invoke_transaction( &self, invoke_transaction: I, @@ -197,7 +209,7 @@ pub trait Provider { where I: AsRef + Send + Sync; - /// Submit a new transaction to be added to the chain + /// Submits a new transaction to be added to the chain. async fn add_declare_transaction( &self, declare_transaction: D, @@ -205,7 +217,7 @@ pub trait Provider { where D: AsRef + Send + Sync; - /// Submit a new deploy account transaction + /// Submits a new deploy account transaction. async fn add_deploy_account_transaction( &self, deploy_account_transaction: D, @@ -213,8 +225,8 @@ pub trait Provider { where D: AsRef + Send + Sync; - /// For a given executed transaction, return the trace of its execution, including internal - /// calls + /// For a given executed transaction, returns the trace of its execution, including internal + /// calls. async fn trace_transaction( &self, transaction_hash: H, @@ -222,12 +234,13 @@ pub trait Provider { where H: AsRef + Send + Sync; - /// Simulate a given sequence of transactions on the requested state, and generate the execution - /// traces. Note that some of the transactions may revert, in which case no error is thrown, but - /// revert details can be seen on the returned trace object. . Note that some of the - /// transactions may revert, this will be reflected by the revert_error property in the trace. - /// Other types of failures (e.g. unexpected error or failure in the validation phase) will - /// result in TRANSACTION_EXECUTION_ERROR. + /// Simulates a given sequence of transactions on the requested state, and generate the + /// execution traces. Note that some of the transactions may revert, in which case no error is + /// thrown, but revert details can be seen on the returned trace object. + /// + /// Note that some of the transactions may revert, this will be reflected by the `revert_error` + /// property in the trace. Other types of failures (e.g. unexpected error or failure in the + /// validation phase) will result in `TRANSACTION_EXECUTION_ERROR`. async fn simulate_transactions( &self, block_id: B, @@ -239,7 +252,7 @@ pub trait Provider { T: AsRef<[BroadcastedTransaction]> + Send + Sync, S: AsRef<[SimulationFlag]> + Send + Sync; - /// Retrieve traces for all transactions in the given block. + /// Retrieves traces for all transactions in the given block. async fn trace_block_transactions( &self, block_id: B, @@ -311,14 +324,29 @@ pub trait ProviderImplError: Error + Debug + Send + Sync { fn as_any(&self) -> &dyn Any; } +/// Errors using any [`Provider`] implementation. This type is deliberately not made generic such +/// that: +/// +/// - the [`Provider`] trait itself can be boxed; +/// - error handling is easier. +/// +/// As a downside, the [`Other`](ProviderError::Other) variant contains a boxed implementation- +/// specific error. It's generally expected that users of [`Provider`] would not need to care about +/// these errors, but in the case where they do, it's slightly harder to access than if generics are +/// used instead. #[derive(Debug, thiserror::Error)] pub enum ProviderError { + /// A Starknet-related error, usually regarding the state or transaction. #[error(transparent)] StarknetError(StarknetError), + /// The request fails as the client is rate-limited. #[error("Request rate limited")] RateLimited, + /// When estimating fees for or simulating a single transaction, the server unexpectedly returns + /// data for zero or more than one transactions. #[error("Array length mismatch")] ArrayLengthMismatch, + /// Boxed implementation-specific errors. #[error("{0}")] Other(Box), }