pezkuwi_subxt_rpcs/lib.rs
1// Copyright 2019-2025 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5//! This crate provides a low level RPC interface to Bizinikiwi based nodes.
6//!
7//! See the [`client`] module for a [`client::RpcClient`] which is driven by implementations
8//! of [`client::RpcClientT`] (several of which are provided behind feature flags).
9//!
10//! See the [`methods`] module for structs which implement sets of concrete RPC calls for
11//! communicating with Bizinikiwi based nodes. These structs are all driven by a
12//! [`client::RpcClient`].
13//!
14//! The RPC clients/methods here are made use of in `subxt`. Enabling the `subxt` feature flag
15//! ensures that all Subxt configurations are also valid RPC configurations.
16//!
17//! The provided RPC client implementations can be used natively (with the default `native` feature
18//! flag) or in WASM based web apps (with the `web` feature flag).
19
20#![cfg_attr(docsrs, feature(doc_cfg))]
21
22// Note: When both 'web' and 'native' features are enabled (e.g., --all-features),
23// 'native' takes priority. This allows CI to run with --all-features.
24#[cfg(not(any(feature = "web", feature = "native")))]
25compile_error!("subxt-rpcs: at least one of the 'web' or 'native' features must be enabled.");
26
27mod macros;
28
29pub mod client;
30pub mod methods;
31pub mod utils;
32
33// Used to enable the js feature for wasm.
34#[cfg(feature = "web")]
35#[allow(unused_imports)]
36pub use getrandom as _;
37
38// Expose the most common things at the top level:
39pub use client::{RpcClient, RpcClientT};
40pub use methods::{ChainHeadRpcMethods, LegacyRpcMethods};
41
42/// Configuration used by some of the RPC methods to determine the shape of
43/// some of the inputs or responses.
44pub trait RpcConfig {
45 /// The block header type.
46 type Header: Header;
47 /// The block hash type.
48 type Hash: Hash;
49 /// The Account ID type.
50 type AccountId: AccountId;
51}
52
53/// A trait which is applied to any type that is a valid block header.
54pub trait Header: std::fmt::Debug + codec::Decode + serde::de::DeserializeOwned {}
55impl<T> Header for T where T: std::fmt::Debug + codec::Decode + serde::de::DeserializeOwned {}
56
57/// A trait which is applied to any type that is a valid block hash.
58pub trait Hash: serde::de::DeserializeOwned + serde::Serialize {}
59impl<T> Hash for T where T: serde::de::DeserializeOwned + serde::Serialize {}
60
61/// A trait which is applied to any type that is a valid Account ID.
62pub trait AccountId: serde::Serialize {}
63impl<T> AccountId for T where T: serde::Serialize {}
64
65// When the subxt feature is enabled, ensure that any valid `pezkuwi_subxt::Config`
66// is also a valid `RpcConfig`.
67#[cfg(feature = "subxt")]
68mod impl_config {
69 use super::*;
70 use pezkuwi_subxt_core::config::HashFor;
71
72 impl<T> RpcConfig for T
73 where
74 T: pezkuwi_subxt_core::Config,
75 {
76 type Header = T::Header;
77 type Hash = HashFor<T>;
78 type AccountId = T::AccountId;
79 }
80}
81
82/// This encapsulates any errors that could be emitted in this crate.
83#[derive(Debug, thiserror::Error)]
84#[non_exhaustive]
85pub enum Error {
86 /// An error which indicates a user fault.
87 #[error("User error: {0}")]
88 User(#[from] UserError),
89 // Dev note: We need the error to be safely sent between threads
90 // for `subscribe_to_block_headers_filling_in_gaps` and friends.
91 /// An error coming from the underlying RPC Client.
92 #[error("RPC error: client error: {0}")]
93 Client(Box<dyn std::error::Error + Send + Sync + 'static>),
94 /// The connection was lost and the client will automatically reconnect. Clients
95 /// should only emit this if they are internally reconnecting, and will buffer any
96 /// calls made to them in the meantime until the connection is re-established.
97 #[error("RPC error: the connection was lost ({0}); reconnect automatically initiated")]
98 DisconnectedWillReconnect(String),
99 /// Cannot deserialize the response.
100 #[error("RPC error: cannot deserialize response: {0}")]
101 Deserialization(serde_json::Error),
102 /// Cannot SCALE decode some part of the response.
103 #[error("RPC error: cannot SCALE decode some part of the response: {0}")]
104 Decode(codec::Error),
105 /// The requested URL is insecure.
106 #[error("RPC error: insecure URL: {0}")]
107 InsecureUrl(String),
108}
109
110impl Error {
111 /// Is the error the `DisconnectedWillReconnect` variant? This should be true
112 /// only if the underlying `RpcClient` implementation was disconnected and is
113 /// automatically reconnecting behind the scenes.
114 pub fn is_disconnected_will_reconnect(&self) -> bool {
115 matches!(self, Error::DisconnectedWillReconnect(_))
116 }
117}
118
119/// This error should be returned when the user is at fault making a call,
120/// for instance because the method name was wrong, parameters invalid or some
121/// invariant not upheld. Implementations of [`RpcClientT`] should turn any such
122/// errors into this, so that they can be handled appropriately. By contrast,
123/// [`Error::Client`] is emitted when the underlying RPC Client implementation
124/// has some problem that isn't user specific (eg network issues or similar).
125#[derive(Debug, Clone, serde::Deserialize, thiserror::Error)]
126#[serde(deny_unknown_fields)]
127pub struct UserError {
128 /// Code
129 pub code: i32,
130 /// Message
131 pub message: String,
132 /// Optional data
133 pub data: Option<Box<serde_json::value::RawValue>>,
134}
135
136impl UserError {
137 /// Returns a standard JSON-RPC "method not found" error.
138 pub fn method_not_found() -> UserError {
139 UserError { code: -32601, message: "Method not found".to_owned(), data: None }
140 }
141}
142
143impl core::fmt::Display for UserError {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 write!(f, "{} ({})", &self.message, &self.code)
146 }
147}