XXX: Generate Runtime API

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
Alexandru Vasile
2023-02-07 19:16:19 +02:00
parent 7038902c74
commit cd497aba54
10 changed files with 57231 additions and 27 deletions
+1 -1
View File
@@ -8,9 +8,9 @@ use color_eyre::eyre::{
WrapErr,
};
use frame_metadata::{
v15::RuntimeMetadataV15,
RuntimeMetadata,
RuntimeMetadataPrefixed,
RuntimeMetadataV15,
META_RESERVED,
};
use jsonrpsee::client_transport::ws::Uri;
File diff suppressed because one or more lines are too long
+18 -8
View File
@@ -7,11 +7,14 @@
mod calls;
mod constants;
mod events;
mod runtime_api;
mod storage;
use scale_info::form::PortableForm;
use subxt_metadata::get_metadata_per_pallet_hash;
use self::runtime_api::generate_runtime_api;
use super::DerivesRegistry;
use crate::{
ir,
@@ -139,7 +142,11 @@ pub fn generate_runtime_api_from_bytes(
type_substitutes: TypeSubstitutes,
crate_path: CratePath,
) -> TokenStream2 {
let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..])
let decoded: Option<frame_metadata::OpaqueMetadata> = Decode::decode(&mut &*bytes)
.unwrap_or_else(|e| abort_call_site!("Failed to decode opaque metadata: {}", e));
let decoded = decoded.unwrap();
let bytes = &decoded.0;
let metadata: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes[..])
.unwrap_or_else(|e| abort_call_site!("Failed to decode metadata: {}", e));
let generator = RuntimeGenerator::new(metadata);
@@ -157,8 +164,6 @@ fn generate_runtime_call_api(
) -> TokenStream2 {
let mut result = quote!();
println!("inside runtime: {:?}", runtime);
for trait_ in runtime {
for method in &trait_.methods {
let trait_name = trait_.name.clone();
@@ -188,8 +193,6 @@ fn generate_runtime_call_api(
}
}
println!("Code gen would generate: {}", result);
result
}
@@ -247,6 +250,10 @@ impl RuntimeGenerator {
})
.collect::<Vec<_>>();
// Generate the runtime API.
let runtime_api =
generate_runtime_api(&self.metadata, &type_gen, types_mod_ident, &crate_path);
// Pallet names and their length are used to create PALLETS array.
// The array is used to identify the pallets composing the metadata for
// validation of just those pallets.
@@ -377,9 +384,9 @@ impl RuntimeGenerator {
TransactionApi
}
pub mod runtime_api {
#runtime_api_fns
}
// pub mod runtime_api {
// #runtime_api_fns
// }
pub struct ConstantsApi;
impl ConstantsApi {
@@ -417,6 +424,9 @@ impl RuntimeGenerator {
Ok(())
}
}
/// Runtime API.
#runtime_api
}
}
}
+106
View File
@@ -0,0 +1,106 @@
// 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::{
types::TypeGenerator,
CratePath,
};
use frame_metadata::v15::{
RuntimeMetadataV15,
TraitMetadata,
};
use proc_macro2::TokenStream as TokenStream2;
use quote::{
format_ident,
quote,
};
use scale_info::form::PortableForm;
/// Generates the accessor functions for the given trait.
fn generate_trait_api(
trait_: &TraitMetadata<PortableForm>,
type_gen: &TypeGenerator,
types_mod_ident: &syn::Ident,
crate_path: &CratePath,
) -> TokenStream2 {
let trait_name = &trait_.name;
let docs = &trait_.docs;
const VEC_ACC: &'static str = "result";
let vec_acc = format_ident!("{}", VEC_ACC);
let methods: Vec<_> = trait_.methods.iter().map(|method| {
let method_name = format_ident!("{}", &method.name);
let func_name = format!("{}_{}", trait_name, method_name);
let docs = &method.docs;
let inputs: Vec<_> = method.inputs.iter().map(|input| {
let name = format_ident!("{}", &input.name);
let ty = type_gen.resolve_type_path(input.ty.id());
let param = quote!(#name: #ty);
let encoded = quote!(#name.encode_to(&mut #vec_acc));
(param, encoded)
}).collect();
let params = inputs.iter().map(|(param, _)| param);
let encoded = inputs.iter().map(|(_, encoded)| encoded);
quote!(
#( #[doc = #docs ] )*
pub fn #method_name( #( #params, )* ) -> #crate_path::runtime_api::RuntimeAPIPayload {
let mut #vec_acc = Vec::new();
#( #encoded; )*
#crate_path::runtime_api::RuntimeAPIPayload::new(
#func_name,
#vec_acc,
[0; 32],
)
}
)
}).collect();
let trait_name = format_ident!("{}", &trait_.name);
quote!(
#( #[doc = #docs ] )*
pub mod #trait_name {
use super::root_mod;
use super::#types_mod_ident;
use #crate_path::ext::codec::Encode;
#( #methods )*
}
)
}
/// Generate the runtime API.
pub fn generate_runtime_api(
metadata: &RuntimeMetadataV15,
type_gen: &TypeGenerator,
types_mod_ident: &syn::Ident,
crate_path: &CratePath,
) -> TokenStream2 {
let runtime = &metadata.runtime;
if runtime.is_empty() {
return quote!()
}
let runtime_mods = runtime
.iter()
.map(|rt| generate_trait_api(rt, type_gen, &types_mod_ident, crate_path));
quote! {
pub mod runtime_api {
use super::root_mod;
use super::#types_mod_ident;
#( #runtime_mods )*
}
}
}
+20 -2
View File
@@ -63,7 +63,16 @@ async fn fetch_metadata_ws(url: &Uri) -> Result<String, FetchMetadataError> {
.max_notifs_per_subscription(4096)
.build_with_tokio(sender, receiver);
Ok(client.request("state_getMetadata", rpc_params![]).await?)
use codec::Encode;
let bytes = 15u32.encode();
let param = format!("0x{}", hex::encode(&bytes));
Ok(client
.request(
"state_call",
rpc_params!["Metadata_metadata_at_version", &param],
)
.await?)
}
async fn fetch_metadata_http(url: &Uri) -> Result<String, FetchMetadataError> {
@@ -71,7 +80,16 @@ async fn fetch_metadata_http(url: &Uri) -> Result<String, FetchMetadataError> {
.request_timeout(Duration::from_secs(180))
.build(url.to_string())?;
Ok(client.request("state_getMetadata", rpc_params![]).await?)
use codec::Encode;
let bytes = 15u32.encode();
let param = format!("0x{}", hex::encode(&bytes));
Ok(client
.request(
"state_call",
rpc_params!["Metadata_metadata_at_version", &param],
)
.await?)
}
#[derive(Debug)]
+10 -2
View File
@@ -24,14 +24,22 @@ use subxt::{
OnlineClient,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
use codec::Encode;
#[subxt::subxt(runtime_metadata_url = "http://localhost:9933")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
polkadot::runtime_api::Metadata_metadata();
let api_tx = polkadot::runtime_api::Core::version();
println!("RuntimeApi payload: {:?}", api_tx);
let api = OnlineClient::<PolkadotConfig>::new().await?;
let bytes = api.runtime_api().at(None).await?.call(api_tx).await?;
println!("Result: {:?}", bytes);
Ok(())
}
-14
View File
@@ -159,30 +159,16 @@ impl<T: Config> OnlineClient<T> {
async fn fetch_metadata(rpc: &Rpc<T>) -> Result<Metadata, Error> {
use codec::Encode;
let param = 15u32.encode();
let bytes = rpc
.state_call("Metadata_metadata_at_version", Some(&param), None)
.await?;
// println!("GOT BYTES: {:?}", bytes);
let decoded: Option<OpaqueMetadata> = Decode::decode(&mut &*bytes)?;
println!("Decoded opaque");
let decoded = decoded.unwrap();
let bytes = &decoded.0;
let meta: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes[..])?;
// let metadata: Metadata = meta.try_into()?;
// println!("Availb methods {:?}", decoded.0);
// let cursor = &mut &*bytes;
// let _ = <Compact<u32>>::decode(cursor)?;
// let meta: frame_metadata::RuntimeMetadataPrefixed = Decode::decode(cursor)?;
println!("METADATA {:#?}", meta);
let metadata: Metadata = meta.try_into()?;
+2
View File
@@ -5,7 +5,9 @@
//! Types associated with executing runtime API calls.
mod runtime_client;
mod runtime_payload;
mod runtime_types;
pub use runtime_client::RuntimeApiClient;
pub use runtime_payload::RuntimeAPIPayload;
pub use runtime_types::RuntimeApi;
+44
View File
@@ -0,0 +1,44 @@
// 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.
/// Payload for a runtime API fn.
#[derive(Debug)]
pub struct RuntimeAPIPayload {
func_name: &'static str,
data: Vec<u8>,
validation_hash: Option<[u8; 32]>,
}
impl RuntimeAPIPayload {
/// Create a new [`RuntimeAPIPayload`] from static data.
pub fn new(
func_name: &'static str,
data: Vec<u8>,
validation_hash: [u8; 32],
) -> Self {
RuntimeAPIPayload {
func_name,
data,
validation_hash: Some(validation_hash),
}
}
/// Do not validate this prior to submitting it.
pub fn unvalidated(self) -> Self {
Self {
validation_hash: None,
..self
}
}
/// Returns the function name.
pub fn func_name(&self) -> &'static str {
&self.func_name
}
/// Returns the parameter data.
pub fn param_data(&self) -> &[u8] {
&self.data
}
}
+24
View File
@@ -13,6 +13,8 @@ use std::{
marker::PhantomData,
};
use super::RuntimeAPIPayload;
/// Execute runtime API calls.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
@@ -56,4 +58,26 @@ where
Ok(data.0)
}
}
/// Execute a runtime API call for the given payload.
pub fn call(
&self,
payload: RuntimeAPIPayload,
) -> impl Future<Output = Result<Vec<u8>, Error>> {
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 call_parameters = Some(payload.param_data());
let data = client
.rpc()
.state_call(function, call_parameters, Some(block_hash))
.await?;
Ok(data.0)
}
}
}