diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 1c6a4f1923..f69b2f4063 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -3957,6 +3957,7 @@ dependencies = [ "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", + "sr-std 2.0.0", "srml-balances 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", diff --git a/substrate/core/sr-primitives/src/traits.rs b/substrate/core/sr-primitives/src/traits.rs index abbbef3a5b..3dc1649e49 100644 --- a/substrate/core/sr-primitives/src/traits.rs +++ b/substrate/core/sr-primitives/src/traits.rs @@ -796,7 +796,7 @@ impl Checkable for T { /// An abstract error concerning an attempt to verify, check or dispatch the transaction. This /// cannot be more concrete because it's designed to work reasonably well over a broad range of /// possible transaction types. -#[cfg_attr(feature = "std", derive(Debug))] +#[cfg_attr(feature = "std", derive(PartialEq, Debug))] pub enum DispatchError { /// General error to do with the inability to pay some fees (e.g. account balance too low). Payment, diff --git a/substrate/srml/balances/src/lib.rs b/substrate/srml/balances/src/lib.rs index 3f727bfa51..d3961e53c9 100644 --- a/substrate/srml/balances/src/lib.rs +++ b/substrate/srml/balances/src/lib.rs @@ -72,8 +72,6 @@ //! - [`Imbalance`](../srml_support/traits/trait.Imbalance.html): Functions for handling //! imbalances between total issuance in the system and account balances. Must be used when a function //! creates new funds (e.g. a reward) or destroys some funds (e.g. a system fee). -//! - [`MakePayment`](../srml_support/traits/trait.MakePayment.html): Simple trait designed -//! for hooking into a transaction payment. //! - [`IsDeadAccount`](../srml_system/trait.IsDeadAccount.html): Determiner to say whether a //! given account is unused. //! @@ -88,6 +86,17 @@ //! //! - `vesting_balance` - Get the amount that is currently being vested and cannot be transferred out of this account. //! +//! ### Signed Extensions +//! +//! The balances module defines the following extensions: +//! +//! - [`TakeFees`]: Consumes fees proportional to the length and weight of the transaction. +//! Additionally, it can contain a single encoded payload as a `tip`. The inclusion priority +//! is increased proportional to the tip. +//! +//! Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed +//! extensions included in a chain. +//! //! ## Usage //! //! The following examples show how to use the Balances module in your custom module. diff --git a/substrate/srml/contracts/src/lib.rs b/substrate/srml/contracts/src/lib.rs index 9f9fae5292..f8485f848a 100644 --- a/substrate/srml/contracts/src/lib.rs +++ b/substrate/srml/contracts/src/lib.rs @@ -425,7 +425,8 @@ where } /// The default dispatch fee computor computes the fee in the same way that -/// the implementation of `MakePayment` for the Balances module does. +/// the implementation of `TakeFees` for the Balances module does. Note that this only takes a fixed +/// fee based on size. Unlike the balances module, weight-fee is applied. pub struct DefaultDispatchFeeComputor(PhantomData); impl ComputeDispatchFee<::Call, BalanceOf> for DefaultDispatchFeeComputor { fn compute_dispatch_fee(call: &::Call) -> BalanceOf { diff --git a/substrate/srml/example/Cargo.toml b/substrate/srml/example/Cargo.toml index 87069a487a..73e280605a 100644 --- a/substrate/srml/example/Cargo.toml +++ b/substrate/srml/example/Cargo.toml @@ -11,9 +11,10 @@ srml-support = { path = "../support", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } balances = { package = "srml-balances", path = "../balances", default-features = false } sr-primitives = { path = "../../core/sr-primitives", default-features = false } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +rio = { package = "sr-io", path = "../../core/sr-io", default-features = false } [dev-dependencies] -sr-io = { path = "../../core/sr-io" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } [features] @@ -25,4 +26,6 @@ std = [ "srml-support/std", "system/std", "balances/std", + "rio/std", + "rstd/std" ] diff --git a/substrate/srml/example/src/lib.rs b/substrate/srml/example/src/lib.rs index 1c0191b69d..3de961f5cd 100644 --- a/substrate/srml/example/src/lib.rs +++ b/substrate/srml/example/src/lib.rs @@ -253,9 +253,15 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] +use rstd::marker::PhantomData; use srml_support::{StorageValue, dispatch::Result, decl_module, decl_storage, decl_event}; use system::{ensure_signed, ensure_root}; -use sr_primitives::weights::SimpleDispatchInfo; +use codec::{Encode, Decode}; +use sr_primitives::{ + traits::{SignedExtension, DispatchError, Bounded}, + transaction_validity::ValidTransaction, + weights::{SimpleDispatchInfo, DispatchInfo}, +}; /// Our module's configuration trait. All our types and consts go in here. If the /// module is dependent on specific other modules, then their configuration traits @@ -500,15 +506,96 @@ impl Module { } } +// Similar to other SRML modules, your module can also define a signed extension and perform some +// checks and [pre/post]processing [before/after] the transaction. A signed extension can be any +// decodable type that implements `SignedExtension`. See the trait definition for the full list of +// bounds. As a convention, you can follow this approach to create an extension for your module: +// - If the extension does not carry any data, then use a tuple struct with just a `marker` +// (needed for the compiler to accept `T: Trait`) will suffice. +// - Otherwise, create a tuple struct which contains the external data. Of course, for the entire +// struct to be decodable, each individual item also needs to be decodable. +// +// Note that a signed extension can also indicate that a particular data must be present in the +// _signing payload_ of a transaction by providing an implementation for the `additional_signed` +// method. This example will not cover this type of extension. See `CheckRuntime` in system module +// for an example. +// +// Using the extension, you can add some hooks to the lifecycle of each transaction. Note that by +// default, an extension is applied to all `Call` functions (i.e. all transactions). the `Call` enum +// variant is given to each function of `SignedExtension`. Hence, you can filter based on module or +// a particular call if needed. +// +// Some extra information, such as encoded length, some static dispatch info like weight and the +// sender of the transaction (if signed) are also provided. +// +// The full list of hooks that can be added to a signed extension can be found +// [here](https://crates.parity.io/sr_primitives/traits/trait.SignedExtension.html). +// +// The signed extensions are aggregated in the runtime file of a substrate chain. All extensions +// should be aggregated in a tuple and passed to the `CheckedExtrinsic` and `UncheckedExtrinsic` +// types defined in the runtime. Lookup `pub type SignedExtra = (...)` in `node/runtime` and +// `node-template` for an example of this. + +/// A simple signed extension that checks for the `set_dummy` call. In that case, it increases the +/// priority and prints some log. +/// +/// Additionally, it drops any transaction with an encoded length higher than 200 bytes. No +/// particular reason why, just to demonstrate the power of signed extensions. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct WatchDummy(PhantomData); + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for WatchDummy { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "WatchDummy") + } +} + +impl SignedExtension for WatchDummy { + type AccountId = T::AccountId; + // Note that this could also be assigned to the top-level call enum. It is passed into the + // balances module directly and since `Trait: balances::Trait`, you could also use `T::Call`. + // In that case, you would have had access to all call variants and could match on variants from + // other modules. + type Call = Call; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } + + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: DispatchInfo, + len: usize, + ) -> rstd::result::Result { + // if the transaction is too big, just drop it. + if len > 200 { return Err(DispatchError::Exhausted) } + + // check for `set_dummy` + match call { + Call::set_dummy(..) => { + rio::print("set_dummy was received."); + + let mut valid_tx = ValidTransaction::default(); + valid_tx.priority = Bounded::max_value(); + Ok(valid_tx) + } + _ => Ok(Default::default()) + } + } +} + #[cfg(test)] mod tests { use super::*; use srml_support::{assert_ok, impl_outer_origin, parameter_types}; - use sr_io::with_externalities; + use rio::with_externalities; use primitives::{H256, Blake2Hasher}; // The testing primitives are very useful for avoiding having to work with signatures - // or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried. + // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use sr_primitives::{ Perbill, traits::{BlakeTwo256, OnInitialize, OnFinalize, IdentityLookup}, testing::Header }; @@ -575,7 +662,7 @@ mod tests { // This function basically just builds a genesis storage key/value store according to // our desired mockup. - fn new_test_ext() -> sr_io::TestExternalities { + fn new_test_ext() -> rio::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); // We use default for brevity, but you can configure as desired if needed. balances::GenesisConfig::::default().assimilate_storage(&mut t).unwrap(); @@ -617,4 +704,21 @@ mod tests { assert_eq!(Example::foo(), 25); }); } + + #[test] + fn signed_ext_watch_dummy_works() { + with_externalities(&mut new_test_ext(), || { + let call = >::set_dummy(10); + let info = DispatchInfo::default(); + + assert_eq!( + WatchDummy::(PhantomData).validate(&1, &call, info, 150).unwrap().priority, + Bounded::max_value() + ); + assert_eq!( + WatchDummy::(PhantomData).validate(&1, &call, info, 250), + Err(DispatchError::Exhausted) + ); + }) + } } diff --git a/substrate/srml/system/src/lib.rs b/substrate/srml/system/src/lib.rs index 56093f4f7e..d0b36ae14d 100644 --- a/substrate/srml/system/src/lib.rs +++ b/substrate/srml/system/src/lib.rs @@ -42,6 +42,23 @@ //! //! See the [`Module`](./struct.Module.html) struct for details of publicly available functions. //! +//! ### Signed Extensions +//! +//! The system module defines the following extensions: +//! +//! - [`CheckWeight`]: Checks the weight and length of the block and ensure that it does not +//! exceed the limits. +//! - ['CheckNonce']: Checks the nonce of the transaction. Contains a single payload of type +//! `T::Index`. +//! - [`CheckEra`]: Checks the era of the transaction. Contains a single payload of type `Era`. +//! - [`CheckGenesis`]: Checks the provided genesis hash of the transaction. Must be a part of the +//! signed payload of the transaction. +//! - [`CheckVersion`]: Checks that the runtime version is the same as the one encoded in the +//! transaction. +//! +//! Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed +//! extensions included in a chain. +//! //! ## Usage //! //! ### Prerequisites