mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 22:47:56 +00:00
frame/utils: introduce substrate-rpc-client crate for RPC utils (#12212)
* hack together a PoC * Update utils/frame/rpc-utils/Cargo.toml Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update utils/frame/rpc-utils/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * rpc_utils -> substrate_rpc_client * try runtime: remove keep connection * make CI happy * cargo fmt * fix ci * update lock file * fix * fix Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: kianenigma <kian@parity.io>
This commit is contained in:
@@ -22,13 +22,6 @@
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
use jsonrpsee::{
|
||||
core::{client::ClientT, Error as RpcError},
|
||||
proc_macros::rpc,
|
||||
rpc_params,
|
||||
ws_client::{WsClient, WsClientBuilder},
|
||||
};
|
||||
|
||||
use log::*;
|
||||
use serde::de::DeserializeOwned;
|
||||
use sp_core::{
|
||||
@@ -46,8 +39,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub mod rpc_api;
|
||||
use substrate_rpc_client::{rpc_params, ws_client, ChainApi, ClientT, StateApi, WsClient};
|
||||
|
||||
type KeyValue = (StorageKey, StorageData);
|
||||
type TopKeyValues = Vec<KeyValue>;
|
||||
@@ -58,40 +50,6 @@ const DEFAULT_TARGET: &str = "wss://rpc.polkadot.io:443";
|
||||
const BATCH_SIZE: usize = 1000;
|
||||
const PAGE: u32 = 1000;
|
||||
|
||||
#[rpc(client)]
|
||||
pub trait RpcApi<Hash> {
|
||||
#[method(name = "childstate_getKeys")]
|
||||
fn child_get_keys(
|
||||
&self,
|
||||
child_key: PrefixedStorageKey,
|
||||
prefix: StorageKey,
|
||||
hash: Option<Hash>,
|
||||
) -> Result<Vec<StorageKey>, RpcError>;
|
||||
|
||||
#[method(name = "childstate_getStorage")]
|
||||
fn child_get_storage(
|
||||
&self,
|
||||
child_key: PrefixedStorageKey,
|
||||
prefix: StorageKey,
|
||||
hash: Option<Hash>,
|
||||
) -> Result<StorageData, RpcError>;
|
||||
|
||||
#[method(name = "state_getStorage")]
|
||||
fn get_storage(&self, prefix: StorageKey, hash: Option<Hash>) -> Result<StorageData, RpcError>;
|
||||
|
||||
#[method(name = "state_getKeysPaged")]
|
||||
fn get_keys_paged(
|
||||
&self,
|
||||
prefix: Option<StorageKey>,
|
||||
count: u32,
|
||||
start_key: Option<StorageKey>,
|
||||
hash: Option<Hash>,
|
||||
) -> Result<Vec<StorageKey>, RpcError>;
|
||||
|
||||
#[method(name = "chain_getFinalizedHead")]
|
||||
fn finalized_head(&self) -> Result<Hash, RpcError>;
|
||||
}
|
||||
|
||||
/// The execution mode.
|
||||
#[derive(Clone)]
|
||||
pub enum Mode<B: BlockT> {
|
||||
@@ -140,14 +98,10 @@ impl Transport {
|
||||
if let Self::Uri(uri) = self {
|
||||
log::debug!(target: LOG_TARGET, "initializing remote client to {:?}", uri);
|
||||
|
||||
let ws_client = WsClientBuilder::default()
|
||||
.max_request_body_size(u32::MAX)
|
||||
.build(&uri)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!(target: LOG_TARGET, "error: {:?}", e);
|
||||
"failed to build ws client"
|
||||
})?;
|
||||
let ws_client = ws_client(uri).await.map_err(|e| {
|
||||
log::error!(target: LOG_TARGET, "error: {:?}", e);
|
||||
"failed to build ws client"
|
||||
})?;
|
||||
|
||||
*self = Self::RemoteClient(Arc::new(ws_client))
|
||||
}
|
||||
@@ -258,7 +212,7 @@ pub struct Builder<B: BlockT> {
|
||||
|
||||
// NOTE: ideally we would use `DefaultNoBound` here, but not worth bringing in frame-support for
|
||||
// that.
|
||||
impl<B: BlockT + DeserializeOwned> Default for Builder<B> {
|
||||
impl<B: BlockT> Default for Builder<B> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mode: Default::default(),
|
||||
@@ -272,7 +226,7 @@ impl<B: BlockT + DeserializeOwned> Default for Builder<B> {
|
||||
}
|
||||
|
||||
// Mode methods
|
||||
impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
impl<B: BlockT> Builder<B> {
|
||||
fn as_online(&self) -> &OnlineConfig<B> {
|
||||
match &self.mode {
|
||||
Mode::Online(config) => config,
|
||||
@@ -291,26 +245,38 @@ impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
}
|
||||
|
||||
// RPC methods
|
||||
impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
impl<B: BlockT> Builder<B>
|
||||
where
|
||||
B::Hash: DeserializeOwned,
|
||||
B::Header: DeserializeOwned,
|
||||
{
|
||||
async fn rpc_get_storage(
|
||||
&self,
|
||||
key: StorageKey,
|
||||
maybe_at: Option<B::Hash>,
|
||||
) -> Result<StorageData, &'static str> {
|
||||
trace!(target: LOG_TARGET, "rpc: get_storage");
|
||||
self.as_online().rpc_client().get_storage(key, maybe_at).await.map_err(|e| {
|
||||
error!(target: LOG_TARGET, "Error = {:?}", e);
|
||||
"rpc get_storage failed."
|
||||
})
|
||||
match self.as_online().rpc_client().storage(key, maybe_at).await {
|
||||
Ok(Some(res)) => Ok(res),
|
||||
Ok(None) => Err("get_storage not found"),
|
||||
Err(e) => {
|
||||
error!(target: LOG_TARGET, "Error = {:?}", e);
|
||||
Err("rpc get_storage failed.")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the latest finalized head.
|
||||
async fn rpc_get_head(&self) -> Result<B::Hash, &'static str> {
|
||||
trace!(target: LOG_TARGET, "rpc: finalized_head");
|
||||
self.as_online().rpc_client().finalized_head().await.map_err(|e| {
|
||||
error!(target: LOG_TARGET, "Error = {:?}", e);
|
||||
"rpc finalized_head failed."
|
||||
})
|
||||
|
||||
// sadly this pretty much unreadable...
|
||||
ChainApi::<(), _, B::Header, ()>::finalized_head(self.as_online().rpc_client())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(target: LOG_TARGET, "Error = {:?}", e);
|
||||
"rpc finalized_head failed."
|
||||
})
|
||||
}
|
||||
|
||||
/// Get all the keys at `prefix` at `hash` using the paged, safe RPC methods.
|
||||
@@ -325,7 +291,7 @@ impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
let page = self
|
||||
.as_online()
|
||||
.rpc_client()
|
||||
.get_keys_paged(Some(prefix.clone()), PAGE, last_key.clone(), Some(at))
|
||||
.storage_keys_paged(Some(prefix.clone()), PAGE, last_key.clone(), Some(at))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(target: LOG_TARGET, "Error = {:?}", e);
|
||||
@@ -471,19 +437,19 @@ impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
child_prefix: StorageKey,
|
||||
at: B::Hash,
|
||||
) -> Result<Vec<StorageKey>, &'static str> {
|
||||
let child_keys = self
|
||||
.as_online()
|
||||
.rpc_client()
|
||||
.child_get_keys(
|
||||
PrefixedStorageKey::new(prefixed_top_key.as_ref().to_vec()),
|
||||
child_prefix,
|
||||
Some(at),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(target: LOG_TARGET, "Error = {:?}", e);
|
||||
"rpc child_get_keys failed."
|
||||
})?;
|
||||
// This is deprecated and will generate a warning which causes the CI to fail.
|
||||
#[allow(warnings)]
|
||||
let child_keys = substrate_rpc_client::ChildStateApi::storage_keys(
|
||||
self.as_online().rpc_client(),
|
||||
PrefixedStorageKey::new(prefixed_top_key.as_ref().to_vec()),
|
||||
child_prefix,
|
||||
Some(at),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(target: LOG_TARGET, "Error = {:?}", e);
|
||||
"rpc child_get_keys failed."
|
||||
})?;
|
||||
|
||||
debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -497,7 +463,11 @@ impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
impl<B: BlockT> Builder<B>
|
||||
where
|
||||
B::Hash: DeserializeOwned,
|
||||
B::Header: DeserializeOwned,
|
||||
{
|
||||
/// Save the given data to the top keys snapshot.
|
||||
fn save_top_snapshot(&self, data: &[KeyValue], path: &PathBuf) -> Result<(), &'static str> {
|
||||
let mut path = path.clone();
|
||||
@@ -726,12 +696,13 @@ impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
|
||||
let child_kv = match self.mode.clone() {
|
||||
Mode::Online(_) => self.load_child_remote_and_maybe_save(&top_kv).await?,
|
||||
Mode::OfflineOrElseOnline(offline_config, _) =>
|
||||
Mode::OfflineOrElseOnline(offline_config, _) => {
|
||||
if let Ok(kv) = self.load_child_snapshot(&offline_config.state_snapshot.path) {
|
||||
kv
|
||||
} else {
|
||||
self.load_child_remote_and_maybe_save(&top_kv).await?
|
||||
},
|
||||
}
|
||||
},
|
||||
Mode::Offline(ref config) => self
|
||||
.load_child_snapshot(&config.state_snapshot.path)
|
||||
.map_err(|why| {
|
||||
@@ -749,7 +720,7 @@ impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
}
|
||||
|
||||
// Public methods
|
||||
impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
impl<B: BlockT> Builder<B> {
|
||||
/// Create a new builder.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
@@ -824,7 +795,13 @@ impl<B: BlockT + DeserializeOwned> Builder<B> {
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Public methods
|
||||
impl<B: BlockT> Builder<B>
|
||||
where
|
||||
B::Header: DeserializeOwned,
|
||||
{
|
||||
/// Build the test externalities.
|
||||
pub async fn build(self) -> Result<TestExternalities, &'static str> {
|
||||
let state_version = self.state_version;
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! WS RPC API for one off RPC calls to a substrate node.
|
||||
// TODO: Consolidate one off RPC calls https://github.com/paritytech/substrate/issues/8988
|
||||
|
||||
use jsonrpsee::{
|
||||
core::client::{Client, ClientT},
|
||||
rpc_params,
|
||||
types::ParamsSer,
|
||||
ws_client::{WsClient, WsClientBuilder},
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use sp_runtime::{generic::SignedBlock, traits::Block as BlockT};
|
||||
use std::sync::Arc;
|
||||
|
||||
enum RpcCall {
|
||||
GetHeader,
|
||||
GetFinalizedHead,
|
||||
GetBlock,
|
||||
GetRuntimeVersion,
|
||||
}
|
||||
|
||||
impl RpcCall {
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
RpcCall::GetHeader => "chain_getHeader",
|
||||
RpcCall::GetFinalizedHead => "chain_getFinalizedHead",
|
||||
RpcCall::GetBlock => "chain_getBlock",
|
||||
RpcCall::GetRuntimeVersion => "state_getRuntimeVersion",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// General purpose method for making RPC calls.
|
||||
async fn make_request<'a, T: DeserializeOwned>(
|
||||
client: &Arc<Client>,
|
||||
call: RpcCall,
|
||||
params: Option<ParamsSer<'a>>,
|
||||
) -> Result<T, String> {
|
||||
client
|
||||
.request::<T>(call.as_str(), params)
|
||||
.await
|
||||
.map_err(|e| format!("{} request failed: {:?}", call.as_str(), e))
|
||||
}
|
||||
|
||||
enum ConnectionPolicy {
|
||||
Reuse(Arc<Client>),
|
||||
Reconnect,
|
||||
}
|
||||
|
||||
/// Simple RPC service that is capable of keeping the connection.
|
||||
///
|
||||
/// Service will connect to `uri` for the first time already during initialization.
|
||||
///
|
||||
/// Be careful with reusing the connection in a multithreaded environment.
|
||||
pub struct RpcService {
|
||||
uri: String,
|
||||
policy: ConnectionPolicy,
|
||||
}
|
||||
|
||||
impl RpcService {
|
||||
/// Creates a new RPC service. If `keep_connection`, then connects to `uri` right away.
|
||||
pub async fn new<S: AsRef<str>>(uri: S, keep_connection: bool) -> Result<Self, String> {
|
||||
let policy = if keep_connection {
|
||||
ConnectionPolicy::Reuse(Arc::new(Self::build_client(uri.as_ref()).await?))
|
||||
} else {
|
||||
ConnectionPolicy::Reconnect
|
||||
};
|
||||
Ok(Self { uri: uri.as_ref().to_string(), policy })
|
||||
}
|
||||
|
||||
/// Returns the address at which requests are sent.
|
||||
pub fn uri(&self) -> String {
|
||||
self.uri.clone()
|
||||
}
|
||||
|
||||
/// Build a websocket client that connects to `self.uri`.
|
||||
async fn build_client<S: AsRef<str>>(uri: S) -> Result<WsClient, String> {
|
||||
WsClientBuilder::default()
|
||||
.max_request_body_size(u32::MAX)
|
||||
.build(uri)
|
||||
.await
|
||||
.map_err(|e| format!("`WsClientBuilder` failed to build: {:?}", e))
|
||||
}
|
||||
|
||||
/// Generic method for making RPC requests.
|
||||
async fn make_request<'a, T: DeserializeOwned>(
|
||||
&self,
|
||||
call: RpcCall,
|
||||
params: Option<ParamsSer<'a>>,
|
||||
) -> Result<T, String> {
|
||||
match self.policy {
|
||||
// `self.keep_connection` must have been `true`.
|
||||
ConnectionPolicy::Reuse(ref client) => make_request(client, call, params).await,
|
||||
ConnectionPolicy::Reconnect => {
|
||||
let client = Arc::new(Self::build_client(&self.uri).await?);
|
||||
make_request(&client, call, params).await
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the header of the block identified by `at`.
|
||||
pub async fn get_header<Block>(&self, at: Block::Hash) -> Result<Block::Header, String>
|
||||
where
|
||||
Block: BlockT,
|
||||
Block::Header: DeserializeOwned,
|
||||
{
|
||||
self.make_request(RpcCall::GetHeader, rpc_params!(at)).await
|
||||
}
|
||||
|
||||
/// Get the finalized head.
|
||||
pub async fn get_finalized_head<Block: BlockT>(&self) -> Result<Block::Hash, String> {
|
||||
self.make_request(RpcCall::GetFinalizedHead, None).await
|
||||
}
|
||||
|
||||
/// Get the signed block identified by `at`.
|
||||
pub async fn get_block<Block: BlockT + DeserializeOwned>(
|
||||
&self,
|
||||
at: Block::Hash,
|
||||
) -> Result<Block, String> {
|
||||
Ok(self
|
||||
.make_request::<SignedBlock<Block>>(RpcCall::GetBlock, rpc_params!(at))
|
||||
.await?
|
||||
.block)
|
||||
}
|
||||
|
||||
/// Get the runtime version of a given chain.
|
||||
pub async fn get_runtime_version<Block: BlockT + DeserializeOwned>(
|
||||
&self,
|
||||
at: Option<Block::Hash>,
|
||||
) -> Result<sp_version::RuntimeVersion, String> {
|
||||
self.make_request(RpcCall::GetRuntimeVersion, rpc_params!(at)).await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user