introduce jsonrpsee proc macros API

This commit is contained in:
Niklas
2022-02-03 18:09:47 +01:00
parent 4053e30399
commit 489650c9c4
8 changed files with 261 additions and 274 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ chameleon = "0.1.0"
scale-info = { version = "1.0.0", features = ["bit-vec"] }
futures = "0.3.13"
hex = "0.4.3"
jsonrpsee = { version = "0.8.0", features = ["async-client", "client-ws-transport"] }
jsonrpsee = { version = "0.8.0", features = ["macros", "async-client", "client-ws-transport"] }
log = "0.4.14"
num-traits = { version = "0.2.14", default-features = false }
serde = { version = "1.0.124", features = ["derive"] }
+14 -6
View File
@@ -31,6 +31,7 @@ use crate::{
Rpc,
RpcClient,
RuntimeVersion,
SubxtRpcApiClient,
SystemProperties,
},
storage::StorageClient,
@@ -89,14 +90,21 @@ impl ClientBuilder {
crate::rpc::build_ws_client(url).await?
};
let rpc = Rpc::new(client);
let (metadata, genesis_hash, runtime_version, properties) = future::join4(
rpc.metadata(),
rpc.genesis_hash(),
rpc.runtime_version(None),
rpc.system_properties(),
let (metadata_bytes, genesis_hash, runtime_version, properties) = future::join4(
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::metadata(&*rpc),
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::genesis_hash(&*rpc),
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::runtime_version(
&*rpc, None,
),
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::system_properties(
&*rpc,
),
)
.await;
let metadata = metadata?;
let metadata_bytes = metadata_bytes?;
let meta: frame_metadata::RuntimeMetadataPrefixed =
Decode::decode(&mut &metadata_bytes[..])?;
let metadata: Metadata = meta.try_into()?;
let events_decoder = EventsDecoder::new(metadata.clone());
+7 -1
View File
@@ -78,7 +78,13 @@ pub trait Config: 'static {
type Signature: Verify + Encode + Send + Sync + 'static;
/// Extrinsic type within blocks.
type Extrinsic: Parameter + Extrinsic + Debug + MaybeSerializeDeserialize;
// TODO(niklasad1): I have no idea if this ok or not ^^
type Extrinsic: Parameter
+ Extrinsic
+ Debug
+ MaybeSerializeDeserialize
+ Send
+ Sync;
}
/// Parameter trait copied from `substrate::frame_support`
+139 -225
View File
@@ -23,29 +23,22 @@
use std::{
collections::HashMap,
ops::Deref,
sync::Arc,
};
use crate::{
error::BasicError,
storage::StorageKeyPrefix,
subscription::{
EventStorageSubscription,
FinalizedEventStorageSubscription,
SystemEvents,
},
Config,
Metadata,
};
use codec::{
Decode,
Encode,
};
use core::{
convert::TryInto,
marker::PhantomData,
};
use frame_metadata::RuntimeMetadataPrefixed;
use codec::Encode;
use core::marker::PhantomData;
use jsonrpsee::core::RpcResult;
pub use jsonrpsee::{
client_transport::ws::{
InvalidUri,
@@ -67,6 +60,7 @@ pub use jsonrpsee::{
Error as RpcError,
JsonValue,
},
proc_macros::rpc,
rpc_params,
};
use serde::{
@@ -87,6 +81,132 @@ use sp_runtime::generic::{
SignedBlock,
};
/// subxt RPC API.
#[rpc(client)]
pub trait SubxtRpcApi<Hash, Header, Xt: Serialize> {
/// Fetch a storage key
#[method(name = "state_getStorage")]
async fn storage(
&self,
key: &StorageKey,
hash: Option<Hash>,
) -> RpcResult<Option<StorageData>>;
/// Returns the keys with prefix with pagination support.
/// Up to `count` keys will be returned.
/// If `start_key` is passed, return next keys in storage in lexicographic order.
#[method(name = "state_getKeysPaged")]
async fn storage_keys_paged(
&self,
prefix: Option<StorageKey>,
count: u32,
start_key: Option<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<Vec<StorageKey>>;
/// Query historical storage entries
#[method(name = "state_queryStorage")]
async fn query_storage(
&self,
keys: Vec<StorageKey>,
from: Hash,
to: Option<Hash>,
) -> RpcResult<Vec<StorageChangeSet<Hash>>>;
/// Query historical storage entries
#[method(name = "state_queryStorageAt")]
async fn query_storage_at(
&self,
keys: &[StorageKey],
at: Option<Hash>,
) -> RpcResult<Vec<StorageChangeSet<Hash>>>;
/// Fetch the genesis hash
#[method(name = "chain_getBlockHash")]
async fn genesis_hash(&self) -> RpcResult<Hash>;
/// Fetch the metadata as bytes.
#[method(name = "state_getMetadata")]
async fn metadata(&self) -> RpcResult<sp_core::Bytes>;
/// Fetch system properties
#[method(name = "system_properties")]
async fn system_properties(&self) -> RpcResult<SystemProperties>;
/// Fetch system chain
#[method(name = "system_chain")]
async fn system_chain(&self) -> RpcResult<String>;
/// Fetch system name
#[method(name = "system_name")]
async fn system_name(&self) -> RpcResult<String>;
/// Fetch system version
#[method(name = "system_version")]
async fn system_version(&self) -> RpcResult<String>;
/// Fetch the runtime version
#[method(name = "state_getRuntimeVersion")]
async fn runtime_version(&self, at: Option<Hash>) -> RpcResult<RuntimeVersion>;
/// Get a header
#[method(name = "state_getRuntimeVersion")]
async fn header(&self, hash: Option<Hash>) -> RpcResult<Option<Header>>;
/// Get a block hash, returns hash of latest block by default
#[method(name = "chain_getBlockHash")]
async fn block_hash(
&self,
block_number: Option<BlockNumber>,
) -> RpcResult<Option<Hash>>;
/// Get a block hash of the latest finalized block
#[method(name = "chain_getFinalizedHead")]
async fn finalized_head(&self) -> RpcResult<Hash>;
/// Get proof of storage entries at a specific block's state.
#[method(name = "state_getReadProof")]
async fn read_proof(
&self,
keys: Vec<StorageKey>,
hash: Option<Hash>,
) -> RpcResult<ReadProof<Hash>>;
/// Get a Block
#[method(name = "chain_getBlock")]
async fn block(
&self,
hash: Option<Hash>,
) -> RpcResult<Option<SignedBlock<Block<Header, Xt>>>>;
/// Insert a key into the keystore.
#[method(name = "author_insertKey")]
async fn insert_key(
&self,
key_type: String,
suri: String,
public: Bytes,
) -> RpcResult<()>;
/// Generate new session keys and returns the corresponding public keys.
#[method(name = "author_rotateKeys")]
async fn rotate_keys(&self) -> RpcResult<Bytes>;
/// Checks if the keystore has private keys for the given session public keys.
///
/// `session_keys` is the SCALE encoded session keys object from the runtime.
///
/// Returns `true` iff all private keys could be found.
#[method(name = "author_hasSessionKeys")]
async fn has_session_keys(&self, session_keys: Bytes) -> RpcResult<bool>;
/// 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")]
async fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult<bool>;
}
/// A number type that can be serialized both as a number or a string that encodes a number in a
/// string.
///
@@ -228,6 +348,14 @@ impl<T: Config> Clone for Rpc<T> {
}
}
impl<T: Config> Deref for Rpc<T> {
type Target = RpcClient;
fn deref(&self) -> &Self::Target {
&*self.client
}
}
impl<T: Config> Rpc<T> {
/// Create a new [`Rpc`]
pub fn new(client: RpcClient) -> Self {
@@ -237,175 +365,6 @@ impl<T: Config> Rpc<T> {
}
}
/// Fetch a storage key
pub async fn storage(
&self,
key: &StorageKey,
hash: Option<T::Hash>,
) -> Result<Option<StorageData>, BasicError> {
let params = rpc_params![key, hash];
let data = self.client.request("state_getStorage", params).await?;
Ok(data)
}
/// Returns the keys with prefix with pagination support.
/// Up to `count` keys will be returned.
/// If `start_key` is passed, return next keys in storage in lexicographic order.
pub async fn storage_keys_paged(
&self,
prefix: Option<StorageKeyPrefix>,
count: u32,
start_key: Option<StorageKey>,
hash: Option<T::Hash>,
) -> Result<Vec<StorageKey>, BasicError> {
let prefix = prefix.map(|p| p.to_storage_key());
let params = rpc_params![prefix, count, start_key, hash];
let data = self.client.request("state_getKeysPaged", params).await?;
Ok(data)
}
/// Query historical storage entries
pub async fn query_storage(
&self,
keys: Vec<StorageKey>,
from: T::Hash,
to: Option<T::Hash>,
) -> Result<Vec<StorageChangeSet<T::Hash>>, BasicError> {
let params = rpc_params![keys, from, to];
self.client
.request("state_queryStorage", params)
.await
.map_err(Into::into)
}
/// Query historical storage entries
pub async fn query_storage_at(
&self,
keys: &[StorageKey],
at: Option<T::Hash>,
) -> Result<Vec<StorageChangeSet<T::Hash>>, BasicError> {
let params = rpc_params![keys, at];
self.client
.request("state_queryStorageAt", params)
.await
.map_err(Into::into)
}
/// Fetch the genesis hash
pub async fn genesis_hash(&self) -> Result<T::Hash, BasicError> {
let block_zero = Some(ListOrValue::Value(NumberOrHex::Number(0)));
let params = rpc_params![block_zero];
let list_or_value: ListOrValue<Option<T::Hash>> =
self.client.request("chain_getBlockHash", params).await?;
match list_or_value {
ListOrValue::Value(genesis_hash) => {
genesis_hash.ok_or_else(|| "Genesis hash not found".into())
}
ListOrValue::List(_) => Err("Expected a Value, got a List".into()),
}
}
/// Fetch the metadata
pub async fn metadata(&self) -> Result<Metadata, BasicError> {
let bytes: Bytes = self
.client
.request("state_getMetadata", rpc_params![])
.await?;
let meta: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes[..])?;
let metadata: Metadata = meta.try_into()?;
Ok(metadata)
}
/// Fetch system properties
pub async fn system_properties(&self) -> Result<SystemProperties, BasicError> {
Ok(self
.client
.request("system_properties", rpc_params![])
.await?)
}
/// Fetch system chain
pub async fn system_chain(&self) -> Result<String, BasicError> {
Ok(self.client.request("system_chain", rpc_params![]).await?)
}
/// Fetch system name
pub async fn system_name(&self) -> Result<String, BasicError> {
Ok(self.client.request("system_name", rpc_params![]).await?)
}
/// Fetch system version
pub async fn system_version(&self) -> Result<String, BasicError> {
Ok(self.client.request("system_version", rpc_params![]).await?)
}
/// Get a header
pub async fn header(
&self,
hash: Option<T::Hash>,
) -> Result<Option<T::Header>, BasicError> {
let params = rpc_params![hash];
let header = self.client.request("chain_getHeader", params).await?;
Ok(header)
}
/// Get a block hash, returns hash of latest block by default
pub async fn block_hash(
&self,
block_number: Option<BlockNumber>,
) -> Result<Option<T::Hash>, BasicError> {
let block_number = block_number.map(ListOrValue::Value);
let params = rpc_params![block_number];
let list_or_value = self.client.request("chain_getBlockHash", params).await?;
match list_or_value {
ListOrValue::Value(hash) => Ok(hash),
ListOrValue::List(_) => Err("Expected a Value, got a List".into()),
}
}
/// Get a block hash of the latest finalized block
pub async fn finalized_head(&self) -> Result<T::Hash, BasicError> {
let hash = self
.client
.request("chain_getFinalizedHead", rpc_params![])
.await?;
Ok(hash)
}
/// Get a Block
pub async fn block(
&self,
hash: Option<T::Hash>,
) -> Result<Option<ChainBlock<T>>, BasicError> {
let params = rpc_params![hash];
let block = self.client.request("chain_getBlock", params).await?;
Ok(block)
}
/// Get proof of storage entries at a specific block's state.
pub async fn read_proof(
&self,
keys: Vec<StorageKey>,
hash: Option<T::Hash>,
) -> Result<ReadProof<T::Hash>, BasicError> {
let params = rpc_params![keys, hash];
let proof = self.client.request("state_getReadProof", params).await?;
Ok(proof)
}
/// Fetch the runtime version
pub async fn runtime_version(
&self,
at: Option<T::Hash>,
) -> Result<RuntimeVersion, BasicError> {
let params = rpc_params![at];
let version = self
.client
.request("state_getRuntimeVersion", params)
.await?;
Ok(version)
}
/// Subscribe to System Events that are imported into blocks.
///
/// *WARNING* these may not be included in the finalized chain, use
@@ -496,51 +455,6 @@ impl<T: Config> Rpc<T> {
.await?;
Ok(subscription)
}
/// Insert a key into the keystore.
pub async fn insert_key(
&self,
key_type: String,
suri: String,
public: Bytes,
) -> Result<(), BasicError> {
let params = rpc_params![key_type, suri, public];
self.client.request("author_insertKey", params).await?;
Ok(())
}
/// Generate new session keys and returns the corresponding public keys.
pub async fn rotate_keys(&self) -> Result<Bytes, BasicError> {
Ok(self
.client
.request("author_rotateKeys", rpc_params![])
.await?)
}
/// Checks if the keystore has private keys for the given session public keys.
///
/// `session_keys` is the SCALE encoded session keys object from the runtime.
///
/// Returns `true` iff all private keys could be found.
pub async fn has_session_keys(
&self,
session_keys: Bytes,
) -> Result<bool, BasicError> {
let params = rpc_params![session_keys];
Ok(self.client.request("author_hasSessionKeys", params).await?)
}
/// Checks if the keystore has private keys for the given public key and key type.
///
/// Returns `true` if a private key could be found.
pub async fn has_key(
&self,
public_key: Bytes,
key_type: String,
) -> Result<bool, BasicError> {
let params = rpc_params![public_key, key_type];
Ok(self.client.request("author_hasKey", params).await?)
}
}
/// Build WS RPC client from URL
+48 -16
View File
@@ -35,7 +35,10 @@ use crate::{
Metadata,
MetadataError,
},
rpc::Rpc,
rpc::{
Rpc,
SubxtRpcApiClient,
},
Config,
StorageHasher,
};
@@ -164,7 +167,14 @@ impl<'a, T: Config> StorageClient<'a, T> {
key: StorageKey,
hash: Option<T::Hash>,
) -> Result<Option<V>, BasicError> {
if let Some(data) = self.rpc.storage(&key, hash).await? {
if let Some(data) =
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::storage(
&*self.rpc.client,
&key,
hash,
)
.await?
{
Ok(Some(Decode::decode(&mut &data.0[..])?))
} else {
Ok(None)
@@ -177,7 +187,13 @@ impl<'a, T: Config> StorageClient<'a, T> {
key: StorageKey,
hash: Option<T::Hash>,
) -> Result<Option<StorageData>, BasicError> {
self.rpc.storage(&key, hash).await
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::storage(
&*self.rpc.client,
&key,
hash,
)
.await
.map_err(Into::into)
}
/// Fetch a StorageKey with an optional block hash.
@@ -215,7 +231,14 @@ impl<'a, T: Config> StorageClient<'a, T> {
from: T::Hash,
to: Option<T::Hash>,
) -> Result<Vec<StorageChangeSet<T::Hash>>, BasicError> {
self.rpc.query_storage(keys, from, to).await
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::query_storage(
&*self.rpc.client,
keys,
from,
to,
)
.await
.map_err(Into::into)
}
/// Fetch up to `count` keys for a storage map in lexicographic order.
@@ -227,10 +250,15 @@ impl<'a, T: Config> StorageClient<'a, T> {
start_key: Option<StorageKey>,
hash: Option<T::Hash>,
) -> Result<Vec<StorageKey>, BasicError> {
let prefix = StorageKeyPrefix::new::<F>();
let keys = self
.rpc
.storage_keys_paged(Some(prefix), count, start_key, hash)
let prefix = StorageKeyPrefix::new::<F>().to_storage_key();
let keys =
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::storage_keys_paged(
&*self.rpc.client,
Some(prefix),
count,
start_key,
hash,
)
.await?;
Ok(keys)
}
@@ -243,10 +271,12 @@ impl<'a, T: Config> StorageClient<'a, T> {
let hash = if let Some(hash) = hash {
hash
} else {
self.rpc
.block_hash(None)
.await?
.expect("didn't pass a block number; qed")
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::block_hash(
&*self.rpc.client,
None,
)
.await?
.expect("didn't pass a block number; qed")
};
Ok(KeyIter {
client: self.clone(),
@@ -287,10 +317,12 @@ impl<'a, T: Config, F: StorageEntry> KeyIter<'a, T, F> {
self.start_key = keys.last().cloned();
let change_sets = self
.client
.rpc
.query_storage_at(&keys, Some(self.hash))
let change_sets =
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::query_storage_at(
&*self.client.rpc.client,
&keys,
Some(self.hash),
)
.await?;
for change_set in change_sets {
for (k, v) in change_set.changes {
+11 -5
View File
@@ -20,7 +20,10 @@ use crate::{
EventsDecoder,
RawEvent,
},
rpc::Rpc,
rpc::{
Rpc,
SubxtRpcApiClient,
},
Config,
Event,
Phase,
@@ -210,10 +213,13 @@ impl<T: Config> FinalizedEventStorageSubscription<T> {
read_subscription_response("HeaderSubscription", &mut self.subscription)
.await?;
self.storage_changes.extend(
self.rpc
.query_storage_at(&[self.storage_key.clone()], Some(header.hash()))
.await
.ok()?,
SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::query_storage_at(
&*self.rpc.client,
&[self.storage_key.clone()],
Some(header.hash()),
)
.await
.ok()?,
);
}
}
+18 -17
View File
@@ -31,7 +31,10 @@ use crate::{
RuntimeError,
TransactionError,
},
rpc::SubstrateTransactionStatus,
rpc::{
SubstrateTransactionStatus,
SubxtRpcApiClient,
},
subscription::SystemEvents,
Config,
Phase,
@@ -389,12 +392,12 @@ impl<'client, T: Config, E: Decode> TransactionInBlock<'client, T, E> {
/// **Note:** This has to download block details from the node and decode events
/// from them.
pub async fn fetch_events(&self) -> Result<TransactionEvents<T>, BasicError> {
let block = self
.client
.rpc()
.block(Some(self.block_hash))
.await?
.ok_or(BasicError::Transaction(TransactionError::BlockHashNotFound))?;
let block = SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::block(
&*self.client.rpc().client,
Some(self.block_hash),
)
.await?
.ok_or(BasicError::Transaction(TransactionError::BlockHashNotFound))?;
let extrinsic_idx = block.block.extrinsics
.iter()
@@ -406,16 +409,14 @@ impl<'client, T: Config, E: Decode> TransactionInBlock<'client, T, E> {
// extrinsic, the extrinsic should be in there somewhere..
.ok_or(BasicError::Transaction(TransactionError::BlockHashNotFound))?;
let raw_events = self
.client
.rpc()
.storage(
&StorageKey::from(SystemEvents::new()),
Some(self.block_hash),
)
.await?
.map(|s| s.0)
.unwrap_or_else(Vec::new);
let raw_events = SubxtRpcApiClient::<T::Hash, T::Header, T::Extrinsic>::storage(
&*self.client.rpc().client,
&StorageKey::from(SystemEvents::new()),
Some(self.block_hash),
)
.await?
.map(|s| s.0)
.unwrap_or_else(Vec::new);
let events = self
.client
+23 -3
View File
@@ -19,6 +19,11 @@ use crate::{
test_node_process_with,
utils::node_runtime::system,
};
use subxt::{
rpc::SubxtRpcApiClient,
Config,
DefaultConfig,
};
use sp_core::storage::{
well_known_keys,
@@ -127,7 +132,22 @@ async fn test_iter() {
async fn fetch_system_info() {
let node_process = test_node_process().await;
let client = node_process.client();
assert_eq!(client.rpc().system_chain().await.unwrap(), "Development");
assert_eq!(client.rpc().system_name().await.unwrap(), "Substrate Node");
assert!(!client.rpc().system_version().await.unwrap().is_empty());
let rpc = client.rpc().client.clone();
type Hash = <DefaultConfig as Config>::Hash;
assert_eq!(
SubxtRpcApiClient::<Hash>::system_chain(&*rpc)
.await
.unwrap(),
"Development"
);
assert_eq!(
SubxtRpcApiClient::<Hash>::system_name(&*rpc).await.unwrap(),
"Substrate Node"
);
assert!(!SubxtRpcApiClient::<Hash>::system_version(&*rpc)
.await
.unwrap()
.is_empty());
}