rpc: backpressured RPC server (bump jsonrpsee 0.20) (#1313)

This is a rather big change in jsonrpsee, the major things in this bump
are:
- Server backpressure (the subscription impls are modified to deal with
that)
- Allow custom error types / return types (remove jsonrpsee::core::Error
and jsonrpee::core::CallError)
- Bug fixes (graceful shutdown in particular not used by substrate
anyway)
   - Less dependencies for the clients in particular
   - Return type requires Clone in method call responses
   - Moved to tokio channels
   - Async subscription API (not used in this PR)

Major changes in this PR:
- The subscriptions are now bounded and if subscription can't keep up
with the server it is dropped
- CLI: add parameter to configure the jsonrpc server bounded message
buffer (default is 64)
- Add our own subscription helper to deal with the unbounded streams in
substrate

The most important things in this PR to review is the added helpers
functions in `substrate/client/rpc/src/utils.rs` and the rest is pretty
much chore.

Regarding the "bounded buffer limit" it may cause the server to handle
the JSON-RPC calls
slower than before.

The message size limit is bounded by "--rpc-response-size" thus "by
default 10MB * 64 = 640MB"
but the subscription message size is not covered by this limit and could
be capped as well.

Hopefully the last release prior to 1.0, sorry in advance for a big PR

Previous attempt: https://github.com/paritytech/substrate/pull/13992

Resolves https://github.com/paritytech/polkadot-sdk/issues/748, resolves
https://github.com/paritytech/polkadot-sdk/issues/627
This commit is contained in:
Niklas Adolfsson
2024-01-23 09:55:13 +01:00
committed by GitHub
parent 76c37c930b
commit e16ef0861f
117 changed files with 1245 additions and 1090 deletions
+39 -36
View File
@@ -18,10 +18,7 @@
//! Authoring RPC module errors.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
use sp_runtime::transaction_validity::InvalidTransaction;
/// Author RPC Result type.
@@ -86,98 +83,104 @@ const POOL_NO_TAGS: i32 = POOL_INVALID_TX + 9;
const POOL_INVALID_BLOCK_ID: i32 = POOL_INVALID_TX + 10;
/// The pool is not accepting future transactions.
const POOL_FUTURE_TX: i32 = POOL_INVALID_TX + 11;
/// Other error.
const OTHER_ERR: i32 = BASE_ERROR + 40;
impl From<Error> for JsonRpseeError {
fn from(e: Error) -> Self {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> ErrorObjectOwned {
use sc_transaction_pool_api::error::Error as PoolError;
match e {
Error::BadFormat(e) => CallError::Custom(ErrorObject::owned(
Error::BadFormat(e) => ErrorObject::owned(
BAD_FORMAT,
format!("Extrinsic has invalid format: {}", e),
None::<()>,
)),
Error::Verification(e) => CallError::Custom(ErrorObject::owned(
),
Error::Verification(e) => ErrorObject::owned(
VERIFICATION_ERROR,
format!("Verification Error: {}", e),
Some(format!("{:?}", e)),
)),
),
Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Custom(e))) => {
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_INVALID_TX,
"Invalid Transaction",
Some(format!("Custom error: {}", e)),
))
)
},
Error::Pool(PoolError::InvalidTransaction(e)) => {
let msg: &str = e.into();
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_INVALID_TX,
"Invalid Transaction",
Some(msg),
))
)
},
Error::Pool(PoolError::UnknownTransaction(e)) => {
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_UNKNOWN_VALIDITY,
"Unknown Transaction Validity",
Some(format!("{:?}", e)),
))
)
},
Error::Pool(PoolError::TemporarilyBanned) =>
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_TEMPORARILY_BANNED,
"Transaction is temporarily banned",
None::<()>,
)),
),
Error::Pool(PoolError::AlreadyImported(hash)) =>
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_ALREADY_IMPORTED,
"Transaction Already Imported",
Some(format!("{:?}", hash)),
)),
Error::Pool(PoolError::TooLowPriority { old, new }) => CallError::Custom(ErrorObject::owned(
),
Error::Pool(PoolError::TooLowPriority { old, new }) => ErrorObject::owned(
POOL_TOO_LOW_PRIORITY,
format!("Priority is too low: ({} vs {})", old, new),
Some("The transaction has too low priority to replace another transaction already in the pool.")
)),
),
Error::Pool(PoolError::CycleDetected) =>
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_CYCLE_DETECTED,
"Cycle Detected",
None::<()>
)),
Error::Pool(PoolError::ImmediatelyDropped) => CallError::Custom(ErrorObject::owned(
),
Error::Pool(PoolError::ImmediatelyDropped) => ErrorObject::owned(
POOL_IMMEDIATELY_DROPPED,
"Immediately Dropped",
Some("The transaction couldn't enter the pool because of the limit"),
)),
Error::Pool(PoolError::Unactionable) => CallError::Custom(ErrorObject::owned(
),
Error::Pool(PoolError::Unactionable) => ErrorObject::owned(
POOL_UNACTIONABLE,
"Unactionable",
Some("The transaction is unactionable since it is not propagable and \
the local node does not author blocks")
)),
Error::Pool(PoolError::NoTagsProvided) => CallError::Custom(ErrorObject::owned(
),
Error::Pool(PoolError::NoTagsProvided) => ErrorObject::owned(
POOL_NO_TAGS,
"No tags provided",
Some("Transaction does not provide any tags, so the pool can't identify it")
)),
),
Error::Pool(PoolError::InvalidBlockId(_)) =>
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_INVALID_BLOCK_ID,
"The provided block ID is not valid",
None::<()>
)),
),
Error::Pool(PoolError::RejectedFutureTransaction) => {
CallError::Custom(ErrorObject::owned(
ErrorObject::owned(
POOL_FUTURE_TX,
"The pool is not accepting future transactions",
None::<()>,
))
)
},
Error::UnsafeRpcCalled(e) => e.into(),
e => CallError::Failed(e.into()),
}.into()
other => ErrorObject::owned(
OTHER_ERR,
other.to_string(),
None::<()>,
)
}
}
}
+12 -11
View File
@@ -18,27 +18,28 @@
//! Substrate block-author/full-node API.
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use sc_transaction_pool_api::TransactionStatus;
use sp_core::Bytes;
pub mod error;
pub mod hash;
use error::Error;
use jsonrpsee::proc_macros::rpc;
use sc_transaction_pool_api::TransactionStatus;
use sp_core::Bytes;
/// Substrate authoring RPC API
#[rpc(client, server)]
pub trait AuthorApi<Hash, BlockHash> {
/// Submit hex-encoded extrinsic for inclusion in block.
#[method(name = "author_submitExtrinsic")]
async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<Hash>;
async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result<Hash, Error>;
/// Insert a key into the keystore.
#[method(name = "author_insertKey")]
fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()>;
fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<(), Error>;
/// Generate new session keys and returns the corresponding public keys.
#[method(name = "author_rotateKeys")]
fn rotate_keys(&self) -> RpcResult<Bytes>;
fn rotate_keys(&self) -> Result<Bytes, Error>;
/// Checks if the keystore has private keys for the given session public keys.
///
@@ -46,24 +47,24 @@ pub trait AuthorApi<Hash, BlockHash> {
///
/// Returns `true` iff all private keys could be found.
#[method(name = "author_hasSessionKeys")]
fn has_session_keys(&self, session_keys: Bytes) -> RpcResult<bool>;
fn has_session_keys(&self, session_keys: Bytes) -> Result<bool, Error>;
/// Checks if the keystore has private keys for the given public key and key type.
///
/// Returns `true` if a private key could be found.
#[method(name = "author_hasKey")]
fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult<bool>;
fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool, Error>;
/// Returns all pending extrinsics, potentially grouped by sender.
#[method(name = "author_pendingExtrinsics")]
fn pending_extrinsics(&self) -> RpcResult<Vec<Bytes>>;
fn pending_extrinsics(&self) -> Result<Vec<Bytes>, Error>;
/// Remove given extrinsic from the pool and temporarily ban it to prevent reimporting.
#[method(name = "author_removeExtrinsic")]
fn remove_extrinsic(
&self,
bytes_or_hash: Vec<hash::ExtrinsicOrHash<Hash>>,
) -> RpcResult<Vec<Hash>>;
) -> Result<Vec<Hash>, Error>;
/// Submit an extrinsic to watch.
///
+5 -9
View File
@@ -18,10 +18,7 @@
//! Error helpers for Chain RPC module.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::{error::ErrorObject, ErrorObjectOwned};
/// Chain RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
@@ -39,12 +36,11 @@ pub enum Error {
/// Base error code for all chain errors.
const BASE_ERROR: i32 = crate::error::base::CHAIN;
impl From<Error> for JsonRpseeError {
fn from(e: Error) -> Self {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> ErrorObjectOwned {
match e {
Error::Other(message) =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, message, None::<()>)).into(),
e => e.into(),
Error::Other(message) => ErrorObject::owned(BASE_ERROR + 1, message, None::<()>),
e => ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>),
}
}
}
+8 -7
View File
@@ -18,20 +18,21 @@
//! Substrate blockchain API.
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
pub mod error;
use error::Error;
use jsonrpsee::proc_macros::rpc;
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
#[rpc(client, server)]
pub trait ChainApi<Number, Hash, Header, SignedBlock> {
/// Get header.
#[method(name = "chain_getHeader", blocking)]
fn header(&self, hash: Option<Hash>) -> RpcResult<Option<Header>>;
fn header(&self, hash: Option<Hash>) -> Result<Option<Header>, Error>;
/// Get header and body of a block.
#[method(name = "chain_getBlock", blocking)]
fn block(&self, hash: Option<Hash>) -> RpcResult<Option<SignedBlock>>;
fn block(&self, hash: Option<Hash>) -> Result<Option<SignedBlock>, Error>;
/// Get hash of the n-th block in the canon chain.
///
@@ -40,11 +41,11 @@ pub trait ChainApi<Number, Hash, Header, SignedBlock> {
fn block_hash(
&self,
hash: Option<ListOrValue<NumberOrHex>>,
) -> RpcResult<ListOrValue<Option<Hash>>>;
) -> Result<ListOrValue<Option<Hash>>, Error>;
/// Get hash of the last finalized block in the canon chain.
#[method(name = "chain_getFinalizedHead", aliases = ["chain_getFinalisedHead"], blocking)]
fn finalized_head(&self) -> RpcResult<Hash>;
fn finalized_head(&self) -> Result<Hash, Error>;
/// All head subscription.
#[subscription(
@@ -17,8 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Substrate child state API
use crate::state::ReadProof;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use crate::state::{Error, ReadProof};
use jsonrpsee::proc_macros::rpc;
use sp_core::storage::{PrefixedStorageKey, StorageData, StorageKey};
/// Substrate child state API
@@ -35,7 +35,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
prefix: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageKey>>;
) -> Result<Vec<StorageKey>, Error>;
/// Returns the keys with prefix from a child storage with pagination support.
/// Up to `count` keys will be returned.
@@ -48,7 +48,7 @@ pub trait ChildStateApi<Hash> {
count: u32,
start_key: Option<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageKey>>;
) -> Result<Vec<StorageKey>, Error>;
/// Returns a child storage entry at a specific block's state.
#[method(name = "childstate_getStorage", blocking)]
@@ -57,7 +57,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
key: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Option<StorageData>>;
) -> Result<Option<StorageData>, Error>;
/// Returns child storage entries for multiple keys at a specific block's state.
#[method(name = "childstate_getStorageEntries", blocking)]
@@ -66,7 +66,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<Vec<Option<StorageData>>>;
) -> Result<Vec<Option<StorageData>>, Error>;
/// Returns the hash of a child storage entry at a block's state.
#[method(name = "childstate_getStorageHash", blocking)]
@@ -75,7 +75,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
key: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Option<Hash>>;
) -> Result<Option<Hash>, Error>;
/// Returns the size of a child storage entry at a block's state.
#[method(name = "childstate_getStorageSize", blocking)]
@@ -84,7 +84,7 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
key: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Option<u64>>;
) -> Result<Option<u64>, Error>;
/// Returns proof of storage for child key entries at a specific block's state.
#[method(name = "state_getChildReadProof", blocking)]
@@ -93,5 +93,5 @@ pub trait ChildStateApi<Hash> {
child_storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<ReadProof<Hash>>;
) -> Result<ReadProof<Hash>, Error>;
}
+9 -14
View File
@@ -18,10 +18,10 @@
//! Error helpers for Dev RPC module.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
/// Dev RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
/// Dev RPC errors.
#[derive(Debug, thiserror::Error)]
@@ -46,21 +46,16 @@ pub enum Error {
/// Base error code for all dev errors.
const BASE_ERROR: i32 = crate::error::base::DEV;
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> Self {
let msg = e.to_string();
match e {
Error::BlockQueryError(_) =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, msg, None::<()>)),
Error::BlockExecutionFailed =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 3, msg, None::<()>)),
Error::WitnessCompactionFailed =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 4, msg, None::<()>)),
Error::ProofExtractionFailed =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 5, msg, None::<()>)),
Error::BlockQueryError(_) => ErrorObject::owned(BASE_ERROR + 1, msg, None::<()>),
Error::BlockExecutionFailed => ErrorObject::owned(BASE_ERROR + 3, msg, None::<()>),
Error::WitnessCompactionFailed => ErrorObject::owned(BASE_ERROR + 4, msg, None::<()>),
Error::ProofExtractionFailed => ErrorObject::owned(BASE_ERROR + 5, msg, None::<()>),
Error::UnsafeRpcCalled(e) => e.into(),
}
.into()
}
}
+3 -2
View File
@@ -23,7 +23,8 @@
pub mod error;
use codec::{Decode, Encode};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use error::Error;
use jsonrpsee::proc_macros::rpc;
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
@@ -59,5 +60,5 @@ pub trait DevApi<Hash> {
/// at the queried node. If either the specified block or the parent is pruned,
/// this function will return `None`.
#[method(name = "dev_getBlockStats")]
fn block_stats(&self, block_hash: Hash) -> RpcResult<Option<BlockStats>>;
fn block_stats(&self, block_hash: Hash) -> Result<Option<BlockStats>, Error>;
}
+1 -1
View File
@@ -25,7 +25,7 @@
mod error;
mod policy;
pub use policy::DenyUnsafe;
pub use policy::{DenyUnsafe, UnsafeRpcError};
pub mod author;
pub mod chain;
+3 -3
View File
@@ -18,7 +18,7 @@
//! Mixnet RPC module errors.
use jsonrpsee::types::error::{CallError, ErrorObject};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
use sc_mixnet::{PostErr, RemoteErr, TopologyErr};
/// Mixnet RPC error type.
@@ -27,7 +27,7 @@ pub struct Error(pub sc_mixnet::Error);
/// Base code for all mixnet errors.
const BASE_ERROR: i32 = crate::error::base::MIXNET;
impl From<Error> for jsonrpsee::core::Error {
impl From<Error> for ErrorObjectOwned {
fn from(err: Error) -> Self {
let code = match err.0 {
sc_mixnet::Error::ServiceUnavailable => BASE_ERROR + 1,
@@ -43,6 +43,6 @@ impl From<Error> for jsonrpsee::core::Error {
sc_mixnet::Error::Remote(RemoteErr::Other(_)) => BASE_ERROR + 200,
sc_mixnet::Error::Remote(RemoteErr::Decode(_)) => BASE_ERROR + 201,
};
CallError::Custom(ErrorObject::owned(code, err.0.to_string(), None::<()>)).into()
ErrorObject::owned(code, err.0.to_string(), None::<()>)
}
}
+3 -2
View File
@@ -20,12 +20,13 @@
pub mod error;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use error::Error;
use jsonrpsee::proc_macros::rpc;
use sp_core::Bytes;
#[rpc(client, server)]
pub trait MixnetApi {
/// Submit encoded extrinsic over the mixnet for inclusion in block.
#[method(name = "mixnet_submitExtrinsic")]
async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<()>;
async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result<(), Error>;
}
@@ -18,10 +18,7 @@
//! Offchain RPC errors.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
/// Offchain RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
@@ -40,15 +37,14 @@ pub enum Error {
/// Base error code for all offchain errors.
const BASE_ERROR: i32 = crate::error::base::OFFCHAIN;
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> Self {
match e {
Error::UnavailableStorageKind => CallError::Custom(ErrorObject::owned(
Error::UnavailableStorageKind => ErrorObject::owned(
BASE_ERROR + 1,
"This storage kind is not available yet",
None::<()>,
))
.into(),
),
Error::UnsafeRpcCalled(e) => e.into(),
}
}
+6 -5
View File
@@ -18,19 +18,20 @@
//! Substrate offchain API.
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use sp_core::{offchain::StorageKind, Bytes};
pub mod error;
use error::Error;
use jsonrpsee::proc_macros::rpc;
use sp_core::{offchain::StorageKind, Bytes};
/// Substrate offchain RPC API
#[rpc(client, server)]
pub trait OffchainApi {
/// Set offchain local storage under given key and prefix.
#[method(name = "offchain_localStorageSet")]
fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> RpcResult<()>;
fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<(), Error>;
/// Get offchain local storage under given key and prefix.
#[method(name = "offchain_localStorageGet")]
fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> RpcResult<Option<Bytes>>;
fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result<Option<Bytes>, Error>;
}
+4 -20
View File
@@ -21,13 +21,7 @@
//! Contains a `DenyUnsafe` type that can be used to deny potentially unsafe
//! RPC when accessed externally.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::{
error::{CallError, ErrorCode},
ErrorObject,
},
};
use jsonrpsee::types::{error::ErrorCode, ErrorObject, ErrorObjectOwned};
/// Signifies whether a potentially unsafe RPC should be denied.
#[derive(Clone, Copy, Debug)]
@@ -61,18 +55,8 @@ impl std::fmt::Display for UnsafeRpcError {
impl std::error::Error for UnsafeRpcError {}
impl From<UnsafeRpcError> for CallError {
fn from(e: UnsafeRpcError) -> CallError {
CallError::Custom(ErrorObject::owned(
ErrorCode::MethodNotFound.code(),
e.to_string(),
None::<()>,
))
}
}
impl From<UnsafeRpcError> for JsonRpseeError {
fn from(e: UnsafeRpcError) -> JsonRpseeError {
JsonRpseeError::Call(e.into())
impl From<UnsafeRpcError> for ErrorObjectOwned {
fn from(e: UnsafeRpcError) -> ErrorObjectOwned {
ErrorObject::owned(ErrorCode::MethodNotFound.code(), e.to_string(), None::<()>)
}
}
+7 -11
View File
@@ -18,10 +18,8 @@
//! State RPC errors.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
/// State RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
@@ -57,16 +55,14 @@ pub enum Error {
/// Base code for all state errors.
const BASE_ERROR: i32 = crate::error::base::STATE;
impl From<Error> for JsonRpseeError {
fn from(e: Error) -> Self {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> ErrorObjectOwned {
match e {
Error::InvalidBlockRange { .. } =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, e.to_string(), None::<()>))
.into(),
ErrorObject::owned(BASE_ERROR + 1, e.to_string(), None::<()>),
Error::InvalidCount { .. } =>
CallError::Custom(ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>))
.into(),
e => Self::to_call_error(e),
ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>),
e => ErrorObject::owned(BASE_ERROR + 3, e.to_string(), None::<()>),
}
}
}
@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use sp_core::Bytes;
/// ReadProof struct returned by the RPC
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadProof<Hash> {
/// Block hash used to generate the proof
+24 -14
View File
@@ -18,7 +18,7 @@
//! Substrate state API.
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use jsonrpsee::proc_macros::rpc;
use sp_core::{
storage::{StorageChangeSet, StorageData, StorageKey},
Bytes,
@@ -29,18 +29,23 @@ pub mod error;
pub mod helpers;
pub use self::helpers::ReadProof;
pub use error::Error;
/// Substrate state API
#[rpc(client, server)]
pub trait StateApi<Hash> {
/// Call a method from the runtime API at a block's state.
#[method(name = "state_call", aliases = ["state_callAt"], blocking)]
fn call(&self, name: String, bytes: Bytes, hash: Option<Hash>) -> RpcResult<Bytes>;
fn call(&self, name: String, bytes: Bytes, hash: Option<Hash>) -> Result<Bytes, Error>;
/// Returns the keys with prefix, leave empty to get all the keys.
#[method(name = "state_getKeys", blocking)]
#[deprecated(since = "2.0.0", note = "Please use `getKeysPaged` with proper paging support")]
fn storage_keys(&self, prefix: StorageKey, hash: Option<Hash>) -> RpcResult<Vec<StorageKey>>;
fn storage_keys(
&self,
prefix: StorageKey,
hash: Option<Hash>,
) -> Result<Vec<StorageKey>, Error>;
/// Returns the keys with prefix, leave empty to get all the keys
#[method(name = "state_getPairs", blocking)]
@@ -48,7 +53,7 @@ pub trait StateApi<Hash> {
&self,
prefix: StorageKey,
hash: Option<Hash>,
) -> RpcResult<Vec<(StorageKey, StorageData)>>;
) -> Result<Vec<(StorageKey, StorageData)>, Error>;
/// Returns the keys with prefix with pagination support.
/// Up to `count` keys will be returned.
@@ -60,27 +65,28 @@ pub trait StateApi<Hash> {
count: u32,
start_key: Option<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageKey>>;
) -> Result<Vec<StorageKey>, Error>;
/// Returns a storage entry at a specific block's state.
#[method(name = "state_getStorage", aliases = ["state_getStorageAt"], blocking)]
fn storage(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<StorageData>>;
fn storage(&self, key: StorageKey, hash: Option<Hash>) -> Result<Option<StorageData>, Error>;
/// Returns the hash of a storage entry at a block's state.
#[method(name = "state_getStorageHash", aliases = ["state_getStorageHashAt"], blocking)]
fn storage_hash(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<Hash>>;
fn storage_hash(&self, key: StorageKey, hash: Option<Hash>) -> Result<Option<Hash>, Error>;
/// Returns the size of a storage entry at a block's state.
#[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"])]
async fn storage_size(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<u64>>;
async fn storage_size(&self, key: StorageKey, hash: Option<Hash>)
-> Result<Option<u64>, Error>;
/// Returns the runtime metadata as an opaque blob.
#[method(name = "state_getMetadata", blocking)]
fn metadata(&self, hash: Option<Hash>) -> RpcResult<Bytes>;
fn metadata(&self, hash: Option<Hash>) -> Result<Bytes, Error>;
/// Get the runtime version.
#[method(name = "state_getRuntimeVersion", aliases = ["chain_getRuntimeVersion"], blocking)]
fn runtime_version(&self, hash: Option<Hash>) -> RpcResult<RuntimeVersion>;
fn runtime_version(&self, hash: Option<Hash>) -> Result<RuntimeVersion, Error>;
/// Query historical storage entries (by key) starting from a block given as the second
/// parameter.
@@ -95,7 +101,7 @@ pub trait StateApi<Hash> {
keys: Vec<StorageKey>,
block: Hash,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageChangeSet<Hash>>>;
) -> Result<Vec<StorageChangeSet<Hash>>, Error>;
/// Query storage entries (by key) at a block hash given as the second parameter.
/// NOTE: Each StorageChangeSet in the result corresponds to exactly one element --
@@ -105,11 +111,15 @@ pub trait StateApi<Hash> {
&self,
keys: Vec<StorageKey>,
at: Option<Hash>,
) -> RpcResult<Vec<StorageChangeSet<Hash>>>;
) -> Result<Vec<StorageChangeSet<Hash>>, Error>;
/// Returns proof of storage entries at a specific block's state.
#[method(name = "state_getReadProof", blocking)]
fn read_proof(&self, keys: Vec<StorageKey>, hash: Option<Hash>) -> RpcResult<ReadProof<Hash>>;
fn read_proof(
&self,
keys: Vec<StorageKey>,
hash: Option<Hash>,
) -> Result<ReadProof<Hash>, Error>;
/// New runtime version subscription
#[subscription(
@@ -288,5 +298,5 @@ pub trait StateApi<Hash> {
targets: Option<String>,
storage_keys: Option<String>,
methods: Option<String>,
) -> RpcResult<sp_rpc::tracing::TraceBlockResponse>;
) -> Result<sp_rpc::tracing::TraceBlockResponse, Error>;
}
@@ -18,10 +18,7 @@
//! Statement RPC errors.
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
};
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
/// Statement RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;
@@ -40,15 +37,14 @@ pub enum Error {
/// Base error code for all statement errors.
const BASE_ERROR: i32 = crate::error::base::STATEMENT;
impl From<Error> for JsonRpseeError {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> Self {
match e {
Error::StatementStore(message) => CallError::Custom(ErrorObject::owned(
Error::StatementStore(message) => ErrorObject::owned(
BASE_ERROR + 1,
format!("Statement store error: {message}"),
None::<()>,
))
.into(),
),
Error::UnsafeRpcCalled(e) => e.into(),
}
}
+17 -12
View File
@@ -19,9 +19,9 @@
//! System RPC module errors.
use crate::system::helpers::Health;
use jsonrpsee::{
core::Error as JsonRpseeError,
types::error::{CallError, ErrorObject},
use jsonrpsee::types::{
error::{ErrorCode, ErrorObject},
ErrorObjectOwned,
};
/// System RPC Result type.
@@ -36,6 +36,12 @@ pub enum Error {
/// Peer argument is malformatted.
#[error("{0}")]
MalformattedPeerArg(String),
/// Call to an unsafe RPC was denied.
#[error(transparent)]
UnsafeRpcCalled(#[from] crate::policy::UnsafeRpcError),
/// Internal error.
#[error("{0}")]
Internal(String),
}
// Base code for all system errors.
@@ -45,17 +51,16 @@ const NOT_HEALTHY_ERROR: i32 = BASE_ERROR + 1;
// Peer argument is malformatted.
const MALFORMATTED_PEER_ARG_ERROR: i32 = BASE_ERROR + 2;
impl From<Error> for JsonRpseeError {
fn from(e: Error) -> Self {
impl From<Error> for ErrorObjectOwned {
fn from(e: Error) -> ErrorObjectOwned {
match e {
Error::NotHealthy(ref h) =>
CallError::Custom(ErrorObject::owned(NOT_HEALTHY_ERROR, e.to_string(), Some(h))),
Error::MalformattedPeerArg(e) => CallError::Custom(ErrorObject::owned(
MALFORMATTED_PEER_ARG_ERROR + 2,
e,
None::<()>,
)),
ErrorObject::owned(NOT_HEALTHY_ERROR, e.to_string(), Some(h)),
Error::MalformattedPeerArg(e) =>
ErrorObject::owned(MALFORMATTED_PEER_ARG_ERROR, e, None::<()>),
Error::UnsafeRpcCalled(e) => e.into(),
Error::Internal(e) =>
ErrorObjectOwned::owned(ErrorCode::InternalError.code(), e, None::<()>),
}
.into()
}
}
@@ -38,7 +38,7 @@ pub struct SystemInfo {
}
/// Health struct returned by the RPC
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Health {
/// Number of connected peers
@@ -58,7 +58,7 @@ impl fmt::Display for Health {
}
/// Network Peer information
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PeerInfo<Hash, Number> {
/// Peer ID
@@ -72,7 +72,7 @@ pub struct PeerInfo<Hash, Number> {
}
/// The role the node is running as
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum NodeRole {
/// The node is a full node
Full,
@@ -81,7 +81,7 @@ pub enum NodeRole {
}
/// The state of the syncing of the node.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncState<Number> {
/// Height of the block at which syncing started.
+22 -24
View File
@@ -18,38 +18,36 @@
//! Substrate system API.
use jsonrpsee::{
core::{JsonValue, RpcResult},
proc_macros::rpc,
};
pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo};
pub mod error;
pub mod helpers;
use jsonrpsee::{core::JsonValue, proc_macros::rpc};
pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo};
pub use error::Error;
/// Substrate system RPC API
#[rpc(client, server)]
pub trait SystemApi<Hash, Number> {
/// Get the node's implementation name. Plain old string.
#[method(name = "system_name")]
fn system_name(&self) -> RpcResult<String>;
fn system_name(&self) -> Result<String, Error>;
/// Get the node implementation's version. Should be a semver string.
#[method(name = "system_version")]
fn system_version(&self) -> RpcResult<String>;
fn system_version(&self) -> Result<String, Error>;
/// Get the chain's name. Given as a string identifier.
#[method(name = "system_chain")]
fn system_chain(&self) -> RpcResult<String>;
fn system_chain(&self) -> Result<String, Error>;
/// Get the chain's type.
#[method(name = "system_chainType")]
fn system_type(&self) -> RpcResult<sc_chain_spec::ChainType>;
fn system_type(&self) -> Result<sc_chain_spec::ChainType, Error>;
/// Get a custom set of properties as a JSON object, defined in the chain spec.
#[method(name = "system_properties")]
fn system_properties(&self) -> RpcResult<sc_chain_spec::Properties>;
fn system_properties(&self) -> Result<sc_chain_spec::Properties, Error>;
/// Return health status of the node.
///
@@ -57,22 +55,22 @@ pub trait SystemApi<Hash, Number> {
/// - connected to some peers (unless running in dev mode)
/// - not performing a major sync
#[method(name = "system_health")]
async fn system_health(&self) -> RpcResult<Health>;
async fn system_health(&self) -> Result<Health, Error>;
/// Returns the base58-encoded PeerId of the node.
#[method(name = "system_localPeerId")]
async fn system_local_peer_id(&self) -> RpcResult<String>;
async fn system_local_peer_id(&self) -> Result<String, Error>;
/// Returns the multi-addresses that the local node is listening on
///
/// The addresses include a trailing `/p2p/` with the local PeerId, and are thus suitable to
/// be passed to `addReservedPeer` or as a bootnode address for example.
#[method(name = "system_localListenAddresses")]
async fn system_local_listen_addresses(&self) -> RpcResult<Vec<String>>;
async fn system_local_listen_addresses(&self) -> Result<Vec<String>, Error>;
/// Returns currently connected peers
#[method(name = "system_peers")]
async fn system_peers(&self) -> RpcResult<Vec<PeerInfo<Hash, Number>>>;
async fn system_peers(&self) -> Result<Vec<PeerInfo<Hash, Number>>, Error>;
/// Returns current state of the network.
///
@@ -81,7 +79,7 @@ pub trait SystemApi<Hash, Number> {
// TODO: the future of this call is uncertain: https://github.com/paritytech/substrate/issues/1890
// https://github.com/paritytech/substrate/issues/5541
#[method(name = "system_unstable_networkState")]
async fn system_network_state(&self) -> RpcResult<JsonValue>;
async fn system_network_state(&self) -> Result<JsonValue, Error>;
/// Adds a reserved peer. Returns the empty string or an error. The string
/// parameter should encode a `p2p` multiaddr.
@@ -89,25 +87,25 @@ pub trait SystemApi<Hash, Number> {
/// `/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`
/// is an example of a valid, passing multiaddr with PeerId attached.
#[method(name = "system_addReservedPeer")]
async fn system_add_reserved_peer(&self, peer: String) -> RpcResult<()>;
async fn system_add_reserved_peer(&self, peer: String) -> Result<(), Error>;
/// Remove a reserved peer. Returns the empty string or an error. The string
/// should encode only the PeerId e.g. `QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`.
#[method(name = "system_removeReservedPeer")]
async fn system_remove_reserved_peer(&self, peer_id: String) -> RpcResult<()>;
async fn system_remove_reserved_peer(&self, peer_id: String) -> Result<(), Error>;
/// Returns the list of reserved peers
#[method(name = "system_reservedPeers")]
async fn system_reserved_peers(&self) -> RpcResult<Vec<String>>;
async fn system_reserved_peers(&self) -> Result<Vec<String>, Error>;
/// Returns the roles the node is running as.
#[method(name = "system_nodeRoles")]
async fn system_node_roles(&self) -> RpcResult<Vec<NodeRole>>;
async fn system_node_roles(&self) -> Result<Vec<NodeRole>, Error>;
/// Returns the state of the syncing of the node: starting block, current best block, highest
/// known block.
#[method(name = "system_syncState")]
async fn system_sync_state(&self) -> RpcResult<SyncState<Number>>;
async fn system_sync_state(&self) -> Result<SyncState<Number>, Error>;
/// Adds the supplied directives to the current log filter
///
@@ -115,9 +113,9 @@ pub trait SystemApi<Hash, Number> {
///
/// `sync=debug,state=trace`
#[method(name = "system_addLogFilter")]
fn system_add_log_filter(&self, directives: String) -> RpcResult<()>;
fn system_add_log_filter(&self, directives: String) -> Result<(), Error>;
/// Resets the log filter to Substrate defaults
#[method(name = "system_resetLogFilter")]
fn system_reset_log_filter(&self) -> RpcResult<()>;
fn system_reset_log_filter(&self) -> Result<(), Error>;
}