diff --git a/Cargo.toml b/Cargo.toml index 26a02c73da..c1d737fc3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,47 +16,6 @@ description = "Submit extrinsics (transactions) to a substrate node via RPC" keywords = ["parity", "substrate", "blockchain"] include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] -[features] -default = ["tokio1"] -# jsonrpsee can be configured to use tokio02 or tokio1. -tokio02 = ["jsonrpsee-http-client/tokio02", "jsonrpsee-ws-client/tokio02"] -tokio1 = ["jsonrpsee-http-client/tokio1", "jsonrpsee-ws-client/tokio1"] - [dependencies] -async-trait = "0.1.49" -bitvec = { version = "0.20.1", default-features = false, features = ["alloc"] } -codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive", "full", "bit-vec"] } -chameleon = "0.1.0" -scale-info = { version = "1.0.0", features = ["bit-vec"] } -futures = "0.3.13" -hex = "0.4.3" -jsonrpsee-proc-macros = "0.3.0" -jsonrpsee-ws-client = { version = "0.3.0", default-features = false } -jsonrpsee-http-client = { version = "0.3.0", default-features = false } -jsonrpsee-types = "0.3.0" -log = "0.4.14" -num-traits = { version = "0.2.14", default-features = false } -serde = { version = "1.0.124", features = ["derive"] } -serde_json = "1.0.64" -thiserror = "1.0.24" -url = "2.2.1" - subxt-macro = { version = "0.1.0", path = "macro" } -sp-arithmetic = { package = "sp-arithmetic", git = "https://github.com/paritytech/substrate/", branch = "master" } -sp-core = { package = "sp-core", git = "https://github.com/paritytech/substrate/", branch = "master" } -sp-runtime = { package = "sp-runtime", git = "https://github.com/paritytech/substrate/", branch = "master" } -sp-version = { package = "sp-version", git = "https://github.com/paritytech/substrate/", branch = "master" } - -frame-metadata = "14.0.0" - -[dev-dependencies] -assert_matches = "1.5.0" -async-std = { version = "1.9.0", features = ["attributes", "tokio1"] } -env_logger = "0.8.3" -tempdir = "0.3.7" -wabt = "0.10.0" -which = "4.0.2" - -sp-keyring = { package = "sp-keyring", git = "https://github.com/paritytech/substrate/", branch = "master" } - diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ea153c53cd..9d7b4185f8 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -9,24 +9,4 @@ path = "src/main.rs" [dependencies] # perform subxt codegen -subxt-codegen = { version = "0.1.0", path = "../codegen" } -# parse command line args -structopt = "0.3.25" -# make the request to a substrate node to get the metadata -ureq = { version = "2.2.0", features = ["json"] } -# colourful error reports -color-eyre = "0.5.11" -# serialize the metadata -serde = { version = "1.0.130", features = ["derive"] } -# serialize as json -serde_json = "1.0.68" -# hex encoded metadata to bytes -hex = "0.4.3" -# actual metadata types -frame-metadata = { version = "14.0.0", features = ["v14", "std"] } -# decode bytes into the metadata types -scale = { package = "parity-scale-codec", version = "2.3.0", default-features = false } -# handle urls to communicate with substrate nodes -url = { version = "2.2.2", features = ["serde"] } -# generate the item mod for codegen -syn = "1.0.80" \ No newline at end of file +subxt-codegen = { version = "0.1.0", path = "../codegen" } \ No newline at end of file diff --git a/cli/src/main.rs b/cli/src/main.rs index 271a720903..c8bec923dc 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -14,144 +14,6 @@ // You should have received a copy of the GNU General Public License // along with subxt. If not, see . -use color_eyre::eyre::{ - self, - WrapErr, -}; -use frame_metadata::RuntimeMetadataPrefixed; -use scale::{ - Decode, - Input, -}; -use std::{ - fs, - io::{ - self, - Read, - Write, - }, - path::PathBuf, -}; -use structopt::StructOpt; - -/// Utilities for working with substrate metadata for subxt. -#[derive(Debug, StructOpt)] -struct Opts { - #[structopt(subcommand)] - command: Command, -} - -#[derive(Debug, StructOpt)] -enum Command { - /// Download metadata from a substrate node, for use with `subxt` codegen. - #[structopt(name = "metadata")] - Metadata { - /// the url of the substrate node to query for metadata - #[structopt( - name = "url", - long, - parse(try_from_str), - default_value = "http://localhost:9933" - )] - url: url::Url, - /// the format of the metadata to display: `json`, `hex` or `bytes` - #[structopt(long, short, default_value = "json")] - format: String, - }, - /// Generate runtime API client code from metadata. - /// - /// # Example (with code formatting) - /// - /// `subxt codegen | rustfmt --edition=2018 --emit=stdout` - Codegen { - /// the url of the substrate node to query for metadata for codegen. - #[structopt(name = "url", long, parse(try_from_str))] - url: Option, - /// the path to the encoded metadata file. - #[structopt(short, long, parse(from_os_str))] - file: Option, - }, -} - -fn main() -> color_eyre::Result<()> { - color_eyre::install()?; - let args = Opts::from_args(); - - match args.command { - Command::Metadata { url, format } => { - let (hex_data, bytes) = fetch_metadata(&url)?; - - match format.as_str() { - "json" => { - let metadata = - ::decode(&mut &bytes[..])?; - let json = serde_json::to_string_pretty(&metadata)?; - println!("{}", json); - Ok(()) - } - "hex" => { - println!("{}", hex_data); - Ok(()) - } - "bytes" => Ok(io::stdout().write_all(&bytes)?), - _ => { - Err(eyre::eyre!( - "Unsupported format `{}`, expected `json`, `hex` or `bytes`", - format - )) - } - } - } - Command::Codegen { url, file } => { - if let Some(file) = file.as_ref() { - if url.is_some() { - eyre::bail!("specify one of `--url` or `--file` but not both") - }; - - let mut file = fs::File::open(file)?; - let mut bytes = Vec::new(); - file.read_to_end(&mut bytes)?; - codegen(&mut &bytes[..])?; - return Ok(()) - } - - let url = url.unwrap_or_else(|| { - url::Url::parse("http://localhost:9933").expect("default url is valid") - }); - let (_, bytes) = fetch_metadata(&url)?; - codegen(&mut &bytes[..])?; - return Ok(()) - } - } -} - -fn fetch_metadata(url: &url::Url) -> color_eyre::Result<(String, Vec)> { - let resp = ureq::post(url.as_str()) - .set("Content-Type", "application/json") - .send_json(ureq::json!({ - "jsonrpc": "2.0", - "method": "state_getMetadata", - "id": 1 - })) - .context("error fetching metadata from the substrate node")?; - let json: serde_json::Value = resp.into_json()?; - - let hex_data = json["result"] - .as_str() - .map(ToString::to_string) - .ok_or(eyre::eyre!("metadata result field should be a string"))?; - let bytes = hex::decode(hex_data.trim_start_matches("0x"))?; - - Ok((hex_data, bytes)) -} - -fn codegen(encoded: &mut I) -> color_eyre::Result<()> { - let metadata = ::decode(encoded)?; - let generator = subxt_codegen::RuntimeGenerator::new(metadata); - let item_mod = syn::parse_quote!( - pub mod api {} - ); - let runtime_api = generator.generate_runtime(item_mod, Default::default()); - println!("{}", runtime_api); - Ok(()) +fn main() { + println!("CLI placeholder") } diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 4fd6c5f084..0563ec4f3e 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -4,18 +4,3 @@ version = "0.1.0" edition = "2021" [dependencies] -async-trait = "0.1.49" -codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive", "full", "bit-vec"] } -darling = "0.13.0" -frame-metadata = "14.0" -heck = "0.3.2" -proc-macro2 = "1.0.24" -proc-macro-crate = "0.1.5" -proc-macro-error = "1.0.4" -quote = "1.0.8" -syn = "1.0.58" -scale-info = { version = "1.0.0", features = ["bit-vec"] } - -[dev-dependencies] -bitvec = { version = "0.20.1", default-features = false, features = ["alloc"] } -pretty_assertions = "0.6.1" diff --git a/codegen/src/api/calls.rs b/codegen/src/api/calls.rs deleted file mode 100644 index 6d52f597af..0000000000 --- a/codegen/src/api/calls.rs +++ /dev/null @@ -1,101 +0,0 @@ -// 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 . - -use crate::types::TypeGenerator; -use frame_metadata::{ - PalletCallMetadata, - PalletMetadata, -}; -use heck::SnakeCase as _; -use proc_macro2::TokenStream as TokenStream2; -use proc_macro_error::abort_call_site; -use quote::{ - format_ident, - quote, -}; -use scale_info::form::PortableForm; - -pub fn generate_calls( - type_gen: &TypeGenerator, - pallet: &PalletMetadata, - call: &PalletCallMetadata, - types_mod_ident: &syn::Ident, -) -> TokenStream2 { - let struct_defs = - super::generate_structs_from_variants(type_gen, call.ty.id(), "Call"); - let (call_structs, call_fns): (Vec<_>, Vec<_>) = struct_defs - .iter() - .map(|struct_def| { - let (call_fn_args, call_args): (Vec<_>, Vec<_>) = struct_def - .named_fields() - .unwrap_or_else(|| { - abort_call_site!( - "Call variant for type {} must have all named fields", - call.ty.id() - ) - }) - .iter() - .map(|(name, ty)| (quote!( #name: #ty ), name)) - .unzip(); - - let pallet_name = &pallet.name; - let call_struct_name = &struct_def.name; - let function_name = struct_def.name.to_string().to_snake_case(); - let fn_name = format_ident!("{}", function_name); - - let call_struct = quote! { - #struct_def - - impl ::subxt::Call for #call_struct_name { - const PALLET: &'static str = #pallet_name; - const FUNCTION: &'static str = #function_name; - } - }; - let client_fn = quote! { - pub fn #fn_name( - &self, - #( #call_fn_args, )* - ) -> ::subxt::SubmittableExtrinsic { - let call = #call_struct_name { #( #call_args, )* }; - ::subxt::SubmittableExtrinsic::new(self.client, call) - } - }; - (call_struct, client_fn) - }) - .unzip(); - - quote! { - pub mod calls { - use super::#types_mod_ident; - #( #call_structs )* - - pub struct TransactionApi<'a, T: ::subxt::Config + ::subxt::ExtrinsicExtraData> { - client: &'a ::subxt::Client, - } - - impl<'a, T: ::subxt::Config> TransactionApi<'a, T> - where - T: ::subxt::Config + ::subxt::ExtrinsicExtraData, - { - pub fn new(client: &'a ::subxt::Client) -> Self { - Self { client } - } - - #( #call_fns )* - } - } - } -} diff --git a/codegen/src/api/events.rs b/codegen/src/api/events.rs deleted file mode 100644 index 2cfe804273..0000000000 --- a/codegen/src/api/events.rs +++ /dev/null @@ -1,57 +0,0 @@ -// 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 . - -use crate::types::TypeGenerator; -use frame_metadata::{ - PalletEventMetadata, - PalletMetadata, -}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use scale_info::form::PortableForm; - -pub fn generate_events( - type_gen: &TypeGenerator, - pallet: &PalletMetadata, - event: &PalletEventMetadata, - types_mod_ident: &syn::Ident, -) -> TokenStream2 { - let struct_defs = - super::generate_structs_from_variants(type_gen, event.ty.id(), "Event"); - let event_structs = struct_defs.iter().map(|struct_def| { - let pallet_name = &pallet.name; - let event_struct = &struct_def.name; - let event_name = struct_def.name.to_string(); - - quote! { - #struct_def - - impl ::subxt::Event for #event_struct { - const PALLET: &'static str = #pallet_name; - const EVENT: &'static str = #event_name; - } - } - }); - let event_type = type_gen.resolve_type_path(event.ty.id(), &[]); - - quote! { - pub type Event = #event_type; - pub mod events { - use super::#types_mod_ident; - #( #event_structs )* - } - } -} diff --git a/codegen/src/api/mod.rs b/codegen/src/api/mod.rs deleted file mode 100644 index bc46d02745..0000000000 --- a/codegen/src/api/mod.rs +++ /dev/null @@ -1,347 +0,0 @@ -// 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 . - -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

( - item_mod: syn::ItemMod, - path: P, - generated_type_derives: Option>, -) -> TokenStream2 -where - P: AsRef, -{ - 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::>(); - - 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::>(); - 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; - 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 for DefaultConfig { - type AccountData = AccountData; - type Extra = ::subxt::DefaultExtra; - } - - pub type AccountData = self::system::storage::Account; - - impl ::subxt::AccountData for AccountData { - fn nonce(result: &::Value) -> ::Index { - result.nonce - } - fn storage_entry(account_id: ::AccountId) -> Self { - Self(account_id) - } - } - - pub struct RuntimeApi> { - pub client: ::subxt::Client, - } - - impl ::core::convert::From<::subxt::Client> for RuntimeApi - where - T: ::subxt::Config + ::subxt::ExtrinsicExtraData, - { - fn from(client: ::subxt::Client) -> Self { - Self { client } - } - } - - impl<'a, T> RuntimeApi - where - T: ::subxt::Config + ::subxt::ExtrinsicExtraData, - { - 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, - { - client: &'a ::subxt::Client, - } - - impl<'a, T> StorageApi<'a, T> - where - T: ::subxt::Config + ::subxt::ExtrinsicExtraData, - { - #( - 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> { - client: &'a ::subxt::Client, - } - - impl<'a, T> TransactionApi<'a, T> - where - T: ::subxt::Config + ::subxt::ExtrinsicExtraData, - { - #( - 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 { - 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 - ) - } -} diff --git a/codegen/src/api/storage.rs b/codegen/src/api/storage.rs deleted file mode 100644 index 22310f30ae..0000000000 --- a/codegen/src/api/storage.rs +++ /dev/null @@ -1,223 +0,0 @@ -// 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 . - -use crate::types::TypeGenerator; -use frame_metadata::{ - PalletMetadata, - PalletStorageMetadata, - StorageEntryMetadata, - StorageEntryModifier, - StorageEntryType, - StorageHasher, -}; -use heck::SnakeCase as _; -use proc_macro2::TokenStream as TokenStream2; -use proc_macro_error::abort_call_site; -use quote::{ - format_ident, - quote, -}; -use scale_info::{ - form::PortableForm, - TypeDef, -}; - -pub fn generate_storage( - type_gen: &TypeGenerator, - pallet: &PalletMetadata, - storage: &PalletStorageMetadata, - types_mod_ident: &syn::Ident, -) -> TokenStream2 { - let (storage_structs, storage_fns): (Vec<_>, Vec<_>) = storage - .entries - .iter() - .map(|entry| generate_storage_entry_fns(&type_gen, &pallet, entry)) - .unzip(); - - quote! { - pub mod storage { - use super::#types_mod_ident; - #( #storage_structs )* - - pub struct StorageApi<'a, T: ::subxt::Config> { - client: &'a ::subxt::Client, - } - - impl<'a, T: ::subxt::Config> StorageApi<'a, T> { - pub fn new(client: &'a ::subxt::Client) -> Self { - Self { client } - } - - #( #storage_fns )* - } - } - } -} - -fn generate_storage_entry_fns( - type_gen: &TypeGenerator, - pallet: &PalletMetadata, - storage_entry: &StorageEntryMetadata, -) -> (TokenStream2, TokenStream2) { - let entry_struct_ident = format_ident!("{}", storage_entry.name); - let (fields, entry_struct, constructor, key_impl) = match storage_entry.ty { - StorageEntryType::Plain(_) => { - let entry_struct = quote!( pub struct #entry_struct_ident; ); - let constructor = quote!( #entry_struct_ident ); - let key_impl = quote!(::subxt::StorageEntryKey::Plain); - (vec![], entry_struct, constructor, key_impl) - } - StorageEntryType::Map { - ref key, - ref hashers, - .. - } => { - let key_ty = type_gen.resolve_type(key.id()); - let hashers = hashers - .iter() - .map(|hasher| { - let hasher = match hasher { - StorageHasher::Blake2_128 => "Blake2_128", - StorageHasher::Blake2_256 => "Blake2_256", - StorageHasher::Blake2_128Concat => "Blake2_128Concat", - StorageHasher::Twox128 => "Twox128", - StorageHasher::Twox256 => "Twox256", - StorageHasher::Twox64Concat => "Twox64Concat", - StorageHasher::Identity => "Identity", - }; - let hasher = format_ident!("{}", hasher); - quote!( ::subxt::StorageHasher::#hasher ) - }) - .collect::>(); - match key_ty.type_def() { - TypeDef::Tuple(tuple) => { - let fields = tuple - .fields() - .iter() - .enumerate() - .map(|(i, f)| { - let field_name = format_ident!("_{}", syn::Index::from(i)); - let field_type = type_gen.resolve_type_path(f.id(), &[]); - (field_name, field_type) - }) - .collect::>(); - // toddo: [AJ] use unzip here? - let tuple_struct_fields = - fields.iter().map(|(_, field_type)| field_type); - let field_names = fields.iter().map(|(field_name, _)| field_name); - let entry_struct = quote! { - pub struct #entry_struct_ident( #( #tuple_struct_fields ),* ); - }; - let constructor = - quote!( #entry_struct_ident( #( #field_names ),* ) ); - let keys = (0..tuple.fields().len()).into_iter().zip(hashers).map( - |(field, hasher)| { - let index = syn::Index::from(field); - quote!( ::subxt::StorageMapKey::new(&self.#index, #hasher) ) - }, - ); - let key_impl = quote! { - ::subxt::StorageEntryKey::Map( - vec![ #( #keys ),* ] - ) - }; - (fields, entry_struct, constructor, key_impl) - } - _ => { - let ty_path = type_gen.resolve_type_path(key.id(), &[]); - let fields = vec![(format_ident!("_0"), ty_path.clone())]; - let entry_struct = quote! { - pub struct #entry_struct_ident( pub #ty_path ); - }; - let constructor = quote!( #entry_struct_ident(_0) ); - let hasher = hashers.get(0).unwrap_or_else(|| { - abort_call_site!("No hasher found for single key") - }); - let key_impl = quote! { - ::subxt::StorageEntryKey::Map( - vec![ ::subxt::StorageMapKey::new(&self.0, #hasher) ] - ) - }; - (fields, entry_struct, constructor, key_impl) - } - } - } - }; - let pallet_name = &pallet.name; - let storage_name = &storage_entry.name; - let fn_name = format_ident!("{}", storage_entry.name.to_snake_case()); - let fn_name_iter = format_ident!("{}_iter", fn_name); - let storage_entry_ty = match storage_entry.ty { - StorageEntryType::Plain(ref ty) => ty, - StorageEntryType::Map { ref value, .. } => value, - }; - let storage_entry_value_ty = type_gen.resolve_type_path(storage_entry_ty.id(), &[]); - let (return_ty, fetch) = match storage_entry.modifier { - StorageEntryModifier::Default => { - (quote!( #storage_entry_value_ty ), quote!(fetch_or_default)) - } - StorageEntryModifier::Optional => { - ( - quote!( ::core::option::Option<#storage_entry_value_ty> ), - quote!(fetch), - ) - } - }; - - let storage_entry_type = quote! { - #entry_struct - - impl ::subxt::StorageEntry for #entry_struct_ident { - const PALLET: &'static str = #pallet_name; - const STORAGE: &'static str = #storage_name; - type Value = #storage_entry_value_ty; - fn key(&self) -> ::subxt::StorageEntryKey { - #key_impl - } - } - }; - - let client_iter_fn = if matches!(storage_entry.ty, StorageEntryType::Map { .. }) { - quote! ( - pub async fn #fn_name_iter( - &self, - hash: ::core::option::Option, - ) -> ::core::result::Result<::subxt::KeyIter<'a, T, #entry_struct_ident>, ::subxt::Error> { - self.client.storage().iter(hash).await - } - ) - } else { - quote!() - }; - - let key_args = fields - .iter() - .map(|(field_name, field_type)| quote!( #field_name: #field_type )); - let client_fns = quote! { - pub async fn #fn_name( - &self, - #( #key_args, )* - hash: ::core::option::Option, - ) -> ::core::result::Result<#return_ty, ::subxt::Error> { - let entry = #constructor; - self.client.storage().#fetch(&entry, hash).await - } - - #client_iter_fn - }; - - (storage_entry_type, client_fns) -} diff --git a/codegen/src/derives.rs b/codegen/src/derives.rs deleted file mode 100644 index a27319b923..0000000000 --- a/codegen/src/derives.rs +++ /dev/null @@ -1,52 +0,0 @@ -// 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 . - -use syn::punctuated::Punctuated; - -#[derive(Debug, Clone)] -pub struct GeneratedTypeDerives { - derives: Punctuated, -} - -impl GeneratedTypeDerives { - pub fn new(derives: Punctuated) -> Self { - Self { derives } - } - - pub fn append(&mut self, derives: impl Iterator) { - for derive in derives { - self.derives.push(derive) - } - } -} - -impl Default for GeneratedTypeDerives { - fn default() -> Self { - let mut derives = Punctuated::new(); - derives.push(syn::parse_quote!(::subxt::codec::Encode)); - derives.push(syn::parse_quote!(::subxt::codec::Decode)); - Self::new(derives) - } -} - -impl quote::ToTokens for GeneratedTypeDerives { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let derives = &self.derives; - tokens.extend(quote::quote! { - #[derive(#derives)] - }) - } -} diff --git a/codegen/src/ir.rs b/codegen/src/ir.rs deleted file mode 100644 index 429d0d9acd..0000000000 --- a/codegen/src/ir.rs +++ /dev/null @@ -1,146 +0,0 @@ -// 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 . - -use proc_macro_error::abort; -use std::collections::HashMap; -use syn::{ - spanned::Spanned as _, - token, -}; - -#[derive(Debug, PartialEq, Eq)] -pub struct ItemMod { - // attrs: Vec, - vis: syn::Visibility, - mod_token: token::Mod, - pub ident: syn::Ident, - brace: token::Brace, - items: Vec, -} - -impl From for ItemMod { - fn from(module: syn::ItemMod) -> Self { - let (brace, items) = match module.content { - Some((brace, items)) => (brace, items), - None => { - abort!(module, "out-of-line subxt modules are not supported",) - } - }; - let items = items - .into_iter() - .map(>::from) - .collect::>(); - Self { - vis: module.vis, - mod_token: module.mod_token, - ident: module.ident, - brace, - items, - } - } -} - -impl ItemMod { - pub fn type_substitutes(&self) -> HashMap { - self.items - .iter() - .filter_map(|item| { - if let Item::Subxt(SubxtItem::TypeSubstitute { - generated_type_path, - substitute_with: substitute_type, - }) = item - { - Some((generated_type_path.clone(), substitute_type.clone())) - } else { - None - } - }) - .collect() - } -} - -#[derive(Debug, PartialEq, Eq)] -pub enum Item { - Rust(syn::Item), - Subxt(SubxtItem), -} - -impl From for Item { - fn from(item: syn::Item) -> Self { - if let syn::Item::Use(ref use_) = item { - let substitute_attrs = use_ - .attrs - .iter() - .map(|attr| { - let meta = attr.parse_meta().unwrap_or_else(|e| { - abort!(attr.span(), "Error parsing attribute: {}", e) - }); - let substitute_type_args = - ::from_meta(&meta) - .unwrap_or_else(|e| { - abort!(attr.span(), "Error parsing attribute meta: {}", e) - }); - substitute_type_args - }) - .collect::>(); - if substitute_attrs.len() > 1 { - abort!( - use_.attrs[0].span(), - "Duplicate `substitute_type` attributes" - ) - } - if let Some(attr) = substitute_attrs.iter().next() { - let use_path = &use_.tree; - let substitute_with: syn::TypePath = syn::parse_quote!( #use_path ); - let type_substitute = SubxtItem::TypeSubstitute { - generated_type_path: attr.substitute_type().to_string(), - substitute_with, - }; - Self::Subxt(type_substitute) - } else { - Self::Rust(item) - } - } else { - Self::Rust(item) - } - } -} - -#[derive(Debug, PartialEq, Eq)] -pub enum SubxtItem { - TypeSubstitute { - generated_type_path: String, - substitute_with: syn::TypePath, - }, -} - -mod attrs { - use darling::FromMeta; - - #[derive(Debug, FromMeta)] - #[darling(rename_all = "snake_case")] - pub enum Subxt { - SubstituteType(String), - } - - impl Subxt { - pub fn substitute_type(&self) -> String { - match self { - Self::SubstituteType(path) => path.clone(), - } - } - } -} diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 7c65ecdf5a..626d30c471 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -14,18 +14,4 @@ // You should have received a copy of the GNU General Public License // along with subxt. If not, see . -//! Library to generate an API for a Substrate runtime from its metadata. - -mod api; -mod derives; -mod ir; -mod struct_def; -mod types; - -pub use self::{ - api::{ - generate_runtime_api, - RuntimeGenerator, - }, - derives::GeneratedTypeDerives, -}; +//! Library to generate an API for a Substrate runtime from its metadata. \ No newline at end of file diff --git a/codegen/src/struct_def.rs b/codegen/src/struct_def.rs deleted file mode 100644 index 1db6c02b13..0000000000 --- a/codegen/src/struct_def.rs +++ /dev/null @@ -1,142 +0,0 @@ -// 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 . - -use super::GeneratedTypeDerives; -use crate::types::{ - TypeGenerator, - TypePath, -}; -use heck::CamelCase as _; -use proc_macro2::TokenStream as TokenStream2; -use proc_macro_error::abort_call_site; -use quote::{ - format_ident, - quote, -}; -use scale_info::form::PortableForm; - -#[derive(Debug)] -pub struct StructDef { - pub name: syn::Ident, - pub fields: StructDefFields, - pub field_visibility: Option, - pub derives: GeneratedTypeDerives, -} - -#[derive(Debug)] -pub enum StructDefFields { - Named(Vec<(syn::Ident, TypePath)>), - Unnamed(Vec), -} - -impl StructDef { - pub fn new( - ident: &str, - fields: &[scale_info::Field], - field_visibility: Option, - type_gen: &TypeGenerator, - ) -> Self { - let name = format_ident!("{}", ident.to_camel_case()); - let fields = fields - .iter() - .map(|field| { - let name = field.name().map(|f| format_ident!("{}", f)); - let ty = type_gen.resolve_type_path(field.ty().id(), &[]); - (name, ty) - }) - .collect::>(); - - let named = fields.iter().all(|(name, _)| name.is_some()); - let unnamed = fields.iter().all(|(name, _)| name.is_none()); - - let fields = if named { - StructDefFields::Named( - fields - .iter() - .map(|(name, field)| { - let name = name.as_ref().unwrap_or_else(|| { - abort_call_site!("All fields should have a name") - }); - (name.clone(), field.clone()) - }) - .collect(), - ) - } else if unnamed { - StructDefFields::Unnamed( - fields.iter().map(|(_, field)| field.clone()).collect(), - ) - } else { - abort_call_site!( - "Struct '{}': Fields should either be all named or all unnamed.", - name, - ) - }; - - let derives = type_gen.derives().clone(); - - Self { - name, - fields, - field_visibility, - derives, - } - } - - pub fn named_fields(&self) -> Option<&[(syn::Ident, TypePath)]> { - if let StructDefFields::Named(ref fields) = self.fields { - Some(fields) - } else { - None - } - } -} - -impl quote::ToTokens for StructDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let visibility = &self.field_visibility; - let derives = &self.derives; - tokens.extend(match self.fields { - StructDefFields::Named(ref named_fields) => { - let fields = named_fields.iter().map(|(name, ty)| { - let compact_attr = - ty.is_compact().then(|| quote!( #[codec(compact)] )); - quote! { #compact_attr #visibility #name: #ty } - }); - let name = &self.name; - quote! { - #derives - pub struct #name { - #( #fields ),* - } - } - } - StructDefFields::Unnamed(ref unnamed_fields) => { - let fields = unnamed_fields.iter().map(|ty| { - let compact_attr = - ty.is_compact().then(|| quote!( #[codec(compact)] )); - quote! { #compact_attr #visibility #ty } - }); - let name = &self.name; - quote! { - #derives - pub struct #name ( - #( #fields ),* - ); - } - } - }) - } -} diff --git a/codegen/src/types/mod.rs b/codegen/src/types/mod.rs deleted file mode 100644 index 73b8b9d062..0000000000 --- a/codegen/src/types/mod.rs +++ /dev/null @@ -1,253 +0,0 @@ -// 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 . - -#[cfg(test)] -mod tests; -mod type_def; -mod type_path; - -use super::GeneratedTypeDerives; -use proc_macro2::{ - Ident, - Span, - TokenStream, -}; -use quote::{ - quote, - ToTokens, -}; -use scale_info::{ - form::PortableForm, - PortableRegistry, - Type, - TypeDef, -}; -use std::collections::{ - BTreeMap, - HashMap, -}; - -pub use self::{ - type_def::TypeDefGen, - type_path::{ - TypeParameter, - TypePath, - TypePathSubstitute, - TypePathType, - }, -}; - -/// Generate a Rust module containing all types defined in the supplied [`PortableRegistry`]. -#[derive(Debug)] -pub struct TypeGenerator<'a> { - /// The name of the module which will contain the generated types. - types_mod_ident: Ident, - /// Registry of type definitions to be transformed into Rust type definitions. - type_registry: &'a PortableRegistry, - /// User defined overrides for generated types. - type_substitutes: HashMap, - /// Set of derives with which to annotate generated types. - derives: GeneratedTypeDerives, -} - -impl<'a> TypeGenerator<'a> { - /// Construct a new [`TypeGenerator`]. - pub fn new( - type_registry: &'a PortableRegistry, - root_mod: &'static str, - type_substitutes: HashMap, - derives: GeneratedTypeDerives, - ) -> Self { - let root_mod_ident = Ident::new(root_mod, Span::call_site()); - Self { - types_mod_ident: root_mod_ident, - type_registry, - type_substitutes, - derives, - } - } - - /// Generate a module containing all types defined in the supplied type registry. - pub fn generate_types_mod(&'a self) -> Module<'a> { - let mut root_mod = - Module::new(self.types_mod_ident.clone(), self.types_mod_ident.clone()); - - for (id, ty) in self.type_registry.types().iter().enumerate() { - if ty.ty().path().namespace().is_empty() { - // prelude types e.g. Option/Result have no namespace, so we don't generate them - continue - } - self.insert_type( - ty.ty().clone(), - id as u32, - ty.ty().path().namespace().to_vec(), - &self.types_mod_ident, - &mut root_mod, - ) - } - - root_mod - } - - fn insert_type( - &'a self, - ty: Type, - id: u32, - path: Vec, - root_mod_ident: &Ident, - module: &mut Module<'a>, - ) { - let joined_path = path.join("::"); - if self.type_substitutes.contains_key(&joined_path) { - return - } - - let segment = path.first().expect("path has at least one segment"); - let mod_ident = Ident::new(segment, Span::call_site()); - - let child_mod = module - .children - .entry(mod_ident.clone()) - .or_insert_with(|| Module::new(mod_ident, root_mod_ident.clone())); - - if path.len() == 1 { - child_mod - .types - .insert(ty.path().clone(), TypeDefGen { ty, type_gen: self }); - } else { - self.insert_type(ty, id, path[1..].to_vec(), root_mod_ident, child_mod) - } - } - - /// # Panics - /// - /// If no type with the given id found in the type registry. - pub fn resolve_type(&self, id: u32) -> Type { - self.type_registry - .resolve(id) - .unwrap_or_else(|| panic!("No type with id {} found", id)) - .clone() - } - - /// # Panics - /// - /// If no type with the given id found in the type registry. - pub fn resolve_type_path( - &self, - id: u32, - parent_type_params: &[TypeParameter], - ) -> TypePath { - if let Some(parent_type_param) = parent_type_params - .iter() - .find(|tp| tp.concrete_type_id == id) - { - return TypePath::Parameter(parent_type_param.clone()) - } - - let mut ty = self.resolve_type(id); - - if ty.path().ident() == Some("Cow".to_string()) { - ty = self.resolve_type( - ty.type_params()[0] - .ty() - .expect("type parameters to Cow are not expected to be skipped; qed") - .id(), - ) - } - - let params_type_ids = match ty.type_def() { - TypeDef::Array(arr) => vec![arr.type_param().id()], - TypeDef::Sequence(seq) => vec![seq.type_param().id()], - TypeDef::Tuple(tuple) => tuple.fields().iter().map(|f| f.id()).collect(), - TypeDef::Compact(compact) => vec![compact.type_param().id()], - TypeDef::BitSequence(seq) => { - vec![seq.bit_order_type().id(), seq.bit_store_type().id()] - } - _ => { - ty.type_params() - .iter() - .filter_map(|f| f.ty().map(|f| f.id())) - .collect() - } - }; - - let params = params_type_ids - .iter() - .map(|tp| self.resolve_type_path(*tp, parent_type_params)) - .collect::>(); - - let joined_path = ty.path().segments().join("::"); - if let Some(substitute_type_path) = self.type_substitutes.get(&joined_path) { - TypePath::Substitute(TypePathSubstitute { - path: substitute_type_path.clone(), - params, - }) - } else { - TypePath::Type(TypePathType { - ty, - params, - root_mod_ident: self.types_mod_ident.clone(), - }) - } - } - - /// Returns the derives with which all generated type will be decorated. - pub fn derives(&self) -> &GeneratedTypeDerives { - &self.derives - } -} - -#[derive(Debug)] -pub struct Module<'a> { - name: Ident, - root_mod: Ident, - children: BTreeMap>, - types: BTreeMap, TypeDefGen<'a>>, -} - -impl<'a> ToTokens for Module<'a> { - fn to_tokens(&self, tokens: &mut TokenStream) { - let name = &self.name; - let root_mod = &self.root_mod; - let modules = self.children.values(); - let types = self.types.values().clone(); - - tokens.extend(quote! { - pub mod #name { - use super::#root_mod; - - #( #modules )* - #( #types )* - } - }) - } -} - -impl<'a> Module<'a> { - pub fn new(name: Ident, root_mod: Ident) -> Self { - Self { - name, - root_mod, - children: BTreeMap::new(), - types: BTreeMap::new(), - } - } - - /// Returns the module ident. - pub fn ident(&self) -> &Ident { - &self.name - } -} diff --git a/codegen/src/types/tests.rs b/codegen/src/types/tests.rs deleted file mode 100644 index 94acde4684..0000000000 --- a/codegen/src/types/tests.rs +++ /dev/null @@ -1,799 +0,0 @@ -// 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 . - -use super::*; -use pretty_assertions::assert_eq; -use scale_info::{ - meta_type, - Registry, - TypeInfo, -}; - -const MOD_PATH: &'static [&'static str] = &["subxt_codegen", "types", "tests"]; - -fn get_mod<'a>(module: &'a Module, path_segs: &[&'static str]) -> Option<&'a Module<'a>> { - let (mod_name, rest) = path_segs.split_first()?; - let mod_ident = Ident::new(mod_name, Span::call_site()); - let module = module.children.get(&mod_ident)?; - if rest.is_empty() { - Some(module) - } else { - get_mod(module, rest) - } -} - -#[test] -fn generate_struct_with_primitives() { - #[allow(unused)] - #[derive(TypeInfo)] - struct S { - a: bool, - b: u32, - c: char, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct S { - pub a: ::core::primitive::bool, - pub b: ::core::primitive::u32, - pub c: ::core::primitive::char, - } - } - } - .to_string() - ) -} - -#[test] -fn generate_struct_with_a_struct_field() { - #[allow(unused)] - #[derive(TypeInfo)] - struct Parent { - a: bool, - b: Child, - } - - #[allow(unused)] - #[derive(TypeInfo)] - struct Child { - a: i32, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Child { - pub a: ::core::primitive::i32, - } - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Parent { - pub a: ::core::primitive::bool, - pub b: root::subxt_codegen::types::tests::Child, - } - } - } - .to_string() - ) -} - -#[test] -fn generate_tuple_struct() { - #[allow(unused)] - #[derive(TypeInfo)] - struct Parent(bool, Child); - - #[allow(unused)] - #[derive(TypeInfo)] - struct Child(i32); - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Child(pub ::core::primitive::i32,); - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Parent(pub ::core::primitive::bool, pub root::subxt_codegen::types::tests::Child,); - } - } - .to_string() - ) -} - -#[test] -fn derive_compact_as_for_uint_wrapper_structs() { - #[allow(unused)] - #[derive(TypeInfo)] - struct Su8 { - a: u8, - } - #[allow(unused)] - #[derive(TypeInfo)] - struct TSu8(u8); - #[allow(unused)] - #[derive(TypeInfo)] - struct Su16 { - a: u16, - } - #[allow(unused)] - #[derive(TypeInfo)] - struct TSu16(u16); - #[allow(unused)] - #[derive(TypeInfo)] - struct Su32 { - a: u32, - } - #[allow(unused)] - #[derive(TypeInfo)] - struct TSu32(u32); - #[allow(unused)] - #[derive(TypeInfo)] - struct Su64 { - a: u64, - } - #[allow(unused)] - #[derive(TypeInfo)] - struct TSu64(u64); - #[allow(unused)] - #[derive(TypeInfo)] - struct Su128 { - a: u128, - } - #[allow(unused)] - #[derive(TypeInfo)] - struct TSu128(u128); - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Su128 { pub a: ::core::primitive::u128, } - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Su16 { pub a: ::core::primitive::u16, } - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Su32 { pub a: ::core::primitive::u32, } - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Su64 { pub a: ::core::primitive::u64, } - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Su8 { pub a: ::core::primitive::u8, } - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct TSu128(pub ::core::primitive::u128,); - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct TSu16(pub ::core::primitive::u16,); - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct TSu32(pub ::core::primitive::u32,); - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct TSu64(pub ::core::primitive::u64,); - - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct TSu8(pub ::core::primitive::u8,); - } - } - .to_string() - ) -} - -#[test] -fn generate_enum() { - #[allow(unused)] - #[derive(TypeInfo)] - enum E { - A, - B(bool), - C { a: u32 }, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub enum E { - # [codec (index = 0)] - A, - # [codec (index = 1)] - B (::core::primitive::bool,), - # [codec (index = 2)] - C { a: ::core::primitive::u32, }, - } - } - } - .to_string() - ) -} - -#[test] -fn generate_array_field() { - #[allow(unused)] - #[derive(TypeInfo)] - struct S { - a: [u8; 32], - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct S { - pub a: [::core::primitive::u8; 32usize], - } - } - } - .to_string() - ) -} - -#[test] -fn option_fields() { - #[allow(unused)] - #[derive(TypeInfo)] - struct S { - a: Option, - b: Option, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct S { - pub a: ::core::option::Option<::core::primitive::bool>, - pub b: ::core::option::Option<::core::primitive::u32>, - } - } - } - .to_string() - ) -} - -#[test] -fn box_fields_struct() { - use std::boxed::Box; - - #[allow(unused)] - #[derive(TypeInfo)] - struct S { - a: std::boxed::Box, - b: Box, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct S { - pub a: ::std::boxed::Box<::core::primitive::bool>, - pub b: ::std::boxed::Box<::core::primitive::u32>, - } - } - } - .to_string() - ) -} - -#[test] -fn box_fields_enum() { - use std::boxed::Box; - - #[allow(unused)] - #[derive(TypeInfo)] - enum E { - A(Box), - B { a: Box }, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub enum E { - # [codec (index = 0)] - A(::std::boxed::Box<::core::primitive::bool>,), - # [codec (index = 1)] - B { a: ::std::boxed::Box<::core::primitive::u32>, }, - } - } - } - .to_string() - ) -} - -#[test] -fn range_fields() { - #[allow(unused)] - #[derive(TypeInfo)] - struct S { - a: core::ops::Range, - b: core::ops::RangeInclusive, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct S { - pub a: ::core::ops::Range<::core::primitive::u32>, - pub b: ::core::ops::RangeInclusive<::core::primitive::u32>, - } - } - } - .to_string() - ) -} - -#[test] -fn generics() { - #[allow(unused)] - #[derive(TypeInfo)] - struct Foo { - a: T, - } - - #[allow(unused)] - #[derive(TypeInfo)] - struct Bar { - b: Foo, - c: Foo, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Bar { - pub b: root::subxt_codegen::types::tests::Foo<::core::primitive::u32>, - pub c: root::subxt_codegen::types::tests::Foo<::core::primitive::u8>, - } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Foo<_0> { - pub a: _0, - } - } - } - .to_string() - ) -} - -#[test] -fn generics_nested() { - #[allow(unused)] - #[derive(TypeInfo)] - struct Foo { - a: T, - b: Option<(T, U)>, - } - - #[allow(unused)] - #[derive(TypeInfo)] - struct Bar { - b: Foo, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::>()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Bar<_0> { - pub b: root::subxt_codegen::types::tests::Foo<_0, ::core::primitive::u32>, - } - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Foo<_0, _1> { - pub a: _0, - pub b: ::core::option::Option<(_0, _1,)>, - } - } - } - .to_string() - ) -} - -#[test] -fn generate_bitvec() { - use bitvec::{ - order::{ - Lsb0, - Msb0, - }, - vec::BitVec, - }; - - #[allow(unused)] - #[derive(TypeInfo)] - struct S { - lsb: BitVec, - msb: BitVec, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct S { - pub lsb: ::subxt::bitvec::vec::BitVec, - pub msb: ::subxt::bitvec::vec::BitVec, - } - } - } - .to_string() - ) -} - -#[test] -fn generics_with_alias_adds_phantom_data_marker() { - trait Trait { - type Type; - } - - impl Trait for bool { - type Type = u32; - } - - type Foo = ::Type; - type Bar = (::Type, ::Type); - - #[allow(unused)] - #[derive(TypeInfo)] - struct NamedFields { - b: Foo, - } - - #[allow(unused)] - #[derive(TypeInfo)] - struct UnnamedFields(Bar); - - let mut registry = Registry::new(); - registry.register_type(&meta_type::>()); - registry.register_type(&meta_type::>()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - #[derive(::subxt::codec::CompactAs)] - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct NamedFields<_0> { - pub b: ::core::primitive::u32, - #[codec(skip)] pub __subxt_unused_type_params: ::core::marker::PhantomData<_0>, - } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct UnnamedFields<_0, _1> ( - pub (::core::primitive::u32, ::core::primitive::u32,), - #[codec(skip)] pub ::core::marker::PhantomData<(_0, _1)>, - ); - } - } - .to_string() - ) -} - -#[test] -fn modules() { - mod modules { - pub mod a { - #[allow(unused)] - #[derive(scale_info::TypeInfo)] - pub struct Foo {} - - pub mod b { - #[allow(unused)] - #[derive(scale_info::TypeInfo)] - pub struct Bar { - a: super::Foo, - } - } - } - - pub mod c { - #[allow(unused)] - #[derive(scale_info::TypeInfo)] - pub struct Foo { - a: super::a::b::Bar, - } - } - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - ); - let types = type_gen.generate_types_mod(); - let tests_mod = get_mod(&types, MOD_PATH).unwrap(); - - assert_eq!( - tests_mod.into_token_stream().to_string(), - quote! { - pub mod tests { - use super::root; - pub mod modules { - use super::root; - pub mod a { - use super::root; - - pub mod b { - use super::root; - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Bar { - pub a: root::subxt_codegen::types::tests::modules::a::Foo, - } - } - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Foo {} - } - - pub mod c { - use super::root; - - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode)] - pub struct Foo { - pub a: root::subxt_codegen::types::tests::modules::a::b::Bar, - } - } - } - } - } - .to_string() - ) -} diff --git a/codegen/src/types/type_def.rs b/codegen/src/types/type_def.rs deleted file mode 100644 index 2c432b88fe..0000000000 --- a/codegen/src/types/type_def.rs +++ /dev/null @@ -1,329 +0,0 @@ -// 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 . - -use super::{ - TypeGenerator, - TypeParameter, - TypePath, -}; -use proc_macro2::TokenStream; -use quote::{ - format_ident, - quote, -}; -use scale_info::{ - form::PortableForm, - Field, - Type, - TypeDef, - TypeDefPrimitive, -}; -use std::collections::HashSet; -use syn::parse_quote; - -/// Generates a Rust `struct` or `enum` definition based on the supplied [`scale-info::Type`]. -/// -/// Field type paths are resolved via the `TypeGenerator`, which contains the registry of all -/// generated types in the module. -#[derive(Debug)] -pub struct TypeDefGen<'a> { - /// The type generation context, allows resolving of type paths for the fields of the - /// generated type. - pub(super) type_gen: &'a TypeGenerator<'a>, - /// Contains the definition of the type to be generated. - pub(super) ty: Type, -} - -impl<'a> quote::ToTokens for TypeDefGen<'a> { - fn to_tokens(&self, tokens: &mut TokenStream) { - let type_params = self - .ty - .type_params() - .iter() - .enumerate() - .filter_map(|(i, tp)| { - match tp.ty() { - Some(ty) => { - let tp_name = format_ident!("_{}", i); - Some(TypeParameter { - concrete_type_id: ty.id(), - name: tp_name, - }) - } - None => None, - } - }) - .collect::>(); - - let type_name = self.ty.path().ident().map(|ident| { - let type_params = if !type_params.is_empty() { - quote! { < #( #type_params ),* > } - } else { - quote! {} - }; - let ty = format_ident!("{}", ident); - let path = parse_quote! { #ty #type_params}; - syn::Type::Path(path) - }); - - let derives = self.type_gen.derives(); - - match self.ty.type_def() { - TypeDef::Composite(composite) => { - let type_name = type_name.expect("structs should have a name"); - let (fields, _) = - self.composite_fields(composite.fields(), &type_params, true); - let derive_as_compact = if composite.fields().len() == 1 { - // any single field wrapper struct with a concrete unsigned int type can derive - // CompactAs. - let field = &composite.fields()[0]; - if !self - .ty - .type_params() - .iter() - .any(|tp| Some(tp.name()) == field.type_name()) - { - let ty = self.type_gen.resolve_type(field.ty().id()); - if matches!( - ty.type_def(), - TypeDef::Primitive( - TypeDefPrimitive::U8 - | TypeDefPrimitive::U16 - | TypeDefPrimitive::U32 - | TypeDefPrimitive::U64 - | TypeDefPrimitive::U128 - ) - ) { - Some(quote!( #[derive(::subxt::codec::CompactAs)] )) - } else { - None - } - } else { - None - } - } else { - None - }; - - let ty_toks = quote! { - #derive_as_compact - #derives - pub struct #type_name #fields - }; - tokens.extend(ty_toks); - } - TypeDef::Variant(variant) => { - let type_name = type_name.expect("variants should have a name"); - let mut variants = Vec::new(); - let mut used_type_params = HashSet::new(); - let type_params_set: HashSet<_> = type_params.iter().cloned().collect(); - - for v in variant.variants() { - let variant_name = format_ident!("{}", v.name()); - let (fields, unused_type_params) = if v.fields().is_empty() { - let unused = type_params_set.iter().cloned().collect::>(); - (quote! {}, unused) - } else { - self.composite_fields(v.fields(), &type_params, false) - }; - let index = proc_macro2::Literal::u8_unsuffixed(v.index()); - variants.push(quote! { - #[codec(index = #index)] - #variant_name #fields - }); - let unused_params_set = unused_type_params.iter().cloned().collect(); - let used_params = type_params_set.difference(&unused_params_set); - - for used_param in used_params { - used_type_params.insert(used_param.clone()); - } - } - - let unused_type_params = type_params_set - .difference(&used_type_params) - .cloned() - .collect::>(); - if !unused_type_params.is_empty() { - let phantom = Self::phantom_data(&unused_type_params); - variants.push(quote! { - __Ignore(#phantom) - }) - } - - let ty_toks = quote! { - #derives - pub enum #type_name { - #( #variants, )* - } - }; - tokens.extend(ty_toks); - } - _ => (), // all built-in types should already be in scope - } - } -} - -impl<'a> TypeDefGen<'a> { - fn composite_fields( - &self, - fields: &'a [Field], - type_params: &'a [TypeParameter], - is_struct: bool, - ) -> (TokenStream, Vec) { - let named = fields.iter().all(|f| f.name().is_some()); - let unnamed = fields.iter().all(|f| f.name().is_none()); - - fn unused_type_params<'a>( - type_params: &'a [TypeParameter], - types: impl Iterator, - ) -> Vec { - let mut used_type_params = HashSet::new(); - for ty in types { - ty.parent_type_params(&mut used_type_params) - } - let type_params_set: HashSet<_> = type_params.iter().cloned().collect(); - let mut unused = type_params_set - .difference(&used_type_params) - .cloned() - .collect::>(); - unused.sort(); - unused - } - - let ty_toks = |ty_name: &str, ty_path: &TypePath| { - if ty_name.contains("Box<") { - quote! { ::std::boxed::Box<#ty_path> } - } else { - quote! { #ty_path } - } - }; - - if named { - let fields = fields - .iter() - .map(|field| { - let name = format_ident!( - "{}", - field.name().expect("named field without a name") - ); - let ty = self - .type_gen - .resolve_type_path(field.ty().id(), type_params); - (name, ty, field.type_name()) - }) - .collect::>(); - - let mut fields_tokens = fields - .iter() - .map(|(name, ty, ty_name)| { - let field_type = match ty_name { - Some(ty_name) => { - let ty = ty_toks(ty_name, ty); - if is_struct { - quote! ( pub #name: #ty ) - } else { - quote! ( #name: #ty ) - } - } - None => { - quote! ( #name: #ty ) - } - }; - if ty.is_compact() { - quote!( #[codec(compact)] #field_type ) - } else { - quote!( #field_type ) - } - }) - .collect::>(); - - let unused_params = - unused_type_params(type_params, fields.iter().map(|(_, ty, _)| ty)); - - if is_struct && !unused_params.is_empty() { - let phantom = Self::phantom_data(&unused_params); - fields_tokens.push(quote! { - #[codec(skip)] pub __subxt_unused_type_params: #phantom - }) - } - - let fields = quote! { - { - #( #fields_tokens, )* - } - }; - (fields, unused_params) - } else if unnamed { - let type_paths = fields - .iter() - .map(|field| { - let ty = self - .type_gen - .resolve_type_path(field.ty().id(), type_params); - (ty, field.type_name()) - }) - .collect::>(); - let mut fields_tokens = type_paths - .iter() - .map(|(ty, ty_name)| { - match ty_name { - Some(ty_name) => { - let ty = ty_toks(ty_name, ty); - if is_struct { - quote! { pub #ty } - } else { - quote! { #ty } - } - } - None => { - quote! { #ty } - } - } - }) - .collect::>(); - - let unused_params = - unused_type_params(type_params, type_paths.iter().map(|(ty, _)| ty)); - - if is_struct && !unused_params.is_empty() { - let phantom_data = Self::phantom_data(&unused_params); - fields_tokens.push(quote! { #[codec(skip)] pub #phantom_data }) - } - - let fields = quote! { ( #( #fields_tokens, )* ) }; - let fields_tokens = if is_struct { - // add a semicolon for tuple structs - quote! { #fields; } - } else { - fields - }; - - (fields_tokens, unused_params) - } else { - panic!("Fields must be either all named or all unnamed") - } - } - - fn phantom_data(params: &[TypeParameter]) -> TokenStream { - let params = if params.len() == 1 { - let param = ¶ms[0]; - quote! { #param } - } else { - quote! { ( #( #params ), * ) } - }; - quote! ( ::core::marker::PhantomData<#params> ) - } -} diff --git a/codegen/src/types/type_path.rs b/codegen/src/types/type_path.rs deleted file mode 100644 index 186619cbf0..0000000000 --- a/codegen/src/types/type_path.rs +++ /dev/null @@ -1,253 +0,0 @@ -// 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 . - -use proc_macro2::{ - Ident, - TokenStream, -}; -use quote::{ - format_ident, - quote, -}; -use scale_info::{ - form::PortableForm, - Type, - TypeDef, - TypeDefPrimitive, -}; -use std::collections::HashSet; -use syn::parse_quote; - -#[derive(Clone, Debug)] -pub enum TypePath { - Parameter(TypeParameter), - Type(TypePathType), - Substitute(TypePathSubstitute), -} - -impl quote::ToTokens for TypePath { - fn to_tokens(&self, tokens: &mut TokenStream) { - let syn_type = self.to_syn_type(); - syn_type.to_tokens(tokens) - } -} - -impl TypePath { - pub(crate) fn to_syn_type(&self) -> syn::Type { - match self { - TypePath::Parameter(ty_param) => syn::Type::Path(parse_quote! { #ty_param }), - TypePath::Type(ty) => ty.to_syn_type(), - TypePath::Substitute(sub) => sub.to_syn_type(), - } - } - - pub(crate) fn is_compact(&self) -> bool { - matches!(self, Self::Type(ty) if ty.is_compact()) - } - - /// Returns the type parameters in a path which are inherited from the containing type. - /// - /// # Example - /// - /// ```rust - /// struct S { - /// a: Vec>, // the parent type param here is `T` - /// } - /// ``` - pub fn parent_type_params(&self, acc: &mut HashSet) { - match self { - Self::Parameter(type_parameter) => { - acc.insert(type_parameter.clone()); - } - Self::Type(type_path) => type_path.parent_type_params(acc), - Self::Substitute(sub) => sub.parent_type_params(acc), - } - } -} - -#[derive(Clone, Debug)] -pub struct TypePathType { - pub(super) ty: Type, - pub(super) params: Vec, - pub(super) root_mod_ident: Ident, -} - -impl TypePathType { - pub(crate) fn is_compact(&self) -> bool { - matches!(self.ty.type_def(), TypeDef::Compact(_)) - } - - fn to_syn_type(&self) -> syn::Type { - let params = &self.params; - match self.ty.type_def() { - TypeDef::Composite(_) | TypeDef::Variant(_) => { - let path_segments = self.ty.path().segments(); - - let ty_path: syn::TypePath = match path_segments { - [] => panic!("Type has no ident"), - [ident] => { - // paths to prelude types - match ident.as_str() { - "Option" => parse_quote!(::core::option::Option), - "Result" => parse_quote!(::core::result::Result), - "Cow" => parse_quote!(::std::borrow::Cow), - "BTreeMap" => parse_quote!(::std::collections::BTreeMap), - "BTreeSet" => parse_quote!(::std::collections::BTreeSet), - "Range" => parse_quote!(::core::ops::Range), - "RangeInclusive" => parse_quote!(::core::ops::RangeInclusive), - ident => panic!("Unknown prelude type '{}'", ident), - } - } - _ => { - // paths to generated types in the root types module - let mut ty_path = path_segments - .iter() - .map(|s| syn::PathSegment::from(format_ident!("{}", s))) - .collect::>(); - ty_path.insert( - 0, - syn::PathSegment::from(self.root_mod_ident.clone()), - ); - parse_quote!( #ty_path ) - } - }; - - let params = &self.params; - let path = if params.is_empty() { - parse_quote! { #ty_path } - } else { - parse_quote! { #ty_path< #( #params ),* > } - }; - syn::Type::Path(path) - } - TypeDef::Sequence(_) => { - let type_param = &self.params[0]; - let type_path = parse_quote! { ::std::vec::Vec<#type_param> }; - syn::Type::Path(type_path) - } - TypeDef::Array(array) => { - let array_type = &self.params[0]; - let array_len = array.len() as usize; - let array = parse_quote! { [#array_type; #array_len] }; - syn::Type::Array(array) - } - TypeDef::Tuple(_) => { - let tuple = parse_quote! { (#( # params, )* ) }; - syn::Type::Tuple(tuple) - } - TypeDef::Primitive(primitive) => { - let path = match primitive { - TypeDefPrimitive::Bool => parse_quote!(::core::primitive::bool), - TypeDefPrimitive::Char => parse_quote!(::core::primitive::char), - TypeDefPrimitive::Str => parse_quote!(::std::string::String), - TypeDefPrimitive::U8 => parse_quote!(::core::primitive::u8), - TypeDefPrimitive::U16 => parse_quote!(::core::primitive::u16), - TypeDefPrimitive::U32 => parse_quote!(::core::primitive::u32), - TypeDefPrimitive::U64 => parse_quote!(::core::primitive::u64), - TypeDefPrimitive::U128 => parse_quote!(::core::primitive::u128), - TypeDefPrimitive::U256 => unimplemented!("not a rust primitive"), - TypeDefPrimitive::I8 => parse_quote!(::core::primitive::i8), - TypeDefPrimitive::I16 => parse_quote!(::core::primitive::i16), - TypeDefPrimitive::I32 => parse_quote!(::core::primitive::i32), - TypeDefPrimitive::I64 => parse_quote!(::core::primitive::i64), - TypeDefPrimitive::I128 => parse_quote!(::core::primitive::i128), - TypeDefPrimitive::I256 => unimplemented!("not a rust primitive"), - }; - syn::Type::Path(path) - } - TypeDef::Compact(_) => { - let compact_type = &self.params[0]; - syn::Type::Path(parse_quote! ( #compact_type )) - } - TypeDef::BitSequence(_) => { - let bit_order_type = &self.params[0]; - let bit_store_type = &self.params[1]; - - let type_path = parse_quote! { ::subxt::bitvec::vec::BitVec<#bit_order_type, #bit_store_type> }; - - syn::Type::Path(type_path) - } - } - } - - /// Returns the type parameters in a path which are inherited from the containing type. - /// - /// # Example - /// - /// ```rust - /// struct S { - /// a: Vec>, // the parent type param here is `T` - /// } - /// ``` - fn parent_type_params(&self, acc: &mut HashSet) { - for p in &self.params { - p.parent_type_params(acc); - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct TypeParameter { - pub(super) concrete_type_id: u32, - pub(super) name: proc_macro2::Ident, -} - -impl quote::ToTokens for TypeParameter { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.name.to_tokens(tokens) - } -} - -#[derive(Clone, Debug)] -pub struct TypePathSubstitute { - pub(super) path: syn::TypePath, - pub(super) params: Vec, -} - -impl quote::ToTokens for TypePathSubstitute { - fn to_tokens(&self, tokens: &mut TokenStream) { - if self.params.is_empty() { - self.path.to_tokens(tokens) - } else { - let substitute_path = &self.path; - let params = &self.params; - tokens.extend(quote! { - #substitute_path< #( #params ),* > - }) - } - } -} - -impl TypePathSubstitute { - fn parent_type_params(&self, acc: &mut HashSet) { - for p in &self.params { - p.parent_type_params(acc); - } - } - - fn to_syn_type(&self) -> syn::Type { - if self.params.is_empty() { - syn::Type::Path(self.path.clone()) - } else { - let substitute_path = &self.path; - let params = &self.params; - parse_quote! ( #substitute_path< #( #params ),* > ) - } - } -} diff --git a/examples/custom_type_derives.rs b/examples/custom_type_derives.rs deleted file mode 100644 index ee3d25788f..0000000000 --- a/examples/custom_type_derives.rs +++ /dev/null @@ -1,30 +0,0 @@ -// 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 . - -#[subxt::subxt( - runtime_metadata_path = "examples/polkadot_metadata.scale", - generated_type_derives = "Clone, Debug" -)] -pub mod polkadot {} - -use polkadot::runtime_types::frame_support::PalletId; - -#[async_std::main] -async fn main() -> Result<(), Box> { - let pallet_id = PalletId([1u8; 8]); - let _ = ::clone(&pallet_id); - Ok(()) -} diff --git a/examples/fetch_all_accounts.rs b/examples/fetch_all_accounts.rs deleted file mode 100644 index d01cfb483c..0000000000 --- a/examples/fetch_all_accounts.rs +++ /dev/null @@ -1,45 +0,0 @@ -// 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 . - -//! To run this example, a local polkadot node should be running. -//! -//! E.g. -//! ```bash -//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location -//! polkadot --dev --tmp -//! ``` - -use subxt::ClientBuilder; - -#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")] -pub mod polkadot {} - -#[async_std::main] -async fn main() -> Result<(), Box> { - env_logger::init(); - - let api = ClientBuilder::new() - .build() - .await? - .to_runtime_api::>(); - - let mut iter = api.storage().system().account_iter(None).await?; - - while let Some((key, account)) = iter.next().await? { - println!("{}: {}", hex::encode(key), account.data.free); - } - Ok(()) -} diff --git a/examples/fetch_remote.rs b/examples/fetch_remote.rs deleted file mode 100644 index a54004551d..0000000000 --- a/examples/fetch_remote.rs +++ /dev/null @@ -1,47 +0,0 @@ -// 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 . - -use subxt::ClientBuilder; - -#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")] -pub mod polkadot {} - -#[async_std::main] -async fn main() -> Result<(), Box> { - env_logger::init(); - - let api = ClientBuilder::new() - .set_url("wss://rpc.polkadot.io") - .build() - .await? - .to_runtime_api::>(); - - let block_number = 1; - - let block_hash = api - .client - .rpc() - .block_hash(Some(block_number.into())) - .await?; - - if let Some(hash) = block_hash { - println!("Block hash for block number {}: {}", block_number, hash); - } else { - println!("Block number {} not found.", block_number); - } - - Ok(()) -} diff --git a/examples/polkadot_balance_transfer.rs b/examples/polkadot_balance_transfer.rs deleted file mode 100644 index ee3535173c..0000000000 --- a/examples/polkadot_balance_transfer.rs +++ /dev/null @@ -1,55 +0,0 @@ -// 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 . - -//! To run this example, a local polkadot node should be running. -//! -//! E.g. -//! ```bash -//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location -//! polkadot --dev --tmp -//! ``` - -use sp_keyring::AccountKeyring; -use subxt::{ - ClientBuilder, - PairSigner, -}; - -#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")] -pub mod polkadot {} - -#[async_std::main] -async fn main() -> Result<(), Box> { - env_logger::init(); - - let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let dest = AccountKeyring::Bob.to_account_id().into(); - - let api = ClientBuilder::new() - .build() - .await? - .to_runtime_api::>(); - let hash = api - .tx() - .balances() - .transfer(dest, 10_000) - .sign_and_submit(&signer) - .await?; - - println!("Balance transfer extrinsic submitted: {}", hash); - - Ok(()) -} diff --git a/examples/polkadot_metadata.scale b/examples/polkadot_metadata.scale deleted file mode 100644 index 678aba3161..0000000000 Binary files a/examples/polkadot_metadata.scale and /dev/null differ diff --git a/examples/submit_and_watch.rs b/examples/submit_and_watch.rs deleted file mode 100644 index 8bc1d8d83a..0000000000 --- a/examples/submit_and_watch.rs +++ /dev/null @@ -1,58 +0,0 @@ -// 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 . - -//! To run this example, a local polkadot node should be running. -//! -//! E.g. -//! ```bash -//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location -//! polkadot --dev --tmp -//! ``` - -use sp_keyring::AccountKeyring; -use subxt::{ - ClientBuilder, - PairSigner, -}; - -#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")] -pub mod polkadot {} - -#[async_std::main] -async fn main() -> Result<(), Box> { - env_logger::init(); - - let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let dest = AccountKeyring::Bob.to_account_id().into(); - - let api = ClientBuilder::new() - .build() - .await? - .to_runtime_api::>(); - let result = api - .tx() - .balances() - .transfer(dest, 10_000) - .sign_and_submit_then_watch(&signer) - .await?; - - if let Some(event) = result.find_event::()? { - println!("Balance transfer success: value: {:?}", event.2); - } else { - println!("Failed to find Balances::Transfer Event"); - } - Ok(()) -} diff --git a/examples/transfer_subscribe.rs b/examples/transfer_subscribe.rs deleted file mode 100644 index 9b14fbcb1d..0000000000 --- a/examples/transfer_subscribe.rs +++ /dev/null @@ -1,68 +0,0 @@ -// 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 . - -//! To run this example, a local polkadot node should be running. -//! -//! E.g. -//! ```bash -//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.11/polkadot" --output /usr/local/bin/polkadot --location -//! polkadot --dev --tmp -//! ``` - -use sp_keyring::AccountKeyring; -use subxt::{ - ClientBuilder, - EventSubscription, - PairSigner, -}; - -#[subxt::subxt(runtime_metadata_path = "examples/polkadot_metadata.scale")] -pub mod polkadot {} - -#[async_std::main] -async fn main() -> Result<(), Box> { - env_logger::init(); - - let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let dest = AccountKeyring::Bob.to_account_id().into(); - - let api = ClientBuilder::new() - .build() - .await? - .to_runtime_api::>(); - - let sub = api.client.rpc().subscribe_events().await?; - let decoder = api.client.events_decoder(); - let mut sub = EventSubscription::::new(sub, decoder); - sub.filter_event::(); - - api.tx() - .balances() - .transfer(dest, 10_000) - .sign_and_submit(&signer) - .await?; - - let raw = sub.next().await.unwrap().unwrap(); - let event = ::decode( - &mut &raw.data[..], - ); - if let Ok(e) = event { - println!("Balance transfer success: value: {:?}", e.2); - } else { - println!("Failed to subscribe to Balances::Transfer Event"); - } - Ok(()) -} diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 57a18f65b6..f223c23219 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -15,23 +15,4 @@ description = "Generate types and helpers for interacting with Substrate runtime proc-macro = true [dependencies] -async-trait = "0.1.49" -codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive", "full"] } -darling = "0.13.0" -frame-metadata = "14.0" -heck = "0.3.2" -proc-macro2 = "1.0.24" -proc-macro-crate = "0.1.5" -proc-macro-error = "1.0.4" -quote = "1.0.8" -syn = "1.0.58" -scale-info = "1.0.0" - subxt-codegen = { version = "0.1.0", path = "../codegen" } - -[dev-dependencies] -pretty_assertions = "0.6.1" -subxt = { path = ".." } -trybuild = "1.0.38" - -sp-keyring = { package = "sp-keyring", git = "https://github.com/paritytech/substrate/" } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 41706634cf..989ecad0b2 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -16,40 +16,9 @@ extern crate proc_macro; -use darling::FromMeta; use proc_macro::TokenStream; -use proc_macro_error::proc_macro_error; -use syn::{ - parse_macro_input, - punctuated::Punctuated, -}; - -#[derive(Debug, FromMeta)] -struct RuntimeMetadataArgs { - runtime_metadata_path: String, - #[darling(default)] - generated_type_derives: Option, -} - -#[derive(Debug, FromMeta)] -struct GeneratedTypeDerives(Punctuated); #[proc_macro_attribute] -#[proc_macro_error] -pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream { - let attr_args = parse_macro_input!(args as syn::AttributeArgs); - let item_mod = parse_macro_input!(input as syn::ItemMod); - - let args = match RuntimeMetadataArgs::from_list(&attr_args) { - Ok(v) => v, - Err(e) => return TokenStream::from(e.write_errors()), - }; - - let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()); - let root_path = std::path::Path::new(&root); - let path = root_path.join(args.runtime_metadata_path); - - let generated_type_derives = args.generated_type_derives.map(|derives| derives.0); - - subxt_codegen::generate_runtime_api(item_mod, &path, generated_type_derives).into() +pub fn subxt(_args: TokenStream, _input: TokenStream) -> TokenStream { + Default::default() } diff --git a/src/client.rs b/src/client.rs deleted file mode 100644 index ae8a5a91c8..0000000000 --- a/src/client.rs +++ /dev/null @@ -1,280 +0,0 @@ -// 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 . - -use futures::future; -pub use sp_runtime::traits::SignedExtension; -pub use sp_version::RuntimeVersion; - -use crate::{ - events::EventsDecoder, - extrinsic::{ - self, - SignedExtra, - Signer, - UncheckedExtrinsic, - }, - rpc::{ - ExtrinsicSuccess, - Rpc, - RpcClient, - SystemProperties, - }, - storage::StorageClient, - AccountData, - Call, - Config, - Error, - ExtrinsicExtraData, - Metadata, -}; - -/// ClientBuilder for constructing a Client. -#[derive(Default)] -pub struct ClientBuilder { - url: Option, - client: Option, - page_size: Option, - accept_weak_inclusion: bool, -} - -impl ClientBuilder { - /// Creates a new ClientBuilder. - pub fn new() -> Self { - Self { - url: None, - client: None, - page_size: None, - accept_weak_inclusion: false, - } - } - - /// Sets the jsonrpsee client. - pub fn set_client>(mut self, client: C) -> Self { - self.client = Some(client.into()); - self - } - - /// Set the substrate rpc address. - pub fn set_url>(mut self, url: P) -> Self { - self.url = Some(url.into()); - self - } - - /// Set the page size. - pub fn set_page_size(mut self, size: u32) -> Self { - self.page_size = Some(size); - self - } - - /// Only check that transactions are InBlock on submit. - pub fn accept_weak_inclusion(mut self) -> Self { - self.accept_weak_inclusion = true; - self - } - - /// Creates a new Client. - pub async fn build(self) -> Result, Error> { - let client = if let Some(client) = self.client { - client - } else { - let url = self.url.as_deref().unwrap_or("ws://127.0.0.1:9944"); - RpcClient::try_from_url(url).await? - }; - let mut rpc = Rpc::new(client); - if self.accept_weak_inclusion { - rpc.accept_weak_inclusion(); - } - let (metadata, genesis_hash, runtime_version, properties) = future::join4( - rpc.metadata(), - rpc.genesis_hash(), - rpc.runtime_version(None), - rpc.system_properties(), - ) - .await; - let metadata = metadata?; - - let events_decoder = EventsDecoder::new(metadata.clone()); - - Ok(Client { - rpc, - genesis_hash: genesis_hash?, - metadata, - events_decoder, - properties: properties.unwrap_or_else(|_| Default::default()), - runtime_version: runtime_version?, - iter_page_size: self.page_size.unwrap_or(10), - }) - } -} - -/// Client to interface with a substrate node. -pub struct Client { - rpc: Rpc, - genesis_hash: T::Hash, - metadata: Metadata, - events_decoder: EventsDecoder, - properties: SystemProperties, - runtime_version: RuntimeVersion, - // _marker: PhantomData<(fn() -> T::Signature, T::Extra)>, - iter_page_size: u32, -} - -impl Clone for Client { - fn clone(&self) -> Self { - Self { - rpc: self.rpc.clone(), - genesis_hash: self.genesis_hash, - metadata: self.metadata.clone(), - events_decoder: self.events_decoder.clone(), - properties: self.properties.clone(), - runtime_version: self.runtime_version.clone(), - iter_page_size: self.iter_page_size, - } - } -} - -impl Client { - /// Returns the genesis hash. - pub fn genesis(&self) -> &T::Hash { - &self.genesis_hash - } - - /// Returns the chain metadata. - pub fn metadata(&self) -> &Metadata { - &self.metadata - } - - /// Returns the system properties - pub fn properties(&self) -> &SystemProperties { - &self.properties - } - - /// Returns the rpc client. - pub fn rpc(&self) -> &Rpc { - &self.rpc - } - - /// Create a client for accessing runtime storage - pub fn storage(&self) -> StorageClient { - StorageClient::new(&self.rpc, &self.metadata, self.iter_page_size) - } - - /// Convert the client to a runtime api wrapper for custom runtime access. - /// - /// The `subxt` proc macro will provide methods to submit extrinsics and read storage specific - /// to the target runtime. - pub fn to_runtime_api>(self) -> R { - self.into() - } - - /// Returns the events decoder. - pub fn events_decoder(&self) -> &EventsDecoder { - &self.events_decoder - } -} - -/// A constructed call ready to be signed and submitted. -pub struct SubmittableExtrinsic<'a, T: Config, C> { - client: &'a Client, - call: C, -} - -impl<'a, T, C> SubmittableExtrinsic<'a, T, C> -where - T: Config + ExtrinsicExtraData, - C: Call + Send + Sync, -{ - /// Create a new [`SubmittableExtrinsic`]. - pub fn new(client: &'a Client, call: C) -> Self { - Self { client, call } - } - - /// Creates and signs an extrinsic and submits it to the chain. - /// - /// Returns when the extrinsic has successfully been included in the block, together with any - /// events which were triggered by the extrinsic. - pub async fn sign_and_submit_then_watch( - self, - signer: &(dyn Signer + Send + Sync), - ) -> Result, Error> - where - <<>::Extra as SignedExtra>::Extra as SignedExtension>::AdditionalSigned: Send + Sync + 'static - { - let extrinsic = self.create_signed(signer).await?; - self.client - .rpc() - .submit_and_watch_extrinsic(extrinsic, self.client.events_decoder()) - .await - } - - /// Creates and signs an extrinsic and submits to the chain for block inclusion. - /// - /// Returns `Ok` with the extrinsic hash if it is valid extrinsic. - /// - /// # Note - /// - /// Success does not mean the extrinsic has been included in the block, just that it is valid - /// and has been included in the transaction pool. - pub async fn sign_and_submit( - self, - signer: &(dyn Signer + Send + Sync), - ) -> Result - where - <<>::Extra as SignedExtra>::Extra as SignedExtension>::AdditionalSigned: Send + Sync + 'static - { - let extrinsic = self.create_signed(signer).await?; - self.client.rpc().submit_extrinsic(extrinsic).await - } - - /// Creates a signed extrinsic. - pub async fn create_signed( - &self, - signer: &(dyn Signer + Send + Sync), - ) -> Result, Error> - where - <<>::Extra as SignedExtra>::Extra as SignedExtension>::AdditionalSigned: Send + Sync + 'static - { - let account_nonce = if let Some(nonce) = signer.nonce() { - nonce - } else { - let account_storage_entry = - <>::AccountData as AccountData>::storage_entry(signer.account_id().clone()); - let account_data = self - .client - .storage() - .fetch_or_default(&account_storage_entry, None) - .await?; - <>::AccountData as AccountData>::nonce( - &account_data, - ) - }; - let call = self - .client - .metadata() - .pallet(C::PALLET) - .and_then(|pallet| pallet.encode_call(&self.call))?; - - let signed = extrinsic::create_signed( - &self.client.runtime_version, - self.client.genesis_hash, - account_nonce, - call, - signer, - ) - .await?; - Ok(signed) - } -} diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 460cc4c5f0..0000000000 --- a/src/config.rs +++ /dev/null @@ -1,105 +0,0 @@ -// 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 . - -use crate::{ - SignedExtra, - StorageEntry, -}; -use codec::{ - Codec, - Encode, - EncodeLike, -}; -use core::fmt::Debug; -use sp_runtime::traits::{ - AtLeast32Bit, - Extrinsic, - Hash, - Header, - MaybeSerializeDeserialize, - Member, - Verify, -}; - -/// Runtime types. -pub trait Config: Clone + Sized + Send + Sync + 'static { - /// Account index (aka nonce) type. This stores the number of previous - /// transactions associated with a sender account. - type Index: Parameter + Member + Default + AtLeast32Bit + Copy + scale_info::TypeInfo; - - /// The block number type used by the runtime. - type BlockNumber: Parameter - + Member - + Default - + Copy - + core::hash::Hash - + core::str::FromStr; - - /// The output of the `Hashing` function. - type Hash: Parameter - + Member - + MaybeSerializeDeserialize - + Ord - + Default - + Copy - + std::hash::Hash - + AsRef<[u8]> - + AsMut<[u8]> - + scale_info::TypeInfo; - - /// The hashing system (algorithm) being used in the runtime (e.g. Blake2). - type Hashing: Hash; - - /// The user account identifier type for the runtime. - type AccountId: Parameter + Member; - - /// The address type. This instead of `::Source`. - type Address: Codec + Clone + PartialEq; - - /// The block header. - type Header: Parameter - + Header - + serde::de::DeserializeOwned; - - /// Signature type. - type Signature: Verify + Encode + Send + Sync + 'static; - - /// Extrinsic type within blocks. - type Extrinsic: Parameter + Extrinsic + Debug + MaybeSerializeDeserialize; -} - -/// Parameter trait copied from `substrate::frame_support` -pub trait Parameter: Codec + EncodeLike + Clone + Eq + Debug {} -impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + Debug {} - -/// Trait to fetch data about an account. -/// -/// Should be implemented on a type implementing `StorageEntry`, -/// usually generated by the `subxt` macro. -pub trait AccountData: StorageEntry { - /// Create a new storage entry key from the account id. - fn storage_entry(account_id: T::AccountId) -> Self; - /// Get the nonce from the storage entry value. - fn nonce(result: &::Value) -> T::Index; -} - -/// Trait to configure the extra data for an extrinsic. -pub trait ExtrinsicExtraData { - /// The type of the [`StorageEntry`] which can be used to retrieve an account nonce. - type AccountData: AccountData; - /// The type of extra data and additional signed data to be included in a transaction. - type Extra: SignedExtra + Send + Sync + 'static; -} diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 5fbfa1897c..0000000000 --- a/src/error.rs +++ /dev/null @@ -1,160 +0,0 @@ -// 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 . - -use crate::{ - events::EventsDecodingError, - metadata::{ - InvalidMetadataError, - MetadataError, - }, - Metadata, -}; -use jsonrpsee_types::Error as RequestError; -use sp_core::crypto::SecretStringError; -use sp_runtime::{ - transaction_validity::TransactionValidityError, - DispatchError, -}; -use thiserror::Error; - -/// Error enum. -#[derive(Debug, Error)] -pub enum Error { - /// Io error. - #[error("Io error: {0}")] - Io(#[from] std::io::Error), - /// Codec error. - #[error("Scale codec error: {0}")] - Codec(#[from] codec::Error), - /// Rpc error. - #[error("Rpc error: {0}")] - Rpc(#[from] RequestError), - /// Serde serialization error - #[error("Serde json error: {0}")] - Serialization(#[from] serde_json::error::Error), - /// Secret string error. - #[error("Secret String Error")] - SecretString(SecretStringError), - /// Extrinsic validity error - #[error("Transaction Validity Error: {0:?}")] - Invalid(TransactionValidityError), - /// Invalid metadata error - #[error("Invalid Metadata: {0}")] - InvalidMetadata(#[from] InvalidMetadataError), - /// Invalid metadata error - #[error("Metadata: {0}")] - Metadata(#[from] MetadataError), - /// Runtime error. - #[error("Runtime error: {0}")] - Runtime(#[from] RuntimeError), - /// Events decoding error. - #[error("Events decoding error: {0}")] - EventsDecoding(#[from] EventsDecodingError), - /// Other error. - #[error("Other error: {0}")] - Other(String), -} - -impl From for Error { - fn from(error: SecretStringError) -> Self { - Error::SecretString(error) - } -} - -impl From for Error { - fn from(error: TransactionValidityError) -> Self { - Error::Invalid(error) - } -} - -impl From<&str> for Error { - fn from(error: &str) -> Self { - Error::Other(error.into()) - } -} - -impl From for Error { - fn from(error: String) -> Self { - Error::Other(error) - } -} - -/// Runtime error. -#[derive(Clone, Debug, Eq, Error, PartialEq)] -pub enum RuntimeError { - /// Module error. - #[error("Runtime module error: {0}")] - Module(PalletError), - /// At least one consumer is remaining so the account cannot be destroyed. - #[error("At least one consumer is remaining so the account cannot be destroyed.")] - ConsumerRemaining, - /// There are no providers so the account cannot be created. - #[error("There are no providers so the account cannot be created.")] - NoProviders, - /// Bad origin. - #[error("Bad origin: throw by ensure_signed, ensure_root or ensure_none.")] - BadOrigin, - /// Cannot lookup. - #[error("Cannot lookup some information required to validate the transaction.")] - CannotLookup, - /// Other error. - #[error("Other error: {0}")] - Other(String), -} - -impl RuntimeError { - /// Converts a `DispatchError` into a subxt error. - pub fn from_dispatch( - metadata: &Metadata, - error: DispatchError, - ) -> Result { - match error { - DispatchError::Module { - index, - error, - message: _, - } => { - let error = metadata.error(index, error)?; - Ok(Self::Module(PalletError { - pallet: error.pallet().to_string(), - error: error.error().to_string(), - description: error.description().to_vec(), - })) - } - DispatchError::BadOrigin => Ok(Self::BadOrigin), - DispatchError::CannotLookup => Ok(Self::CannotLookup), - DispatchError::ConsumerRemaining => Ok(Self::ConsumerRemaining), - DispatchError::NoProviders => Ok(Self::NoProviders), - DispatchError::Arithmetic(_math_error) => { - Ok(Self::Other("math_error".into())) - } - DispatchError::Token(_token_error) => Ok(Self::Other("token error".into())), - DispatchError::Other(msg) => Ok(Self::Other(msg.to_string())), - } - } -} - -/// Module error. -#[derive(Clone, Debug, Eq, Error, PartialEq)] -#[error("{error} from {pallet}")] -pub struct PalletError { - /// The module where the error originated. - pub pallet: String, - /// The actual error code. - pub error: String, - /// The error description. - pub description: Vec, -} diff --git a/src/events.rs b/src/events.rs deleted file mode 100644 index 634ff10e5a..0000000000 --- a/src/events.rs +++ /dev/null @@ -1,388 +0,0 @@ -// 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 . - -use codec::{ - Codec, - Compact, - Decode, - Encode, - Input, -}; -use std::marker::PhantomData; - -use crate::{ - metadata::{ - EventMetadata, - MetadataError, - }, - Config, - Error, - Metadata, - Phase, - RuntimeError, -}; -use scale_info::{ - TypeDef, - TypeDefPrimitive, -}; -use sp_core::Bytes; - -/// Raw bytes for an Event -#[derive(Debug)] -pub struct RawEvent { - /// The name of the pallet from whence the Event originated. - pub pallet: String, - /// The index of the pallet from whence the Event originated. - pub pallet_index: u8, - /// The name of the pallet Event variant. - pub variant: String, - /// The index of the pallet Event variant. - pub variant_index: u8, - /// The raw Event data - pub data: Bytes, -} - -/// Events decoder. -#[derive(Debug, Clone)] -pub struct EventsDecoder { - metadata: Metadata, - marker: PhantomData, -} - -impl EventsDecoder -where - T: Config, -{ - /// Creates a new `EventsDecoder`. - pub fn new(metadata: Metadata) -> Self { - Self { - metadata, - marker: Default::default(), - } - } - - /// Decode events. - pub fn decode_events(&self, input: &mut &[u8]) -> Result, Error> { - let compact_len = >::decode(input)?; - let len = compact_len.0 as usize; - log::debug!("decoding {} events", len); - - let mut r = Vec::new(); - for _ in 0..len { - // decode EventRecord - let phase = Phase::decode(input)?; - let pallet_index = input.read_byte()?; - let variant_index = input.read_byte()?; - log::debug!( - "phase {:?}, pallet_index {}, event_variant: {}", - phase, - pallet_index, - variant_index - ); - log::debug!("remaining input: {}", hex::encode(&input)); - - let event_metadata = self.metadata.event(pallet_index, variant_index)?; - - let mut event_data = Vec::::new(); - let mut event_errors = Vec::::new(); - let result = self.decode_raw_event( - &event_metadata, - input, - &mut event_data, - &mut event_errors, - ); - let raw = match result { - Ok(()) => { - log::debug!("raw bytes: {}", hex::encode(&event_data),); - - let event = RawEvent { - pallet: event_metadata.pallet().to_string(), - pallet_index, - variant: event_metadata.event().to_string(), - variant_index, - data: event_data.into(), - }; - - // topics come after the event data in EventRecord - let topics = Vec::::decode(input)?; - log::debug!("topics: {:?}", topics); - - Raw::Event(event) - } - Err(err) => return Err(err), - }; - - if event_errors.is_empty() { - r.push((phase.clone(), raw)); - } - - for err in event_errors { - r.push((phase.clone(), Raw::Error(err))); - } - } - Ok(r) - } - - fn decode_raw_event( - &self, - event_metadata: &EventMetadata, - input: &mut &[u8], - output: &mut Vec, - errors: &mut Vec, - ) -> Result<(), Error> { - log::debug!( - "Decoding Event '{}::{}'", - event_metadata.pallet(), - event_metadata.event() - ); - for arg in event_metadata.variant().fields() { - let type_id = arg.ty().id(); - if event_metadata.pallet() == "System" - && event_metadata.event() == "ExtrinsicFailed" - { - let ty = self - .metadata - .resolve_type(type_id) - .ok_or(MetadataError::TypeNotFound(type_id))?; - - if ty.path().ident() == Some("DispatchError".to_string()) { - let dispatch_error = sp_runtime::DispatchError::decode(input)?; - log::info!("Dispatch Error {:?}", dispatch_error); - dispatch_error.encode_to(output); - let runtime_error = - RuntimeError::from_dispatch(&self.metadata, dispatch_error)?; - errors.push(runtime_error); - continue - } - } - self.decode_type(type_id, input, output)? - } - Ok(()) - } - - fn decode_type( - &self, - type_id: u32, - input: &mut &[u8], - output: &mut Vec, - ) -> Result<(), Error> { - let ty = self - .metadata - .resolve_type(type_id) - .ok_or(MetadataError::TypeNotFound(type_id))?; - - fn decode_raw( - input: &mut &[u8], - output: &mut Vec, - ) -> Result<(), Error> { - let decoded = T::decode(input)?; - decoded.encode_to(output); - Ok(()) - } - - match ty.type_def() { - TypeDef::Composite(composite) => { - for field in composite.fields() { - self.decode_type(field.ty().id(), input, output)? - } - Ok(()) - } - TypeDef::Variant(variant) => { - let variant_index = u8::decode(input)?; - variant_index.encode_to(output); - let variant = variant.variants().get(variant_index as usize).ok_or( - Error::Other(format!("Variant {} not found", variant_index)), - )?; - for field in variant.fields() { - self.decode_type(field.ty().id(), input, output)?; - } - Ok(()) - } - TypeDef::Sequence(seq) => { - let len = >::decode(input)?; - len.encode_to(output); - for _ in 0..len.0 { - self.decode_type(seq.type_param().id(), input, output)?; - } - Ok(()) - } - TypeDef::Array(arr) => { - for _ in 0..arr.len() { - self.decode_type(arr.type_param().id(), input, output)?; - } - Ok(()) - } - TypeDef::Tuple(tuple) => { - for field in tuple.fields() { - self.decode_type(field.id(), input, output)?; - } - Ok(()) - } - TypeDef::Primitive(primitive) => { - match primitive { - TypeDefPrimitive::Bool => decode_raw::(input, output), - TypeDefPrimitive::Char => { - Err(EventsDecodingError::UnsupportedPrimitive( - TypeDefPrimitive::Char, - ) - .into()) - } - TypeDefPrimitive::Str => decode_raw::(input, output), - TypeDefPrimitive::U8 => decode_raw::(input, output), - TypeDefPrimitive::U16 => decode_raw::(input, output), - TypeDefPrimitive::U32 => decode_raw::(input, output), - TypeDefPrimitive::U64 => decode_raw::(input, output), - TypeDefPrimitive::U128 => decode_raw::(input, output), - TypeDefPrimitive::U256 => { - Err(EventsDecodingError::UnsupportedPrimitive( - TypeDefPrimitive::U256, - ) - .into()) - } - TypeDefPrimitive::I8 => decode_raw::(input, output), - TypeDefPrimitive::I16 => decode_raw::(input, output), - TypeDefPrimitive::I32 => decode_raw::(input, output), - TypeDefPrimitive::I64 => decode_raw::(input, output), - TypeDefPrimitive::I128 => decode_raw::(input, output), - TypeDefPrimitive::I256 => { - Err(EventsDecodingError::UnsupportedPrimitive( - TypeDefPrimitive::I256, - ) - .into()) - } - } - } - TypeDef::Compact(_compact) => { - let inner = self - .metadata - .resolve_type(type_id) - .ok_or(MetadataError::TypeNotFound(type_id))?; - let mut decode_compact_primitive = |primitive: &TypeDefPrimitive| { - match primitive { - TypeDefPrimitive::U8 => decode_raw::>(input, output), - TypeDefPrimitive::U16 => { - decode_raw::>(input, output) - } - TypeDefPrimitive::U32 => { - decode_raw::>(input, output) - } - TypeDefPrimitive::U64 => { - decode_raw::>(input, output) - } - TypeDefPrimitive::U128 => { - decode_raw::>(input, output) - } - prim => { - Err(EventsDecodingError::InvalidCompactPrimitive( - prim.clone(), - ) - .into()) - } - } - }; - match inner.type_def() { - TypeDef::Primitive(primitive) => decode_compact_primitive(primitive), - TypeDef::Composite(composite) => { - match composite.fields() { - [field] => { - let field_ty = - self.metadata.resolve_type(field.ty().id()).ok_or( - MetadataError::TypeNotFound(field.ty().id()), - )?; - if let TypeDef::Primitive(primitive) = field_ty.type_def() - { - decode_compact_primitive(primitive) - } else { - Err(EventsDecodingError::InvalidCompactType("Composite type must have a single primitive field".into()).into()) - } - } - _ => { - Err(EventsDecodingError::InvalidCompactType( - "Composite type must have a single field".into(), - ) - .into()) - } - } - } - _ => { - Err(EventsDecodingError::InvalidCompactType( - "Compact type must be a primitive or a composite type".into(), - ) - .into()) - } - } - } - TypeDef::BitSequence(_bitseq) => { - // decode_raw:: - unimplemented!("BitVec decoding for events not implemented yet") - } - } - } -} - -/// Raw event or error event -#[derive(Debug)] -pub enum Raw { - /// Event - Event(RawEvent), - /// Error - Error(RuntimeError), -} - -#[derive(Debug, thiserror::Error)] -pub enum EventsDecodingError { - /// Unsupported primitive type - #[error("Unsupported primitive type {0:?}")] - UnsupportedPrimitive(TypeDefPrimitive), - /// Invalid compact type, must be an unsigned int. - #[error("Invalid compact primitive {0:?}")] - InvalidCompactPrimitive(TypeDefPrimitive), - #[error("Invalid compact composite type {0}")] - InvalidCompactType(String), -} - -// #[cfg(test)] -// mod tests { -// use super::*; -// use std::convert::TryFrom; -// -// type DefaultConfig = crate::NodeTemplateRuntime; -// -// #[test] -// fn test_decode_option() { -// let decoder = EventsDecoder::::new( -// Metadata::default(), -// ); -// -// let value = Some(0u8); -// let input = value.encode(); -// let mut output = Vec::::new(); -// let mut errors = Vec::::new(); -// -// decoder -// .decode_raw_bytes( -// &[EventArg::Option(Box::new(EventArg::Primitive( -// "u8".to_string(), -// )))], -// &mut &input[..], -// &mut output, -// &mut errors, -// ) -// .unwrap(); -// -// assert_eq!(output, vec![1, 0]); -// } -// } diff --git a/src/extrinsic/extra.rs b/src/extrinsic/extra.rs deleted file mode 100644 index 62c66bd6f6..0000000000 --- a/src/extrinsic/extra.rs +++ /dev/null @@ -1,307 +0,0 @@ -// 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 . - -use codec::{ - Decode, - Encode, -}; -use core::{ - fmt::Debug, - marker::PhantomData, -}; -use scale_info::TypeInfo; -use sp_runtime::{ - generic::Era, - traits::SignedExtension, - transaction_validity::TransactionValidityError, -}; - -use crate::Config; - -/// Extra type. -// pub type Extra = <::Extra as SignedExtra>::Extra; - -/// SignedExtra checks copied from substrate, in order to remove requirement to implement -/// substrate's `frame_system::Trait` - -/// Ensure the runtime version registered in the transaction is the same as at present. -/// -/// # Note -/// -/// This is modified from the substrate version to allow passing in of the version, which is -/// returned via `additional_signed()`. - -/// Ensure the runtime version registered in the transaction is the same as at present. -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct CheckSpecVersion( - pub PhantomData, - /// Local version to be used for `AdditionalSigned` - #[codec(skip)] - pub u32, -); - -impl SignedExtension for CheckSpecVersion -where - T: Config + Clone + Debug + Eq + Send + Sync, -{ - const IDENTIFIER: &'static str = "CheckSpecVersion"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = u32; - type Pre = (); - fn additional_signed( - &self, - ) -> Result { - Ok(self.1) - } -} - -/// Ensure the transaction version registered in the transaction is the same as at present. -/// -/// # Note -/// -/// This is modified from the substrate version to allow passing in of the version, which is -/// returned via `additional_signed()`. -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct CheckTxVersion( - pub PhantomData, - /// Local version to be used for `AdditionalSigned` - #[codec(skip)] - pub u32, -); - -impl SignedExtension for CheckTxVersion -where - T: Config + Clone + Debug + Eq + Send + Sync, -{ - const IDENTIFIER: &'static str = "CheckTxVersion"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = u32; - type Pre = (); - fn additional_signed( - &self, - ) -> Result { - Ok(self.1) - } -} - -/// Check genesis hash -/// -/// # Note -/// -/// This is modified from the substrate version to allow passing in of the genesis hash, which is -/// returned via `additional_signed()`. -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct CheckGenesis( - pub PhantomData, - /// Local genesis hash to be used for `AdditionalSigned` - #[codec(skip)] - pub T::Hash, -); - -impl SignedExtension for CheckGenesis -where - T: Config + Clone + Debug + Eq + Send + Sync, -{ - const IDENTIFIER: &'static str = "CheckGenesis"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = T::Hash; - type Pre = (); - fn additional_signed( - &self, - ) -> Result { - Ok(self.1) - } -} - -/// Check for transaction mortality. -/// -/// # Note -/// -/// This is modified from the substrate version to allow passing in of the genesis hash, which is -/// returned via `additional_signed()`. It assumes therefore `Era::Immortal` (The transaction is -/// valid forever) -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct CheckMortality( - /// The default structure for the Extra encoding - pub (Era, PhantomData), - /// Local genesis hash to be used for `AdditionalSigned` - #[codec(skip)] - pub T::Hash, -); - -impl SignedExtension for CheckMortality -where - T: Config + Clone + Debug + Eq + Send + Sync, -{ - const IDENTIFIER: &'static str = "CheckMortality"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = T::Hash; - type Pre = (); - fn additional_signed( - &self, - ) -> Result { - Ok(self.1) - } -} - -/// Nonce check and increment to give replay protection for transactions. -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct CheckNonce(#[codec(compact)] pub T::Index); - -impl SignedExtension for CheckNonce -where - T: Config + Clone + Debug + Eq + Send + Sync, -{ - const IDENTIFIER: &'static str = "CheckNonce"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = (); - type Pre = (); - fn additional_signed( - &self, - ) -> Result { - Ok(()) - } -} - -/// Resource limit check. -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct CheckWeight(pub PhantomData); - -impl SignedExtension for CheckWeight -where - T: Config + Clone + Debug + Eq + Send + Sync, -{ - const IDENTIFIER: &'static str = "CheckWeight"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = (); - type Pre = (); - fn additional_signed( - &self, - ) -> Result { - Ok(()) - } -} - -/// Require the transactor pay for themselves and maybe include a tip to gain additional priority -/// in the queue. -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct ChargeTransactionPayment(#[codec(compact)] pub u128); - -impl SignedExtension for ChargeTransactionPayment { - const IDENTIFIER: &'static str = "ChargeTransactionPayment"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = (); - type Pre = (); - fn additional_signed( - &self, - ) -> Result { - Ok(()) - } -} - -/// Trait for implementing transaction extras for a runtime. -pub trait SignedExtra: SignedExtension { - /// The type the extras. - type Extra: SignedExtension + Send + Sync; - - /// Creates a new `SignedExtra`. - fn new( - spec_version: u32, - tx_version: u32, - nonce: T::Index, - genesis_hash: T::Hash, - ) -> Self; - - /// Returns the transaction extra. - fn extra(&self) -> Self::Extra; -} - -/// Default `SignedExtra` for substrate runtimes. -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct DefaultExtra { - spec_version: u32, - tx_version: u32, - nonce: T::Index, - genesis_hash: T::Hash, -} - -impl SignedExtra for DefaultExtra { - type Extra = ( - CheckSpecVersion, - CheckTxVersion, - CheckGenesis, - CheckMortality, - CheckNonce, - CheckWeight, - ChargeTransactionPayment, - ); - - fn new( - spec_version: u32, - tx_version: u32, - nonce: T::Index, - genesis_hash: T::Hash, - ) -> Self { - DefaultExtra { - spec_version, - tx_version, - nonce, - genesis_hash, - } - } - - fn extra(&self) -> Self::Extra { - ( - CheckSpecVersion(PhantomData, self.spec_version), - CheckTxVersion(PhantomData, self.tx_version), - CheckGenesis(PhantomData, self.genesis_hash), - CheckMortality((Era::Immortal, PhantomData), self.genesis_hash), - CheckNonce(self.nonce), - CheckWeight(PhantomData), - ChargeTransactionPayment(u128::default()), - ) - } -} - -impl SignedExtension for DefaultExtra { - const IDENTIFIER: &'static str = "DefaultExtra"; - type AccountId = T::AccountId; - type Call = (); - type AdditionalSigned = - <>::Extra as SignedExtension>::AdditionalSigned; - type Pre = (); - - fn additional_signed( - &self, - ) -> Result { - self.extra().additional_signed() - } -} diff --git a/src/extrinsic/mod.rs b/src/extrinsic/mod.rs deleted file mode 100644 index 542382bab5..0000000000 --- a/src/extrinsic/mod.rs +++ /dev/null @@ -1,88 +0,0 @@ -// 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 . - -//! Create signed or unsigned extrinsics. - -mod extra; -mod signer; - -pub use self::{ - extra::{ - ChargeTransactionPayment, - CheckGenesis, - CheckMortality, - CheckNonce, - CheckSpecVersion, - CheckTxVersion, - CheckWeight, - DefaultExtra, - SignedExtra, - }, - signer::{ - PairSigner, - Signer, - }, -}; - -use sp_runtime::traits::SignedExtension; -use sp_version::RuntimeVersion; - -use crate::{ - Config, - Encoded, - Error, - ExtrinsicExtraData, -}; - -/// UncheckedExtrinsic type. -pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic< - ::Address, - Encoded, - ::Signature, - <>::Extra as SignedExtra>::Extra, ->; - -/// SignedPayload type. -pub type SignedPayload = sp_runtime::generic::SignedPayload< - Encoded, - <>::Extra as SignedExtra>::Extra, ->; - -/// Creates a signed extrinsic -pub async fn create_signed( - runtime_version: &RuntimeVersion, - genesis_hash: T::Hash, - nonce: T::Index, - call: Encoded, - signer: &(dyn Signer + Send + Sync), -) -> Result, Error> -where - T: Config + ExtrinsicExtraData, - <<>::Extra as SignedExtra>::Extra as SignedExtension>::AdditionalSigned: - Send + Sync, -{ - let spec_version = runtime_version.spec_version; - let tx_version = runtime_version.transaction_version; - let extra = >::Extra::new( - spec_version, - tx_version, - nonce, - genesis_hash, - ); - let payload = SignedPayload::::new(call, extra.extra())?; - let signed = signer.sign(payload).await?; - Ok(signed) -} diff --git a/src/extrinsic/signer.rs b/src/extrinsic/signer.rs deleted file mode 100644 index 36c42e01a5..0000000000 --- a/src/extrinsic/signer.rs +++ /dev/null @@ -1,130 +0,0 @@ -// 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 . - -//! A library to **sub**mit e**xt**rinsics to a -//! [substrate](https://github.com/paritytech/substrate) node via RPC. - -use super::{ - SignedPayload, - UncheckedExtrinsic, -}; -use crate::{ - Config, - ExtrinsicExtraData, - SignedExtra, -}; -use codec::Encode; -use sp_core::Pair; -use sp_runtime::traits::{ - IdentifyAccount, - SignedExtension, - Verify, -}; - -/// Extrinsic signer. -#[async_trait::async_trait] -pub trait Signer> { - /// Returns the account id. - fn account_id(&self) -> &T::AccountId; - - /// Optionally returns a nonce. - fn nonce(&self) -> Option; - - /// Takes an unsigned extrinsic and returns a signed extrinsic. - /// - /// Some signers may fail, for instance because the hardware on which the keys are located has - /// refused the operation. - async fn sign( - &self, - extrinsic: SignedPayload, - ) -> Result, String>; -} - -/// Extrinsic signer using a private key. -#[derive(Clone, Debug)] -pub struct PairSigner { - account_id: T::AccountId, - nonce: Option, - signer: P, -} - -impl PairSigner -where - T: Config + ExtrinsicExtraData, - T::Signature: From, - ::Signer: - From + IdentifyAccount, - P: Pair, -{ - /// Creates a new `Signer` from a `Pair`. - pub fn new(signer: P) -> Self { - let account_id = - ::Signer::from(signer.public()).into_account(); - Self { - account_id, - nonce: None, - signer, - } - } - - /// Sets the nonce to a new value. - pub fn set_nonce(&mut self, nonce: T::Index) { - self.nonce = Some(nonce); - } - - /// Increment the nonce. - pub fn increment_nonce(&mut self) { - self.nonce = self.nonce.map(|nonce| nonce + 1u32.into()); - } - - /// Returns the signer. - pub fn signer(&self) -> &P { - &self.signer - } -} - -#[async_trait::async_trait] -impl Signer for PairSigner -where - T: Config + ExtrinsicExtraData, - T::AccountId: Into + 'static, - <<>::Extra as SignedExtra>::Extra as SignedExtension>::AdditionalSigned: Send + Sync + 'static, - P: Pair + 'static, - P::Signature: Into + 'static, -{ - fn account_id(&self) -> &T::AccountId { - &self.account_id - } - - fn nonce(&self) -> Option { - self.nonce - } - - async fn sign( - &self, - extrinsic: SignedPayload, - ) -> Result, String> { - let signature = extrinsic.using_encoded(|payload| self.signer.sign(payload)); - let (call, extra, _) = extrinsic.deconstruct(); - let extrinsic = UncheckedExtrinsic::::new_signed( - call, - self.account_id.clone().into(), - signature.into(), - extra, - ); - Ok(extrinsic) - } -} diff --git a/src/lib.rs b/src/lib.rs index 95119878b5..36c00e99b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,193 +13,3 @@ // // You should have received a copy of the GNU General Public License // along with subxt. If not, see . - -//! A library to **sub**mit e**xt**rinsics to a -//! [substrate](https://github.com/paritytech/substrate) node via RPC. - -#![deny( - bad_style, - const_err, - improper_ctypes, - missing_docs, - non_shorthand_field_patterns, - no_mangle_generic_items, - overflowing_literals, - path_statements, - patterns_in_fns_without_body, - private_in_public, - unconditional_recursion, - unused_allocation, - unused_comparisons, - unused_parens, - while_true, - trivial_casts, - trivial_numeric_casts, - unused_extern_crates, - clippy::all -)] -#![allow(clippy::type_complexity)] - -pub use frame_metadata::StorageHasher; -pub use subxt_macro::subxt; - -pub use bitvec; -pub use codec; -pub use sp_arithmetic; -pub use sp_core; -pub use sp_runtime; - -use codec::{ - Decode, - DecodeAll, - Encode, -}; -use core::{ - fmt::Debug, - marker::PhantomData, -}; - -mod client; -mod config; -mod error; -mod events; -pub mod extrinsic; -mod metadata; -pub mod rpc; -pub mod storage; -mod subscription; - -pub use crate::{ - client::{ - Client, - ClientBuilder, - SubmittableExtrinsic, - }, - config::{ - AccountData, - Config, - ExtrinsicExtraData, - }, - error::{ - Error, - PalletError, - RuntimeError, - }, - events::{ - EventsDecoder, - RawEvent, - }, - extrinsic::{ - DefaultExtra, - PairSigner, - SignedExtra, - Signer, - UncheckedExtrinsic, - }, - metadata::Metadata, - rpc::{ - BlockNumber, - ExtrinsicSuccess, - ReadProof, - RpcClient, - SystemProperties, - }, - storage::{ - KeyIter, - StorageEntry, - StorageEntryKey, - StorageMapKey, - }, - subscription::{ - EventStorageSubscription, - EventSubscription, - FinalizedEventStorageSubscription, - }, -}; - -/// Call trait. -pub trait Call: Encode { - /// Pallet name. - const PALLET: &'static str; - /// Function name. - const FUNCTION: &'static str; - - /// Returns true if the given pallet and function names match this call. - fn is_call(pallet: &str, function: &str) -> bool { - Self::PALLET == pallet && Self::FUNCTION == function - } -} - -/// Event trait. -pub trait Event: Decode { - /// Pallet name. - const PALLET: &'static str; - /// Event name. - const EVENT: &'static str; - - /// Returns true if the given pallet and event names match this event. - fn is_event(pallet: &str, event: &str) -> bool { - Self::PALLET == pallet && Self::EVENT == event - } -} - -/// Wraps an already encoded byte vector, prevents being encoded as a raw byte vector as part of -/// the transaction payload -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Encoded(pub Vec); - -impl codec::Encode for Encoded { - fn encode(&self) -> Vec { - self.0.to_owned() - } -} - -/// A phase of a block's execution. -#[derive(Clone, Debug, Eq, PartialEq, Decode)] -pub enum Phase { - /// Applying an extrinsic. - ApplyExtrinsic(u32), - /// Finalizing the block. - Finalization, - /// Initializing the block. - Initialization, -} - -/// A wrapper for any type `T` which implement encode/decode in a way compatible with `Vec`. -/// -/// This type is similar to [`WrapperOpaque`], but it differs in the way it stores the type `T`. -/// While [`WrapperOpaque`] stores the decoded type, the [`WrapperKeepOpaque`] stores the type only -/// in its opaque format, aka as a `Vec`. To access the real type `T` [`Self::try_decode`] needs -/// to be used. -#[derive(Debug, Eq, PartialEq, Default, Clone, Decode, Encode)] -pub struct WrapperKeepOpaque { - data: Vec, - _phantom: PhantomData, -} - -impl WrapperKeepOpaque { - /// Try to decode the wrapped type from the inner `data`. - /// - /// Returns `None` if the decoding failed. - pub fn try_decode(&self) -> Option { - T::decode_all(&mut &self.data[..]).ok() - } - - /// Returns the length of the encoded `T`. - pub fn encoded_len(&self) -> usize { - self.data.len() - } - - /// Returns the encoded data. - pub fn encoded(&self) -> &[u8] { - &self.data - } - - /// Create from the given encoded `data`. - pub fn from_encoded(data: Vec) -> Self { - Self { - data, - _phantom: PhantomData, - } - } -} diff --git a/src/metadata.rs b/src/metadata.rs deleted file mode 100644 index 7aeb26cfa4..0000000000 --- a/src/metadata.rs +++ /dev/null @@ -1,357 +0,0 @@ -// 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 . - -use std::{ - collections::HashMap, - convert::TryFrom, -}; - -use codec::Error as CodecError; - -use frame_metadata::{ - PalletConstantMetadata, - RuntimeMetadata, - RuntimeMetadataLastVersion, - RuntimeMetadataPrefixed, - StorageEntryMetadata, - META_RESERVED, -}; - -use crate::{ - Call, - Encoded, -}; -use scale_info::{ - form::PortableForm, - Type, - Variant, -}; - -/// Metadata error. -#[derive(Debug, thiserror::Error)] -pub enum MetadataError { - /// Module is not in metadata. - #[error("Pallet {0} not found")] - PalletNotFound(String), - /// Pallet is not in metadata. - #[error("Pallet index {0} not found")] - PalletIndexNotFound(u8), - /// Call is not in metadata. - #[error("Call {0} not found")] - CallNotFound(&'static str), - /// Event is not in metadata. - #[error("Pallet {0}, Event {0} not found")] - EventNotFound(u8, u8), - /// Event is not in metadata. - #[error("Pallet {0}, Error {0} not found")] - ErrorNotFound(u8, u8), - /// Storage is not in metadata. - #[error("Storage {0} not found")] - StorageNotFound(&'static str), - /// Storage type does not match requested type. - #[error("Storage type error")] - StorageTypeError, - /// Default error. - #[error("Failed to decode default: {0}")] - DefaultError(CodecError), - /// Failure to decode constant value. - #[error("Failed to decode constant value: {0}")] - ConstantValueError(CodecError), - /// Constant is not in metadata. - #[error("Constant {0} not found")] - ConstantNotFound(&'static str), - #[error("Type {0} missing from type registry")] - TypeNotFound(u32), -} - -/// Runtime metadata. -#[derive(Clone, Debug)] -pub struct Metadata { - metadata: RuntimeMetadataLastVersion, - pallets: HashMap, - events: HashMap<(u8, u8), EventMetadata>, - errors: HashMap<(u8, u8), ErrorMetadata>, -} - -impl Metadata { - /// Returns a reference to [`PalletMetadata`]. - pub fn pallet(&self, name: &'static str) -> Result<&PalletMetadata, MetadataError> { - self.pallets - .get(name) - .ok_or(MetadataError::PalletNotFound(name.to_string())) - } - - /// Returns the metadata for the event at the given pallet and event indices. - pub fn event( - &self, - pallet_index: u8, - event_index: u8, - ) -> Result<&EventMetadata, MetadataError> { - let event = self - .events - .get(&(pallet_index, event_index)) - .ok_or(MetadataError::EventNotFound(pallet_index, event_index))?; - Ok(event) - } - - /// Returns the metadata for the error at the given pallet and error indices. - pub fn error( - &self, - pallet_index: u8, - error_index: u8, - ) -> Result<&ErrorMetadata, MetadataError> { - let error = self - .errors - .get(&(pallet_index, error_index)) - .ok_or(MetadataError::ErrorNotFound(pallet_index, error_index))?; - Ok(error) - } - - /// Resolve a type definition. - pub fn resolve_type(&self, id: u32) -> Option<&Type> { - self.metadata.types.resolve(id) - } - - /// Return the runtime metadata. - pub fn runtime_metadata(&self) -> &RuntimeMetadataLastVersion { - &self.metadata - } -} - -#[derive(Clone, Debug)] -pub struct PalletMetadata { - index: u8, - name: String, - calls: HashMap, - storage: HashMap>, - constants: HashMap>, -} - -impl PalletMetadata { - pub fn encode_call(&self, call: &C) -> Result - where - C: Call, - { - let fn_index = self - .calls - .get(C::FUNCTION) - .ok_or(MetadataError::CallNotFound(C::FUNCTION))?; - let mut bytes = vec![self.index, *fn_index]; - bytes.extend(call.encode()); - Ok(Encoded(bytes)) - } - - pub fn storage( - &self, - key: &'static str, - ) -> Result<&StorageEntryMetadata, MetadataError> { - self.storage - .get(key) - .ok_or(MetadataError::StorageNotFound(key)) - } - - /// Get a constant's metadata by name - pub fn constant( - &self, - key: &'static str, - ) -> Result<&PalletConstantMetadata, MetadataError> { - self.constants - .get(key) - .ok_or(MetadataError::ConstantNotFound(key)) - } -} - -#[derive(Clone, Debug)] -pub struct EventMetadata { - pallet: String, - event: String, - variant: Variant, -} - -impl EventMetadata { - /// Get the name of the pallet from which the event was emitted. - pub fn pallet(&self) -> &str { - &self.pallet - } - - /// Get the name of the pallet event which was emitted. - pub fn event(&self) -> &str { - &self.event - } - - /// Get the type def variant for the pallet event. - pub fn variant(&self) -> &Variant { - &self.variant - } -} - -#[derive(Clone, Debug)] -pub struct ErrorMetadata { - pallet: String, - error: String, - variant: Variant, -} - -impl ErrorMetadata { - /// Get the name of the pallet from which the error originates. - pub fn pallet(&self) -> &str { - &self.pallet - } - - /// Get the name of the specific pallet error. - pub fn error(&self) -> &str { - &self.error - } - - /// Get the description of the specific pallet error. - pub fn description(&self) -> &[String] { - self.variant.docs() - } -} - -#[derive(Debug, thiserror::Error)] -pub enum InvalidMetadataError { - #[error("Invalid prefix")] - InvalidPrefix, - #[error("Invalid version")] - InvalidVersion, - #[error("Type {0} missing from type registry")] - MissingType(u32), - #[error("Type {0} was not a variant/enum type")] - TypeDefNotVariant(u32), -} - -impl TryFrom for Metadata { - type Error = InvalidMetadataError; - - fn try_from(metadata: RuntimeMetadataPrefixed) -> Result { - if metadata.0 != META_RESERVED { - return Err(InvalidMetadataError::InvalidPrefix.into()) - } - let metadata = match metadata.1 { - RuntimeMetadata::V14(meta) => meta, - _ => return Err(InvalidMetadataError::InvalidVersion.into()), - }; - - let get_type_def_variant = |type_id: u32| { - let ty = metadata - .types - .resolve(type_id) - .ok_or(InvalidMetadataError::MissingType(type_id))?; - if let scale_info::TypeDef::Variant(var) = ty.type_def() { - Ok(var) - } else { - Err(InvalidMetadataError::TypeDefNotVariant(type_id)) - } - }; - let pallets = metadata - .pallets - .iter() - .map(|pallet| { - let calls = pallet.calls.as_ref().map_or(Ok(HashMap::new()), |call| { - let type_def_variant = get_type_def_variant(call.ty.id())?; - let calls = type_def_variant - .variants() - .iter() - .map(|v| (v.name().clone(), v.index())) - .collect(); - Ok(calls) - })?; - - let storage = pallet.storage.as_ref().map_or(HashMap::new(), |storage| { - storage - .entries - .iter() - .map(|entry| (entry.name.clone(), entry.clone())) - .collect() - }); - - let constants = pallet - .constants - .iter() - .map(|constant| (constant.name.clone(), constant.clone())) - .collect(); - - let pallet_metadata = PalletMetadata { - index: pallet.index, - name: pallet.name.to_string(), - calls, - storage, - constants, - }; - - Ok((pallet.name.to_string(), pallet_metadata)) - }) - .collect::>()?; - - let pallet_events = metadata - .pallets - .iter() - .filter_map(|pallet| { - pallet.event.as_ref().map(|event| { - let type_def_variant = get_type_def_variant(event.ty.id())?; - Ok((pallet, type_def_variant)) - }) - }) - .collect::, _>>()?; - let events = pallet_events - .iter() - .flat_map(|(pallet, type_def_variant)| { - type_def_variant.variants().iter().map(move |var| { - let key = (pallet.index, var.index()); - let value = EventMetadata { - pallet: pallet.name.clone(), - event: var.name().clone(), - variant: var.clone(), - }; - (key, value) - }) - }) - .collect(); - - let pallet_errors = metadata - .pallets - .iter() - .filter_map(|pallet| { - pallet.error.as_ref().map(|error| { - let type_def_variant = get_type_def_variant(error.ty.id())?; - Ok((pallet, type_def_variant)) - }) - }) - .collect::, _>>()?; - let errors = pallet_errors - .iter() - .flat_map(|(pallet, type_def_variant)| { - type_def_variant.variants().iter().map(move |var| { - let key = (pallet.index, var.index()); - let value = ErrorMetadata { - pallet: pallet.name.clone(), - error: var.name().clone(), - variant: var.clone(), - }; - (key, value) - }) - }) - .collect(); - - Ok(Self { - metadata, - pallets, - events, - errors, - }) - } -} diff --git a/src/rpc.rs b/src/rpc.rs deleted file mode 100644 index 2be0a5cbc4..0000000000 --- a/src/rpc.rs +++ /dev/null @@ -1,739 +0,0 @@ -// 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 . - -//! RPC types and client for interacting with a substrate node. - -// jsonrpsee subscriptions are interminable. -// Allows `while let status = subscription.next().await {}` -// Related: https://github.com/paritytech/subxt/issues/66 -#![allow(irrefutable_let_patterns)] - -use std::sync::Arc; - -use codec::{ - Decode, - Encode, - Error as CodecError, -}; -use core::{ - convert::TryInto, - marker::PhantomData, -}; -use frame_metadata::RuntimeMetadataPrefixed; -use jsonrpsee_http_client::{ - HttpClient, - HttpClientBuilder, -}; -use jsonrpsee_types::{ - to_json_value, - traits::{ - Client, - SubscriptionClient, - }, - DeserializeOwned, - Error as RpcError, - JsonValue, - Subscription, -}; -use jsonrpsee_ws_client::{ - WsClient, - WsClientBuilder, -}; -use serde::{ - Deserialize, - Serialize, -}; -use sp_core::{ - storage::{ - StorageChangeSet, - StorageData, - StorageKey, - }, - Bytes, - U256, -}; -use sp_runtime::{ - generic::{ - Block, - SignedBlock, - }, - traits::Hash, -}; -use sp_version::RuntimeVersion; - -use crate::{ - error::Error, - events::{ - EventsDecoder, - RawEvent, - }, - storage::StorageKeyPrefix, - subscription::{ - EventStorageSubscription, - EventSubscription, - FinalizedEventStorageSubscription, - SystemEvents, - }, - Config, - Event, - Metadata, -}; - -/// A number type that can be serialized both as a number or a string that encodes a number in a -/// string. -/// -/// We allow two representations of the block number as input. Either we deserialize to the type -/// that is specified in the block type or we attempt to parse given hex value. -/// -/// The primary motivation for having this type is to avoid overflows when using big integers in -/// JavaScript (which we consider as an important RPC API consumer). -#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)] -#[serde(untagged)] -pub enum NumberOrHex { - /// The number represented directly. - Number(u64), - /// Hex representation of the number. - Hex(U256), -} - -/// RPC list or value wrapper. -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(untagged)] -pub enum ListOrValue { - /// A list of values of given type. - List(Vec), - /// A single value of given type. - Value(T), -} - -/// Alias for the type of a block returned by `chain_getBlock` -pub type ChainBlock = - SignedBlock::Header, ::Extrinsic>>; - -/// Wrapper for NumberOrHex to allow custom From impls -#[derive(Serialize)] -pub struct BlockNumber(NumberOrHex); - -impl From for BlockNumber { - fn from(x: NumberOrHex) -> Self { - BlockNumber(x) - } -} - -impl From for BlockNumber { - fn from(x: u32) -> Self { - NumberOrHex::Number(x.into()).into() - } -} - -/// System properties for a Substrate-based runtime -#[derive(serde::Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)] -#[serde(rename_all = "camelCase")] -pub struct SystemProperties { - /// The address format - pub ss58_format: u8, - /// The number of digits after the decimal point in the native token - pub token_decimals: u8, - /// The symbol of the native token - pub token_symbol: String, -} - -/// Possible transaction status events. -/// -/// # Note -/// -/// This is copied from `sp-transaction-pool` to avoid a dependency on that crate. Therefore it -/// must be kept compatible with that type from the target substrate version. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum TransactionStatus { - /// Transaction is part of the future queue. - Future, - /// Transaction is part of the ready queue. - Ready, - /// The transaction has been broadcast to the given peers. - Broadcast(Vec), - /// Transaction has been included in block with given hash. - InBlock(BlockHash), - /// The block this transaction was included in has been retracted. - Retracted(BlockHash), - /// Maximum number of finality watchers has been reached, - /// old watchers are being removed. - FinalityTimeout(BlockHash), - /// Transaction has been finalized by a finality-gadget, e.g GRANDPA - Finalized(BlockHash), - /// Transaction has been replaced in the pool, by another transaction - /// that provides the same tags. (e.g. same (sender, nonce)). - Usurped(Hash), - /// Transaction has been dropped from the pool because of the limit. - Dropped, - /// Transaction is no longer valid in the current state. - Invalid, -} - -/// Rpc client wrapper. -/// This is workaround because adding generic types causes the macros to fail. -#[derive(Clone)] -pub enum RpcClient { - /// JSONRPC client WebSocket transport. - WebSocket(Arc), - /// JSONRPC client HTTP transport. - // NOTE: Arc because `HttpClient` is not clone. - Http(Arc), -} - -impl RpcClient { - /// Create a new [`RpcClient`] from the given URL. - /// - /// Infers the protocol from the URL, supports: - /// - Websockets (`ws://`, `wss://`) - /// - Http (`http://`, `https://`) - pub async fn try_from_url(url: &str) -> Result { - if url.starts_with("ws://") || url.starts_with("wss://") { - let client = WsClientBuilder::default() - .max_notifs_per_subscription(4096) - .build(url) - .await?; - Ok(RpcClient::WebSocket(Arc::new(client))) - } else { - let client = HttpClientBuilder::default().build(&url)?; - Ok(RpcClient::Http(Arc::new(client))) - } - } - - /// Start a JSON-RPC request. - pub async fn request<'a, T: DeserializeOwned + std::fmt::Debug>( - &self, - method: &str, - params: &[JsonValue], - ) -> Result { - let params = params.into(); - log::debug!("request {}: {:?}", method, params); - let data = match self { - Self::WebSocket(inner) => { - inner.request(method, params).await.map_err(Into::into) - } - Self::Http(inner) => inner.request(method, params).await.map_err(Into::into), - }; - data - } - - /// Start a JSON-RPC Subscription. - pub async fn subscribe<'a, T: DeserializeOwned>( - &self, - subscribe_method: &str, - params: &[JsonValue], - unsubscribe_method: &str, - ) -> Result, Error> { - let params = params.into(); - match self { - Self::WebSocket(inner) => { - inner - .subscribe(subscribe_method, params, unsubscribe_method) - .await - .map_err(Into::into) - } - Self::Http(_) => { - Err(RpcError::Custom( - "Subscriptions not supported on HTTP transport".to_owned(), - ) - .into()) - } - } - } -} - -impl From for RpcClient { - fn from(client: WsClient) -> Self { - RpcClient::WebSocket(Arc::new(client)) - } -} - -impl From> for RpcClient { - fn from(client: Arc) -> Self { - RpcClient::WebSocket(client) - } -} - -impl From for RpcClient { - fn from(client: HttpClient) -> Self { - RpcClient::Http(Arc::new(client)) - } -} - -impl From> for RpcClient { - fn from(client: Arc) -> Self { - RpcClient::Http(client) - } -} - -/// ReadProof struct returned by the RPC -/// -/// # Note -/// -/// This is copied from `sc-rpc-api` to avoid a dependency on that crate. Therefore it -/// must be kept compatible with that type from the target substrate version. -#[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ReadProof { - /// Block hash used to generate the proof - pub at: Hash, - /// A proof used to prove that storage entries are included in the storage trie - pub proof: Vec, -} - -/// Client for substrate rpc interfaces -pub struct Rpc { - /// Rpc client for sending requests. - pub client: RpcClient, - marker: PhantomData, - accept_weak_inclusion: bool, -} - -impl Clone for Rpc { - fn clone(&self) -> Self { - Self { - client: self.client.clone(), - marker: PhantomData, - accept_weak_inclusion: self.accept_weak_inclusion, - } - } -} - -impl Rpc { - /// Create a new [`Rpc`] - pub fn new(client: RpcClient) -> Self { - Self { - client, - marker: PhantomData, - accept_weak_inclusion: false, - } - } - - /// Configure the Rpc to accept non-finalized blocks - /// in `submit_and_watch_extrinsic` - pub fn accept_weak_inclusion(&mut self) { - self.accept_weak_inclusion = true; - } - - /// Fetch a storage key - pub async fn storage( - &self, - key: &StorageKey, - hash: Option, - ) -> Result, Error> { - let params = &[to_json_value(key)?, to_json_value(hash)?]; - let data = self.client.request("state_getStorage", params).await?; - Ok(data) - } - - /// Returns the keys with prefix with pagination support. - /// Up to `count` keys will be returned. - /// If `start_key` is passed, return next keys in storage in lexicographic order. - pub async fn storage_keys_paged( - &self, - prefix: Option, - count: u32, - start_key: Option, - hash: Option, - ) -> Result, Error> { - let prefix = prefix.map(|p| p.to_storage_key()); - let params = &[ - to_json_value(prefix)?, - to_json_value(count)?, - to_json_value(start_key)?, - to_json_value(hash)?, - ]; - let data = self.client.request("state_getKeysPaged", params).await?; - Ok(data) - } - - /// Query historical storage entries - pub async fn query_storage( - &self, - keys: Vec, - from: T::Hash, - to: Option, - ) -> Result>, Error> { - let params = &[ - to_json_value(keys)?, - to_json_value(from)?, - to_json_value(to)?, - ]; - self.client - .request("state_queryStorage", params) - .await - .map_err(Into::into) - } - - /// Query historical storage entries - pub async fn query_storage_at( - &self, - keys: &[StorageKey], - at: Option, - ) -> Result>, Error> { - let params = &[to_json_value(keys)?, to_json_value(at)?]; - self.client - .request("state_queryStorageAt", params) - .await - .map_err(Into::into) - } - - /// Fetch the genesis hash - pub async fn genesis_hash(&self) -> Result { - let block_zero = Some(ListOrValue::Value(NumberOrHex::Number(0))); - let params = &[to_json_value(block_zero)?]; - let list_or_value: ListOrValue> = - self.client.request("chain_getBlockHash", params).await?; - match list_or_value { - ListOrValue::Value(genesis_hash) => { - genesis_hash.ok_or_else(|| "Genesis hash not found".into()) - } - ListOrValue::List(_) => Err("Expected a Value, got a List".into()), - } - } - - /// Fetch the metadata - pub async fn metadata(&self) -> Result { - let bytes: Bytes = self.client.request("state_getMetadata", &[]).await?; - let meta: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes[..])?; - let metadata: Metadata = meta.try_into()?; - Ok(metadata) - } - - /// Fetch system properties - pub async fn system_properties(&self) -> Result { - Ok(self.client.request("system_properties", &[]).await?) - } - - /// Get a header - pub async fn header( - &self, - hash: Option, - ) -> Result, Error> { - let params = &[to_json_value(hash)?]; - let header = self.client.request("chain_getHeader", params).await?; - Ok(header) - } - - /// Get a block hash, returns hash of latest block by default - pub async fn block_hash( - &self, - block_number: Option, - ) -> Result, Error> { - let block_number = block_number.map(ListOrValue::Value); - let params = &[to_json_value(block_number)?]; - let list_or_value = self.client.request("chain_getBlockHash", params).await?; - match list_or_value { - ListOrValue::Value(hash) => Ok(hash), - ListOrValue::List(_) => Err("Expected a Value, got a List".into()), - } - } - - /// Get a block hash of the latest finalized block - pub async fn finalized_head(&self) -> Result { - let hash = self.client.request("chain_getFinalizedHead", &[]).await?; - Ok(hash) - } - - /// Get a Block - pub async fn block( - &self, - hash: Option, - ) -> Result>, Error> { - let params = &[to_json_value(hash)?]; - let block = self.client.request("chain_getBlock", params).await?; - Ok(block) - } - - /// Get proof of storage entries at a specific block's state. - pub async fn read_proof( - &self, - keys: Vec, - hash: Option, - ) -> Result, Error> { - let params = &[to_json_value(keys)?, to_json_value(hash)?]; - let proof = self.client.request("state_getReadProof", params).await?; - Ok(proof) - } - - /// Fetch the runtime version - pub async fn runtime_version( - &self, - at: Option, - ) -> Result { - let params = &[to_json_value(at)?]; - let version = self - .client - .request("state_getRuntimeVersion", params) - .await?; - Ok(version) - } - - /// Subscribe to System Events that are imported into blocks. - /// - /// *WARNING* these may not be included in the finalized chain, use - /// `subscribe_finalized_events` to ensure events are finalized. - pub async fn subscribe_events(&self) -> Result, Error> { - let keys = Some(vec![StorageKey::from(SystemEvents::new())]); - let params = &[to_json_value(keys)?]; - - let subscription = self - .client - .subscribe("state_subscribeStorage", params, "state_unsubscribeStorage") - .await?; - Ok(EventStorageSubscription::Imported(subscription)) - } - - /// Subscribe to finalized events. - pub async fn subscribe_finalized_events( - &self, - ) -> Result, Error> { - Ok(EventStorageSubscription::Finalized( - FinalizedEventStorageSubscription::new( - self.clone(), - self.subscribe_finalized_blocks().await?, - ), - )) - } - - /// Subscribe to blocks. - pub async fn subscribe_blocks(&self) -> Result, Error> { - let subscription = self - .client - .subscribe("chain_subscribeNewHeads", &[], "chain_unsubscribeNewHeads") - .await?; - - Ok(subscription) - } - - /// Subscribe to finalized blocks. - pub async fn subscribe_finalized_blocks( - &self, - ) -> Result, Error> { - let subscription = self - .client - .subscribe( - "chain_subscribeFinalizedHeads", - &[], - "chain_unsubscribeFinalizedHeads", - ) - .await?; - Ok(subscription) - } - - /// Create and submit an extrinsic and return corresponding Hash if successful - pub async fn submit_extrinsic( - &self, - extrinsic: E, - ) -> Result { - let bytes: Bytes = extrinsic.encode().into(); - let params = &[to_json_value(bytes)?]; - let xt_hash = self - .client - .request("author_submitExtrinsic", params) - .await?; - Ok(xt_hash) - } - - /// Create and submit an extrinsic and return a subscription to the events triggered. - pub async fn watch_extrinsic( - &self, - extrinsic: E, - ) -> Result>, Error> { - let bytes: Bytes = extrinsic.encode().into(); - let params = &[to_json_value(bytes)?]; - let subscription = self - .client - .subscribe( - "author_submitAndWatchExtrinsic", - params, - "author_unwatchExtrinsic", - ) - .await?; - Ok(subscription) - } - - /// Create and submit an extrinsic and return corresponding Event if successful - pub async fn submit_and_watch_extrinsic<'a, E: Encode + 'static>( - &self, - extrinsic: E, - decoder: &'a EventsDecoder, - ) -> Result, Error> { - let ext_hash = T::Hashing::hash_of(&extrinsic); - log::info!("Submitting Extrinsic `{:?}`", ext_hash); - - let events_sub = if self.accept_weak_inclusion { - self.subscribe_events().await - } else { - self.subscribe_finalized_events().await - }?; - let mut xt_sub = self.watch_extrinsic(extrinsic).await?; - - while let Ok(Some(status)) = xt_sub.next().await { - log::info!("received status {:?}", status); - match status { - // ignore in progress extrinsic for now - TransactionStatus::Future - | TransactionStatus::Ready - | TransactionStatus::Broadcast(_) => continue, - TransactionStatus::InBlock(block_hash) => { - if self.accept_weak_inclusion { - return self - .process_block(events_sub, decoder, block_hash, ext_hash) - .await - } - continue - } - TransactionStatus::Invalid => return Err("Extrinsic Invalid".into()), - TransactionStatus::Usurped(_) => return Err("Extrinsic Usurped".into()), - TransactionStatus::Dropped => return Err("Extrinsic Dropped".into()), - TransactionStatus::Retracted(_) => { - return Err("Extrinsic Retracted".into()) - } - TransactionStatus::Finalized(block_hash) => { - // read finalized blocks by default - return self - .process_block(events_sub, decoder, block_hash, ext_hash) - .await - } - TransactionStatus::FinalityTimeout(_) => { - return Err("Extrinsic FinalityTimeout".into()) - } - } - } - Err(RpcError::Custom("RPC subscription dropped".into()).into()) - } - - async fn process_block<'a>( - &self, - events_sub: EventStorageSubscription, - decoder: &'a EventsDecoder, - block_hash: T::Hash, - ext_hash: T::Hash, - ) -> Result, Error> { - log::info!("Fetching block {:?}", block_hash); - if let Some(signed_block) = self.block(Some(block_hash)).await? { - log::info!( - "Found block {:?}, with {} extrinsics", - block_hash, - signed_block.block.extrinsics.len() - ); - let ext_index = signed_block - .block - .extrinsics - .iter() - .position(|ext| { - let hash = T::Hashing::hash_of(ext); - hash == ext_hash - }) - .ok_or_else(|| { - Error::Other(format!( - "Failed to find Extrinsic with hash {:?}", - ext_hash, - )) - })?; - let mut sub = EventSubscription::new(events_sub, decoder); - sub.filter_extrinsic(block_hash, ext_index); - let mut events = vec![]; - while let Some(event) = sub.next().await { - events.push(event?); - } - Ok(ExtrinsicSuccess { - block: block_hash, - extrinsic: ext_hash, - events, - }) - } else { - Err(format!("Failed to find block {:?}", block_hash).into()) - } - } - - /// Insert a key into the keystore. - pub async fn insert_key( - &self, - key_type: String, - suri: String, - public: Bytes, - ) -> Result<(), Error> { - let params = &[ - to_json_value(key_type)?, - to_json_value(suri)?, - to_json_value(public)?, - ]; - self.client.request("author_insertKey", params).await?; - Ok(()) - } - - /// Generate new session keys and returns the corresponding public keys. - pub async fn rotate_keys(&self) -> Result { - Ok(self.client.request("author_rotateKeys", &[]).await?) - } - - /// Checks if the keystore has private keys for the given session public keys. - /// - /// `session_keys` is the SCALE encoded session keys object from the runtime. - /// - /// Returns `true` iff all private keys could be found. - pub async fn has_session_keys(&self, session_keys: Bytes) -> Result { - let params = &[to_json_value(session_keys)?]; - Ok(self.client.request("author_hasSessionKeys", params).await?) - } - - /// Checks if the keystore has private keys for the given public key and key type. - /// - /// Returns `true` if a private key could be found. - pub async fn has_key( - &self, - public_key: Bytes, - key_type: String, - ) -> Result { - let params = &[to_json_value(public_key)?, to_json_value(key_type)?]; - Ok(self.client.request("author_hasKey", params).await?) - } -} - -/// Captures data for when an extrinsic is successfully included in a block -#[derive(Debug)] -pub struct ExtrinsicSuccess { - /// Block hash. - pub block: T::Hash, - /// Extrinsic hash. - pub extrinsic: T::Hash, - /// Raw runtime events, can be decoded by the caller. - pub events: Vec, -} - -impl ExtrinsicSuccess { - /// Find the Event for the given module/variant, with raw encoded event data. - /// Returns `None` if the Event is not found. - pub fn find_event_raw(&self, module: &str, variant: &str) -> Option<&RawEvent> { - self.events - .iter() - .find(|raw| raw.pallet == module && raw.variant == variant) - } - - /// Find the Event for the given module/variant, attempting to decode the event data. - /// Returns `None` if the Event is not found. - /// Returns `Err` if the data fails to decode into the supplied type. - pub fn find_event(&self) -> Result, CodecError> { - if let Some(event) = self.find_event_raw(E::PALLET, E::EVENT) { - Ok(Some(E::decode(&mut &event.data[..])?)) - } else { - Ok(None) - } - } -} diff --git a/src/storage.rs b/src/storage.rs deleted file mode 100644 index e94d640926..0000000000 --- a/src/storage.rs +++ /dev/null @@ -1,297 +0,0 @@ -// 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 . - -//! For querying runtime storage. - -use codec::{ - Decode, - Encode, -}; -use sp_core::storage::{ - StorageChangeSet, - StorageData, - StorageKey, -}; -pub use sp_runtime::traits::SignedExtension; -pub use sp_version::RuntimeVersion; -use std::marker::PhantomData; - -use crate::{ - metadata::{ - Metadata, - MetadataError, - }, - rpc::Rpc, - Config, - Error, - StorageHasher, -}; - -/// Storage entry trait. -pub trait StorageEntry { - /// Pallet name. - const PALLET: &'static str; - /// Storage name. - const STORAGE: &'static str; - /// Type of the storage entry value. - type Value: Decode; - /// Get the key data for the storage. - fn key(&self) -> StorageEntryKey; -} - -/// The prefix of the key to a [`StorageEntry`] -pub struct StorageKeyPrefix(Vec); - -impl StorageKeyPrefix { - /// Create the storage key prefix for a [`StorageEntry`] - pub fn new() -> Self { - let mut bytes = sp_core::twox_128(T::PALLET.as_bytes()).to_vec(); - bytes.extend(&sp_core::twox_128(T::STORAGE.as_bytes())[..]); - Self(bytes) - } - - /// Convert the prefix into a [`StorageKey`] - pub fn to_storage_key(self) -> StorageKey { - StorageKey(self.0) - } -} - -/// Storage key. -pub enum StorageEntryKey { - /// Plain key. - Plain, - /// Map key(s). - Map(Vec), -} - -impl StorageEntryKey { - /// Construct the final [`sp_core::storage::StorageKey`] for the storage entry. - pub fn final_key(&self, prefix: StorageKeyPrefix) -> sp_core::storage::StorageKey { - let mut bytes = prefix.0; - if let Self::Map(map_keys) = self { - for map_key in map_keys { - bytes.extend(Self::hash(&map_key.hasher, &map_key.value)) - } - } - sp_core::storage::StorageKey(bytes) - } - - fn hash(hasher: &StorageHasher, bytes: &[u8]) -> Vec { - match hasher { - StorageHasher::Identity => bytes.to_vec(), - StorageHasher::Blake2_128 => sp_core::blake2_128(bytes).to_vec(), - StorageHasher::Blake2_128Concat => { - // copied from substrate Blake2_128Concat::hash since StorageHasher is not public - sp_core::blake2_128(bytes) - .iter() - .chain(bytes) - .cloned() - .collect() - } - StorageHasher::Blake2_256 => sp_core::blake2_256(bytes).to_vec(), - StorageHasher::Twox128 => sp_core::twox_128(bytes).to_vec(), - StorageHasher::Twox256 => sp_core::twox_256(bytes).to_vec(), - StorageHasher::Twox64Concat => { - sp_core::twox_64(bytes) - .iter() - .chain(bytes) - .cloned() - .collect() - } - } - } -} - -/// Storage key for a Map. -pub struct StorageMapKey { - value: Vec, - hasher: StorageHasher, -} - -impl StorageMapKey { - /// Create a new [`StorageMapKey`] with the encoded data and the hasher. - pub fn new(value: &T, hasher: StorageHasher) -> Self { - Self { - value: value.encode(), - hasher, - } - } -} - -/// Client for querying runtime storage. -#[derive(Clone)] -pub struct StorageClient<'a, T: Config> { - rpc: &'a Rpc, - metadata: &'a Metadata, - iter_page_size: u32, -} - -impl<'a, T: Config> StorageClient<'a, T> { - /// Create a new [`StorageClient`] - pub fn new(rpc: &'a Rpc, metadata: &'a Metadata, iter_page_size: u32) -> Self { - Self { - rpc, - metadata, - iter_page_size, - } - } - - /// Fetch the value under an unhashed storage key - pub async fn fetch_unhashed( - &self, - key: StorageKey, - hash: Option, - ) -> Result, Error> { - if let Some(data) = self.rpc.storage(&key, hash).await? { - Ok(Some(Decode::decode(&mut &data.0[..])?)) - } else { - Ok(None) - } - } - - /// Fetch the raw encoded value under the raw storage key. - pub async fn fetch_raw( - &self, - key: StorageKey, - hash: Option, - ) -> Result, Error> { - self.rpc.storage(&key, hash).await - } - - /// Fetch a StorageKey with an optional block hash. - pub async fn fetch( - &self, - store: &F, - hash: Option, - ) -> Result, Error> { - let prefix = StorageKeyPrefix::new::(); - let key = store.key().final_key(prefix); - self.fetch_unhashed::(key, hash).await - } - - /// Fetch a StorageKey that has a default value with an optional block hash. - pub async fn fetch_or_default( - &self, - store: &F, - hash: Option, - ) -> Result { - if let Some(data) = self.fetch(store, hash).await? { - Ok(data) - } else { - let pallet_metadata = self.metadata.pallet(F::PALLET)?; - let storage_metadata = pallet_metadata.storage(F::STORAGE)?; - let default = Decode::decode(&mut &storage_metadata.default[..]) - .map_err(MetadataError::DefaultError)?; - Ok(default) - } - } - - /// Query historical storage entries - pub async fn query_storage( - &self, - keys: Vec, - from: T::Hash, - to: Option, - ) -> Result>, Error> { - self.rpc.query_storage(keys, from, to).await - } - - /// Fetch up to `count` keys for a storage map in lexicographic order. - /// - /// Supports pagination by passing a value to `start_key`. - pub async fn fetch_keys( - &self, - count: u32, - start_key: Option, - hash: Option, - ) -> Result, Error> { - let prefix = StorageKeyPrefix::new::(); - let keys = self - .rpc - .storage_keys_paged(Some(prefix), count, start_key, hash) - .await?; - Ok(keys) - } - - /// Returns an iterator of key value pairs. - pub async fn iter( - &self, - hash: Option, - ) -> Result, Error> { - let hash = if let Some(hash) = hash { - hash - } else { - self.rpc - .block_hash(None) - .await? - .expect("didn't pass a block number; qed") - }; - Ok(KeyIter { - client: self.clone(), - hash, - count: self.iter_page_size, - start_key: None, - buffer: Default::default(), - _marker: PhantomData, - }) - } -} - -/// Iterates over key value pairs in a map. -pub struct KeyIter<'a, T: Config, F: StorageEntry> { - client: StorageClient<'a, T>, - _marker: PhantomData, - count: u32, - hash: T::Hash, - start_key: Option, - buffer: Vec<(StorageKey, StorageData)>, -} - -impl<'a, T: Config, F: StorageEntry> KeyIter<'a, T, F> { - /// Returns the next key value pair from a map. - pub async fn next(&mut self) -> Result, Error> { - loop { - if let Some((k, v)) = self.buffer.pop() { - return Ok(Some((k, Decode::decode(&mut &v.0[..])?))) - } else { - let keys = self - .client - .fetch_keys::(self.count, self.start_key.take(), Some(self.hash)) - .await?; - - if keys.is_empty() { - return Ok(None) - } - - self.start_key = keys.last().cloned(); - - let change_sets = self - .client - .rpc - .query_storage_at(&keys, Some(self.hash)) - .await?; - for change_set in change_sets { - for (k, v) in change_set.changes { - if let Some(v) = v { - self.buffer.push((k, v)); - } - } - } - debug_assert_eq!(self.buffer.len(), keys.len()); - } - } - } -} diff --git a/src/subscription.rs b/src/subscription.rs deleted file mode 100644 index 31ad50c3e8..0000000000 --- a/src/subscription.rs +++ /dev/null @@ -1,229 +0,0 @@ -// 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 . - -use jsonrpsee_types::{ - DeserializeOwned, - Subscription, -}; -use sp_core::{ - storage::{ - StorageChangeSet, - StorageKey, - }, - twox_128, -}; -use sp_runtime::traits::Header; -use std::collections::VecDeque; - -use crate::{ - error::Error, - events::{ - EventsDecoder, - Raw, - RawEvent, - }, - rpc::Rpc, - Config, - Event, - Phase, -}; - -/// Event subscription simplifies filtering a storage change set stream for -/// events of interest. -pub struct EventSubscription<'a, T: Config> { - subscription: EventStorageSubscription, - decoder: &'a EventsDecoder, - block: Option, - extrinsic: Option, - event: Option<(&'static str, &'static str)>, - events: VecDeque, - finished: bool, -} - -impl<'a, T: Config> EventSubscription<'a, T> { - /// Creates a new event subscription. - pub fn new( - subscription: EventStorageSubscription, - decoder: &'a EventsDecoder, - ) -> Self { - Self { - subscription, - decoder, - block: None, - extrinsic: None, - event: None, - events: Default::default(), - finished: false, - } - } - - /// Only returns events contained in the block with the given hash. - pub fn filter_block(&mut self, block: T::Hash) { - self.block = Some(block); - } - - /// Only returns events from block emitted by extrinsic with index. - pub fn filter_extrinsic(&mut self, block: T::Hash, ext_index: usize) { - self.block = Some(block); - self.extrinsic = Some(ext_index); - } - - /// Filters events by type. - pub fn filter_event(&mut self) { - self.event = Some((E::PALLET, E::EVENT)); - } - - /// Gets the next event. - pub async fn next(&mut self) -> Option> { - loop { - if let Some(event) = self.events.pop_front() { - return Some(Ok(event)) - } - if self.finished { - return None - } - // always return None if subscription has closed - let change_set = self.subscription.next().await?; - if let Some(hash) = self.block.as_ref() { - if &change_set.block == hash { - self.finished = true; - } else { - continue - } - } - for (_key, data) in change_set.changes { - if let Some(data) = data { - let raw_events = match self.decoder.decode_events(&mut &data.0[..]) { - Ok(events) => events, - Err(error) => return Some(Err(error)), - }; - for (phase, raw) in raw_events { - if let Phase::ApplyExtrinsic(i) = phase { - if let Some(ext_index) = self.extrinsic { - if i as usize != ext_index { - continue - } - } - let event = match raw { - Raw::Event(event) => event, - Raw::Error(err) => return Some(Err(err.into())), - }; - if let Some((module, variant)) = self.event { - if event.pallet != module || event.variant != variant { - continue - } - } - self.events.push_back(event); - } - } - } - } - } - } -} - -pub(crate) struct SystemEvents(StorageKey); - -impl SystemEvents { - pub(crate) fn new() -> Self { - let mut storage_key = twox_128(b"System").to_vec(); - storage_key.extend(twox_128(b"Events").to_vec()); - log::debug!("Events storage key {:?}", hex::encode(&storage_key)); - Self(StorageKey(storage_key)) - } -} - -impl From for StorageKey { - fn from(key: SystemEvents) -> Self { - key.0 - } -} - -/// Event subscription to only fetch finalized storage changes. -pub struct FinalizedEventStorageSubscription { - rpc: Rpc, - subscription: Subscription, - storage_changes: VecDeque>, - storage_key: StorageKey, -} - -impl FinalizedEventStorageSubscription { - /// Creates a new finalized event storage subscription. - pub fn new(rpc: Rpc, subscription: Subscription) -> Self { - Self { - rpc, - subscription, - storage_changes: Default::default(), - storage_key: SystemEvents::new().into(), - } - } - - /// Gets the next change_set. - pub async fn next(&mut self) -> Option> { - loop { - if let Some(storage_change) = self.storage_changes.pop_front() { - return Some(storage_change) - } - let header: T::Header = - read_subscription_response("HeaderSubscription", &mut self.subscription) - .await?; - self.storage_changes.extend( - self.rpc - .query_storage_at(&[self.storage_key.clone()], Some(header.hash())) - .await - .ok()?, - ); - } - } -} - -/// Wrapper over imported and finalized event subscriptions. -pub enum EventStorageSubscription { - /// Events that are InBlock - Imported(Subscription>), - /// Events that are Finalized - Finalized(FinalizedEventStorageSubscription), -} - -impl EventStorageSubscription { - /// Gets the next change_set from the subscription. - pub async fn next(&mut self) -> Option> { - match self { - Self::Imported(event_sub) => { - read_subscription_response("StorageChangeSetSubscription", event_sub) - .await - } - Self::Finalized(event_sub) => event_sub.next().await, - } - } -} - -async fn read_subscription_response( - sub_name: &str, - sub: &mut Subscription, -) -> Option -where - T: DeserializeOwned, -{ - match sub.next().await { - Ok(Some(next)) => Some(next), - Ok(None) => None, - Err(e) => { - log::error!("Subscription {} failed: {:?} dropping", sub_name, e); - None - } - } -} diff --git a/tests/integration/client.rs b/tests/integration/client.rs deleted file mode 100644 index 70b5382597..0000000000 --- a/tests/integration/client.rs +++ /dev/null @@ -1,124 +0,0 @@ -// 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 . - -use crate::{ - runtime::node_runtime::system, - test_node_process, - test_node_process_with, -}; - -use sp_core::storage::{ - well_known_keys, - StorageKey, -}; -use sp_keyring::AccountKeyring; - -#[async_std::test] -async fn insert_key() { - let test_node_process = test_node_process_with(AccountKeyring::Bob).await; - let client = test_node_process.client(); - let public = AccountKeyring::Alice.public().as_array_ref().to_vec(); - client - .rpc() - .insert_key( - "aura".to_string(), - "//Alice".to_string(), - public.clone().into(), - ) - .await - .unwrap(); - assert!(client - .rpc() - .has_key(public.clone().into(), "aura".to_string()) - .await - .unwrap()); -} - -#[async_std::test] -async fn fetch_block_hash() { - let node_process = test_node_process().await; - node_process.client().rpc().block_hash(None).await.unwrap(); -} - -#[async_std::test] -async fn fetch_block() { - let node_process = test_node_process().await; - let client = node_process.client(); - let block_hash = client.rpc().block_hash(None).await.unwrap(); - client.rpc().block(block_hash).await.unwrap(); -} - -#[async_std::test] -async fn fetch_read_proof() { - let node_process = test_node_process().await; - let client = node_process.client(); - let block_hash = client.rpc().block_hash(None).await.unwrap(); - client - .rpc() - .read_proof( - vec![ - StorageKey(well_known_keys::HEAP_PAGES.to_vec()), - StorageKey(well_known_keys::EXTRINSIC_INDEX.to_vec()), - ], - block_hash, - ) - .await - .unwrap(); -} - -#[async_std::test] -async fn chain_subscribe_blocks() { - let node_process = test_node_process().await; - let client = node_process.client(); - let mut blocks = client.rpc().subscribe_blocks().await.unwrap(); - blocks.next().await.unwrap(); -} - -#[async_std::test] -async fn chain_subscribe_finalized_blocks() { - let node_process = test_node_process().await; - let client = node_process.client(); - let mut blocks = client.rpc().subscribe_finalized_blocks().await.unwrap(); - blocks.next().await.unwrap(); -} - -#[async_std::test] -async fn fetch_keys() { - let node_process = test_node_process().await; - let client = node_process.client(); - let keys = client - .storage() - .fetch_keys::(4, None, None) - .await - .unwrap(); - assert_eq!(keys.len(), 4) -} - -#[async_std::test] -async fn test_iter() { - let node_process = test_node_process().await; - let client = node_process.client(); - let mut iter = client - .storage() - .iter::(None) - .await - .unwrap(); - let mut i = 0; - while let Some(_) = iter.next().await.unwrap() { - i += 1; - } - assert_eq!(i, 13); -} diff --git a/tests/integration/codegen/mod.rs b/tests/integration/codegen/mod.rs deleted file mode 100644 index 1120941d7c..0000000000 --- a/tests/integration/codegen/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -// 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 . - -/// Checks that code generated by `subxt-cli codegen` compiles. Allows inspection of compiler errors -/// directly, more accurately than via the macro and `cargo expand`. -/// -/// Generate by: -/// -/// - run `polkadot --dev --tmp` node locally -/// - `cargo run --release -p subxt-cli -- codegen | rustfmt --edition=2018 --emit=stdout > tests/integration/codegen/polkadot.rs` -#[rustfmt::skip] -mod polkadot; diff --git a/tests/integration/codegen/polkadot.rs b/tests/integration/codegen/polkadot.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/tests/integration/codegen/polkadot.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/integration/frame/balances.rs b/tests/integration/frame/balances.rs deleted file mode 100644 index e130a4bece..0000000000 --- a/tests/integration/frame/balances.rs +++ /dev/null @@ -1,229 +0,0 @@ -// 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 . - -use crate::{ - node_runtime::{ - balances, - runtime_types, - system, - DefaultConfig, - }, - test_context, -}; -use codec::Decode; -use sp_core::{ - sr25519::Pair, - Pair as _, -}; -use sp_keyring::AccountKeyring; -use subxt::{ - extrinsic::{ - PairSigner, - Signer, - }, - Error, - EventSubscription, - PalletError, - RuntimeError, -}; - -#[async_std::test] -async fn tx_basic_transfer() { - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let bob = PairSigner::::new(AccountKeyring::Bob.pair()); - let bob_address = bob.account_id().clone().into(); - let cxt = test_context().await; - let api = &cxt.api; - - let alice_pre = api - .storage() - .system() - .account(alice.account_id().clone().into(), None) - .await - .unwrap(); - let bob_pre = api - .storage() - .system() - .account(bob.account_id().clone().into(), None) - .await - .unwrap(); - - let result = api - .tx() - .balances() - .transfer(bob_address, 10_000) - .sign_and_submit_then_watch(&alice) - .await - .unwrap(); - let event = result - .find_event::() - .unwrap() - .unwrap(); - let _extrinsic_success = result - .find_event::() - .expect("Failed to decode ExtrinisicSuccess".into()) - .expect("Failed to find ExtrinisicSuccess"); - - let expected_event = balances::events::Transfer( - alice.account_id().clone(), - bob.account_id().clone(), - 10_000, - ); - assert_eq!(event, expected_event); - - let alice_post = api - .storage() - .system() - .account(alice.account_id().clone().into(), None) - .await - .unwrap(); - let bob_post = api - .storage() - .system() - .account(bob.account_id().clone().into(), None) - .await - .unwrap(); - - assert!(alice_pre.data.free - 10_000 >= alice_post.data.free); - assert_eq!(bob_pre.data.free + 10_000, bob_post.data.free); -} - -#[async_std::test] -async fn storage_total_issuance() { - let cxt = test_context().await; - let total_issuance = cxt - .api - .storage() - .balances() - .total_issuance(None) - .await - .unwrap(); - assert_ne!(total_issuance, 0); -} - -#[async_std::test] -async fn storage_balance_lock() -> Result<(), subxt::Error> { - let bob = PairSigner::::new(AccountKeyring::Bob.pair()); - let charlie = AccountKeyring::Charlie.to_account_id(); - let cxt = test_context().await; - - let result = cxt - .api - .tx() - .staking() - .bond( - charlie.into(), - 100_000_000_000_000, - runtime_types::pallet_staking::RewardDestination::Stash, - ) - .sign_and_submit_then_watch(&bob) - .await?; - - let success = result.find_event::()?; - assert!(success.is_some(), "No ExtrinsicSuccess Event found"); - - let locks = cxt - .api - .storage() - .balances() - .locks(AccountKeyring::Bob.to_account_id(), None) - .await?; - - assert_eq!( - locks.0, - vec![runtime_types::pallet_balances::BalanceLock { - id: *b"staking ", - amount: 100_000_000_000_000, - reasons: runtime_types::pallet_balances::Reasons::All, - }] - ); - - Ok(()) -} - -#[async_std::test] -async fn transfer_error() { - env_logger::try_init().ok(); - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let alice_addr = alice.account_id().clone().into(); - let hans = PairSigner::::new(Pair::generate().0); - let hans_address = hans.account_id().clone().into(); - let cxt = test_context().await; - - cxt.api - .tx() - .balances() - .transfer(hans_address, 100_000_000_000_000_000) - .sign_and_submit_then_watch(&alice) - .await - .unwrap(); - - let res = cxt - .api - .tx() - .balances() - .transfer(alice_addr, 100_000_000_000_000_000) - .sign_and_submit_then_watch(&hans) - .await; - - if let Err(Error::Runtime(RuntimeError::Module(error))) = res { - let error2 = PalletError { - pallet: "Balances".into(), - error: "InsufficientBalance".into(), - description: vec!["Balance too low to send value".to_string()], - }; - assert_eq!(error, error2); - } else { - panic!("expected an error"); - } -} - -#[async_std::test] -async fn transfer_subscription() { - env_logger::try_init().ok(); - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let bob = AccountKeyring::Bob.to_account_id(); - let bob_addr = bob.clone().into(); - let cxt = test_context().await; - let sub = cxt.client().rpc().subscribe_events().await.unwrap(); - let decoder = cxt.client().events_decoder(); - let mut sub = EventSubscription::::new(sub, &decoder); - sub.filter_event::(); - - cxt.api - .tx() - .balances() - .transfer(bob_addr, 10_000) - .sign_and_submit_then_watch(&alice) - .await - .unwrap(); - - let raw = sub.next().await.unwrap().unwrap(); - let event = balances::events::Transfer::decode(&mut &raw.data[..]).unwrap(); - assert_eq!( - event, - balances::events::Transfer(alice.account_id().clone(), bob.clone(), 10_000,) - ); -} - -#[async_std::test] -async fn constant_existential_deposit() { - let cxt = test_context().await; - let balances_metadata = cxt.client().metadata().pallet("Balances").unwrap(); - let constant_metadata = balances_metadata.constant("ExistentialDeposit").unwrap(); - let existential_deposit = u128::decode(&mut &constant_metadata.value[..]).unwrap(); - assert_eq!(existential_deposit, 100_000_000_000_000); -} diff --git a/tests/integration/frame/contracts.rs b/tests/integration/frame/contracts.rs deleted file mode 100644 index 75e18d38d0..0000000000 --- a/tests/integration/frame/contracts.rs +++ /dev/null @@ -1,212 +0,0 @@ -// 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 . - -use sp_keyring::AccountKeyring; - -use crate::{ - node_runtime::{ - contracts::{ - calls::TransactionApi, - events, - storage, - }, - system, - DefaultConfig, - }, - test_context, - TestContext, -}; -use sp_core::sr25519::Pair; -use sp_runtime::MultiAddress; -use subxt::{ - Client, - Config, - Error, - ExtrinsicSuccess, - PairSigner, -}; - -struct ContractsTestContext { - cxt: TestContext, - signer: PairSigner, -} - -type Hash = ::Hash; -type AccountId = ::AccountId; - -impl ContractsTestContext { - async fn init() -> Self { - let cxt = test_context().await; - let signer = PairSigner::new(AccountKeyring::Alice.pair()); - - Self { cxt, signer } - } - - fn client(&self) -> &Client { - &self.cxt.client() - } - - fn contracts_tx(&self) -> TransactionApi { - self.cxt.api.tx().contracts() - } - - async fn instantiate_with_code(&self) -> Result<(Hash, AccountId), Error> { - log::info!("instantiate_with_code:"); - const CONTRACT: &str = r#" - (module - (func (export "call")) - (func (export "deploy")) - ) - "#; - let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); - - let result = self - .cxt - .api - .tx() - .contracts() - .instantiate_with_code( - 100_000_000_000_000_000, // endowment - 500_000_000_000, // gas_limit - code, - vec![], // data - vec![], // salt - ) - .sign_and_submit_then_watch(&self.signer) - .await?; - - let code_stored = result - .find_event::()? - .ok_or_else(|| Error::Other("Failed to find a CodeStored event".into()))?; - let instantiated = result - .find_event::()? - .ok_or_else(|| Error::Other("Failed to find a Instantiated event".into()))?; - let _extrinsic_success = result - .find_event::()? - .ok_or_else(|| { - Error::Other("Failed to find a ExtrinsicSuccess event".into()) - })?; - - log::info!(" Block hash: {:?}", result.block); - log::info!(" Code hash: {:?}", code_stored.code_hash); - log::info!(" Contract address: {:?}", instantiated.contract); - Ok((code_stored.code_hash, instantiated.contract)) - } - - async fn instantiate( - &self, - code_hash: Hash, - data: Vec, - salt: Vec, - ) -> Result { - // call instantiate extrinsic - let result = self - .contracts_tx() - .instantiate( - 100_000_000_000_000_000, // endowment - 500_000_000_000, // gas_limit - code_hash, - data, - salt, - ) - .sign_and_submit_then_watch(&self.signer) - .await?; - - log::info!("Instantiate result: {:?}", result); - let instantiated = result - .find_event::()? - .ok_or_else(|| Error::Other("Failed to find a Instantiated event".into()))?; - - Ok(instantiated.contract) - } - - async fn call( - &self, - contract: AccountId, - input_data: Vec, - ) -> Result, Error> { - log::info!("call: {:?}", contract); - let result = self - .contracts_tx() - .call( - MultiAddress::Id(contract), - 0, // value - 500_000_000, // gas_limit - input_data, - ) - .sign_and_submit_then_watch(&self.signer) - .await?; - - log::info!("Call result: {:?}", result); - Ok(result) - } -} - -#[async_std::test] -async fn tx_instantiate_with_code() { - let ctx = ContractsTestContext::init().await; - let result = ctx.instantiate_with_code().await; - - assert!( - result.is_ok(), - "Error calling instantiate_with_code and receiving CodeStored and Instantiated Events: {:?}", - result - ); -} - -#[async_std::test] -async fn tx_instantiate() { - let ctx = ContractsTestContext::init().await; - let (code_hash, _) = ctx.instantiate_with_code().await.unwrap(); - - let instantiated = ctx.instantiate(code_hash.into(), vec![], vec![1u8]).await; - - assert!( - instantiated.is_ok(), - "Error instantiating contract: {:?}", - instantiated - ); -} - -#[async_std::test] -async fn tx_call() { - let cxt = ContractsTestContext::init().await; - let (_, contract) = cxt.instantiate_with_code().await.unwrap(); - - let contract_info = cxt - .cxt - .api - .storage() - .contracts() - .contract_info_of(contract.clone(), None) - .await; - assert!(contract_info.is_ok()); - - let keys = cxt - .client() - .storage() - .fetch_keys::(5, None, None) - .await - .unwrap() - .iter() - .map(|key| hex::encode(&key.0)) - .collect::>(); - println!("keys post: {:?}", keys); - - let executed = cxt.call(contract, vec![]).await; - - assert!(executed.is_ok(), "Error calling contract: {:?}", executed); -} diff --git a/tests/integration/frame/mod.rs b/tests/integration/frame/mod.rs deleted file mode 100644 index 8d18e46748..0000000000 --- a/tests/integration/frame/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// 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 . - -//! Test interactions with some built-in FRAME pallets. - -mod balances; -mod contracts; -mod staking; -mod sudo; -mod system; diff --git a/tests/integration/frame/staking.rs b/tests/integration/frame/staking.rs deleted file mode 100644 index 24071a0043..0000000000 --- a/tests/integration/frame/staking.rs +++ /dev/null @@ -1,258 +0,0 @@ -// 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 . - -use crate::{ - node_runtime::{ - runtime_types::pallet_staking::{ - RewardDestination, - ValidatorPrefs, - }, - staking, - system, - DefaultConfig, - }, - test_context, -}; -use assert_matches::assert_matches; -use sp_core::{ - sr25519, - Pair, -}; -use sp_keyring::AccountKeyring; -use subxt::{ - extrinsic::{ - PairSigner, - Signer, - }, - Error, - RuntimeError, -}; - -/// Helper function to generate a crypto pair from seed -fn get_from_seed(seed: &str) -> sr25519::Pair { - sr25519::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") -} - -fn default_validator_prefs() -> ValidatorPrefs { - ValidatorPrefs { - commission: sp_runtime::Perbill::default(), - blocked: false, - } -} - -#[async_std::test] -async fn validate_with_controller_account() -> Result<(), Error> { - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let cxt = test_context().await; - let result = cxt - .api - .tx() - .staking() - .validate(default_validator_prefs()) - .sign_and_submit_then_watch(&alice) - .await?; - - let success = result.find_event::()?; - assert!(success.is_some()); - - Ok(()) -} - -#[async_std::test] -async fn validate_not_possible_for_stash_account() -> Result<(), Error> { - let alice_stash = PairSigner::::new(get_from_seed("Alice//stash")); - let cxt = test_context().await; - let announce_validator = cxt - .api - .tx() - .staking() - .validate(default_validator_prefs()) - .sign_and_submit_then_watch(&alice_stash) - .await; - assert_matches!(announce_validator, Err(Error::Runtime(RuntimeError::Module(module_err))) => { - assert_eq!(module_err.pallet, "Staking"); - assert_eq!(module_err.error, "NotController"); - }); - Ok(()) -} - -#[async_std::test] -async fn nominate_with_controller_account() -> Result<(), Error> { - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let bob = PairSigner::::new(AccountKeyring::Bob.pair()); - let cxt = test_context().await; - - let result = cxt - .api - .tx() - .staking() - .nominate(vec![bob.account_id().clone().into()]) - .sign_and_submit_then_watch(&alice) - .await?; - - let success = result.find_event::()?; - assert!(success.is_some()); - - Ok(()) -} - -#[async_std::test] -async fn nominate_not_possible_for_stash_account() -> Result<(), Error> { - let alice_stash = - PairSigner::::new(get_from_seed("Alice//stash")); - let bob = PairSigner::::new(AccountKeyring::Bob.pair()); - let cxt = test_context().await; - - let nomination = cxt - .api - .tx() - .staking() - .nominate(vec![bob.account_id().clone().into()]) - .sign_and_submit_then_watch(&alice_stash) - .await; - - assert_matches!(nomination, Err(Error::Runtime(RuntimeError::Module(module_err))) => { - assert_eq!(module_err.pallet, "Staking"); - assert_eq!(module_err.error, "NotController"); - }); - Ok(()) -} - -#[async_std::test] -async fn chill_works_for_controller_only() -> Result<(), Error> { - let alice_stash = - PairSigner::::new(get_from_seed("Alice//stash")); - let bob_stash = - PairSigner::::new(get_from_seed("Bob//stash")); - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let cxt = test_context().await; - - // this will fail the second time, which is why this is one test, not two - cxt.api - .tx() - .staking() - .nominate(vec![bob_stash.account_id().clone().into()]) - .sign_and_submit_then_watch(&alice) - .await?; - - let ledger = cxt - .api - .storage() - .staking() - .ledger(alice.account_id().clone(), None) - .await? - .unwrap(); - assert_eq!(alice_stash.account_id(), &ledger.stash); - - let chill = cxt - .api - .tx() - .staking() - .chill() - .sign_and_submit_then_watch(&alice_stash) - .await; - - assert_matches!(chill, Err(Error::Runtime(RuntimeError::Module(module_err))) => { - assert_eq!(module_err.pallet, "Staking"); - assert_eq!(module_err.error, "NotController"); - }); - - let result = cxt - .api - .tx() - .staking() - .chill() - .sign_and_submit_then_watch(&alice) - .await?; - let chill = result.find_event::()?; - assert!(chill.is_some()); - Ok(()) -} - -#[async_std::test] -async fn tx_bond() -> Result<(), Error> { - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let cxt = test_context().await; - - let bond = cxt - .api - .tx() - .staking() - .bond( - AccountKeyring::Bob.to_account_id().into(), - 100_000_000_000_000, - RewardDestination::Stash, - ) - .sign_and_submit_then_watch(&alice) - .await; - - assert!(bond.is_ok()); - - let bond_again = cxt - .api - .tx() - .staking() - .bond( - AccountKeyring::Bob.to_account_id().into(), - 100_000_000_000_000, - RewardDestination::Stash, - ) - .sign_and_submit_then_watch(&alice) - .await; - - assert_matches!(bond_again, Err(Error::Runtime(RuntimeError::Module(module_err))) => { - assert_eq!(module_err.pallet, "Staking"); - assert_eq!(module_err.error, "AlreadyBonded"); - }); - - Ok(()) -} - -#[async_std::test] -async fn storage_history_depth() -> Result<(), Error> { - let cxt = test_context().await; - let history_depth = cxt.api.storage().staking().history_depth(None).await?; - assert_eq!(history_depth, 84); - Ok(()) -} - -#[async_std::test] -async fn storage_current_era() -> Result<(), Error> { - let cxt = test_context().await; - let _current_era = cxt - .api - .storage() - .staking() - .current_era(None) - .await? - .expect("current era always exists"); - Ok(()) -} - -#[async_std::test] -async fn storage_era_reward_points() -> Result<(), Error> { - let cxt = test_context().await; - let current_era_result = cxt - .api - .storage() - .staking() - .eras_reward_points(0, None) - .await; - assert!(current_era_result.is_ok()); - - Ok(()) -} diff --git a/tests/integration/frame/sudo.rs b/tests/integration/frame/sudo.rs deleted file mode 100644 index 071f650437..0000000000 --- a/tests/integration/frame/sudo.rs +++ /dev/null @@ -1,77 +0,0 @@ -// 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 . - -use crate::{ - node_runtime::{ - runtime_types, - sudo, - DefaultConfig, - }, - test_context, -}; -use assert_matches::assert_matches; -use sp_keyring::AccountKeyring; -use subxt::extrinsic::PairSigner; - -type Call = runtime_types::node_runtime::Call; -type BalancesCall = runtime_types::pallet_balances::pallet::Call; - -#[async_std::test] -async fn test_sudo() { - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let bob = AccountKeyring::Bob.to_account_id().clone().into(); - let cxt = test_context().await; - - let call = Call::Balances(BalancesCall::transfer { - dest: bob, - value: 10_000, - }); - - let res = cxt - .api - .tx() - .sudo() - .sudo(call) - .sign_and_submit_then_watch(&alice) - .await - .unwrap(); - let sudid = res.find_event::(); - assert_matches!(sudid, Ok(Some(_))) -} - -#[async_std::test] -async fn test_sudo_unchecked_weight() { - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let bob = AccountKeyring::Bob.to_account_id().into(); - let cxt = test_context().await; - - let call = Call::Balances(BalancesCall::transfer { - dest: bob, - value: 10_000, - }); - - let res = cxt - .api - .tx() - .sudo() - .sudo_unchecked_weight(call, 0) - .sign_and_submit_then_watch(&alice) - .await - .unwrap(); - - let sudid = res.find_event::(); - assert_matches!(sudid, Ok(Some(_))) -} diff --git a/tests/integration/frame/system.rs b/tests/integration/frame/system.rs deleted file mode 100644 index 5f0205e598..0000000000 --- a/tests/integration/frame/system.rs +++ /dev/null @@ -1,61 +0,0 @@ -// 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 . - -use crate::{ - node_runtime::{ - system, - DefaultConfig, - }, - test_context, -}; -use assert_matches::assert_matches; -use sp_keyring::AccountKeyring; -use subxt::extrinsic::{ - PairSigner, - Signer, -}; - -#[async_std::test] -async fn storage_account() { - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - - let cxt = test_context().await; - let account_info = cxt - .api - .storage() - .system() - .account(alice.account_id().clone().into(), None) - .await; - assert_matches!(account_info, Ok(_)) -} - -#[async_std::test] -async fn tx_remark_with_event() { - let alice = PairSigner::::new(AccountKeyring::Alice.pair()); - let cxt = test_context().await; - - let result = cxt - .api - .tx() - .system() - .remark_with_event(b"remarkable".to_vec()) - .sign_and_submit_then_watch(&alice) - .await - .unwrap(); - - let remarked = result.find_event::(); - assert_matches!(remarked, Ok(Some(_))); -} diff --git a/tests/integration/main.rs b/tests/integration/main.rs deleted file mode 100644 index 40ee6f31b7..0000000000 --- a/tests/integration/main.rs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 . - -mod codegen; -mod runtime; -mod utils; - -#[cfg(test)] -mod client; -#[cfg(test)] -mod frame; - -pub use runtime::node_runtime; -pub use utils::*; diff --git a/tests/integration/node_runtime.scale b/tests/integration/node_runtime.scale deleted file mode 100644 index cff019d32c..0000000000 Binary files a/tests/integration/node_runtime.scale and /dev/null differ diff --git a/tests/integration/runtime.rs b/tests/integration/runtime.rs deleted file mode 100644 index cccef567f4..0000000000 --- a/tests/integration/runtime.rs +++ /dev/null @@ -1,24 +0,0 @@ -// 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 . - -#[subxt::subxt( - runtime_metadata_path = "tests/integration/node_runtime.scale", - generated_type_derives = "Debug, Eq, PartialEq" -)] -pub mod node_runtime { - #[subxt(substitute_type = "sp_arithmetic::per_things::Perbill")] - use sp_runtime::Perbill; -} diff --git a/tests/integration/utils/context.rs b/tests/integration/utils/context.rs deleted file mode 100644 index 321a568ca4..0000000000 --- a/tests/integration/utils/context.rs +++ /dev/null @@ -1,70 +0,0 @@ -// 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 . - -pub use crate::{ - node_runtime::{ - self, - DefaultConfig, - }, - TestNodeProcess, -}; - -use sp_keyring::AccountKeyring; -use subxt::Client; - -/// substrate node should be installed on the $PATH -const SUBSTRATE_NODE_PATH: &str = "substrate"; - -pub async fn test_node_process_with( - key: AccountKeyring, -) -> TestNodeProcess { - let path = std::env::var("SUBSTRATE_NODE_PATH").unwrap_or_else(|_| { - if which::which(SUBSTRATE_NODE_PATH).is_err() { - panic!("A substrate binary should be installed on your path for integration tests. \ - See https://github.com/paritytech/subxt/tree/master#integration-testing") - } - SUBSTRATE_NODE_PATH.to_string() - }); - - let proc = TestNodeProcess::::build(path.as_str()) - .with_authority(key) - .scan_for_open_ports() - .spawn::() - .await; - proc.unwrap() -} - -pub async fn test_node_process() -> TestNodeProcess { - test_node_process_with(AccountKeyring::Alice).await -} - -pub struct TestContext { - pub node_proc: TestNodeProcess, - pub api: node_runtime::RuntimeApi, -} - -impl TestContext { - pub fn client(&self) -> &Client { - &self.api.client - } -} - -pub async fn test_context() -> TestContext { - env_logger::try_init().ok(); - let node_proc = test_node_process_with(AccountKeyring::Alice).await; - let api = node_proc.client().clone().to_runtime_api(); - TestContext { node_proc, api } -} diff --git a/tests/integration/utils/mod.rs b/tests/integration/utils/mod.rs deleted file mode 100644 index d0e05c8234..0000000000 --- a/tests/integration/utils/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 . - -mod context; -mod node_proc; - -pub use context::*; -pub use node_proc::TestNodeProcess; diff --git a/tests/integration/utils/node_proc.rs b/tests/integration/utils/node_proc.rs deleted file mode 100644 index 41505a0cc6..0000000000 --- a/tests/integration/utils/node_proc.rs +++ /dev/null @@ -1,233 +0,0 @@ -// 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 . - -use sp_keyring::AccountKeyring; -use std::{ - ffi::{ - OsStr, - OsString, - }, - net::TcpListener, - process, - sync::atomic::{ - AtomicU16, - Ordering, - }, - thread, - time, -}; -use subxt::{ - Client, - ClientBuilder, - Config, -}; - -/// Spawn a local substrate node for testing subxt. -pub struct TestNodeProcess { - proc: process::Child, - client: Client, -} - -impl Drop for TestNodeProcess -where - R: Config, -{ - fn drop(&mut self) { - let _ = self.kill(); - } -} - -impl TestNodeProcess -where - R: Config, -{ - /// Construct a builder for spawning a test node process. - pub fn build(program: S) -> TestNodeProcessBuilder - where - S: AsRef + Clone, - { - TestNodeProcessBuilder::new(program) - } - - /// Attempt to kill the running substrate process. - pub fn kill(&mut self) -> Result<(), String> { - log::info!("Killing contracts node process {}", self.proc.id()); - if let Err(err) = self.proc.kill() { - let err = format!( - "Error killing contracts node process {}: {}", - self.proc.id(), - err - ); - log::error!("{}", err); - return Err(err.into()) - } - Ok(()) - } - - /// Returns the subxt client connected to the running node. - pub fn client(&self) -> &Client { - &self.client - } -} - -/// Construct a test node process. -pub struct TestNodeProcessBuilder { - node_path: OsString, - authority: Option, - scan_port_range: bool, -} - -impl TestNodeProcessBuilder { - pub fn new

(node_path: P) -> TestNodeProcessBuilder - where - P: AsRef, - { - Self { - node_path: node_path.as_ref().into(), - authority: None, - scan_port_range: false, - } - } - - /// Set the authority dev account for a node in validator mode e.g. --alice. - pub fn with_authority(&mut self, account: AccountKeyring) -> &mut Self { - self.authority = Some(account); - self - } - - /// Enable port scanning to scan for open ports. - /// - /// Allows spawning multiple node instances for tests to run in parallel. - pub fn scan_for_open_ports(&mut self) -> &mut Self { - self.scan_port_range = true; - self - } - - /// Spawn the substrate node at the given path, and wait for rpc to be initialized. - pub async fn spawn(&self) -> Result, String> - where - R: Config, - { - let mut cmd = process::Command::new(&self.node_path); - cmd.env("RUST_LOG", "error").arg("--dev").arg("--tmp"); - - if let Some(authority) = self.authority { - let authority = format!("{:?}", authority); - let arg = format!("--{}", authority.as_str().to_lowercase()); - cmd.arg(arg); - } - - let ws_port = if self.scan_port_range { - let (p2p_port, http_port, ws_port) = next_open_port() - .ok_or("No available ports in the given port range".to_owned())?; - - cmd.arg(format!("--port={}", p2p_port)); - cmd.arg(format!("--rpc-port={}", http_port)); - cmd.arg(format!("--ws-port={}", ws_port)); - ws_port - } else { - // the default Websockets port - 9944 - }; - - let ws_url = format!("ws://127.0.0.1:{}", ws_port); - - let mut proc = cmd.spawn().map_err(|e| { - format!( - "Error spawning substrate node '{}': {}", - self.node_path.to_string_lossy(), - e - ) - })?; - // wait for rpc to be initialized - const MAX_ATTEMPTS: u32 = 6; - let mut attempts = 1; - let mut wait_secs = 1; - let client = loop { - thread::sleep(time::Duration::from_secs(wait_secs)); - log::info!( - "Connecting to contracts enabled node, attempt {}/{}", - attempts, - MAX_ATTEMPTS - ); - let result = ClientBuilder::new().set_url(ws_url.clone()).build().await; - match result { - Ok(client) => break Ok(client), - Err(err) => { - if attempts < MAX_ATTEMPTS { - attempts += 1; - wait_secs = wait_secs * 2; // backoff - continue - } - break Err(err) - } - } - }; - match client { - Ok(client) => Ok(TestNodeProcess { proc, client }), - Err(err) => { - let err = format!( - "Failed to connect to node rpc at {} after {} attempts: {}", - ws_url, attempts, err - ); - log::error!("{}", err); - proc.kill().map_err(|e| { - format!("Error killing substrate process '{}': {}", proc.id(), e) - })?; - Err(err.into()) - } - } - } -} - -/// The start of the port range to scan. -const START_PORT: u16 = 9900; -/// The end of the port range to scan. -const END_PORT: u16 = 10000; -/// The maximum number of ports to scan before giving up. -const MAX_PORTS: u16 = 1000; -/// Next available unclaimed port for test node endpoints. -static PORT: AtomicU16 = AtomicU16::new(START_PORT); - -/// Returns the next set of 3 open ports. -/// -/// Returns None if there are not 3 open ports available. -fn next_open_port() -> Option<(u16, u16, u16)> { - let mut ports = Vec::new(); - let mut ports_scanned = 0u16; - loop { - let _ = PORT.compare_exchange( - END_PORT, - START_PORT, - Ordering::SeqCst, - Ordering::SeqCst, - ); - let next = PORT.fetch_add(1, Ordering::SeqCst); - match TcpListener::bind(("0.0.0.0", next)) { - Ok(_) => { - ports.push(next); - if ports.len() == 3 { - return Some((ports[0], ports[1], ports[2])) - } - } - Err(_) => (), - } - ports_scanned += 1; - if ports_scanned == MAX_PORTS { - return None - } - } -} diff --git a/tests/node_runtime.scale b/tests/node_runtime.scale deleted file mode 100644 index 80692153a8..0000000000 Binary files a/tests/node_runtime.scale and /dev/null differ