mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-22 11:27:58 +00:00
Generate runtime API from metadata (#294)
* Remove test macro * Remove client crate * Create tests crate and move pallet specific tests there * Extract client, remove metadata and extra, more demolition * Update substrate dependencies to git dependencies * Remove Store stuff for now * Comment out some Call usages * Add back Runtime trait coped from original System trait * Make subxt lib compile * Delete old proc macros and copy over type generation from chameleon * WIP make transfer balance test pass * Change to subxt attribute macro * WIP provide user defined type substitutes * User defined type substitutes compile * WIP submitting transactions * WIP transfer balance test * Fix macro * Cargo fmt * WIP generating storage hashers * WIP add AccountData trait for fetching the nonce * Support single type storage map keys * WIP impl AccountInfo retrieval * Fix up storage struct generation * Implement AccountData triait directly on storage entry * Borrow storage map key and convert account id * Implement storage fetch client methods * Remove legacy metadata storage key construction * Rename CheckEra to CheckMortality * Substitute perthings types for compact impls * Fmt * Downgrade dyn-clone for cargo-contract compat * Scale-fo 1.0 * scale-info 1.0 * Remove special range handling * Restore wildcard type params * Frame metadata 14.0 * WIP decoding events * WIP more dynamically decoding events * Fmt * Decode events, handle errors * Uncomment some tests * Remove unused get_mod function * Fix some warnings * Fix some more warnings * Fix some more warnings * Add tests mod * Rename node-runtime tests mod to frame * Fix some warnings * Fmt * WIP generate storage client with getters * Storage client compiling * Generate storage client api * Fix up system account query account ids * WIP generating tx api fns * Only generate tx api fields when calls available * Fix tx api call fns * Fmt * WIP generate event structs * call functions not async * Derive Eq for comparison on generated types * Generate event structs * Fix call name * Fmt * Update node runtime metadata to substrate c000780db * Download latest substrate release for integration testing * Fix event decoding * Remove unused imports * Fix plain storage access, total_issuance pass * Fmt * Restore contracts tests * Backoff connecting to substrate node * Add required TypeInfo impls for local SignedExtension impls * Remove unnecessary assert formatting * Fix handling of DispatchError * Refactor contracts tests * Troubleshooting contract not found * Remove more client feature stuff * Fix dynamic event variant decoding, write consumed index to output * Fmt * Use substrate branch with heavy dependency removed * Remove sp-rcp dependency, define types locally * Ignore cargo timeing files * Use my branch for substrate test deps * Fix storage key type gen * Comment out fetching contract info * Add key iteration, extract storage client from main client * Debugging key generation * Use substrate master branch * Fix call test * Remove TypeSegmenter and dynclone dependency * Publicly expose Rpc mod * Unused import warnings * Add getter for runtime metadata * Add pallet and event indices for raw events * Add is_call and is_event convenience trait functions * Add missing docs * Refactor tests crate * Restore remaining client tests * Fmt * Fix warnings * Restore get_mod as test helper and fmt * Use client references for api calls * Fix api usages with methods * Use Bytes for RawEvent debug * Update metadata * Restoring some Balances tests * Populate runtime storage metadata * Restore balances lock test * Restore Balances error test * Fmt * Restore transfer subscription API * Staking test * Restore another staking test * Restore another staking test * Restore another staking test * Partially restore chill_works_for_controller_only staking test * Fix fetching Optional storage entries * Restore staking bond test * Restore remaining staking tests * Fmt * Restore sudo tests * Add some system tests * Fmt * Resolve some todos * Remove pass through rpc methods on Client, expose via rpc() getter * Remove more rpc pass through methods * Remove submit tx pass through rpc methods * Add some comments to SubmittableExtrinsic methods * Construct the runtime api from the client * Fmt * Use From trait instead of new for AccountData query * Rename subxt_proc_macro crate to subxt_macro * Fix AccountData From impl * Extract codegen crate from macro crate * Fmt * Replace chameleon hidden field name * Extract StructDef for generating structs * More refactoring of StructDef, moving towards sharing with typegen * Replace explicit tests crate with single implicit integration tests crate * Rename from substrate-subxt to subxt * Fix runtime path relative to root Cargo.toml * Move RpcClient creation to RpcClient * WIP get examples to compile * Rename Runtime to Config trait * WIP implementing default Config * WIP implementing default extrinsic extras * fix metadata constants (#299) * Move DefaultConfig definition and impl to macro * Extract type substitute parsing to ir mod * Extract calls, events and storage from api generation * Add some hardcoded type substitute defaults * Fmt * Add utility pallet tests (#300) * add batch call test example * add pallet utility tests * add utility module * fix warnings * Add polkadot runtime metadata for example * Fix system errors and fmt * Add subxt-cli crate * Add metadata and codegen subcommands * Make subxt-cli codegen command work * Fmt * Add polkadot codegen test * Comment about how to run codegen * Derive AsCompact for structs with single concrete unsigned int field * Fix bitvec codegen, adds as non optional dependency * Regenerate polkadot api with bitvec fix * Edition 2021 * Fix polkadot codegen with bitvec * Polkadot balance transfer is working * Fix fetch remote * Fix transfer_subscribe example * Fix submit_and_watch example * Fmt * Generate storage iter method for iterating over keys * Fmt * Fix existential deposit test * Fix staking tests * Add option for custom generated type derives * Add generated type derives for test runtime api * Fmt * Copy WrapperTypeOpaque from substrate, add Encode/Decode * Fmt * Extract type generator to module, separate & fix tests * Fully qualified primitive and prelude types * Fix up remaining type gen tests * Skip formatting of generated polkadot example code * Remove empty utility test file. * Newline * Update cli/src/main.rs Co-authored-by: David <dvdplm@gmail.com> * Rename subxt-cli executable to subxt * Update src/client.rs Co-authored-by: David <dvdplm@gmail.com> * Add some code docs to TypeGenerator. * Extract TypePath to own file * Extract type def generation to separate file * Renamed ModuleType to TypeDefGen * Fmt * Factor out type parameter from final_key * Fix some type paths * Resolve some todos * Resolve some panic todos in events * Add EventsDecodingError * Decode compact composite types with a single primitive field * Decode compact composite types with a single primitive field * Update src/metadata.rs Co-authored-by: Andrew Plaza <aplaza@liquidthink.net> * Remove Perbill compact substitute types * Remove todos regarding maintaining Rust code items, promoted to follow up issue. * Remove todo regarding overridding default config impl * Remove todo regarding overridding default Extra * Remove todo regarding AccountData storage type defintion * Remove todo regarding borrowing storage key arguments * Remove type substitution tests todo * Remove `Box` field name type hack todo * Remove Compact todo * Remove sudo todos * Remove BitVec implementation todo * Fmt * Add health warning to README * Fix up health warning Co-authored-by: Paulo Martins <paulormart@users.noreply.github.com> Co-authored-by: David <dvdplm@gmail.com> Co-authored-by: Andrew Plaza <aplaza@liquidthink.net>
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
mod calls;
|
||||
mod events;
|
||||
mod storage;
|
||||
|
||||
use super::GeneratedTypeDerives;
|
||||
use crate::{
|
||||
ir,
|
||||
struct_def::StructDef,
|
||||
types::TypeGenerator,
|
||||
};
|
||||
use codec::Decode;
|
||||
use frame_metadata::{
|
||||
v14::RuntimeMetadataV14,
|
||||
RuntimeMetadata,
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
use heck::SnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro_error::abort_call_site;
|
||||
use quote::{
|
||||
format_ident,
|
||||
quote,
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
io::Read,
|
||||
path,
|
||||
string::ToString,
|
||||
};
|
||||
use syn::{
|
||||
parse_quote,
|
||||
punctuated::Punctuated,
|
||||
};
|
||||
|
||||
pub fn generate_runtime_api<P>(
|
||||
item_mod: syn::ItemMod,
|
||||
path: P,
|
||||
generated_type_derives: Option<Punctuated<syn::Path, syn::Token![,]>>,
|
||||
) -> TokenStream2
|
||||
where
|
||||
P: AsRef<path::Path>,
|
||||
{
|
||||
let mut file = fs::File::open(&path).unwrap_or_else(|e| {
|
||||
abort_call_site!("Failed to open {}: {}", path.as_ref().to_string_lossy(), e)
|
||||
});
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)
|
||||
.unwrap_or_else(|e| abort_call_site!("Failed to read metadata file: {}", e));
|
||||
|
||||
let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..])
|
||||
.unwrap_or_else(|e| abort_call_site!("Failed to decode metadata: {}", e));
|
||||
|
||||
let mut derives = GeneratedTypeDerives::default();
|
||||
if let Some(user_derives) = generated_type_derives {
|
||||
derives.append(user_derives.iter().cloned())
|
||||
}
|
||||
|
||||
let generator = RuntimeGenerator::new(metadata);
|
||||
generator.generate_runtime(item_mod, derives)
|
||||
}
|
||||
|
||||
pub struct RuntimeGenerator {
|
||||
metadata: RuntimeMetadataV14,
|
||||
}
|
||||
|
||||
impl RuntimeGenerator {
|
||||
pub fn new(metadata: RuntimeMetadataPrefixed) -> Self {
|
||||
match metadata.1 {
|
||||
RuntimeMetadata::V14(v14) => Self { metadata: v14 },
|
||||
_ => panic!("Unsupported metadata version {:?}", metadata.1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_runtime(
|
||||
&self,
|
||||
item_mod: syn::ItemMod,
|
||||
derives: GeneratedTypeDerives,
|
||||
) -> TokenStream2 {
|
||||
let item_mod_ir = ir::ItemMod::from(item_mod);
|
||||
|
||||
// some hardcoded default type substitutes, can be overridden by user
|
||||
let mut type_substitutes = [
|
||||
(
|
||||
"bitvec::order::Lsb0",
|
||||
parse_quote!(::subxt::bitvec::order::Lsb0),
|
||||
),
|
||||
(
|
||||
"bitvec::order::Msb0",
|
||||
parse_quote!(::subxt::bitvec::order::Msb0),
|
||||
),
|
||||
(
|
||||
"sp_core::crypto::AccountId32",
|
||||
parse_quote!(::subxt::sp_core::crypto::AccountId32),
|
||||
),
|
||||
(
|
||||
"primitive_types::H256",
|
||||
parse_quote!(::subxt::sp_core::H256),
|
||||
),
|
||||
(
|
||||
"sp_runtime::multiaddress::MultiAddress",
|
||||
parse_quote!(::subxt::sp_runtime::MultiAddress),
|
||||
),
|
||||
(
|
||||
"frame_support::traits::misc::WrapperKeepOpaque",
|
||||
parse_quote!(::subxt::WrapperKeepOpaque),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
.map(|(path, substitute): &(&str, syn::TypePath)| {
|
||||
(path.to_string(), substitute.clone())
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
for (path, substitute) in item_mod_ir.type_substitutes().iter() {
|
||||
type_substitutes.insert(path.to_string(), substitute.clone());
|
||||
}
|
||||
|
||||
let type_gen = TypeGenerator::new(
|
||||
&self.metadata.types,
|
||||
"runtime_types",
|
||||
type_substitutes,
|
||||
derives.clone(),
|
||||
);
|
||||
let types_mod = type_gen.generate_types_mod();
|
||||
let types_mod_ident = types_mod.ident();
|
||||
let pallets_with_mod_names = self
|
||||
.metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.map(|pallet| {
|
||||
(
|
||||
pallet,
|
||||
format_ident!("{}", pallet.name.to_string().to_snake_case()),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let modules = pallets_with_mod_names.iter().map(|(pallet, mod_name)| {
|
||||
let calls = if let Some(ref calls) = pallet.calls {
|
||||
calls::generate_calls(&type_gen, pallet, calls, types_mod_ident)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let event = if let Some(ref event) = pallet.event {
|
||||
events::generate_events(&type_gen, pallet, event, types_mod_ident)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let storage_mod = if let Some(ref storage) = pallet.storage {
|
||||
storage::generate_storage(&type_gen, pallet, storage, types_mod_ident)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
quote! {
|
||||
pub mod #mod_name {
|
||||
use super::#types_mod_ident;
|
||||
#calls
|
||||
#event
|
||||
#storage_mod
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let outer_event_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name);
|
||||
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index);
|
||||
|
||||
p.event.as_ref().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Event),
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let outer_event = quote! {
|
||||
#derives
|
||||
pub enum Event {
|
||||
#( #outer_event_variants )*
|
||||
}
|
||||
};
|
||||
|
||||
let mod_ident = item_mod_ir.ident;
|
||||
let pallets_with_storage =
|
||||
pallets_with_mod_names
|
||||
.iter()
|
||||
.filter_map(|(pallet, pallet_mod_name)| {
|
||||
pallet.storage.as_ref().map(|_| pallet_mod_name)
|
||||
});
|
||||
let pallets_with_calls =
|
||||
pallets_with_mod_names
|
||||
.iter()
|
||||
.filter_map(|(pallet, pallet_mod_name)| {
|
||||
pallet.calls.as_ref().map(|_| pallet_mod_name)
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[allow(dead_code, unused_imports, non_camel_case_types)]
|
||||
pub mod #mod_ident {
|
||||
#outer_event
|
||||
#( #modules )*
|
||||
#types_mod
|
||||
|
||||
/// Default configuration of common types for a target Substrate runtime.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct DefaultConfig;
|
||||
|
||||
impl ::subxt::Config for DefaultConfig {
|
||||
type Index = u32;
|
||||
type BlockNumber = u32;
|
||||
type Hash = ::subxt::sp_core::H256;
|
||||
type Hashing = ::subxt::sp_runtime::traits::BlakeTwo256;
|
||||
type AccountId = ::subxt::sp_runtime::AccountId32;
|
||||
type Address = ::subxt::sp_runtime::MultiAddress<Self::AccountId, u32>;
|
||||
type Header = ::subxt::sp_runtime::generic::Header<
|
||||
Self::BlockNumber, ::subxt::sp_runtime::traits::BlakeTwo256
|
||||
>;
|
||||
type Signature = ::subxt::sp_runtime::MultiSignature;
|
||||
type Extrinsic = ::subxt::sp_runtime::OpaqueExtrinsic;
|
||||
}
|
||||
|
||||
impl ::subxt::ExtrinsicExtraData<DefaultConfig> for DefaultConfig {
|
||||
type AccountData = AccountData;
|
||||
type Extra = ::subxt::DefaultExtra<DefaultConfig>;
|
||||
}
|
||||
|
||||
pub type AccountData = self::system::storage::Account;
|
||||
|
||||
impl ::subxt::AccountData<DefaultConfig> for AccountData {
|
||||
fn nonce(result: &<Self as ::subxt::StorageEntry>::Value) -> <DefaultConfig as ::subxt::Config>::Index {
|
||||
result.nonce
|
||||
}
|
||||
fn storage_entry(account_id: <DefaultConfig as ::subxt::Config>::AccountId) -> Self {
|
||||
Self(account_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RuntimeApi<T: ::subxt::Config + ::subxt::ExtrinsicExtraData<T>> {
|
||||
pub client: ::subxt::Client<T>,
|
||||
}
|
||||
|
||||
impl<T> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T>
|
||||
where
|
||||
T: ::subxt::Config + ::subxt::ExtrinsicExtraData<T>,
|
||||
{
|
||||
fn from(client: ::subxt::Client<T>) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> RuntimeApi<T>
|
||||
where
|
||||
T: ::subxt::Config + ::subxt::ExtrinsicExtraData<T>,
|
||||
{
|
||||
pub fn storage(&'a self) -> StorageApi<'a, T> {
|
||||
StorageApi { client: &self.client }
|
||||
}
|
||||
|
||||
pub fn tx(&'a self) -> TransactionApi<'a, T> {
|
||||
TransactionApi { client: &self.client }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StorageApi<'a, T>
|
||||
where
|
||||
T: ::subxt::Config + ::subxt::ExtrinsicExtraData<T>,
|
||||
{
|
||||
client: &'a ::subxt::Client<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> StorageApi<'a, T>
|
||||
where
|
||||
T: ::subxt::Config + ::subxt::ExtrinsicExtraData<T>,
|
||||
{
|
||||
#(
|
||||
pub fn #pallets_with_storage(&self) -> #pallets_with_storage::storage::StorageApi<'a, T> {
|
||||
#pallets_with_storage::storage::StorageApi::new(self.client)
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
pub struct TransactionApi<'a, T: ::subxt::Config + ::subxt::ExtrinsicExtraData<T>> {
|
||||
client: &'a ::subxt::Client<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> TransactionApi<'a, T>
|
||||
where
|
||||
T: ::subxt::Config + ::subxt::ExtrinsicExtraData<T>,
|
||||
{
|
||||
#(
|
||||
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T> {
|
||||
#pallets_with_calls::calls::TransactionApi::new(self.client)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_structs_from_variants(
|
||||
type_gen: &TypeGenerator,
|
||||
type_id: u32,
|
||||
error_message_type_name: &str,
|
||||
) -> Vec<StructDef> {
|
||||
let ty = type_gen.resolve_type(type_id);
|
||||
if let scale_info::TypeDef::Variant(variant) = ty.type_def() {
|
||||
variant
|
||||
.variants()
|
||||
.iter()
|
||||
.map(|var| {
|
||||
StructDef::new(
|
||||
var.name(),
|
||||
var.fields(),
|
||||
Some(syn::parse_quote!(pub)),
|
||||
type_gen,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
abort_call_site!(
|
||||
"{} type should be an variant/enum type",
|
||||
error_message_type_name
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user