diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 20cd48ddc4..48d6fd2823 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,8 +16,7 @@ jobs: - name: setup run: | - rustup install nightly - rustup component add rustfmt --toolchain nightly + rustup install nightly --profile default - name: fmt run: cargo +nightly fmt --all -- --check diff --git a/CHANGELOG.md b/CHANGELOG.md index d76ca86423..9ba4cc4058 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# Version 0.8.0 (2020-05-26) + +* Update to Substrate release candidate [#116](https://github.com/paritytech/substrate-subxt/pull/116) +* Update to alpha.8 [#114](https://github.com/paritytech/substrate-subxt/pull/114) +* Refactors the api [#113](https://github.com/paritytech/substrate-subxt/pull/113) + # Version 0.7.0 (2020-05-13) * Split subxt [#102](https://github.com/paritytech/substrate-subxt/pull/102) diff --git a/Cargo.toml b/Cargo.toml index 2b11669bbe..9b98fb18e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "proc-macro"] [package] name = "substrate-subxt" -version = "0.7.0" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" @@ -27,22 +27,22 @@ serde_json = "1.0" url = "2.1" codec = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive", "full"] } -frame-metadata = { version = "11.0.0-alpha.8", package = "frame-metadata" } -frame-support = { version = "2.0.0-alpha.8", package = "frame-support" } -sp-runtime = { version = "2.0.0-alpha.8", package = "sp-runtime" } -sp-version = { version = "2.0.0-alpha.8", package = "sp-version" } -pallet-indices = { version = "2.0.0-alpha.8", package = "pallet-indices" } +frame-metadata = { version = "11.0.0-rc2", package = "frame-metadata" } +frame-support = { version = "2.0.0-rc2", package = "frame-support" } +sp-runtime = { version = "2.0.0-rc2", package = "sp-runtime" } +sp-version = { version = "2.0.0-rc2", package = "sp-version" } +pallet-indices = { version = "2.0.0-rc2", package = "pallet-indices" } hex = "0.4.0" -sp-rpc = { version = "2.0.0-alpha.8", package = "sp-rpc" } -sp-core = { version = "2.0.0-alpha.8", package = "sp-core" } -sc-rpc-api = { version = "0.8.0-alpha.8", package = "sc-rpc-api" } -sp-transaction-pool = { version = "2.0.0-alpha.8", package = "sp-transaction-pool" } -substrate-subxt-proc-macro = { version = "0.7.0", path = "proc-macro" } +sp-rpc = { version = "2.0.0-rc2", package = "sp-rpc" } +sp-core = { version = "2.0.0-rc2", package = "sp-core" } +sc-rpc-api = { version = "0.8.0-rc2", package = "sc-rpc-api" } +sp-transaction-pool = { version = "2.0.0-rc2", package = "sp-transaction-pool" } +substrate-subxt-proc-macro = { version = "0.8.0", path = "proc-macro" } [dev-dependencies] async-std = { version = "1.5.0", features = ["attributes"] } env_logger = "0.7" wabt = "0.9" -frame-system = { version = "2.0.0-alpha.8", package = "frame-system" } -pallet-balances = { version = "2.0.0-alpha.8", package = "pallet-balances" } -sp-keyring = { version = "2.0.0-alpha.8", package = "sp-keyring" } +frame-system = { version = "2.0.0-rc2", package = "frame-system" } +pallet-balances = { version = "2.0.0-rc2", package = "pallet-balances" } +sp-keyring = { version = "2.0.0-rc2", package = "sp-keyring" } diff --git a/examples/kusama_balance_transfer.rs b/examples/kusama_balance_transfer.rs index 646edf4e7f..dba35b8771 100644 --- a/examples/kusama_balance_transfer.rs +++ b/examples/kusama_balance_transfer.rs @@ -19,22 +19,18 @@ use substrate_subxt::{ balances::*, ClientBuilder, KusamaRuntime, + PairSigner, }; #[async_std::main] async fn main() -> Result<(), Box> { env_logger::init(); - let signer = AccountKeyring::Alice.pair(); + let signer = PairSigner::new(AccountKeyring::Alice.pair()); let dest = AccountKeyring::Bob.to_account_id().into(); let client = ClientBuilder::::new().build().await?; - - let hash = client - .xt(signer, None) - .await? - .transfer(&dest, 10_000) - .await?; + let hash = client.transfer(&signer, &dest, 10_000).await?; println!("Balance transfer extrinsic submitted: {}", hash); diff --git a/examples/submit_and_watch.rs b/examples/submit_and_watch.rs index ede981bc29..81701f7383 100644 --- a/examples/submit_and_watch.rs +++ b/examples/submit_and_watch.rs @@ -19,22 +19,18 @@ use substrate_subxt::{ balances::*, ClientBuilder, DefaultNodeRuntime, + PairSigner, }; #[async_std::main] async fn main() -> Result<(), Box> { env_logger::init(); - let signer = AccountKeyring::Alice.pair(); + let signer = PairSigner::new(AccountKeyring::Alice.pair()); let dest = AccountKeyring::Bob.to_account_id().into(); let client = ClientBuilder::::new().build().await?; - let result = client - .xt(signer, None) - .await? - .watch() - .transfer(&dest, 10_000) - .await?; + let result = client.transfer_and_watch(&signer, &dest, 10_000).await?; if let Some(event) = result.transfer()? { println!("Balance transfer success: value: {:?}", event.amount); diff --git a/proc-macro/Cargo.toml b/proc-macro/Cargo.toml index ed1625133e..44639ea14a 100644 --- a/proc-macro/Cargo.toml +++ b/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-subxt-proc-macro" -version = "0.7.0" +version = "0.8.0" authors = ["David Craven ", "Parity Technologies "] edition = "2018" autotests = false @@ -27,7 +27,7 @@ async-std = { version = "1.5.0", features = ["attributes"] } codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } env_logger = "0.7.1" pretty_assertions = "0.6.1" -sp-keyring = "2.0.0-alpha.8" +sp-keyring = "2.0.0-rc2" substrate-subxt = { path = ".." } trybuild = "1.0.25" diff --git a/proc-macro/src/call.rs b/proc-macro/src/call.rs index 9ccb419b87..234edbd8dd 100644 --- a/proc-macro/src/call.rs +++ b/proc-macro/src/call.rs @@ -28,6 +28,7 @@ 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); @@ -43,26 +44,9 @@ pub fn call(s: Structure) -> TokenStream { let filtered_fields = utils::filter_fields(&fields, &marker); let args = utils::fields_to_args(&filtered_fields); let build_struct = utils::build_struct(ident, &fields); - let xt_builder = generate_trait( - &module, - &call_name, - "XtBuilder", - quote!(&'a self), - quote!(T::Hash), - &args, - &build_struct, - &marker, - ); - let events_subscriber = generate_trait( - &module, - &call_name, - "EventsSubscriber", - quote!(self), - quote!(#subxt::ExtrinsicSuccess), - &args, - &build_struct, - &marker, - ); + let call_trait = format_ident!("{}CallExt", call_name.to_camel_case()); + let call = format_ident!("{}", call_name); + let call_and_watch = format_ident!("{}_and_watch", call_name); quote! { impl#generics #subxt::Call for #ident<#(#params),*> { @@ -76,52 +60,45 @@ pub fn call(s: Structure) -> TokenStream { } } - #xt_builder - - #events_subscriber - } -} - -pub fn generate_trait( - module: &syn::Path, - call: &str, - ty: &str, - me: TokenStream, - ret: TokenStream, - args: &TokenStream, - build_struct: &TokenStream, - marker: &syn::Ident, -) -> TokenStream { - let subxt = utils::use_crate("substrate-subxt"); - let codec = utils::use_crate("parity-scale-codec"); - let call_trait = format_ident!("{}Call{}", call.to_camel_case(), ty); - let call = format_ident!("{}", call); - let ty = format_ident!("{}", ty); - quote! { /// Call extension trait. - pub trait #call_trait { - /// Create and submit the extrinsic. + pub trait #call_trait> { + /// Create and submit an extrinsic. fn #call<'a>( - #me, + &'a self, + signer: &'a (dyn #subxt::Signer + Send + Sync), #args - ) -> core::pin::Pin> + Send + 'a>>; + ) -> 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), + #args + ) -> core::pin::Pin, #subxt::Error>> + Send + 'a>>; } - impl #call_trait for #subxt::#ty + impl #call_trait for #subxt::Client where T: #module + #subxt::system::System + Send + Sync + 'static, - P: #subxt::sp_core::Pair, - S: #subxt::sp_runtime::traits::Verify + #codec::Codec + From + Send + 'static, - S::Signer: From + #subxt::sp_runtime::traits::IdentifyAccount, - T::Address: From, - E: #subxt::SignedExtra + #subxt::sp_runtime::traits::SignedExtension + 'static, + S: #codec::Encode + Send + Sync + 'static, + E: #subxt::SignedExtra + #subxt::sp_runtime::traits::SignedExtension + Send + Sync + 'static, { fn #call<'a>( - #me, + &'a self, + signer: &'a (dyn #subxt::Signer + Send + Sync), #args - ) -> core::pin::Pin> + Send + 'a>> { + ) -> core::pin::Pin> + Send + 'a>> { let #marker = core::marker::PhantomData::; - Box::pin(self.submit(#build_struct)) + Box::pin(self.submit(#build_struct, signer)) + } + + fn #call_and_watch<'a>( + &'a self, + signer: &'a (dyn #subxt::Signer + Send + Sync), + #args + ) -> core::pin::Pin, #subxt::Error>> + Send + 'a>> { + let #marker = core::marker::PhantomData::; + Box::pin(self.watch(#build_struct, signer)) } } } @@ -154,62 +131,48 @@ mod tests { } /// Call extension trait. - pub trait TransferCallXtBuilder { - /// Create and submit the extrinsic. + pub trait TransferCallExt> { + /// Create and submit an extrinsic. fn transfer<'a>( &'a self, + signer: &'a (dyn substrate_subxt::Signer + Send + Sync), to: &'a ::Address, amount: T::Balance, ) -> core::pin::Pin> + Send + 'a>>; - } - impl TransferCallXtBuilder for substrate_subxt::XtBuilder - where - T: Balances + substrate_subxt::system::System + Send + Sync + 'static, - P: substrate_subxt::sp_core::Pair, - S: substrate_subxt::sp_runtime::traits::Verify + codec::Codec + From + Send + 'static, - S::Signer: From + substrate_subxt::sp_runtime::traits::IdentifyAccount< - AccountId = T::AccountId>, - T::Address: From, - E: substrate_subxt::SignedExtra + substrate_subxt::sp_runtime::traits::SignedExtension + 'static, - { - fn transfer<'a>( + /// Create, submit and watch an extrinsic. + fn transfer_and_watch<'a>( &'a self, - to: &'a ::Address, - amount: T::Balance, - ) -> core::pin::Pin> + Send + 'a>> { - let _ = core::marker::PhantomData::; - Box::pin(self.submit(TransferCall { to, amount, })) - } - } - - /// Call extension trait. - pub trait TransferCallEventsSubscriber { - /// Create and submit the extrinsic. - fn transfer<'a>( - self, + signer: &'a (dyn substrate_subxt::Signer + Send + Sync), to: &'a ::Address, amount: T::Balance, ) -> core::pin::Pin, substrate_subxt::Error>> + Send + 'a>>; } - impl TransferCallEventsSubscriber for substrate_subxt::EventsSubscriber + impl TransferCallExt for substrate_subxt::Client where T: Balances + substrate_subxt::system::System + Send + Sync + 'static, - P: substrate_subxt::sp_core::Pair, - S: substrate_subxt::sp_runtime::traits::Verify + codec::Codec + From + Send + 'static, - S::Signer: From + substrate_subxt::sp_runtime::traits::IdentifyAccount< - AccountId = T::AccountId>, - T::Address: From, - E: substrate_subxt::SignedExtra + substrate_subxt::sp_runtime::traits::SignedExtension + 'static, + S: codec::Encode + Send + Sync + 'static, + E: substrate_subxt::SignedExtra + substrate_subxt::sp_runtime::traits::SignedExtension + Send + Sync + 'static, { fn transfer<'a>( - self, + &'a self, + signer: &'a (dyn substrate_subxt::Signer + Send + Sync), + to: &'a ::Address, + amount: T::Balance, + ) -> core::pin::Pin> + Send + 'a>> { + let _ = core::marker::PhantomData::; + Box::pin(self.submit(TransferCall { to, amount, }, signer)) + } + + fn transfer_and_watch<'a>( + &'a self, + signer: &'a (dyn substrate_subxt::Signer + Send + Sync), to: &'a ::Address, amount: T::Balance, ) -> core::pin::Pin, substrate_subxt::Error>> + Send + 'a>> { let _ = core::marker::PhantomData::; - Box::pin(self.submit(TransferCall { to, amount, })) + Box::pin(self.watch(TransferCall { to, amount, }, signer)) } } }; diff --git a/proc-macro/src/test.rs b/proc-macro/src/test.rs index 5a8f7a29d1..f625ad68c6 100644 --- a/proc-macro/src/test.rs +++ b/proc-macro/src/test.rs @@ -228,7 +228,7 @@ impl Test { let prelude = prelude.map(|block| block.stmts).unwrap_or_default(); let step = steps .into_iter() - .map(|step| step.into_tokens(&account, state.as_ref())); + .map(|step| step.into_tokens(state.as_ref())); quote! { #[async_std::test] #[ignore] @@ -236,6 +236,8 @@ impl Test { #env_logger let client = #subxt::ClientBuilder::<#runtime, #signature, #extra>::new() .build().await.unwrap(); + let signer = #subxt::PairSigner::new(#sp_keyring::AccountKeyring::#account.pair()); + #[allow(unused)] let alice = #sp_keyring::AccountKeyring::Alice.to_account_id(); #[allow(unused)] @@ -304,12 +306,7 @@ impl From for Step { } impl Step { - fn into_tokens( - self, - account: &syn::Ident, - test_state: Option<&State>, - ) -> TokenStream { - let sp_keyring = utils::use_crate("sp-keyring"); + fn into_tokens(self, test_state: Option<&State>) -> TokenStream { let Step { state, call, @@ -359,14 +356,11 @@ impl Step { }); let assert = assert.map(|block| block.stmts).unwrap_or_default(); quote! { - let xt = client.xt(#sp_keyring::AccountKeyring::#account.pair(), None).await.unwrap(); - #pre #[allow(unused)] - let result = xt - .watch() - .submit(#call) + let result = client + .watch(#call, &signer) .await .unwrap(); @@ -461,6 +455,7 @@ mod tests { substrate_subxt::sp_runtime::MultiSignature, substrate_subxt::DefaultExtra >::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(); #[allow(unused)] @@ -475,8 +470,6 @@ mod tests { let ferdie = sp_keyring::AccountKeyring::Ferdie.to_account_id(); { - let xt = client.xt(sp_keyring::AccountKeyring::Alice.pair(), None).await.unwrap(); - struct State { alice: A, bob: B, @@ -495,12 +488,11 @@ mod tests { }; #[allow(unused)] - let result = xt - .watch() - .submit(TransferCall { + let result = client + .watch(TransferCall { to: &bob, amount: 10_000, - }) + }, &signer) .await .unwrap(); diff --git a/proc-macro/tests/balances.rs b/proc-macro/tests/balances.rs index a600277c16..062d4be74f 100644 --- a/proc-macro/tests/balances.rs +++ b/proc-macro/tests/balances.rs @@ -36,6 +36,7 @@ use substrate_subxt::{ }, ClientBuilder, KusamaRuntime, + PairSigner, }; #[module] @@ -112,6 +113,7 @@ subxt_test!({ async fn transfer_balance_example() -> Result<(), Box> { env_logger::init(); let client = ClientBuilder::::new().build().await?; + let signer = PairSigner::new(AccountKeyring::Alice.pair()); let alice = AccountKeyring::Alice.to_account_id(); let bob = AccountKeyring::Bob.to_account_id(); @@ -119,13 +121,12 @@ async fn transfer_balance_example() -> Result<(), Box> { let bob_account = client.account(&bob).await?; let pre = (alice_account, bob_account); - let builder = client.xt(AccountKeyring::Alice.pair(), None).await?; + let _hash = client + .transfer(&signer, &bob.clone().into(), 10_000) + .await?; - let _hash = builder.transfer(&bob.clone().into(), 10_000).await?; - - let result = builder - .watch() - .transfer(&bob.clone().into(), 10_000) + let result = client + .transfer_and_watch(&signer, &bob.clone().into(), 10_000) .await?; assert_eq!( diff --git a/src/extrinsic.rs b/src/extra.rs similarity index 79% rename from src/extrinsic.rs rename to src/extra.rs index 8dedf70386..b7f4cc6ce3 100644 --- a/src/extrinsic.rs +++ b/src/extra.rs @@ -15,7 +15,6 @@ // along with substrate-subxt. If not, see . use codec::{ - Codec, Decode, Encode, }; @@ -23,18 +22,9 @@ use core::{ fmt::Debug, marker::PhantomData, }; -use sp_core::Pair; use sp_runtime::{ - generic::{ - Era, - SignedPayload, - UncheckedExtrinsic, - }, - traits::{ - IdentifyAccount, - SignedExtension, - Verify, - }, + generic::Era, + traits::SignedExtension, transaction_validity::TransactionValidityError, }; @@ -52,19 +42,51 @@ use crate::frame::{ /// /// 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)] -pub struct CheckVersion( +pub struct CheckSpecVersion( pub PhantomData, /// Local version to be used for `AdditionalSigned` #[codec(skip)] pub u32, ); -impl SignedExtension for CheckVersion +impl SignedExtension for CheckSpecVersion where T: System + Clone + Debug + Eq + Send + Sync, { - const IDENTIFIER: &'static str = "CheckVersion"; + 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)] +pub struct CheckTxVersion( + pub PhantomData, + /// Local version to be used for `AdditionalSigned` + #[codec(skip)] + pub u32, +); + +impl SignedExtension for CheckTxVersion +where + T: System + Clone + Debug + Eq + Send + Sync, +{ + const IDENTIFIER: &'static str = "CheckTxVersion"; type AccountId = u64; type Call = (); type AdditionalSigned = u32; @@ -199,33 +221,18 @@ where } } -/// Checks if a transaction would exhausts the block gas limit. -#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] -pub struct CheckBlockGasLimit(pub PhantomData); - -impl SignedExtension for CheckBlockGasLimit -where - T: System + Clone + Debug + Eq + Send + Sync, -{ - const IDENTIFIER: &'static str = "CheckBlockGasLimit"; - 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 { /// The type the extras. type Extra: SignedExtension; /// Creates a new `SignedExtra`. - fn new(version: u32, nonce: T::Index, genesis_hash: T::Hash) -> Self; + 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; @@ -234,7 +241,8 @@ pub trait SignedExtra { /// Default `SignedExtra` for substrate runtimes. #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct DefaultExtra { - version: u32, + spec_version: u32, + tx_version: u32, nonce: T::Index, genesis_hash: T::Hash, } @@ -243,18 +251,24 @@ impl SignedExtra for DefaultExtra { type Extra = ( - CheckVersion, + CheckSpecVersion, + CheckTxVersion, CheckGenesis, CheckEra, CheckNonce, CheckWeight, ChargeTransactionPayment, - CheckBlockGasLimit, ); - fn new(version: u32, nonce: T::Index, genesis_hash: T::Hash) -> Self { + fn new( + spec_version: u32, + tx_version: u32, + nonce: T::Index, + genesis_hash: T::Hash, + ) -> Self { DefaultExtra { - version, + spec_version, + tx_version, nonce, genesis_hash, } @@ -262,13 +276,13 @@ impl SignedExtra fn extra(&self) -> Self::Extra { ( - CheckVersion(PhantomData, self.version), + CheckSpecVersion(PhantomData, self.spec_version), + CheckTxVersion(PhantomData, self.tx_version), CheckGenesis(PhantomData, self.genesis_hash), CheckEra((Era::Immortal, PhantomData), self.genesis_hash), CheckNonce(self.nonce), CheckWeight(PhantomData), ChargeTransactionPayment(::Balance::default()), - CheckBlockGasLimit(PhantomData), ) } } @@ -289,32 +303,3 @@ impl SignedExtension self.extra().additional_signed() } } - -pub(crate) fn create_and_sign( - signer: P, - call: C, - extra: E, -) -> Result< - UncheckedExtrinsic>::Extra>, - TransactionValidityError, -> -where - P: Pair, - S: Verify + Codec + From, - S::Signer: From + IdentifyAccount, - C: Encode, - E: SignedExtra + SignedExtension, - T::Address: From, -{ - let raw_payload = SignedPayload::new(call, extra.extra())?; - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let (call, extra, _) = raw_payload.deconstruct(); - let account_id = S::Signer::from(signer.public()).into_account(); - - Ok(UncheckedExtrinsic::new_signed( - call, - account_id.into(), - signature.into(), - extra, - )) -} diff --git a/src/lib.rs b/src/lib.rs index 1c6bc8bc5e..9d38535627 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,46 +45,36 @@ extern crate substrate_subxt_proc_macro; pub use sp_core; pub use sp_runtime; -use std::{ - convert::TryFrom, - marker::PhantomData, -}; - -use codec::{ - Codec, - Encode, -}; +use codec::Encode; use futures::future; use jsonrpsee::client::Subscription; use sc_rpc_api::state::ReadProof; -use sp_core::{ - storage::{ - StorageChangeSet, - StorageKey, - }, - Pair, +use sp_core::storage::{ + StorageChangeSet, + StorageKey, }; use sp_runtime::{ generic::{ SignedPayload, UncheckedExtrinsic, }, - traits::{ - IdentifyAccount, - SignedExtension, - Verify, - }, + traits::SignedExtension, MultiSignature, }; use sp_version::RuntimeVersion; +use std::{ + convert::TryFrom, + marker::PhantomData, +}; mod error; mod events; -mod extrinsic; +mod extra; mod frame; mod metadata; mod rpc; mod runtimes; +mod signer; pub use crate::{ error::Error, @@ -93,7 +83,7 @@ pub use crate::{ EventsError, RawEvent, }, - extrinsic::*, + extra::*, frame::*, metadata::{ Metadata, @@ -104,17 +94,15 @@ pub use crate::{ ExtrinsicSuccess, }, runtimes::*, + signer::*, substrate_subxt_proc_macro::*, }; use crate::{ - frame::{ - balances::Balances, - system::{ - AccountStoreExt, - Phase, - System, - SystemEvent, - }, + frame::system::{ + AccountStoreExt, + Phase, + System, + SystemEvent, }, rpc::{ ChainBlock, @@ -283,28 +271,6 @@ impl Client { Ok(proof) } - /// Create and submit an extrinsic and return corresponding Hash if successful - pub async fn submit_extrinsic( - &self, - extrinsic: X, - ) -> Result { - let xt_hash = self.rpc.submit_extrinsic(extrinsic).await?; - Ok(xt_hash) - } - - /// Create and submit an extrinsic and return corresponding Event if successful - pub async fn submit_and_watch_extrinsic( - self, - extrinsic: X, - decoder: EventsDecoder, - ) -> Result, Error> { - let success = self - .rpc - .submit_and_watch_extrinsic(extrinsic, decoder) - .await?; - Ok(success) - } - /// Subscribe to events. pub async fn subscribe_events( &self, @@ -330,190 +296,107 @@ impl Client { impl Client where - T: System + Balances + Send + Sync + 'static, - S: 'static, - E: SignedExtra + SignedExtension + 'static, + T: System + Send + Sync + 'static, + S: Encode + Send + Sync + 'static, + E: SignedExtra + SignedExtension + Send + Sync + 'static, { - /// Creates raw payload to be signed for the supplied `Call` without private key - pub async fn create_raw_payload>( + /// Creates an unsigned extrinsic. + /// + /// If `nonce` is `None` the nonce will be fetched from the chain. + pub async fn create_unsigned>( &self, - account_id: &::AccountId, call: C, + account_id: &::AccountId, + nonce: Option, ) -> Result>::Extra>, Error> { - let account_nonce = self.account(account_id).await?.nonce; - let version = self.runtime_version.spec_version; + let account_nonce = if let Some(nonce) = nonce { + nonce + } else { + self.account(account_id).await?.nonce + }; + 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(version, account_nonce, genesis_hash); + let extra: E = E::new(spec_version, tx_version, account_nonce, genesis_hash); let raw_payload = SignedPayload::new(call, extra.extra())?; Ok(raw_payload) } - /// Create a transaction builder for a private key. - pub async fn xt

( - &self, - signer: P, - nonce: Option, - ) -> Result, Error> - where - P: Pair, - P::Signature: Codec, - S: Verify, - S::Signer: From + IdentifyAccount, - { - let account_id = S::Signer::from(signer.public()).into_account(); - let nonce = match nonce { - Some(nonce) => nonce, - None => self.account(&account_id).await?.nonce, - }; - - let genesis_hash = self.genesis_hash; - let runtime_version = self.runtime_version.clone(); - Ok(XtBuilder { - client: self.clone(), - nonce, - runtime_version, - genesis_hash, - signer, - }) - } -} - -/// Transaction builder. -#[derive(Clone)] -pub struct XtBuilder { - client: Client, - nonce: T::Index, - runtime_version: RuntimeVersion, - genesis_hash: T::Hash, - signer: P, -} - -impl XtBuilder { - /// Returns the chain metadata. - pub fn metadata(&self) -> &Metadata { - self.client.metadata() - } - - /// Returns the nonce. - pub fn nonce(&self) -> T::Index { - self.nonce - } - - /// Sets the nonce to a new value. - pub fn set_nonce(&mut self, nonce: T::Index) -> &mut XtBuilder { - self.nonce = nonce; - self - } - - /// Increment the nonce - pub fn increment_nonce(&mut self) -> &mut XtBuilder { - self.set_nonce(self.nonce() + 1.into()); - self - } -} - -impl XtBuilder -where - P: Pair, - S: Verify + Codec + From, - S::Signer: From + IdentifyAccount, - T::Address: From, - E: SignedExtra + SignedExtension + 'static, -{ - /// Creates and signs an Extrinsic for the supplied `Call` - pub fn create_and_sign>( + /// Creates a signed extrinsic. + pub async fn create_signed>( &self, call: C, + signer: &(dyn Signer + Send + Sync), ) -> Result< UncheckedExtrinsic>::Extra>, Error, > { - let signer = self.signer.clone(); - let account_nonce = self.nonce; - let version = self.runtime_version.spec_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 unsigned = self + .create_unsigned(call, signer.account_id(), signer.nonce()) + .await?; + Ok(signer.sign(unsigned)) + } - log::info!( - "Creating Extrinsic with genesis hash {:?} and account nonce {:?}", - genesis_hash, - account_nonce - ); + /// Returns an events decoder for a call. + pub fn events_decoder>(&self) -> Result, Error> { + let metadata = self.metadata().clone(); + let mut decoder = EventsDecoder::try_from(metadata)?; + C::events_decoder(&mut decoder)?; + Ok(decoder) + } - let extra = E::new(version, account_nonce, genesis_hash); - let xt = extrinsic::create_and_sign::<_, _, _, S, _>(signer, call, extra)?; - Ok(xt) + /// Create and submit an extrinsic and return corresponding Hash if successful + pub async fn submit_extrinsic( + &self, + extrinsic: UncheckedExtrinsic< + T::Address, + Encoded, + S, + >::Extra, + >, + ) -> Result { + self.rpc.submit_extrinsic(extrinsic).await + } + + /// 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, + >, + decoder: EventsDecoder, + ) -> Result, Error> { + self.rpc + .submit_and_watch_extrinsic(extrinsic, decoder) + .await } /// Submits a transaction to the chain. - pub async fn submit>(&self, call: C) -> Result { - let extrinsic = self.create_and_sign(call)?; - let xt_hash = self.client.submit_extrinsic(extrinsic).await?; - Ok(xt_hash) + pub async fn submit>( + &self, + call: C, + signer: &(dyn Signer + Send + Sync), + ) -> Result { + let extrinsic = self.create_signed(call, signer).await?; + self.submit_extrinsic(extrinsic).await } /// Submits transaction to the chain and watch for events. - pub fn watch(self) -> EventsSubscriber { - let metadata = self.client.metadata().clone(); - let decoder = EventsDecoder::try_from(metadata).map_err(Into::into); - EventsSubscriber { - client: self.client.clone(), - builder: self, - decoder, - } - } -} - -/// Submits an extrinsic and subscribes to the triggered events -pub struct EventsSubscriber { - client: Client, - builder: XtBuilder, - decoder: Result, EventsError>, -} - -impl EventsSubscriber { - /// Access the events decoder for registering custom type sizes - pub fn events_decoder< - F: FnOnce(&mut EventsDecoder) -> Result, - >( - self, - f: F, - ) -> Self { - let mut this = self; - if let Ok(ref mut decoder) = this.decoder { - if let Err(err) = f(decoder) { - this.decoder = Err(err) - } - } - this - } -} - -impl EventsSubscriber -where - P: Pair, - S: Verify + Codec + From, - S::Signer: From + IdentifyAccount, - T::Address: From, - E: SignedExtra + SignedExtension + 'static, -{ - /// Submits transaction to the chain and watch for events. - pub async fn submit>(self, call: C) -> Result, Error> { - let mut decoder = self.decoder?; - C::events_decoder(&mut decoder)?; - let extrinsic = self.builder.create_and_sign(call)?; - let xt_success = self - .client - .submit_and_watch_extrinsic(extrinsic, decoder) - .await?; - Ok(xt_success) + pub async fn watch>( + &self, + call: C, + signer: &(dyn Signer + Send + Sync), + ) -> Result, Error> { + let extrinsic = self.create_signed(call, signer).await?; + let decoder = self.events_decoder::()?; + self.submit_and_watch_extrinsic(extrinsic, decoder).await } } @@ -530,9 +413,12 @@ impl codec::Encode for Encoded { #[cfg(test)] mod tests { - use sp_core::storage::{ - well_known_keys, - StorageKey, + use sp_core::{ + storage::{ + well_known_keys, + StorageKey, + }, + Pair, }; use sp_keyring::{ AccountKeyring, @@ -552,25 +438,37 @@ mod tests { #[ignore] // requires locally running substrate node async fn test_tx_transfer_balance() { env_logger::try_init().ok(); - let signer = AccountKeyring::Alice.pair(); + let mut signer = PairSigner::new(AccountKeyring::Alice.pair()); let dest = AccountKeyring::Bob.to_account_id().into(); let client = test_client().await; - let mut xt = client.xt(signer, None).await.unwrap(); - let _ = xt - .submit(balances::TransferCall { - to: &dest, - amount: 10_000, - }) + let nonce = client + .account(&AccountKeyring::Alice.to_account_id()) + .await + .unwrap() + .nonce; + signer.set_nonce(nonce); + client + .submit( + balances::TransferCall { + to: &dest, + amount: 10_000, + }, + &signer, + ) .await .unwrap(); // check that nonce is handled correctly - xt.increment_nonce() - .submit(balances::TransferCall { - to: &dest, - amount: 10_000, - }) + signer.increment_nonce(); + client + .submit( + balances::TransferCall { + to: &dest, + amount: 10_000, + }, + &signer, + ) .await .unwrap(); } @@ -634,12 +532,13 @@ mod tests { // create raw payload with AccoundId and sign it let raw_payload = client - .create_raw_payload( - &signer_account_id, + .create_unsigned( balances::TransferCall { to: &dest, amount: 10_000, }, + &signer_account_id, + None, ) .await .unwrap(); @@ -647,12 +546,16 @@ mod tests { let raw_multisig = MultiSignature::from(raw_signature); // create signature with Xtbuilder - let xt = client.xt(signer_pair.clone(), None).await.unwrap(); - let xt_multi_sig = xt - .create_and_sign(balances::TransferCall { - to: &dest, - amount: 10_000, - }) + let signer = PairSigner::new(Ed25519Keyring::Alice.pair()); + let xt_multi_sig = client + .create_signed( + balances::TransferCall { + to: &dest, + amount: 10_000, + }, + &signer, + ) + .await .unwrap() .signature .unwrap() diff --git a/src/rpc.rs b/src/rpc.rs index b6bef3f34f..c5898bf9b5 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -342,7 +342,7 @@ impl Rpc { /// Create and submit an extrinsic and return corresponding Event if successful pub async fn submit_and_watch_extrinsic( - self, + &self, extrinsic: E, decoder: EventsDecoder, ) -> Result, Error> { diff --git a/src/signer.rs b/src/signer.rs new file mode 100644 index 0000000000..b33b3fdd33 --- /dev/null +++ b/src/signer.rs @@ -0,0 +1,122 @@ +// 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 . + +//! A library to **sub**mit e**xt**rinsics to a +//! [substrate](https://github.com/paritytech/substrate) node via RPC. + +use crate::{ + extra::SignedExtra, + frame::system::System, + Encoded, +}; +use codec::Encode; +use sp_core::Pair; +use sp_runtime::{ + generic::{ + SignedPayload, + UncheckedExtrinsic, + }, + traits::{ + IdentifyAccount, + Verify, + }, +}; +use std::marker::PhantomData; + +/// Extrinsic signer. +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. + fn sign( + &self, + extrinsic: SignedPayload, + ) -> UncheckedExtrinsic; +} + +/// Extrinsic signer using a private key. +pub struct PairSigner, P: Pair> { + _marker: PhantomData<(S, E)>, + account_id: T::AccountId, + nonce: Option, + signer: P, +} + +impl PairSigner +where + T: System, + S: Encode + Verify + From, + S::Signer: From + IdentifyAccount, + E: SignedExtra, + 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(); + Self { + _marker: PhantomData, + 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 + 1.into()); + } +} + +impl Signer for PairSigner +where + T: System, + T::AccountId: Into, + S: Encode, + E: SignedExtra, + P: Pair, + P::Signature: Into, +{ + fn account_id(&self) -> &T::AccountId { + &self.account_id + } + + fn nonce(&self) -> Option { + self.nonce + } + + fn sign( + &self, + extrinsic: SignedPayload, + ) -> UncheckedExtrinsic { + let signature = extrinsic.using_encoded(|payload| self.signer.sign(payload)); + let (call, extra, _) = extrinsic.deconstruct(); + UncheckedExtrinsic::new_signed( + call, + self.account_id.clone().into(), + signature.into(), + extra, + ) + } +}