mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-01 04:17:57 +00:00
XXX/Dynamic: Add dynamic Runtime API calls
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
@@ -20,6 +20,8 @@ use subxt::{
|
||||
},
|
||||
SubstrateConfig,
|
||||
},
|
||||
dynamic::Value,
|
||||
runtime_api::dynamic,
|
||||
tx::PairSigner,
|
||||
OnlineClient,
|
||||
};
|
||||
@@ -49,10 +51,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let alice = AccountKeyring::Alice.to_account_id().into();
|
||||
let api_tx = polkadot::runtime_api::AccountNonceApi::account_nonce(alice);
|
||||
println!("RuntimeApi payload: {:?}", api_tx);
|
||||
|
||||
let bytes = api.runtime_api().at(None).await?.call(api_tx).await?;
|
||||
println!("Result: {:?}", bytes);
|
||||
|
||||
let alice = AccountKeyring::Alice.to_account_id();
|
||||
let api_tx = dynamic::<polkadot::runtime_api::AccountNonceApi::account_nonce_target>(
|
||||
"AccountNonceApi_account_nonce",
|
||||
vec![Value::from_bytes(&alice)],
|
||||
);
|
||||
let bytes = api.runtime_api().at(None).await?.dyn_call(api_tx).await?;
|
||||
println!("Result: {:?}", bytes);
|
||||
|
||||
// Send from Alice to Bob.
|
||||
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
let dest = AccountKeyring::Bob.to_account_id().into();
|
||||
|
||||
@@ -9,5 +9,5 @@ mod runtime_payload;
|
||||
mod runtime_types;
|
||||
|
||||
pub use runtime_client::RuntimeApiClient;
|
||||
pub use runtime_payload::RuntimeAPIPayload;
|
||||
pub use runtime_payload::{RuntimeApiPayload, RuntimeAPIPayload, dynamic, DynamicRuntimeApiPayload, StaticRuntimeApiPayload};
|
||||
pub use runtime_types::RuntimeApi;
|
||||
|
||||
@@ -2,9 +2,23 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Payload for a runtime API fn.
|
||||
use codec::{
|
||||
Decode,
|
||||
Encode,
|
||||
};
|
||||
use scale_value::Composite;
|
||||
|
||||
use crate::{
|
||||
Error,
|
||||
Metadata,
|
||||
};
|
||||
|
||||
/// Payload for calling into a runtime API function.
|
||||
#[derive(Debug)]
|
||||
pub struct RuntimeAPIPayload<ReturnTy> {
|
||||
func_name: &'static str,
|
||||
@@ -46,3 +60,115 @@ impl<ReturnTy> RuntimeAPIPayload<ReturnTy> {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
/// RuntimeApiPayload
|
||||
pub trait RuntimeApiPayload {
|
||||
/// The return type into which the result of the call is interpreted.
|
||||
type Target;
|
||||
|
||||
// TODO: Could do with some lifetimes.
|
||||
/// The function name of the runtime API.
|
||||
fn func_name(&self) -> String;
|
||||
|
||||
/// Encode arguments to the provided output.
|
||||
fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>)
|
||||
-> Result<(), Error>;
|
||||
|
||||
/// Encode arguments and return the output. This is a convenience
|
||||
/// wrapper around [`RuntimeApiPayload::encode_params_to`].
|
||||
fn encode_args(&self, metadata: &Metadata) -> Result<Vec<u8>, Error> {
|
||||
let mut v = Vec::new();
|
||||
self.encode_args_to(metadata, &mut v)?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
/// StaticRuntimeApiPayload
|
||||
pub struct StaticRuntimeApiPayload<ArgData, ReturnTy> {
|
||||
func_name: &'static str,
|
||||
data: ArgData,
|
||||
_marker: PhantomData<ReturnTy>,
|
||||
}
|
||||
|
||||
impl<ArgData, ReturnTy> RuntimeApiPayload for StaticRuntimeApiPayload<ArgData, ReturnTy>
|
||||
where
|
||||
ArgData: Encode,
|
||||
ReturnTy: Decode,
|
||||
{
|
||||
type Target = ReturnTy;
|
||||
|
||||
fn func_name(&self) -> String {
|
||||
self.func_name.into()
|
||||
}
|
||||
|
||||
fn encode_args_to(
|
||||
&self,
|
||||
_metadata: &Metadata,
|
||||
out: &mut Vec<u8>,
|
||||
) -> Result<(), Error> {
|
||||
self.data.encode_to(out);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// DynamicRuntimeApiPayload
|
||||
pub struct DynamicRuntimeApiPayload<ReturnTy> {
|
||||
func_name: &'static str,
|
||||
fields: Composite<()>,
|
||||
_marker: PhantomData<ReturnTy>,
|
||||
}
|
||||
|
||||
// impl<'a, ReturnTy> DynamicRuntimeApiPayload<'a, ReturnTy> {
|
||||
// pub fn into_value(self) -> Value<()> {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Construct a dynamic runtime API call.
|
||||
pub fn dynamic<ReturnTy>(
|
||||
func_name: &'static str,
|
||||
fields: impl Into<Composite<()>>,
|
||||
) -> DynamicRuntimeApiPayload<ReturnTy> {
|
||||
DynamicRuntimeApiPayload {
|
||||
func_name: func_name.into(),
|
||||
fields: fields.into(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl<ReturnTy> RuntimeApiPayload for DynamicRuntimeApiPayload<ReturnTy>
|
||||
where
|
||||
ReturnTy: Decode,
|
||||
{
|
||||
type Target = ReturnTy;
|
||||
|
||||
fn encode_args_to(
|
||||
&self,
|
||||
metadata: &Metadata,
|
||||
out: &mut Vec<u8>,
|
||||
) -> Result<(), Error> {
|
||||
let args = match &self.fields {
|
||||
// TODO: Composite::Named WIP.
|
||||
Composite::Named(_) => panic!("Composite::Named unsupported yet."),
|
||||
Composite::Unnamed(args) => args,
|
||||
};
|
||||
|
||||
let fn_metadata = metadata.runtime_fn(&self.func_name)?;
|
||||
let param_ty = fn_metadata.params_ty_ids();
|
||||
|
||||
if param_ty.len() != args.len() {
|
||||
return Err(Error::Other(
|
||||
"Provided different number of params than expected".into(),
|
||||
))
|
||||
}
|
||||
|
||||
for (value, ty) in args.iter().zip(param_ty) {
|
||||
scale_value::scale::encode_as_type(value, *ty, metadata.types(), out)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn func_name(&self) -> String {
|
||||
self.func_name.to_owned().into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,13 @@ use std::{
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use super::RuntimeAPIPayload;
|
||||
use super::{
|
||||
runtime_payload::{
|
||||
DynamicRuntimeApiPayload,
|
||||
RuntimeApiPayload,
|
||||
},
|
||||
RuntimeAPIPayload,
|
||||
};
|
||||
|
||||
/// Execute runtime API calls.
|
||||
#[derive(Derivative)]
|
||||
@@ -74,6 +80,7 @@ where
|
||||
let payload = payload;
|
||||
let function = payload.func_name();
|
||||
let call_parameters = Some(payload.param_data());
|
||||
println!("StaticCall: {:?}", call_parameters);
|
||||
|
||||
let data = client
|
||||
.rpc()
|
||||
@@ -84,4 +91,36 @@ where
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamic call.
|
||||
pub fn dyn_call<DynCall>(
|
||||
&self,
|
||||
payload: DynCall,
|
||||
) -> impl Future<Output = Result<DynCall::Target, Error>>
|
||||
where
|
||||
DynCall: RuntimeApiPayload,
|
||||
<DynCall as RuntimeApiPayload>::Target: Decode,
|
||||
{
|
||||
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 payload = payload;
|
||||
let function = payload.func_name();
|
||||
|
||||
let bytes = payload.encode_args(&client.metadata())?;
|
||||
let call_parameters = Some(bytes.as_slice());
|
||||
println!("DynCall: {:?}", call_parameters);
|
||||
|
||||
let data = client
|
||||
.rpc()
|
||||
.state_call(&function, call_parameters, Some(block_hash))
|
||||
.await?;
|
||||
|
||||
let result: DynCall::Target = Decode::decode(&mut &data.0[..])?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user