From 61c905bbb9eafcd3493c3b77dbe4742438f43eb8 Mon Sep 17 00:00:00 2001 From: clint Date: Wed, 27 Sep 2023 17:12:28 +0800 Subject: [PATCH] feat: Add read and write jsonrpc transport --- examples/rw_jsonrpc.rs | 21 +++ starknet-providers/src/jsonrpc/mod.rs | 6 +- .../src/jsonrpc/transports/http.rs | 2 +- .../src/jsonrpc/transports/mod.rs | 5 +- .../src/jsonrpc/transports/rw.rs | 124 ++++++++++++++++++ 5 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 examples/rw_jsonrpc.rs create mode 100644 starknet-providers/src/jsonrpc/transports/rw.rs diff --git a/examples/rw_jsonrpc.rs b/examples/rw_jsonrpc.rs new file mode 100644 index 00000000..17b1a4a2 --- /dev/null +++ b/examples/rw_jsonrpc.rs @@ -0,0 +1,21 @@ +use starknet_providers::{ + jsonrpc::{HttpTransport, JsonRpcClient, RwTransport}, + Provider, +}; +use url::Url; + +#[tokio::main] +async fn main() { + let read = HttpTransport::new( + Url::parse("https://starknet-goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161") + .unwrap(), + ); + let write = HttpTransport::new( + Url::parse("https://starknet-goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161") + .unwrap(), + ); + let rw = RwTransport::new(read, write); + let rpc_client = JsonRpcClient::new(rw); + let block_number = rpc_client.block_number().await.unwrap(); + dbg!(block_number); +} diff --git a/starknet-providers/src/jsonrpc/mod.rs b/starknet-providers/src/jsonrpc/mod.rs index cc547257..7a0fc0a3 100644 --- a/starknet-providers/src/jsonrpc/mod.rs +++ b/starknet-providers/src/jsonrpc/mod.rs @@ -20,14 +20,16 @@ use crate::{ }; mod transports; -pub use transports::{HttpTransport, HttpTransportError, JsonRpcTransport}; +pub use transports::{ + HttpTransport, HttpTransportError, JsonRpcTransport, RwTransport, RwTransportError, +}; #[derive(Debug)] pub struct JsonRpcClient { transport: T, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum JsonRpcMethod { #[serde(rename = "starknet_getBlockWithTxHashes")] GetBlockWithTxHashes, diff --git a/starknet-providers/src/jsonrpc/transports/http.rs b/starknet-providers/src/jsonrpc/transports/http.rs index 388e7c0d..752aa6de 100644 --- a/starknet-providers/src/jsonrpc/transports/http.rs +++ b/starknet-providers/src/jsonrpc/transports/http.rs @@ -51,7 +51,7 @@ impl JsonRpcTransport for HttpTransport { ) -> Result, Self::Error> where P: Serialize + Send + Sync, - R: DeserializeOwned + Send + Sync, + R: DeserializeOwned + Send, { let request_body = JsonRpcRequest { id: 1, diff --git a/starknet-providers/src/jsonrpc/transports/mod.rs b/starknet-providers/src/jsonrpc/transports/mod.rs index 2299c3ca..953ff38d 100644 --- a/starknet-providers/src/jsonrpc/transports/mod.rs +++ b/starknet-providers/src/jsonrpc/transports/mod.rs @@ -6,7 +6,10 @@ use std::error::Error; use crate::jsonrpc::{JsonRpcMethod, JsonRpcResponse}; mod http; +mod rw; + pub use http::{HttpTransport, HttpTransportError}; +pub use rw::{RwTransport, RwTransportError}; #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -21,5 +24,5 @@ pub trait JsonRpcTransport { ) -> Result, Self::Error> where P: Serialize + Send + Sync, - R: DeserializeOwned + Send + Sync; + R: DeserializeOwned + Send; } diff --git a/starknet-providers/src/jsonrpc/transports/rw.rs b/starknet-providers/src/jsonrpc/transports/rw.rs new file mode 100644 index 00000000..7dd8aaef --- /dev/null +++ b/starknet-providers/src/jsonrpc/transports/rw.rs @@ -0,0 +1,124 @@ +//! A [JsonRpcTransport] implementation that serves as a wrapper around two different [JsonRpcTransport] +//! and uses a dedicated client for read and the other for write operations + +use std::{error::Error as StdError, fmt::Debug}; + +use async_trait::async_trait; +use serde::{de::DeserializeOwned, Serialize}; +use thiserror::Error; + +use crate::jsonrpc::{ + JsonRpcMethod, + JsonRpcMethod::{AddDeclareTransaction, AddDeployAccountTransaction, AddInvokeTransaction}, + JsonRpcResponse, JsonRpcTransport, +}; + +/// A client containing two clients. +/// +/// One is used for _read_ operations +/// One is used for _write_ operations that consume gas `["starknet_addInvokeTransaction", +/// "starknet_addDeclareTransaction", "starknet_addDeployAccountTransaction"]` +/// +/// **Note**: if the method is unknown this client falls back to the _read_ client +// # Example +#[derive(Debug, Clone)] +pub struct RwTransport { + /// client used to read + r: Read, + /// client used to write + w: Write, +} + +impl RwTransport { + /// Creates a new client using two different clients + /// + /// # Example + /// + /// ```no_run + /// # use url::Url; + /// async fn t(){ + /// use starknet_providers::jsonrpc::{JsonRpcClient,HttpTransport,RwTransport}; + /// let read = HttpTransport::new(Url::parse("http://localhost:5050").unwrap()); + /// let write = HttpTransport::new(Url::parse("http://localhost:5050").unwrap()); + /// let rw = RwTransport::new(read, write); + /// let client = JsonRpcClient::new(rw); + /// # } + /// ``` + pub fn new(r: Read, w: Write) -> RwTransport { + Self { r, w } + } + + /// Returns the client used for read operations + pub fn read_client(&self) -> &Read { + &self.r + } + + /// Returns the client used for write operations + pub fn write_client(&self) -> &Write { + &self.w + } + + /// Returns a new `RwClient` with transposed clients + pub fn transpose(self) -> RwTransport { + let RwTransport { r, w } = self; + RwTransport::new(w, r) + } + + /// Consumes the client and returns the underlying clients + pub fn split(self) -> (Read, Write) { + let RwTransport { r, w } = self; + (r, w) + } +} + +#[derive(Error, Debug)] +/// Error thrown when using either read or write client +pub enum RwTransportError +where + Read: JsonRpcTransport, + ::Error: StdError + Sync + Send, + Write: JsonRpcTransport, + ::Error: StdError + Sync + Send, +{ + /// Thrown if the _read_ request failed + #[error(transparent)] + Read(Read::Error), + #[error(transparent)] + /// Thrown if the _write_ request failed + Write(Write::Error), +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl JsonRpcTransport for RwTransport +where + Read: JsonRpcTransport + Debug + Send + Sync, + Write: JsonRpcTransport + Debug + Send + Sync, +{ + type Error = RwTransportError; + + /// Sends a POST request with the provided method and the params serialized as JSON + /// over HTTP + async fn send_request( + &self, + method: JsonRpcMethod, + params: P, + ) -> Result, Self::Error> + where + P: Serialize + Send + Sync, + R: DeserializeOwned + Send, + { + match method { + AddInvokeTransaction | AddDeclareTransaction | AddDeployAccountTransaction => self + .w + .send_request(method, params) + .await + .map_err(RwTransportError::Write), + _ => self + .r + .send_request(method, params) + .await + .map_err(RwTransportError::Read), + } + } +}