From 90cb9fc4cc7ae293fc7c46454ff0aee6f73a1cc7 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 3 Oct 2019 18:17:28 +0100 Subject: [PATCH] Remove runtime dependency (#21) * Remove System::Event type * WIP: removing requirement for concrete Runtime impl for extras * Use built in substrate types for constructing/signing the extrinsic * Implement SignedExtension for copied types * Fix remaining compilation errors * Allow plugging in of predefined runtime types with blanket impls * rustfmt * Add CheckBlockGasLimit * Construct DefaultExtrinsic in XtBuilder * Derive Debug for extras * Replace SignedExtension macro with explicit implementations * Replace Lookup associated type with Address * Move balances and contracts to main deps --- Cargo.toml | 4 +- src/error.rs | 4 + src/extrinsic.rs | 263 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 97 +++------------- src/srml/balances.rs | 11 +- src/srml/contracts.rs | 15 ++- src/srml/system.rs | 37 +++--- 7 files changed, 328 insertions(+), 103 deletions(-) create mode 100644 src/extrinsic.rs diff --git a/Cargo.toml b/Cargo.toml index 9c0490023d..5f183328aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,8 @@ runtime_primitives = { git = "https://github.com/paritytech/substrate/", package serde = { version = "1.0", features = ["derive"] } sr-version = { git = "https://github.com/paritytech/substrate/", package = "sr-version" } srml-system = { git = "https://github.com/paritytech/substrate/", package = "srml-system" } +srml-balances = { git = "https://github.com/paritytech/substrate/", package = "srml-balances" } +srml-contracts = { git = "https://github.com/paritytech/substrate/", package = "srml-contracts" } substrate-rpc-api = { git = "https://github.com/paritytech/substrate/", package = "substrate-rpc-api" } substrate-rpc-primitives = { git = "https://github.com/paritytech/substrate/", package = "substrate-rpc-primitives" } substrate-primitives = { git = "https://github.com/paritytech/substrate/", package = "substrate-primitives" } @@ -34,8 +36,6 @@ url = "1.7" [dev-dependencies] env_logger = "0.6" node-runtime = { git = "https://github.com/paritytech/substrate/", package = "node-runtime" } -srml-balances = { git = "https://github.com/paritytech/substrate/", package = "srml-balances" } -srml-contracts = { git = "https://github.com/paritytech/substrate/", package = "srml-contracts" } substrate-keyring = { git = "https://github.com/paritytech/substrate/", package = "substrate-keyring" } tokio = "0.1" wabt = "0.9.0" diff --git a/src/error.rs b/src/error.rs index 9557bcb6a2..9e9cfb98cb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,6 +20,7 @@ use crate::{ }; use jsonrpc_core_client::RpcError; use parity_scale_codec::Error as CodecError; +use runtime_primitives::transaction_validity::TransactionValidityError; use std::io::Error as IoError; use substrate_primitives::crypto::SecretStringError; @@ -39,6 +40,9 @@ pub enum Error { SecretString(SecretStringError), /// Metadata error. Metadata(MetadataError), + /// Extrinsic validity error + #[display(fmt = "Transaction Validity Error: {:?}", _0)] + Invalid(TransactionValidityError), /// Other error. Other(String), } diff --git a/src/extrinsic.rs b/src/extrinsic.rs new file mode 100644 index 0000000000..1f51cfdc2f --- /dev/null +++ b/src/extrinsic.rs @@ -0,0 +1,263 @@ +// Copyright 2019 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 . + +use crate::srml::{ + balances::Balances, + system::System, +}; +use parity_scale_codec::{ + Codec, + Decode, + Encode, +}; +use runtime_primitives::{ + generic::{ + Era, + SignedPayload, + UncheckedExtrinsic, + }, + traits::SignedExtension, + transaction_validity::TransactionValidityError, +}; +use std::marker::PhantomData; +use substrate_primitives::Pair; + +/// SignedExtra checks copied from substrate, in order to remove requirement to implement +/// substrate's `srml_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()`. +#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] +pub struct CheckVersion( + PhantomData, + /// Local version to be used for `AdditionalSigned` + #[codec(skip)] + u32, +); + +impl SignedExtension for CheckVersion where T: System + Send + Sync { + 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)] +pub struct CheckGenesis( + PhantomData, + /// Local genesis hash to be used for `AdditionalSigned` + #[codec(skip)] + T::Hash, +); + +impl SignedExtension for CheckGenesis where T: System + Send + Sync { + 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)] +pub struct CheckEra( + /// The default structure for the Extra encoding + (Era, PhantomData), + /// Local genesis hash to be used for `AdditionalSigned` + #[codec(skip)] + T::Hash, +); + +impl SignedExtension for CheckEra where T: System + Send + Sync { + 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)] +pub struct CheckNonce(#[codec(compact)] T::Index); + +impl SignedExtension for CheckNonce where T: System + Send + Sync { + 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)] +pub struct CheckWeight(PhantomData); + +impl SignedExtension for CheckWeight where T: System + Send + Sync { + 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)] +pub struct TakeFees(#[codec(compact)] T::Balance); + +impl SignedExtension for TakeFees where T: Balances + Send + Sync { + type AccountId = u64; + type Call = (); + type AdditionalSigned = (); + type Pre = (); + fn additional_signed(&self) -> Result { + Ok(()) + } +} + +/// Checks if a transaction would exhausts the block gas limit. +#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] +pub struct CheckBlockGasLimit(PhantomData); + +impl SignedExtension for CheckBlockGasLimit where T: System + Send + Sync { + type AccountId = u64; + type Call = (); + type AdditionalSigned = (); + type Pre = (); + fn additional_signed(&self) -> Result { + Ok(()) + } +} + +pub trait SignedExtra { + type Extra: SignedExtension; + + fn extra(&self) -> Self::Extra; +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] +pub struct DefaultExtra { + version: u32, + nonce: T::Index, + genesis_hash: T::Hash, +} + +impl DefaultExtra { + pub fn new(version: u32, nonce: T::Index, genesis_hash: T::Hash) -> Self { + DefaultExtra { + version, + nonce, + genesis_hash, + } + } +} + +impl SignedExtra for DefaultExtra { + type Extra = ( + CheckVersion, + CheckGenesis, + CheckEra, + CheckNonce, + CheckWeight, + TakeFees, + CheckBlockGasLimit, + ); + + fn extra(&self) -> Self::Extra { + ( + CheckVersion(PhantomData, self.version), + CheckGenesis(PhantomData, self.genesis_hash), + CheckEra((Era::Immortal, PhantomData), self.genesis_hash), + CheckNonce(self.nonce), + CheckWeight(PhantomData), + TakeFees(::Balance::default()), + CheckBlockGasLimit(PhantomData), + ) + } +} + +impl SignedExtension for DefaultExtra { + type AccountId = T::AccountId; + type Call = (); + type AdditionalSigned = + <>::Extra as SignedExtension>::AdditionalSigned; + type Pre = (); + + fn additional_signed( + &self, + ) -> Result { + self.extra().additional_signed() + } +} + +pub fn create_and_sign( + signer: P, + call: C, + extra: E, +) -> Result< + UncheckedExtrinsic< + T::Address, + C, + P::Signature, + >::Extra, + >, + TransactionValidityError, +> +where + P: Pair, + P::Public: Into, + P::Signature: Codec, + C: Encode, + E: SignedExtra + SignedExtension, +{ + let raw_payload = SignedPayload::new(call, extra.extra())?; + let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(UncheckedExtrinsic::new_signed( + call, + signer.public().into(), + signature.into(), + extra, + )) +} diff --git a/src/lib.rs b/src/lib.rs index 166e8a2fab..4cbc3e66f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,19 +31,14 @@ use metadata::Metadata; use parity_scale_codec::{ Codec, Decode, - Encode, -}; -use runtime_primitives::{ - generic::UncheckedExtrinsic, - traits::StaticLookup, }; +use runtime_primitives::generic::UncheckedExtrinsic; use sr_version::RuntimeVersion; use std::{ convert::TryFrom, marker::PhantomData, }; use substrate_primitives::{ - blake2_256, storage::{ StorageChangeSet, StorageKey, @@ -55,6 +50,10 @@ use url::Url; use crate::{ codec::Encoded, events::EventsDecoder, + extrinsic::{ + DefaultExtra, + SignedExtra, + }, metadata::MetadataError, rpc::{ BlockNumber, @@ -76,6 +75,7 @@ use crate::{ mod codec; mod error; mod events; +mod extrinsic; mod metadata; mod rpc; mod srml; @@ -235,7 +235,7 @@ impl Client { ) -> impl Future, Error = Error> where P: Pair, - P::Public: Into + Into<::Source>, + P::Public: Into + Into, P::Signature: Codec, { let client = self.clone(); @@ -324,10 +324,10 @@ where } } -impl XtBuilder +impl XtBuilder where P: Pair, - P::Public: Into<::Source>, + P::Public: Into, P::Signature: Codec, { /// Creates and signs an Extrinsic for the supplied `Call` @@ -335,16 +335,16 @@ where &self, ) -> Result< UncheckedExtrinsic< - ::Source, + T::Address, Encoded, P::Signature, - T::SignedExtra, + as SignedExtra>::Extra, >, - MetadataError, + Error, > where P: Pair, - P::Public: Into<::Source>, + P::Public: Into, P::Signature: Codec, { let signer = self.signer.clone(); @@ -362,27 +362,9 @@ where account_nonce ); - let extra = T::extra(account_nonce); - let raw_payload = ( - call.clone(), - extra.clone(), - version, - (&genesis_hash, &genesis_hash), - ); - let signature = raw_payload.using_encoded(|payload| { - if payload.len() > 256 { - signer.sign(&blake2_256(payload)[..]) - } else { - signer.sign(payload) - } - }); - - Ok(UncheckedExtrinsic::new_signed( - raw_payload.0, - signer.public().into(), - signature.into(), - extra, - )) + let extra = extrinsic::DefaultExtra::new(version, account_nonce, genesis_hash); + let xt = extrinsic::create_and_sign(signer, call, extra)?; + Ok(xt) } /// Submits a transaction to the chain. @@ -427,14 +409,11 @@ mod tests { BalancesStore, BalancesXt, }, - contracts::{ - Contracts, - ContractsXt, - }, + contracts::ContractsXt, }; use futures::stream::Stream; + use node_runtime::Runtime; use parity_scale_codec::Encode; - use runtime_primitives::generic::Era; use runtime_support::StorageMap; use substrate_keyring::AccountKeyring; use substrate_primitives::{ @@ -442,47 +421,9 @@ mod tests { Pair, }; - struct Runtime; - - impl System for Runtime { - type Index = ::Index; - type BlockNumber = ::BlockNumber; - type Hash = ::Hash; - type Hashing = ::Hashing; - type AccountId = ::AccountId; - type Lookup = ::Lookup; - type Header = ::Header; - type Event = ::Event; - - type SignedExtra = ( - srml_system::CheckVersion, - srml_system::CheckGenesis, - srml_system::CheckEra, - srml_system::CheckNonce, - srml_system::CheckWeight, - srml_balances::TakeFees, - ); - fn extra(nonce: Self::Index) -> Self::SignedExtra { - ( - srml_system::CheckVersion::::new(), - srml_system::CheckGenesis::::new(), - srml_system::CheckEra::::from(Era::Immortal), - srml_system::CheckNonce::::from(nonce), - srml_system::CheckWeight::::new(), - srml_balances::TakeFees::::from(0), - ) - } - } - - impl Balances for Runtime { - type Balance = ::Balance; - } - - impl Contracts for Runtime {} - type Index = ::Index; type AccountId = ::AccountId; - type Address = <::Lookup as StaticLookup>::Source; + type Address = ::Address; type Balance = ::Balance; fn test_setup() -> (tokio::runtime::Runtime, Client) { diff --git a/src/srml/balances.rs b/src/srml/balances.rs index 2061f63825..425815f391 100644 --- a/src/srml/balances.rs +++ b/src/srml/balances.rs @@ -23,7 +23,6 @@ use runtime_primitives::traits::{ MaybeSerializeDebug, Member, SimpleArithmetic, - StaticLookup, }; use runtime_support::Parameter; use substrate_primitives::Pair; @@ -41,6 +40,14 @@ pub trait Balances: System { + From<::BlockNumber>; } +/// Blanket impl for using existing runtime types +impl Balances for T +where + ::Header: serde::de::DeserializeOwned, +{ + type Balance = T::Balance; +} + /// The Balances extension trait for the Client. pub trait BalancesStore { /// Balances type. @@ -134,7 +141,7 @@ where /// of the transfer, the account will be reaped. pub fn transfer( self, - to: <::Lookup as StaticLookup>::Source, + to: ::Address, amount: ::Balance, ) -> Result { self.module.call("transfer", (to, compact(amount))) diff --git a/src/srml/contracts.rs b/src/srml/contracts.rs index 12873156d2..d6e8e41e0e 100644 --- a/src/srml/contracts.rs +++ b/src/srml/contracts.rs @@ -13,7 +13,6 @@ use crate::{ Valid, XtBuilder, }; -use runtime_primitives::traits::StaticLookup; use substrate_primitives::Pair; /// Gas units are chosen to be represented by u64 so that gas metering @@ -23,6 +22,18 @@ pub type Gas = u64; /// The subset of the `srml_contracts::Trait` that a client must implement. pub trait Contracts: System + Balances {} +/// Blanket impl for using existing runtime types +impl< + T: srml_contracts::Trait + + srml_system::Trait + + srml_balances::Trait + + std::fmt::Debug, + > Contracts for T +where + ::Header: serde::de::DeserializeOwned, +{ +} + /// The Contracts extension trait for the XtBuilder. pub trait ContractsXt { /// Contracts type. @@ -106,7 +117,7 @@ where /// will be transferred. pub fn call( &self, - dest: <::Lookup as StaticLookup>::Source, + dest: ::Address, value: ::Balance, gas_limit: Gas, data: Vec, diff --git a/src/srml/system.rs b/src/srml/system.rs index f377682486..921d3c5b0c 100644 --- a/src/srml/system.rs +++ b/src/srml/system.rs @@ -15,27 +15,27 @@ use futures::future::{ self, Future, }; +use parity_scale_codec::Codec; use runtime_primitives::traits::{ Bounded, CheckEqual, Hash, Header, + MaybeDebug, MaybeDisplay, MaybeSerializeDebug, MaybeSerializeDebugButNotDeserialize, Member, - SignedExtension, SimpleArithmetic, SimpleBitOps, StaticLookup, }; use runtime_support::Parameter; use serde::de::DeserializeOwned; -use srml_system::Event; use substrate_primitives::Pair; /// The subset of the `srml_system::Trait` that a client must implement. -pub trait System { +pub trait System: 'static + Eq + Clone + std::fmt::Debug { /// Account index (aka nonce) type. This stores the number of previous /// transactions associated with a sender account. type Index: Parameter @@ -81,28 +81,27 @@ pub trait System { + Ord + Default; - /// Converting trait to take a source type and convert to `AccountId`. - /// - /// Used to define the type and conversion mechanism for referencing - /// accounts in transactions. It's perfectly reasonable for this to be an - /// identity conversion (with the source type being `AccountId`), but other - /// modules (e.g. Indices module) may provide more functional/efficient - /// alternatives. - type Lookup: StaticLookup; + /// The address type. This instead of `::Source`. + type Address: Codec + Clone + PartialEq + MaybeDebug; /// The block header. type Header: Parameter + Header + DeserializeOwned; +} - /// The aggregated event type of the runtime. - type Event: Parameter + Member + From; - - /// The `SignedExtension` to the basic transaction logic. - type SignedExtra: SignedExtension; - - /// Creates the `SignedExtra` from the account nonce. - fn extra(nonce: Self::Index) -> Self::SignedExtra; +/// Blanket impl for using existing runtime types +impl System for T +where + ::Header: serde::de::DeserializeOwned, +{ + type Index = T::Index; + type BlockNumber = T::BlockNumber; + type Hash = T::Hash; + type Hashing = T::Hashing; + type AccountId = T::AccountId; + type Address = ::Source; + type Header = T::Header; } /// The System extension trait for the Client.