mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 23:01:01 +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,
|
SubstrateConfig,
|
||||||
},
|
},
|
||||||
|
dynamic::Value,
|
||||||
|
runtime_api::dynamic,
|
||||||
tx::PairSigner,
|
tx::PairSigner,
|
||||||
OnlineClient,
|
OnlineClient,
|
||||||
};
|
};
|
||||||
@@ -49,10 +51,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let alice = AccountKeyring::Alice.to_account_id().into();
|
let alice = AccountKeyring::Alice.to_account_id().into();
|
||||||
let api_tx = polkadot::runtime_api::AccountNonceApi::account_nonce(alice);
|
let api_tx = polkadot::runtime_api::AccountNonceApi::account_nonce(alice);
|
||||||
println!("RuntimeApi payload: {:?}", api_tx);
|
println!("RuntimeApi payload: {:?}", api_tx);
|
||||||
|
|
||||||
let bytes = api.runtime_api().at(None).await?.call(api_tx).await?;
|
let bytes = api.runtime_api().at(None).await?.call(api_tx).await?;
|
||||||
println!("Result: {:?}", bytes);
|
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.
|
// Send from Alice to Bob.
|
||||||
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
let signer = PairSigner::new(AccountKeyring::Alice.pair());
|
||||||
let dest = AccountKeyring::Bob.to_account_id().into();
|
let dest = AccountKeyring::Bob.to_account_id().into();
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ mod runtime_payload;
|
|||||||
mod runtime_types;
|
mod runtime_types;
|
||||||
|
|
||||||
pub use runtime_client::RuntimeApiClient;
|
pub use runtime_client::RuntimeApiClient;
|
||||||
pub use runtime_payload::RuntimeAPIPayload;
|
pub use runtime_payload::{RuntimeApiPayload, RuntimeAPIPayload, dynamic, DynamicRuntimeApiPayload, StaticRuntimeApiPayload};
|
||||||
pub use runtime_types::RuntimeApi;
|
pub use runtime_types::RuntimeApi;
|
||||||
|
|||||||
@@ -2,9 +2,23 @@
|
|||||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||||
// see LICENSE for license details.
|
// 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)]
|
#[derive(Debug)]
|
||||||
pub struct RuntimeAPIPayload<ReturnTy> {
|
pub struct RuntimeAPIPayload<ReturnTy> {
|
||||||
func_name: &'static str,
|
func_name: &'static str,
|
||||||
@@ -46,3 +60,115 @@ impl<ReturnTy> RuntimeAPIPayload<ReturnTy> {
|
|||||||
&self.data
|
&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,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::RuntimeAPIPayload;
|
use super::{
|
||||||
|
runtime_payload::{
|
||||||
|
DynamicRuntimeApiPayload,
|
||||||
|
RuntimeApiPayload,
|
||||||
|
},
|
||||||
|
RuntimeAPIPayload,
|
||||||
|
};
|
||||||
|
|
||||||
/// Execute runtime API calls.
|
/// Execute runtime API calls.
|
||||||
#[derive(Derivative)]
|
#[derive(Derivative)]
|
||||||
@@ -74,6 +80,7 @@ where
|
|||||||
let payload = payload;
|
let payload = payload;
|
||||||
let function = payload.func_name();
|
let function = payload.func_name();
|
||||||
let call_parameters = Some(payload.param_data());
|
let call_parameters = Some(payload.param_data());
|
||||||
|
println!("StaticCall: {:?}", call_parameters);
|
||||||
|
|
||||||
let data = client
|
let data = client
|
||||||
.rpc()
|
.rpc()
|
||||||
@@ -84,4 +91,36 @@ where
|
|||||||
Ok(result)
|
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