diff --git a/Cargo.toml b/Cargo.toml index f1e867bcea8..0e167a12c39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,12 +21,12 @@ client = ["substrate-subxt-client"] [dependencies] log = "0.4.8" -thiserror = "1.0.19" +thiserror = "1.0.20" futures = "0.3.5" jsonrpsee = { version = "0.1.0", features = ["ws"] } -num-traits = { version = "0.2.11", default-features = false } -serde = { version = "1.0.111", features = ["derive"] } -serde_json = "1.0.53" +num-traits = { version = "0.2.12", default-features = false } +serde = { version = "1.0.113", features = ["derive"] } +serde_json = "1.0.55" url = "2.1.1" codec = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive", "full"] } diff --git a/client/Cargo.toml b/client/Cargo.toml index c7ad49ef1ee..5264a4bb7d5 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -19,9 +19,9 @@ jsonrpsee = "0.1.0" log = "0.4.8" sc-network = { version = "0.8.0-rc3", default-features = false } sc-service = { version = "0.8.0-rc3", default-features = false } -serde_json = "1.0.53" +serde_json = "1.0.55" sp-keyring = "2.0.0-rc3" -thiserror = "1.0.19" +thiserror = "1.0.20" [dev-dependencies] async-std = { version = "=1.5.0", features = ["attributes"] } diff --git a/client/src/lib.rs b/client/src/lib.rs index e500721e3aa..f0a88e2e88c 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -233,6 +233,7 @@ fn start_subxt_client( pruning: Default::default(), rpc_cors: Default::default(), rpc_http: Default::default(), + rpc_ipc: Default::default(), rpc_ws: Default::default(), rpc_ws_max_connections: Default::default(), rpc_methods: Default::default(), diff --git a/proc-macro/Cargo.toml b/proc-macro/Cargo.toml index c88b64d32e9..84274c64eef 100644 --- a/proc-macro/Cargo.toml +++ b/proc-macro/Cargo.toml @@ -18,8 +18,9 @@ proc-macro = true heck = "0.3.1" proc-macro2 = "1.0.18" proc-macro-crate = "0.1.4" +proc-macro-error = "1.0.2" quote = "1.0.7" -syn = "1.0.30" +syn = "1.0.31" synstructure = "0.12.4" [dev-dependencies] @@ -29,7 +30,7 @@ env_logger = "0.7.1" pretty_assertions = "0.6.1" sp-keyring = "2.0.0-rc3" substrate-subxt = { path = ".." } -trybuild = "1.0.28" +trybuild = "1.0.30" [[test]] name = "balances" diff --git a/proc-macro/src/call.rs b/proc-macro/src/call.rs index 095e0952eb4..cedf993fe01 100644 --- a/proc-macro/src/call.rs +++ b/proc-macro/src/call.rs @@ -28,7 +28,6 @@ use synstructure::Structure; pub fn call(s: Structure) -> TokenStream { let subxt = utils::use_crate("substrate-subxt"); - let codec = utils::use_crate("parity-scale-codec"); let ident = &s.ast().ident; let generics = &s.ast().generics; let params = utils::type_params(generics); @@ -60,32 +59,29 @@ pub fn call(s: Structure) -> TokenStream { } /// Call extension trait. - pub trait #call_trait> { + pub trait #call_trait { /// Create and submit an extrinsic. fn #call<'a>( &'a self, - signer: &'a (dyn #subxt::Signer + Send + Sync), + signer: &'a (dyn #subxt::Signer + Send + Sync), #args ) -> core::pin::Pin> + Send + 'a>>; /// Create, submit and watch an extrinsic. fn #call_and_watch<'a>( &'a self, - signer: &'a (dyn #subxt::Signer + Send + Sync), + signer: &'a (dyn #subxt::Signer + Send + Sync), #args ) -> core::pin::Pin, #subxt::Error>> + Send + 'a>>; } - impl #call_trait for #subxt::Client + impl #call_trait for #subxt::Client where - T: #module + #subxt::system::System + Send + Sync + 'static, - S: #codec::Encode + Send + Sync + 'static, - E: #subxt::SignedExtra + #subxt::sp_runtime::traits::SignedExtension + Send + Sync + 'static, - <>::Extra as #subxt::sp_runtime::traits::SignedExtension>::AdditionalSigned: Send + Sync, + <>::Extra as #subxt::SignedExtension>::AdditionalSigned: Send + Sync, { fn #call<'a>( &'a self, - signer: &'a (dyn #subxt::Signer + Send + Sync), + signer: &'a (dyn #subxt::Signer + Send + Sync), #args ) -> core::pin::Pin> + Send + 'a>> { let #marker = core::marker::PhantomData::; @@ -94,7 +90,7 @@ pub fn call(s: Structure) -> TokenStream { fn #call_and_watch<'a>( &'a self, - signer: &'a (dyn #subxt::Signer + Send + Sync), + signer: &'a (dyn #subxt::Signer + Send + Sync), #args ) -> core::pin::Pin, #subxt::Error>> + Send + 'a>> { let #marker = core::marker::PhantomData::; @@ -130,11 +126,11 @@ mod tests { } /// Call extension trait. - pub trait TransferCallExt> { + pub trait TransferCallExt { /// Create and submit an extrinsic. fn transfer<'a>( &'a self, - signer: &'a (dyn substrate_subxt::Signer + Send + Sync), + signer: &'a (dyn substrate_subxt::Signer + Send + Sync), to: &'a ::Address, amount: T::Balance, ) -> core::pin::Pin> + Send + 'a>>; @@ -142,22 +138,19 @@ mod tests { /// Create, submit and watch an extrinsic. fn transfer_and_watch<'a>( &'a self, - signer: &'a (dyn substrate_subxt::Signer + Send + Sync), + signer: &'a (dyn substrate_subxt::Signer + Send + Sync), to: &'a ::Address, amount: T::Balance, ) -> core::pin::Pin, substrate_subxt::Error>> + Send + 'a>>; } - impl TransferCallExt for substrate_subxt::Client + impl TransferCallExt for substrate_subxt::Client where - T: Balances + substrate_subxt::system::System + Send + Sync + 'static, - S: codec::Encode + Send + Sync + 'static, - E: substrate_subxt::SignedExtra + substrate_subxt::sp_runtime::traits::SignedExtension + Send + Sync + 'static, - <>::Extra as substrate_subxt::sp_runtime::traits::SignedExtension>::AdditionalSigned: Send + Sync, + <>::Extra as substrate_subxt::SignedExtension>::AdditionalSigned: Send + Sync, { fn transfer<'a>( &'a self, - signer: &'a (dyn substrate_subxt::Signer + Send + Sync), + signer: &'a (dyn substrate_subxt::Signer + Send + Sync), to: &'a ::Address, amount: T::Balance, ) -> core::pin::Pin> + Send + 'a>> { @@ -167,7 +160,7 @@ mod tests { fn transfer_and_watch<'a>( &'a self, - signer: &'a (dyn substrate_subxt::Signer + Send + Sync), + signer: &'a (dyn substrate_subxt::Signer + Send + Sync), to: &'a ::Address, amount: T::Balance, ) -> core::pin::Pin, substrate_subxt::Error>> + Send + 'a>> { diff --git a/proc-macro/src/event.rs b/proc-macro/src/event.rs index a57df0d15f2..6c03551fd0a 100644 --- a/proc-macro/src/event.rs +++ b/proc-macro/src/event.rs @@ -36,7 +36,7 @@ pub fn event(s: Structure) -> TokenStream { let event = format_ident!("{}", event_name.to_snake_case()); let event_trait = format_ident!("{}EventExt", event_name); - let expanded = quote! { + quote! { impl #subxt::Event for #ident { const MODULE: &'static str = MODULE; const EVENT: &'static str = #event_name; @@ -53,9 +53,7 @@ pub fn event(s: Structure) -> TokenStream { self.find_event() } } - }; - - TokenStream::from(expanded) + } } #[cfg(test)] diff --git a/proc-macro/src/lib.rs b/proc-macro/src/lib.rs index c42006f935b..ccbea7e2b4f 100644 --- a/proc-macro/src/lib.rs +++ b/proc-macro/src/lib.rs @@ -24,32 +24,35 @@ mod test; mod utils; use proc_macro::TokenStream; +use proc_macro_error::proc_macro_error; use synstructure::{ decl_derive, Structure, }; #[proc_macro_attribute] +#[proc_macro_error] pub fn module(args: TokenStream, input: TokenStream) -> TokenStream { module::module(args.into(), input.into()).into() } -decl_derive!([Call] => call); +decl_derive!([Call] => #[proc_macro_error] call); fn call(s: Structure) -> TokenStream { call::call(s).into() } -decl_derive!([Event] => event); +decl_derive!([Event] => #[proc_macro_error] event); fn event(s: Structure) -> TokenStream { event::event(s).into() } -decl_derive!([Store, attributes(store)] => store); +decl_derive!([Store, attributes(store)] => #[proc_macro_error] store); fn store(s: Structure) -> TokenStream { store::store(s).into() } #[proc_macro] +#[proc_macro_error] pub fn subxt_test(input: TokenStream) -> TokenStream { test::test(input.into()).into() } diff --git a/proc-macro/src/module.rs b/proc-macro/src/module.rs index 162bb584a79..5dddfd58993 100644 --- a/proc-macro/src/module.rs +++ b/proc-macro/src/module.rs @@ -17,6 +17,7 @@ use crate::utils; use heck::SnakeCase; use proc_macro2::TokenStream; +use proc_macro_error::abort; use quote::{ format_ident, quote, @@ -48,8 +49,10 @@ type ModuleAttrs = utils::Attrs; fn ignore(attrs: &[syn::Attribute]) -> bool { for attr in attrs { if let Some(ident) = attr.path.get_ident() { - if ident.to_string() == "module" { - let attrs: ModuleAttrs = syn::parse2(attr.tokens.clone()).unwrap(); + if ident == "module" { + let attrs: ModuleAttrs = syn::parse2(attr.tokens.clone()) + .map_err(|err| abort!("{}", err)) + .unwrap(); if !attrs.attrs.is_empty() { return true } @@ -69,10 +72,12 @@ fn with_module_ident(module: &syn::Ident) -> syn::Ident { pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream { let input: Result = syn::parse2(tokens.clone()); - if input.is_err() { + let input = if let Ok(input) = input { + input + } else { + // handle #[module(ignore)] by just returning the tokens return tokens - } - let input = input.unwrap(); + }; let subxt = utils::use_crate("substrate-subxt"); let module = &input.ident; diff --git a/proc-macro/src/store.rs b/proc-macro/src/store.rs index db528c61aca..47294589a15 100644 --- a/proc-macro/src/store.rs +++ b/proc-macro/src/store.rs @@ -20,6 +20,7 @@ use heck::{ SnakeCase, }; use proc_macro2::TokenStream; +use proc_macro_error::abort; use quote::{ format_ident, quote, @@ -50,7 +51,9 @@ impl Parse for StoreAttr { type StoreAttrs = utils::Attrs; fn parse_returns_attr(attr: &syn::Attribute) -> Option<(syn::Type, syn::Type, bool)> { - let attrs: StoreAttrs = syn::parse2(attr.tokens.clone()).unwrap(); + let attrs: StoreAttrs = syn::parse2(attr.tokens.clone()) + .map_err(|err| abort!("{}", err)) + .unwrap(); attrs.attrs.into_iter().next().map(|attr| { let StoreAttr::Returns(attr) = attr; let ty = attr.value; @@ -81,7 +84,9 @@ pub fn store(s: Structure) -> TokenStream { .iter() .filter_map(|bi| bi.ast().attrs.iter().filter_map(parse_returns_attr).next()) .next() - .expect("#[store(returns = ..)] needs to be specified."); + .unwrap_or_else(|| { + abort!(ident, "#[store(returns = ..)] needs to be specified.") + }); let fetch = if uses_default { quote!(fetch_or_default) } else { @@ -93,7 +98,13 @@ pub fn store(s: Structure) -> TokenStream { 0 => "plain", 1 => "map", 2 => "double_map", - _ => panic!("invalid number of arguments"), + _ => { + abort!( + ident, + "Expected 0-2 fields but found {}", + filtered_fields.len() + ); + } } ); let keys = filtered_fields @@ -127,12 +138,7 @@ pub fn store(s: Structure) -> TokenStream { ) -> core::pin::Pin> + Send + 'a>>; } - impl #store_trait for #subxt::Client - where - T: #module + Send + Sync, - S: 'static, - E: Send + Sync + 'static, - { + impl #store_trait for #subxt::Client { fn #store<'a>( &'a self, #args @@ -185,12 +191,7 @@ mod tests { ) -> core::pin::Pin, substrate_subxt::Error>> + Send + 'a>>; } - impl AccountStoreExt for substrate_subxt::Client - where - T: Balances + Send + Sync, - S: 'static, - E: Send + Sync + 'static, - { + impl AccountStoreExt for substrate_subxt::Client { fn account<'a>( &'a self, account_id: &'a ::AccountId, diff --git a/proc-macro/src/test.rs b/proc-macro/src/test.rs index bef72e0eb3c..fc5ebfeb52a 100644 --- a/proc-macro/src/test.rs +++ b/proc-macro/src/test.rs @@ -16,6 +16,7 @@ use crate::utils; use proc_macro2::TokenStream; +use proc_macro_error::abort; use quote::{ format_ident, quote, @@ -34,8 +35,6 @@ mod kw { custom_keyword!(name); custom_keyword!(runtime); custom_keyword!(account); - custom_keyword!(signature); - custom_keyword!(extra); custom_keyword!(prelude); custom_keyword!(step); custom_keyword!(state); @@ -70,10 +69,9 @@ struct Items { impl Parse for Items { fn parse(input: ParseStream) -> syn::Result { let content; - Ok(Self { - brace: syn::braced!(content in input), - items: content.parse_terminated(I::parse)?, - }) + let brace = syn::braced!(content in input); + let items = content.parse_terminated(I::parse)?; + Ok(Self { brace, items }) } } @@ -82,10 +80,8 @@ type ItemTest = Items; #[derive(Debug)] enum TestItem { Name(Item), - Runtime(Item), + Runtime(Item>), Account(Item), - Signature(Item), - Extra(Item), State(Item), Prelude(Item), Step(Item), @@ -99,10 +95,6 @@ impl Parse for TestItem { Ok(TestItem::Runtime(input.parse()?)) } else if input.peek(kw::account) { Ok(TestItem::Account(input.parse()?)) - } else if input.peek(kw::signature) { - Ok(TestItem::Signature(input.parse()?)) - } else if input.peek(kw::extra) { - Ok(TestItem::Extra(input.parse()?)) } else if input.peek(kw::state) { Ok(TestItem::State(input.parse()?)) } else if input.peek(kw::prelude) { @@ -142,10 +134,8 @@ type StateItem = Item; struct Test { name: syn::Ident, - runtime: syn::Type, + runtime: Box, account: syn::Ident, - signature: syn::Type, - extra: syn::Type, state: Option, prelude: Option, steps: Vec, @@ -156,11 +146,11 @@ impl From for Test { let mut name = None; let mut runtime = None; let mut account = None; - let mut signature = None; - let mut extra = None; let mut state = None; let mut prelude = None; let mut steps = vec![]; + + let span = test.brace.span; for test_item in test.items { match test_item { TestItem::Name(item) => { @@ -172,12 +162,6 @@ impl From for Test { TestItem::Account(item) => { account = Some(item.value); } - TestItem::Signature(item) => { - signature = Some(item.value); - } - TestItem::Extra(item) => { - extra = Some(item.value); - } TestItem::State(item) => { state = Some(item.value.into()); } @@ -193,14 +177,8 @@ impl From for Test { let runtime = runtime .unwrap_or_else(|| syn::parse2(quote!(#subxt::DefaultNodeRuntime)).unwrap()); Self { - name: name.expect("No name specified"), + name: name.unwrap_or_else(|| abort!(span, "No name specified")), account: account.unwrap_or_else(|| format_ident!("Alice")), - signature: signature.unwrap_or_else(|| { - syn::parse2(quote!(#subxt::sp_runtime::MultiSignature)).unwrap() - }), - extra: extra.unwrap_or_else(|| { - syn::parse2(quote!(#subxt::DefaultExtra<#runtime>)).unwrap() - }), runtime, state, prelude, @@ -219,8 +197,6 @@ impl Test { name, runtime, account, - signature, - extra, state, prelude, steps, @@ -234,7 +210,7 @@ impl Test { #[ignore] async fn #name() { #env_logger - let client = #subxt::ClientBuilder::<#runtime, #signature, #extra>::new() + let client = #subxt::ClientBuilder::<#runtime>::new() .build().await.unwrap(); let signer = #subxt::PairSigner::new(#sp_keyring::AccountKeyring::#account.pair()); @@ -277,6 +253,7 @@ impl From for Step { let mut event = vec![]; let mut assert = None; + let span = step.brace.span; for step_item in step.items { match step_item { StepItem::State(item) => { @@ -297,7 +274,7 @@ impl From for Step { Self { state, - call: call.expect("Step requires a call."), + call: call.unwrap_or_else(|| abort!(span, "Step requires a call.")), event_name, event, assert, @@ -404,14 +381,15 @@ impl From for State { fn struct_name(expr: &syn::Expr) -> syn::Path { if let syn::Expr::Struct(syn::ExprStruct { path, .. }) = expr { - return path.clone() + path.clone() } else { - panic!("not a struct"); + abort!(expr, "Expected a struct"); } } pub fn test(input: TokenStream) -> TokenStream { - let item_test: ItemTest = syn::parse2(input).unwrap(); + let item_test: ItemTest = + syn::parse2(input).map_err(|err| abort!("{}", err)).unwrap(); Test::from(item_test).into_tokens() } @@ -450,11 +428,7 @@ mod tests { #[ignore] async fn test_transfer_balance() { env_logger::try_init().ok(); - let client = substrate_subxt::ClientBuilder::< - KusamaRuntime, - substrate_subxt::sp_runtime::MultiSignature, - substrate_subxt::DefaultExtra - >::new().build().await.unwrap(); + let client = substrate_subxt::ClientBuilder::::new().build().await.unwrap(); let signer = substrate_subxt::PairSigner::new(sp_keyring::AccountKeyring::Alice.pair()); #[allow(unused)] let alice = sp_keyring::AccountKeyring::Alice.to_account_id(); diff --git a/proc-macro/src/utils.rs b/proc-macro/src/utils.rs index 25a9198eaea..a595d42fe6f 100644 --- a/proc-macro/src/utils.rs +++ b/proc-macro/src/utils.rs @@ -56,7 +56,7 @@ pub fn bindings<'a>(s: &'a Structure) -> Vec<&'a BindingInfo<'a>> { type Field = (syn::Ident, syn::Type); -pub fn fields<'a>(bindings: &'a [&'a BindingInfo<'a>]) -> Vec { +pub fn fields(bindings: &[&BindingInfo<'_>]) -> Vec { bindings .iter() .enumerate() @@ -72,7 +72,7 @@ pub fn fields<'a>(bindings: &'a [&'a BindingInfo<'a>]) -> Vec { .collect() } -pub fn marker_field<'a>(fields: &'a [Field]) -> Option { +pub fn marker_field(fields: &[Field]) -> Option { fields .iter() .filter_map(|(field, ty)| { @@ -86,7 +86,7 @@ pub fn marker_field<'a>(fields: &'a [Field]) -> Option { .cloned() } -pub fn filter_fields<'a>(fields: &'a [Field], field: &'a syn::Ident) -> Vec { +pub fn filter_fields(fields: &[Field], field: &syn::Ident) -> Vec { fields .iter() .filter_map(|(field2, ty)| { @@ -99,12 +99,12 @@ pub fn filter_fields<'a>(fields: &'a [Field], field: &'a syn::Ident) -> Vec(fields: &'a [Field]) -> TokenStream { +pub fn fields_to_args(fields: &[Field]) -> TokenStream { let args = fields.iter().map(|(field, ty)| quote!(#field: #ty,)); quote!(#(#args)*) } -pub fn build_struct<'a>(ident: &'a syn::Ident, fields: &'a [Field]) -> TokenStream { +pub fn build_struct(ident: &syn::Ident, fields: &[Field]) -> TokenStream { let fields = fields.iter().map(|(field, _)| field); quote!(#ident { #(#fields,)* }) } @@ -170,7 +170,7 @@ pub fn type_params(generics: &syn::Generics) -> Vec { pub fn parse_option(ty: &syn::Type) -> Option { if let syn::Type::Path(ty_path) = ty { if let Some(seg) = ty_path.path.segments.first() { - if seg.ident.to_string() == "Option" { + if &seg.ident == "Option" { if let syn::PathArguments::AngleBracketed(args) = &seg.arguments { if let Some(syn::GenericArgument::Type(ty)) = args.args.first() { return Some(ty.clone()) @@ -191,10 +191,9 @@ pub struct Attrs { impl Parse for Attrs { fn parse(input: ParseStream) -> syn::Result { let content; - Ok(Self { - paren: syn::parenthesized!(content in input), - attrs: content.parse_terminated(A::parse)?, - }) + let paren = syn::parenthesized!(content in input); + let attrs = content.parse_terminated(A::parse)?; + Ok(Self { paren, attrs }) } } diff --git a/src/error.rs b/src/error.rs index bda8fbb5277..cbadeb35f5d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,15 +19,19 @@ use jsonrpsee::{ transport::ws::WsNewDnsError, }; use sp_core::crypto::SecretStringError; -use sp_runtime::transaction_validity::TransactionValidityError; +use sp_runtime::{ + transaction_validity::TransactionValidityError, + DispatchError, +}; +use thiserror::Error; -use crate::{ - events::EventsError, - metadata::MetadataError, +use crate::metadata::{ + Metadata, + MetadataError, }; /// Error enum. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Error)] pub enum Error { /// Io error. #[error("Io error: {0}")] @@ -50,12 +54,21 @@ pub enum Error { /// Extrinsic validity error #[error("Transaction Validity Error: {0:?}")] Invalid(TransactionValidityError), - /// Events error. - #[error("Event error: {0}")] - Events(#[from] EventsError), /// Metadata error. #[error("Metadata error: {0}")] Metadata(#[from] MetadataError), + /// Type size unavailable. + #[error("Type size unavailable while decoding event: {0:?}")] + TypeSizeUnavailable(String), + /// Runtime error. + #[error("Runtime error: {0}")] + Runtime(RuntimeError), + /// 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), @@ -84,3 +97,34 @@ impl From for Error { Error::Other(error) } } + +impl Error { + /// Converts a `DispatchError` into a subxt error. + pub fn from_dispatch(metadata: &Metadata, error: DispatchError) -> Result<(), Self> { + match error { + DispatchError::Module { + index, + error, + message: _, + } => { + let module = metadata.module_with_errors(index)?; + let error = module.error(error)?; + Err(Error::Runtime(RuntimeError { + module: module.name().to_string(), + error: error.to_string(), + })) + } + DispatchError::BadOrigin => Err(Error::BadOrigin), + DispatchError::CannotLookup => Err(Error::CannotLookup), + DispatchError::Other(msg) => Err(Error::Other(msg.into())), + } + } +} + +/// Runtime errors. +#[derive(Clone, Debug, Eq, Error, PartialEq)] +#[error("{error} from {module}")] +pub struct RuntimeError { + pub module: String, + pub error: String, +} diff --git a/src/events.rs b/src/events.rs index 81b3376d225..b226947e4a4 100644 --- a/src/events.rs +++ b/src/events.rs @@ -19,10 +19,14 @@ use codec::{ Compact, Decode, Encode, - Error as CodecError, Input, Output, }; +use frame_support::dispatch::DispatchInfo; +use sp_runtime::{ + DispatchError, + DispatchResult, +}; use std::{ collections::{ HashMap, @@ -33,26 +37,17 @@ use std::{ Send, }, }; -use thiserror::Error; use crate::{ + error::Error, metadata::{ EventArg, Metadata, - MetadataError, }, Phase, System, - SystemEvent, }; -/// Top level Event that can be produced by a substrate runtime -#[derive(Debug)] -pub enum RuntimeEvent { - System(SystemEvent), - Raw(RawEvent), -} - /// Raw bytes for an Event #[derive(Debug)] pub struct RawEvent { @@ -64,20 +59,6 @@ pub struct RawEvent { pub data: Vec, } -/// Events error. -#[derive(Debug, Error)] -pub enum EventsError { - /// Codec error. - #[error("Scale codec error: {0:?}")] - CodecError(#[from] CodecError), - /// Metadata error. - #[error("Metadata error: {0:?}")] - Metadata(#[from] MetadataError), - /// Type size unavailable. - #[error("Type Sizes Unavailable: {0:?}")] - TypeSizeUnavailable(String), -} - /// Event decoder. #[derive(Debug)] pub struct EventsDecoder { @@ -95,6 +76,8 @@ impl EventsDecoder { marker: PhantomData, }; // register default event arg type sizes for dynamic decoding of events + decoder.register_type_size::<()>("PhantomData"); + decoder.register_type_size::("DispatchInfo"); decoder.register_type_size::("bool"); decoder.register_type_size::("ReferendumIndex"); decoder.register_type_size::<[u8; 16]>("Kind"); @@ -132,10 +115,7 @@ impl EventsDecoder { for event in module.events() { for arg in event.arguments() { for primitive in arg.primitives() { - if module.name() != "System" - && !self.type_sizes.contains_key(&primitive) - && !primitive.contains("PhantomData") - { + if !self.type_sizes.contains_key(&primitive) { missing.insert(format!( "{}::{}::{}", module.name(), @@ -161,7 +141,7 @@ impl EventsDecoder { args: &[EventArg], input: &mut I, output: &mut W, - ) -> Result<(), EventsError> { + ) -> Result<(), Error> { for arg in args { match arg { EventArg::Vec(arg) => { @@ -173,16 +153,22 @@ impl EventsDecoder { } EventArg::Tuple(args) => self.decode_raw_bytes(args, input, output)?, EventArg::Primitive(name) => { - if name.contains("PhantomData") { - // PhantomData is size 0 - return Ok(()) - } - if let Some(size) = self.type_sizes.get(name) { - let mut buf = vec![0; *size]; - input.read(&mut buf)?; - output.write(&buf); - } else { - return Err(EventsError::TypeSizeUnavailable(name.to_owned())) + let result = match name.as_str() { + "DispatchResult" => DispatchResult::decode(input)?, + "DispatchError" => Err(DispatchError::decode(input)?), + _ => { + if let Some(size) = self.type_sizes.get(name) { + let mut buf = vec![0; *size]; + input.read(&mut buf)?; + output.write(&buf); + Ok(()) + } else { + return Err(Error::TypeSizeUnavailable(name.to_owned())) + } + } + }; + if let Err(error) = result { + Error::from_dispatch(&self.metadata, error)?; } } } @@ -194,7 +180,7 @@ impl EventsDecoder { pub fn decode_events( &self, input: &mut &[u8], - ) -> Result)>, EventsError> { + ) -> Result, Error> { let compact_len = >::decode(input)?; let len = compact_len.0 as usize; @@ -205,33 +191,24 @@ impl EventsDecoder { let module_variant = input.read_byte()?; let module = self.metadata.module_with_events(module_variant)?; - let event = if module.name() == "System" { - let system_event = SystemEvent::decode(input)?; - RuntimeEvent::System(system_event) - } else { - let event_variant = input.read_byte()?; - let event_metadata = module.event(event_variant)?; - - log::debug!( - "received event '{}::{}'", - module.name(), - event_metadata.name - ); - - let mut event_data = Vec::::new(); - self.decode_raw_bytes( - &event_metadata.arguments(), - input, - &mut event_data, - )?; - - log::debug!("raw bytes: {}", hex::encode(&event_data),); - - RuntimeEvent::Raw(RawEvent { - module: module.name().to_string(), - variant: event_metadata.name.clone(), - data: event_data, - }) + let event_variant = input.read_byte()?; + let event_metadata = module.event(event_variant)?; + + log::debug!( + "received event '{}::{}'", + module.name(), + event_metadata.name + ); + + let mut event_data = Vec::::new(); + self.decode_raw_bytes(&event_metadata.arguments(), input, &mut event_data)?; + + log::debug!("raw bytes: {}", hex::encode(&event_data),); + + let event = RawEvent { + module: module.name().to_string(), + variant: event_metadata.name.clone(), + data: event_data, }; // topics come after the event data in EventRecord diff --git a/src/extra.rs b/src/extra.rs index b7f4cc6ce3e..fa7ff54a55c 100644 --- a/src/extra.rs +++ b/src/extra.rs @@ -222,9 +222,9 @@ where } /// Trait for implementing transaction extras for a runtime. -pub trait SignedExtra { +pub trait SignedExtra: SignedExtension { /// The type the extras. - type Extra: SignedExtension; + type Extra: SignedExtension + Send + Sync; /// Creates a new `SignedExtra`. fn new( diff --git a/src/frame/balances.rs b/src/frame/balances.rs index 6f6eb4ebc3f..3d1a6111ba1 100644 --- a/src/frame/balances.rs +++ b/src/frame/balances.rs @@ -110,36 +110,56 @@ pub struct TransferEvent { mod tests { use super::*; use crate::{ - system::{ - AccountStore, - AccountStoreExt, + error::{ + Error, + RuntimeError, }, - tests::test_client, + signer::{ + PairSigner, + Signer, + }, + system::AccountStoreExt, + tests::{ + test_client, + TestRuntime, + }, + }; + use sp_core::{ + sr25519::Pair, + Pair as _, }; use sp_keyring::AccountKeyring; - subxt_test!({ - name: test_transfer, - step: { - state: { - alice: AccountStore { account_id: &alice }, - bob: AccountStore { account_id: &bob }, - }, - call: TransferCall { - to: &bob.clone().into(), - amount: 10_000, - }, - event: TransferEvent { - from: alice.clone(), - to: bob.clone(), - amount: 10_000, - }, - assert: { - assert!(pre.alice.data.free - 10_000 >= post.alice.data.free); - assert_eq!(pre.bob.data.free + 10_000, post.bob.data.free); - }, - }, - }); + #[async_std::test] + async fn test_transfer() { + env_logger::try_init().ok(); + let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let bob = PairSigner::::new(AccountKeyring::Bob.pair()); + let (client, _) = test_client().await; + + let alice_pre = client.account(alice.account_id(), None).await.unwrap(); + let bob_pre = client.account(bob.account_id(), None).await.unwrap(); + + let event = client + .transfer_and_watch(&alice, &bob.account_id(), 10_000) + .await + .unwrap() + .transfer() + .unwrap() + .unwrap(); + let expected_event = TransferEvent { + from: alice.account_id().clone(), + to: bob.account_id().clone(), + amount: 10_000, + }; + assert_eq!(event, expected_event); + + let alice_post = client.account(alice.account_id(), None).await.unwrap(); + let bob_post = client.account(bob.account_id(), 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 test_state_total_issuance() { @@ -157,4 +177,28 @@ mod tests { let info = client.account(&account, None).await.unwrap(); assert_ne!(info.data.free, 0); } + + #[async_std::test] + async fn test_transfer_error() { + env_logger::try_init().ok(); + let alice = PairSigner::new(AccountKeyring::Alice.pair()); + let hans = PairSigner::new(Pair::generate().0); + let (client, _) = test_client().await; + client + .transfer_and_watch(&alice, hans.account_id(), 100_000_000_000) + .await + .unwrap(); + let res = client + .transfer_and_watch(&hans, alice.account_id(), 100_000_000_000) + .await; + if let Err(Error::Runtime(error)) = res { + let error2 = RuntimeError { + module: "Balances".into(), + error: "InsufficientBalance".into(), + }; + assert_eq!(error, error2); + } else { + panic!("expected an error"); + } + } } diff --git a/src/frame/mod.rs b/src/frame/mod.rs index 8c8ada3893e..ab0fbf6f24c 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -31,6 +31,7 @@ use sp_core::storage::StorageKey; pub mod balances; pub mod contracts; +pub mod sudo; pub mod system; /// Store trait. diff --git a/src/frame/sudo.rs b/src/frame/sudo.rs new file mode 100644 index 00000000000..541a923bfc5 --- /dev/null +++ b/src/frame/sudo.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of substrate-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 substrate-subxt. If not, see . + +//! Implements support for the frame_sudo module. + +use crate::{ + frame::system::{ + System, + SystemEventsDecoder, + }, + Encoded, +}; +use codec::Encode; +use core::marker::PhantomData; + +/// The subset of the `frame_sudo::Trait` that a client must implement. +#[module] +pub trait Sudo: System {} + +/// Execute a transaction with sudo permissions. +#[derive(Clone, Debug, Eq, PartialEq, Call, Encode)] +pub struct SudoCall<'a, T: Sudo> { + /// Runtime marker. + pub _runtime: PhantomData, + /// Encoded transaction. + pub call: &'a Encoded, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + error::Error, + frame::balances::TransferCall, + signer::PairSigner, + tests::{ + test_client, + TestRuntime, + }, + }; + use sp_keyring::AccountKeyring; + + #[async_std::test] + async fn test_sudo() { + env_logger::try_init().ok(); + let alice = PairSigner::::new(AccountKeyring::Alice.pair()); + let (client, _) = test_client().await; + + let call = client + .encode(TransferCall { + to: &AccountKeyring::Bob.to_account_id(), + amount: 10_000, + }) + .unwrap(); + + let res = client.sudo_and_watch(&alice, &call).await; + assert!( + if let Err(Error::BadOrigin) = res { + true + } else { + false + } + ); + } +} diff --git a/src/frame/system.rs b/src/frame/system.rs index 6734d47322f..bc1b8297ff3 100644 --- a/src/frame/system.rs +++ b/src/frame/system.rs @@ -155,21 +155,6 @@ pub struct SetCodeCall<'a, T: System> { pub code: &'a [u8], } -/// Event for the System module. -#[derive(Clone, Debug, Eq, PartialEq, Decode)] -pub enum SystemEvent { - /// An extrinsic completed successfully. - ExtrinsicSuccess(DispatchInfo), - /// An extrinsic failed. - ExtrinsicFailed(DispatchError, DispatchInfo), - /// `:code` was updated. - CodeUpdated, - /// A new account was created. - NewAccount(T::AccountId), - /// An account was reaped. - KilledAccount(T::AccountId), -} - /// A phase of a block's execution. #[derive(Clone, Debug, Eq, PartialEq, Decode)] pub enum Phase { @@ -178,3 +163,44 @@ pub enum Phase { /// The end. Finalization, } + +/// An extrinsic completed successfully. +#[derive(Clone, Debug, Eq, PartialEq, Event, Decode)] +pub struct ExtrinsicSuccessEvent { + /// Runtime marker. + pub _runtime: PhantomData, + /// The dispatch info. + pub info: DispatchInfo, +} + +/// An extrinsic failed. +#[derive(Clone, Debug, Eq, PartialEq, Event, Decode)] +pub struct ExtrinsicFailedEvent { + /// Runtime marker. + pub _runtime: PhantomData, + /// The dispatch error. + pub error: DispatchError, + /// The dispatch info. + pub info: DispatchInfo, +} + +/// `:code` was updated. +#[derive(Clone, Debug, Eq, PartialEq, Event, Decode)] +pub struct CodeUpdatedEvent { + /// Runtime marker. + pub _runtime: PhantomData, +} + +/// A new account was created. +#[derive(Clone, Debug, Eq, PartialEq, Event, Decode)] +pub struct NewAccountEvent { + /// Created account id. + pub account: T::AccountId, +} + +/// An account was reaped. +#[derive(Clone, Debug, Eq, PartialEq, Event, Decode)] +pub struct KilledAccountEvent { + /// Killed account id. + pub account: T::AccountId, +} diff --git a/src/lib.rs b/src/lib.rs index bc8239f5f99..b374780ac26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,10 +48,7 @@ pub use substrate_subxt_client as client; pub use sp_core; pub use sp_runtime; -use codec::{ - Decode, - Encode, -}; +use codec::Decode; use futures::future; use jsonrpsee::client::Subscription; use sc_rpc_api::state::ReadProof; @@ -59,14 +56,7 @@ use sp_core::storage::{ StorageChangeSet, StorageKey, }; -use sp_runtime::{ - generic::{ - SignedPayload, - UncheckedExtrinsic, - }, - traits::SignedExtension, - MultiSignature, -}; +pub use sp_runtime::traits::SignedExtension; use sp_version::RuntimeVersion; use std::marker::PhantomData; @@ -83,7 +73,6 @@ pub use crate::{ error::Error, events::{ EventsDecoder, - EventsError, RawEvent, }, extra::*, @@ -105,7 +94,6 @@ use crate::{ AccountStoreExt, Phase, System, - SystemEvent, }, rpc::{ ChainBlock, @@ -115,13 +103,13 @@ use crate::{ /// ClientBuilder for constructing a Client. #[derive(Default)] -pub struct ClientBuilder> { - _marker: std::marker::PhantomData<(T, S, E)>, +pub struct ClientBuilder { + _marker: std::marker::PhantomData, url: Option, client: Option, } -impl ClientBuilder { +impl ClientBuilder { /// Creates a new ClientBuilder. pub fn new() -> Self { Self { @@ -144,7 +132,7 @@ impl ClientBuilder { } /// Creates a new Client. - pub async fn build(self) -> Result, Error> { + pub async fn build(self) -> Result, Error> { let client = if let Some(client) = self.client { client } else { @@ -173,15 +161,15 @@ impl ClientBuilder { } /// Client to interface with a substrate node. -pub struct Client> { +pub struct Client { rpc: Rpc, genesis_hash: T::Hash, metadata: Metadata, runtime_version: RuntimeVersion, - _marker: PhantomData<(fn() -> S, E)>, + _marker: PhantomData<(fn() -> T::Signature, T::Extra)>, } -impl Clone for Client { +impl Clone for Client { fn clone(&self) -> Self { Self { rpc: self.rpc.clone(), @@ -193,7 +181,7 @@ impl Clone for Client { } } -impl Client { +impl Client { /// Returns the genesis hash. pub fn genesis(&self) -> &T::Hash { &self.genesis_hash @@ -309,14 +297,15 @@ impl Client { let headers = self.rpc.subscribe_finalized_blocks().await?; Ok(headers) } -} -impl Client -where - T: System + Send + Sync + 'static, - S: Encode + Send + Sync + 'static, - E: SignedExtra + SignedExtension + Send + Sync + 'static, -{ + /// Encodes a call. + pub fn encode>(&self, call: C) -> Result { + Ok(self + .metadata() + .module_with_calls(C::MODULE) + .and_then(|module| module.call(C::FUNCTION, call))?) + } + /// Creates an unsigned extrinsic. /// /// If `nonce` is `None` the nonce will be fetched from the chain. @@ -325,7 +314,11 @@ where call: C, account_id: &::AccountId, nonce: Option, - ) -> Result>::Extra>, Error> { + ) -> Result, Error> + where + <>::Extra as SignedExtension>::AdditionalSigned: + Send + Sync, + { let account_nonce = if let Some(nonce) = nonce { nonce } else { @@ -334,12 +327,10 @@ where let spec_version = self.runtime_version.spec_version; let tx_version = self.runtime_version.transaction_version; let genesis_hash = self.genesis_hash; - let call = self - .metadata() - .module_with_calls(C::MODULE) - .and_then(|module| module.call(C::FUNCTION, call))?; - let extra: E = E::new(spec_version, tx_version, account_nonce, genesis_hash); - let raw_payload = SignedPayload::new(call, extra.extra())?; + let call = self.encode(call)?; + let extra: T::Extra = + T::Extra::new(spec_version, tx_version, account_nonce, genesis_hash); + let raw_payload = SignedPayload::::new(call, extra.extra())?; Ok(raw_payload) } @@ -347,13 +338,11 @@ where pub async fn create_signed + Send + Sync>( &self, call: C, - signer: &(dyn Signer + Send + Sync), - ) -> Result< - UncheckedExtrinsic>::Extra>, - Error, - > + signer: &(dyn Signer + Send + Sync), + ) -> Result, Error> where - <>::Extra as SignedExtension>::AdditionalSigned: Send + Sync, + <>::Extra as SignedExtension>::AdditionalSigned: + Send + Sync, { let unsigned = self .create_unsigned(call, signer.account_id(), signer.nonce()) @@ -373,12 +362,7 @@ where /// Create and submit an extrinsic and return corresponding Hash if successful pub async fn submit_extrinsic( &self, - extrinsic: UncheckedExtrinsic< - T::Address, - Encoded, - S, - >::Extra, - >, + extrinsic: UncheckedExtrinsic, ) -> Result { self.rpc.submit_extrinsic(extrinsic).await } @@ -386,12 +370,7 @@ where /// Create and submit an extrinsic and return corresponding Event if successful pub async fn submit_and_watch_extrinsic( &self, - extrinsic: UncheckedExtrinsic< - T::Address, - Encoded, - S, - >::Extra, - >, + extrinsic: UncheckedExtrinsic, decoder: EventsDecoder, ) -> Result, Error> { self.rpc @@ -403,10 +382,11 @@ where pub async fn submit + Send + Sync>( &self, call: C, - signer: &(dyn Signer + Send + Sync), + signer: &(dyn Signer + Send + Sync), ) -> Result where - <>::Extra as SignedExtension>::AdditionalSigned: Send + Sync, + <>::Extra as SignedExtension>::AdditionalSigned: + Send + Sync, { let extrinsic = self.create_signed(call, signer).await?; self.submit_extrinsic(extrinsic).await @@ -416,10 +396,11 @@ where pub async fn watch + Send + Sync>( &self, call: C, - signer: &(dyn Signer + Send + Sync), + signer: &(dyn Signer + Send + Sync), ) -> Result, Error> where - <>::Extra as SignedExtension>::AdditionalSigned: Send + Sync, + <>::Extra as SignedExtension>::AdditionalSigned: + Send + Sync, { let extrinsic = self.create_signed(call, signer).await?; let decoder = self.events_decoder::(); @@ -429,7 +410,7 @@ where /// Wraps an already encoded byte vector, prevents being encoded as a raw byte vector as part of /// the transaction payload -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Encoded(pub Vec); impl codec::Encode for Encoded { @@ -441,6 +422,7 @@ impl codec::Encode for Encoded { #[cfg(test)] mod tests { use super::*; + use codec::Encode; use sp_core::{ storage::{ well_known_keys, @@ -452,6 +434,7 @@ mod tests { AccountKeyring, Ed25519Keyring, }; + use sp_runtime::MultiSignature; use substrate_subxt_client::{ DatabaseConfig, Role, @@ -460,7 +443,9 @@ mod tests { }; use tempdir::TempDir; - pub(crate) async fn test_client() -> (Client, TempDir) { + pub(crate) type TestRuntime = crate::NodeTemplateRuntime; + + pub(crate) async fn test_client() -> (Client, TempDir) { let tmp = TempDir::new("subxt-").expect("failed to create tempdir"); let config = SubxtClientConfig { impl_name: "substrate-subxt-full-client", diff --git a/src/metadata.rs b/src/metadata.rs index cf5f9c98526..899aed4c238 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -58,6 +58,9 @@ pub enum MetadataError { /// Event is not in metadata. #[error("Event {0} not found")] EventNotFound(u8), + /// Event is not in metadata. + #[error("Error {0} not found")] + ErrorNotFound(u8), /// Storage is not in metadata. #[error("Storage {0} not found")] StorageNotFound(&'static str), @@ -75,6 +78,7 @@ pub struct Metadata { modules: HashMap, modules_with_calls: HashMap, modules_with_events: HashMap, + modules_with_errors: HashMap, } impl Metadata { @@ -116,6 +120,16 @@ impl Metadata { .ok_or(MetadataError::ModuleIndexNotFound(module_index)) } + pub(crate) fn module_with_errors( + &self, + module_index: u8, + ) -> Result<&ModuleWithErrors, MetadataError> { + self.modules_with_errors + .values() + .find(|&module| module.index == module_index) + .ok_or(MetadataError::ModuleIndexNotFound(module_index)) + } + /// Pretty print metadata. pub fn pretty(&self) -> String { let mut string = String::new(); @@ -206,6 +220,25 @@ impl ModuleWithEvents { } } +#[derive(Clone, Debug)] +pub struct ModuleWithErrors { + index: u8, + name: String, + errors: HashMap, +} + +impl ModuleWithErrors { + pub fn name(&self) -> &str { + &self.name + } + + pub fn error(&self, index: u8) -> Result<&String, MetadataError> { + self.errors + .get(&index) + .ok_or(MetadataError::ErrorNotFound(index)) + } +} + #[derive(Clone, Debug)] pub struct StorageMetadata { module_prefix: String, @@ -437,6 +470,7 @@ impl TryFrom for Metadata { let mut modules = HashMap::new(); let mut modules_with_calls = HashMap::new(); let mut modules_with_events = HashMap::new(); + let mut modules_with_errors = HashMap::new(); for module in convert(meta.modules)?.into_iter() { let module_name = convert(module.name.clone())?; @@ -490,11 +524,24 @@ impl TryFrom for Metadata { }, ); } + let mut error_map = HashMap::new(); + for (index, error) in convert(module.errors)?.into_iter().enumerate() { + error_map.insert(index as u8, convert_error(error)?); + } + modules_with_errors.insert( + module_name.clone(), + ModuleWithErrors { + index: modules_with_errors.len() as u8, + name: module_name.clone(), + errors: error_map, + }, + ); } Ok(Metadata { modules, modules_with_calls, modules_with_events, + modules_with_errors, }) } } @@ -534,3 +581,9 @@ fn convert_entry( default, }) } + +fn convert_error( + error: frame_metadata::ErrorMetadata, +) -> Result { + convert(error.name) +} diff --git a/src/rpc.rs b/src/rpc.rs index faba5db5a21..3e85570761a 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -68,13 +68,11 @@ use crate::{ events::{ EventsDecoder, RawEvent, - RuntimeEvent, }, frame::{ system::{ Phase, System, - SystemEvent, }, Event, }, @@ -403,36 +401,16 @@ pub struct ExtrinsicSuccess { /// Extrinsic hash. pub extrinsic: T::Hash, /// Raw runtime events, can be decoded by the caller. - pub events: Vec>, + 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_map(|evt| { - match evt { - RuntimeEvent::Raw(ref raw) - if raw.module == module && raw.variant == variant => - { - Some(raw) - } - _ => None, - } - }) - } - - /// Returns all System Events - pub fn system_events(&self) -> Vec<&SystemEvent> { self.events .iter() - .filter_map(|evt| { - match evt { - RuntimeEvent::System(evt) => Some(evt), - _ => None, - } - }) - .collect() + .find(|raw| raw.module == module && raw.variant == variant) } /// Find the Event for the given module/variant, attempting to decode the event data. @@ -486,7 +464,7 @@ pub async fn wait_for_block_events( } } } - Err(err) => return Err(err.into()), + Err(err) => return Err(err), } } } diff --git a/src/runtimes.rs b/src/runtimes.rs index 4e247d50489..0f236351580 100644 --- a/src/runtimes.rs +++ b/src/runtimes.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with substrate-subxt. If not, see . +use codec::Encode; use sp_runtime::{ generic::Header, traits::{ @@ -25,15 +26,45 @@ use sp_runtime::{ OpaqueExtrinsic, }; -use crate::frame::{ - balances::{ - AccountData, - Balances, +use crate::{ + extra::{ + DefaultExtra, + SignedExtra, }, - contracts::Contracts, - system::System, + frame::{ + balances::{ + AccountData, + Balances, + }, + contracts::Contracts, + sudo::Sudo, + system::System, + }, + Encoded, }; +/// Runtime trait. +pub trait Runtime: System + Sized + Send + Sync + 'static { + /// Signature type. + type Signature: Verify + Encode + Send + Sync + 'static; + /// Transaction extras. + type Extra: SignedExtra + Send + Sync + 'static; +} + +/// Extra type. +pub type Extra = <::Extra as SignedExtra>::Extra; + +/// UncheckedExtrinsic type. +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic< + ::Address, + Encoded, + ::Signature, + Extra, +>; + +/// SignedPayload type. +pub type SignedPayload = sp_runtime::generic::SignedPayload>; + /// Concrete type definitions compatible with those in the default substrate `node_runtime` /// /// # Note @@ -43,6 +74,11 @@ use crate::frame::{ #[derive(Debug, Clone, Eq, PartialEq)] pub struct DefaultNodeRuntime; +impl Runtime for DefaultNodeRuntime { + type Signature = MultiSignature; + type Extra = DefaultExtra; +} + impl System for DefaultNodeRuntime { type Index = u32; type BlockNumber = u32; @@ -61,6 +97,8 @@ impl Balances for DefaultNodeRuntime { impl Contracts for DefaultNodeRuntime {} +impl Sudo for DefaultNodeRuntime {} + /// Concrete type definitions compatible with the node template. /// /// # Note @@ -70,6 +108,11 @@ impl Contracts for DefaultNodeRuntime {} #[derive(Debug, Clone, Eq, PartialEq)] pub struct NodeTemplateRuntime; +impl Runtime for NodeTemplateRuntime { + type Signature = MultiSignature; + type Extra = DefaultExtra; +} + impl System for NodeTemplateRuntime { type Index = u32; type BlockNumber = u32; @@ -86,6 +129,8 @@ impl Balances for NodeTemplateRuntime { type Balance = u128; } +impl Sudo for NodeTemplateRuntime {} + /// Concrete type definitions compatible with those for kusama, v0.7 /// /// # Note @@ -95,6 +140,11 @@ impl Balances for NodeTemplateRuntime { #[derive(Debug, Clone, Eq, PartialEq)] pub struct KusamaRuntime; +impl Runtime for KusamaRuntime { + type Signature = MultiSignature; + type Extra = DefaultExtra; +} + impl System for KusamaRuntime { type Index = u32; type BlockNumber = u32; diff --git a/src/signer.rs b/src/signer.rs index d10dc9a3ba6..ebbcd416540 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -19,33 +19,26 @@ use crate::{ extra::SignedExtra, - frame::system::System, - Encoded, -}; -use codec::Encode; -use sp_core::Pair; -use sp_runtime::{ - generic::{ + runtimes::{ + Runtime, SignedPayload, UncheckedExtrinsic, }, - traits::{ - IdentifyAccount, - SignedExtension, - Verify, - }, +}; +use codec::Encode; +use sp_core::Pair; +use sp_runtime::traits::{ + IdentifyAccount, + SignedExtension, + Verify, }; use std::{ future::Future, - marker::PhantomData, pin::Pin, }; /// Extrinsic signer. -pub trait Signer> -where - <>::Extra as SignedExtension>::AdditionalSigned: Send + Sync, -{ +pub trait Signer { /// Returns the account id. fn account_id(&self) -> &T::AccountId; @@ -58,41 +51,30 @@ where /// refused the operation. fn sign( &self, - extrinsic: SignedPayload, - ) -> Pin< - Box< - dyn Future< - Output = Result< - UncheckedExtrinsic, - String, - >, - > + Send - + Sync, - >, - >; + extrinsic: SignedPayload, + ) -> Pin, String>> + Send + Sync>>; } /// Extrinsic signer using a private key. -pub struct PairSigner, P: Pair> { - _marker: PhantomData<(S, E)>, +pub struct PairSigner { account_id: T::AccountId, nonce: Option, signer: P, } -impl PairSigner +impl PairSigner where - T: System, - S: Encode + Verify + From, - S::Signer: From + IdentifyAccount, - E: SignedExtra, + T: Runtime, + T::Signature: From, + ::Signer: + From + IdentifyAccount, P: Pair, { /// Creates a new `Signer` from a `Pair`. pub fn new(signer: P) -> Self { - let account_id = S::Signer::from(signer.public()).into_account(); + let account_id = + ::Signer::from(signer.public()).into_account(); Self { - _marker: PhantomData, account_id, nonce: None, signer, @@ -115,15 +97,14 @@ where } } -impl Signer for PairSigner +impl Signer for PairSigner where - T: System + 'static, + T: Runtime, T::AccountId: Into + 'static, - S: Encode + 'static + Send + Sync, - E: SignedExtra + 'static, + <>::Extra as SignedExtension>::AdditionalSigned: + Send + Sync, P: Pair + 'static, - P::Signature: Into + 'static, - <>::Extra as SignedExtension>::AdditionalSigned: Send + Sync, + P::Signature: Into + 'static, { fn account_id(&self) -> &T::AccountId { &self.account_id @@ -135,25 +116,17 @@ where fn sign( &self, - extrinsic: SignedPayload, - ) -> Pin< - Box< - dyn Future< - Output = Result< - UncheckedExtrinsic, - String, - >, - > + Send - + Sync, - >, - > { + extrinsic: SignedPayload, + ) -> Pin, String>> + Send + Sync>> + { let signature = extrinsic.using_encoded(|payload| self.signer.sign(payload)); let (call, extra, _) = extrinsic.deconstruct(); - Box::pin(futures::future::ok(UncheckedExtrinsic::new_signed( + let extrinsic = UncheckedExtrinsic::::new_signed( call, self.account_id.clone().into(), signature.into(), extra, - ))) + ); + Box::pin(async move { Ok(extrinsic) }) } }