mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 01:11:10 +00:00
Add Runtime API to execute runtime calls (#777)
* rpc: Add RuntimeAPI call via `state_call` method Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Add runtime API Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Expose the RuntimeAPI client Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * tests: Test the runtime API call against the metadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * client: Fetch the metadata from runtime API Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * blocks: Fix doc typo Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * blocks: Use &str instead of String to identify fn names Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update subxt/src/runtime_api/runtime_client.rs Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
@@ -18,6 +18,7 @@ use crate::{
|
||||
},
|
||||
events,
|
||||
rpc::types::ChainBlockResponse,
|
||||
runtime_api::RuntimeApi,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use futures::lock::Mutex as AsyncMutex;
|
||||
@@ -89,6 +90,11 @@ where
|
||||
self.cached_events.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Execute a runtime API call at this block.
|
||||
pub async fn runtime_api(&self) -> Result<RuntimeApi<T, C>, Error> {
|
||||
Ok(RuntimeApi::new(self.client.clone(), self.hash()))
|
||||
}
|
||||
}
|
||||
|
||||
/// The body of a block.
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::{
|
||||
constants::ConstantsClient,
|
||||
events::EventsClient,
|
||||
rpc::types::RuntimeVersion,
|
||||
runtime_api::RuntimeApiClient,
|
||||
storage::StorageClient,
|
||||
tx::TxClient,
|
||||
Config,
|
||||
@@ -49,6 +50,11 @@ pub trait OfflineClientT<T: Config>: Clone + Send + Sync + 'static {
|
||||
fn blocks(&self) -> BlocksClient<T, Self> {
|
||||
BlocksClient::new(self.clone())
|
||||
}
|
||||
|
||||
/// Work with runtime API.
|
||||
fn runtime_api(&self) -> RuntimeApiClient<T, Self> {
|
||||
RuntimeApiClient::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// A client that is capable of performing offline-only operations.
|
||||
|
||||
@@ -19,12 +19,18 @@ use crate::{
|
||||
Rpc,
|
||||
RpcClientT,
|
||||
},
|
||||
runtime_api::RuntimeApiClient,
|
||||
storage::StorageClient,
|
||||
tx::TxClient,
|
||||
Config,
|
||||
Metadata,
|
||||
};
|
||||
use codec::{
|
||||
Compact,
|
||||
Decode,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
use futures::future;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
@@ -95,7 +101,7 @@ impl<T: Config> OnlineClient<T> {
|
||||
let (genesis_hash, runtime_version, metadata) = future::join3(
|
||||
rpc.genesis_hash(),
|
||||
rpc.runtime_version(None),
|
||||
rpc.metadata(None),
|
||||
OnlineClient::fetch_metadata(&rpc),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -109,6 +115,15 @@ impl<T: Config> OnlineClient<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Fetch the metadata from substrate using the runtime API.
|
||||
async fn fetch_metadata(rpc: &Rpc<T>) -> Result<Metadata, Error> {
|
||||
let bytes = rpc.state_call("Metadata_metadata", None, None).await?;
|
||||
let cursor = &mut &*bytes;
|
||||
let _ = <Compact<u32>>::decode(cursor)?;
|
||||
let meta: RuntimeMetadataPrefixed = Decode::decode(cursor)?;
|
||||
Ok(meta.try_into()?)
|
||||
}
|
||||
|
||||
/// Create an object which can be used to keep the runtime up to date
|
||||
/// in a separate thread.
|
||||
///
|
||||
@@ -214,6 +229,11 @@ impl<T: Config> OnlineClient<T> {
|
||||
pub fn blocks(&self) -> BlocksClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::blocks(self)
|
||||
}
|
||||
|
||||
/// Work with runtime API.
|
||||
pub fn runtime_api(&self) -> RuntimeApiClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::runtime_api(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> OfflineClientT<T> for OnlineClient<T> {
|
||||
|
||||
@@ -155,6 +155,7 @@ pub mod error;
|
||||
pub mod events;
|
||||
pub mod metadata;
|
||||
pub mod rpc;
|
||||
pub mod runtime_api;
|
||||
pub mod storage;
|
||||
pub mod tx;
|
||||
pub mod utils;
|
||||
|
||||
@@ -371,6 +371,25 @@ impl<T: Config> Rpc<T> {
|
||||
Ok(xt_hash)
|
||||
}
|
||||
|
||||
/// Execute a runtime API call.
|
||||
pub async fn state_call(
|
||||
&self,
|
||||
function: &str,
|
||||
call_parameters: Option<&[u8]>,
|
||||
at: Option<T::Hash>,
|
||||
) -> Result<types::Bytes, Error> {
|
||||
let call_parameters = call_parameters.unwrap_or_default();
|
||||
|
||||
let bytes: types::Bytes = self
|
||||
.client
|
||||
.request(
|
||||
"state_call",
|
||||
rpc_params![function, to_hex(call_parameters), at],
|
||||
)
|
||||
.await?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// Create and submit an extrinsic and return a subscription to the events triggered.
|
||||
pub async fn watch_extrinsic<X: Encode>(
|
||||
&self,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Types associated with executing runtime API calls.
|
||||
|
||||
mod runtime_client;
|
||||
mod runtime_types;
|
||||
|
||||
pub use runtime_client::RuntimeApiClient;
|
||||
pub use runtime_types::RuntimeApi;
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::runtime_types::RuntimeApi;
|
||||
|
||||
use crate::{
|
||||
client::OnlineClientT,
|
||||
error::Error,
|
||||
Config,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use std::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Execute runtime API calls.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = "Client: Clone"))]
|
||||
pub struct RuntimeApiClient<T, Client> {
|
||||
client: Client,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, Client> RuntimeApiClient<T, Client> {
|
||||
/// Create a new [`RuntimeApiClient`]
|
||||
pub fn new(client: Client) -> Self {
|
||||
Self {
|
||||
client,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> RuntimeApiClient<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Obtain a runtime API at some block hash.
|
||||
pub fn at(
|
||||
&self,
|
||||
block_hash: Option<T::Hash>,
|
||||
) -> impl Future<Output = Result<RuntimeApi<T, Client>, Error>> + Send + 'static {
|
||||
// Clone and pass the client in like this so that we can explicitly
|
||||
// return a Future that's Send + 'static, rather than tied to &self.
|
||||
let client = self.client.clone();
|
||||
async move {
|
||||
// If block hash is not provided, get the hash
|
||||
// for the latest block and use that.
|
||||
let block_hash = match block_hash {
|
||||
Some(hash) => hash,
|
||||
None => {
|
||||
client
|
||||
.rpc()
|
||||
.block_hash(None)
|
||||
.await?
|
||||
.expect("substrate RPC returns the best block when no block number is provided; qed")
|
||||
}
|
||||
};
|
||||
|
||||
Ok(RuntimeApi::new(client, block_hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{
|
||||
client::OnlineClientT,
|
||||
error::Error,
|
||||
Config,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use std::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Execute runtime API calls.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = "Client: Clone"))]
|
||||
pub struct RuntimeApi<T: Config, Client> {
|
||||
client: Client,
|
||||
block_hash: T::Hash,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Config, Client> RuntimeApi<T, Client> {
|
||||
/// Create a new [`RuntimeApi`]
|
||||
pub(crate) fn new(client: Client, block_hash: T::Hash) -> Self {
|
||||
Self {
|
||||
client,
|
||||
block_hash,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> RuntimeApi<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Execute a raw runtime API call.
|
||||
pub fn call_raw<'a>(
|
||||
&self,
|
||||
function: &'a str,
|
||||
call_parameters: Option<&'a [u8]>,
|
||||
) -> impl Future<Output = Result<Vec<u8>, Error>> + 'a {
|
||||
let client = self.client.clone();
|
||||
let block_hash = self.block_hash;
|
||||
// Ensure that the returned future doesn't have a lifetime tied to api.runtime_api(),
|
||||
// which is a temporary thing we'll be throwing away quickly:
|
||||
async move {
|
||||
let data = client
|
||||
.rpc()
|
||||
.state_call(function, call_parameters, Some(block_hash))
|
||||
.await?;
|
||||
Ok(data.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user