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}