diff --git a/subxt/src/client/online_client.rs b/subxt/src/client/online_client.rs index 5fa9b3c898..b70d103db1 100644 --- a/subxt/src/client/online_client.rs +++ b/subxt/src/client/online_client.rs @@ -74,7 +74,7 @@ impl OnlineClient { pub async fn from_url(url: impl AsRef) -> Result, Error> { let client = jsonrpsee_helpers::ws_client(url.as_ref()) .await - .map_err(|e| crate::error::RpcError(e.to_string()))?; + .map_err(|e| crate::error::RpcError::ClientError(Box::new(e)))?; OnlineClient::from_rpc_client(Arc::new(client)).await } } diff --git a/subxt/src/error.rs b/subxt/src/error.rs index 04f1ed9134..4e848b15a0 100644 --- a/subxt/src/error.rs +++ b/subxt/src/error.rs @@ -102,10 +102,17 @@ impl From for Error { } /// An RPC error. Since we are generic over the RPC client that is used, -/// the error is any custom string. +/// the error is boxed and could be casted. #[derive(Debug, thiserror::Error)] -#[error("RPC error: {0}")] -pub struct RpcError(pub String); +#[error("RPC error")] +pub enum RpcError { + // Dev note: We need the error to be safely sent between threads + // for `subscribe_to_block_headers_filling_in_gaps` and friends. + /// Error related to the RPC client. + ClientError(Box), + /// The RPC subscription dropped. + SubscriptionDropped, +} /// This is our attempt to decode a runtime DispatchError. We either /// successfully decode it into a [`ModuleError`], or we fail and keep diff --git a/subxt/src/rpc/jsonrpsee_impl.rs b/subxt/src/rpc/jsonrpsee_impl.rs index 6133faabf1..52b9a3e61e 100644 --- a/subxt/src/rpc/jsonrpsee_impl.rs +++ b/subxt/src/rpc/jsonrpsee_impl.rs @@ -32,10 +32,10 @@ impl RpcClientT for Client { params: Option>, ) -> RpcFuture<'a, Box> { Box::pin(async move { - let params = prep_params_for_jsonrpsee(params)?; + let params = prep_params_for_jsonrpsee(params); let res = ClientT::request(self, method, Some(params)) .await - .map_err(|e| RpcError(e.to_string()))?; + .map_err(|e| RpcError::ClientError(Box::new(e)))?; Ok(res) }) } @@ -47,7 +47,7 @@ impl RpcClientT for Client { unsub: &'a str, ) -> RpcFuture<'a, RpcSubscription> { Box::pin(async move { - let params = prep_params_for_jsonrpsee(params)?; + let params = prep_params_for_jsonrpsee(params); let sub = SubscriptionClientT::subscribe::>( self, sub, @@ -55,8 +55,8 @@ impl RpcClientT for Client { unsub, ) .await - .map_err(|e| RpcError(e.to_string()))? - .map_err(|e| RpcError(e.to_string())) + .map_err(|e| RpcError::ClientError(Box::new(e)))? + .map_err(|e| RpcError::ClientError(Box::new(e))) .boxed(); Ok(sub) }) @@ -65,22 +65,18 @@ impl RpcClientT for Client { // This is ugly; we have to encode to Value's to be compat with the jsonrpc interface. // Remove and simplify this once something like https://github.com/paritytech/jsonrpsee/issues/862 is in: -fn prep_params_for_jsonrpsee( - params: Option>, -) -> Result, RpcError> { +fn prep_params_for_jsonrpsee(params: Option>) -> ParamsSer<'static> { let params = match params { Some(params) => params, // No params? avoid any work and bail early. - None => return Ok(ParamsSer::Array(Vec::new())), + None => return ParamsSer::Array(Vec::new()), }; let val = serde_json::to_value(¶ms).expect("RawValue guarantees valid JSON"); let arr = match val { - Value::Array(arr) => Ok(arr), + Value::Array(arr) => arr, _ => { - Err(RpcError(format!( - "RPC Params are expected to be an array but got {params}" - ))) + panic!("RPC Params are expected to be an array but got {params}"); } - }?; - Ok(ParamsSer::Array(arr)) + }; + ParamsSer::Array(arr) } diff --git a/subxt/src/rpc/rpc_client_t.rs b/subxt/src/rpc/rpc_client_t.rs index 17f4ec9ab9..d32924ffbe 100644 --- a/subxt/src/rpc/rpc_client_t.rs +++ b/subxt/src/rpc/rpc_client_t.rs @@ -20,6 +20,11 @@ pub use serde_json::value::RawValue; /// the caller. This is the case because we want the methods to be object-safe (which prohibits /// generics), and want to avoid any unnecessary allocations in serializing/deserializing /// parameters. +/// +/// # Panics +/// +/// Implementations are free to panic if the `RawValue`'s passed to `request_raw` or +/// `subscribe_raw` are not JSON arrays. Internally, we ensure that this is always the case. pub trait RpcClientT: Send + Sync + 'static { /// Make a raw request for which we expect a single response back from. Implementations /// should expect that the params will either be `None`, or be an already-serialized diff --git a/subxt/src/tx/tx_progress.rs b/subxt/src/tx/tx_progress.rs index 9cbf752164..45d409b474 100644 --- a/subxt/src/tx/tx_progress.rs +++ b/subxt/src/tx/tx_progress.rs @@ -107,7 +107,7 @@ where _ => continue, } } - Err(RpcError("RPC subscription dropped".to_string()).into()) + Err(RpcError::SubscriptionDropped.into()) } /// Wait for the transaction to be finalized, and return a [`TxInBlock`] @@ -133,7 +133,7 @@ where _ => continue, } } - Err(RpcError("RPC subscription dropped".to_string()).into()) + Err(RpcError::SubscriptionDropped.into()) } /// Wait for the transaction to be finalized, and for the transaction events to indicate