diff --git a/core/sr-primitives/src/generic/checked_extrinsic.rs b/core/sr-primitives/src/generic/checked_extrinsic.rs index 3f73bca07a5f8..04ccd1162c6c6 100644 --- a/core/sr-primitives/src/generic/checked_extrinsic.rs +++ b/core/sr-primitives/src/generic/checked_extrinsic.rs @@ -22,7 +22,7 @@ use crate::traits::{ self, Member, MaybeDisplay, SignedExtension, DispatchError, Dispatchable, DispatchResult, ValidateUnsigned }; -use crate::weights::{Weigh, Weight}; +use crate::weights::{GetDispatchInfo, DispatchInfo}; use crate::transaction_validity::TransactionValidity; /// Definition of something that the external world might want to say; its @@ -57,13 +57,13 @@ where } fn validate>(&self, - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, ) -> TransactionValidity { if let Some((ref id, ref extra)) = self.signed { - Extra::validate(extra, id, weight, len).into() + Extra::validate(extra, id, info, len).into() } else { - match Extra::validate_unsigned(weight, len) { + match Extra::validate_unsigned(info, len) { Ok(extra) => match U::validate_unsigned(&self.function) { TransactionValidity::Valid(v) => TransactionValidity::Valid(v.combine_with(extra)), @@ -75,25 +75,25 @@ where } fn dispatch(self, - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, ) -> Result { let maybe_who = if let Some((id, extra)) = self.signed { - Extra::pre_dispatch(extra, &id, weight, len)?; + Extra::pre_dispatch(extra, &id, info, len)?; Some(id) } else { - Extra::pre_dispatch_unsigned(weight, len)?; + Extra::pre_dispatch_unsigned(info, len)?; None }; Ok(self.function.dispatch(Origin::from(maybe_who))) } } -impl Weigh for CheckedExtrinsic +impl GetDispatchInfo for CheckedExtrinsic where - Call: Weigh, + Call: GetDispatchInfo, { - fn weigh(&self) -> Weight { - self.function.weigh() + fn get_dispatch_info(&self) -> DispatchInfo { + self.function.get_dispatch_info() } } diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index 61e65a21d5b61..75f940eb313ad 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -24,7 +24,7 @@ use crate::traits::{ ValidateUnsigned, SignedExtension, Dispatchable, }; use crate::{generic, KeyTypeId}; -use crate::weights::{Weigh, Weight}; +use crate::weights::{GetDispatchInfo, DispatchInfo}; pub use substrate_primitives::H256; use substrate_primitives::U256; use substrate_primitives::ed25519::{Public as AuthorityId}; @@ -240,7 +240,7 @@ impl Applyable for TestXt where /// Checks to see if this is a valid *transaction*. It returns information on it if so. fn validate>(&self, - _weight: Weight, + _info: DispatchInfo, _len: usize, ) -> TransactionValidity { TransactionValidity::Valid(Default::default()) @@ -249,23 +249,26 @@ impl Applyable for TestXt where /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, /// index and sender. fn dispatch(self, - weight: Weight, + info: DispatchInfo, len: usize, ) -> Result { let maybe_who = if let Some(who) = self.0 { - Extra::pre_dispatch(self.2, &who, weight, len)?; + Extra::pre_dispatch(self.2, &who, info, len)?; Some(who) } else { - Extra::pre_dispatch_unsigned(weight, len)?; + Extra::pre_dispatch_unsigned(info, len)?; None }; Ok(self.1.dispatch(maybe_who.into())) } } -impl Weigh for TestXt { - fn weigh(&self) -> Weight { +impl GetDispatchInfo for TestXt { + fn get_dispatch_info(&self) -> DispatchInfo { // for testing: weight == size. - self.0.using_encoded(|d| d.len() as Weight) + DispatchInfo { + weight: self.encode().len() as u32, + ..Default::default() + } } } diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index 0708e561f6c2a..3539e2fcfd814 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -25,6 +25,7 @@ use substrate_primitives::{self, Hasher, Blake2Hasher}; use crate::codec::{Codec, Encode, Decode, HasCompact}; use crate::transaction_validity::{ValidTransaction, TransactionValidity}; use crate::generic::{Digest, DigestItem}; +use crate::weights::DispatchInfo; pub use substrate_primitives::crypto::TypedKey; pub use integer_sqrt::IntegerSquareRoot; pub use num_traits::{ @@ -752,6 +753,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))] pub enum DispatchError { /// General error to do with the inability to pay some fees (e.g. account balance too low). Payment, @@ -825,7 +827,7 @@ pub trait SignedExtension: fn validate( &self, _who: &Self::AccountId, - _weight: crate::weights::Weight, + _info: DispatchInfo, _len: usize, ) -> Result { Ok(Default::default()) } @@ -833,23 +835,23 @@ pub trait SignedExtension: fn pre_dispatch( self, who: &Self::AccountId, - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, - ) -> Result<(), DispatchError> { self.validate(who, weight, len).map(|_| ()) } + ) -> Result<(), DispatchError> { self.validate(who, info, len).map(|_| ()) } /// Validate an unsigned transaction for the transaction queue. Normally the default /// implementation is fine since `ValidateUnsigned` is a better way of recognising and /// validating unsigned transactions. fn validate_unsigned( - _weight: crate::weights::Weight, + _info: DispatchInfo, _len: usize, ) -> Result { Ok(Default::default()) } /// Do any pre-flight stuff for a unsigned transaction. fn pre_dispatch_unsigned( - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, - ) -> Result<(), DispatchError> { Self::validate_unsigned(weight, len).map(|_| ()) } + ) -> Result<(), DispatchError> { Self::validate_unsigned(info, len).map(|_| ()) } } macro_rules! tuple_impl_indexed { @@ -869,33 +871,33 @@ macro_rules! tuple_impl_indexed { fn validate( &self, who: &Self::AccountId, - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, ) -> Result { - let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, weight, len)?),+]; + let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, info, len)?),+]; Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) } fn pre_dispatch( self, who: &Self::AccountId, - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, ) -> Result<(), DispatchError> { - $(self.$index.pre_dispatch(who, weight, len)?;)+ + $(self.$index.pre_dispatch(who, info, len)?;)+ Ok(()) } fn validate_unsigned( - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, ) -> Result { - let aggregator = vec![$($direct::validate_unsigned(weight, len)?),+]; + let aggregator = vec![$($direct::validate_unsigned(info, len)?),+]; Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) } fn pre_dispatch_unsigned( - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, ) -> Result<(), DispatchError> { - $($direct::pre_dispatch_unsigned(weight, len)?;)+ + $($direct::pre_dispatch_unsigned(info, len)?;)+ Ok(()) } } @@ -944,14 +946,14 @@ pub trait Applyable: Sized + Send + Sync { /// Checks to see if this is a valid *transaction*. It returns information on it if so. fn validate>(&self, - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, ) -> TransactionValidity; /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, /// index and sender. fn dispatch(self, - weight: crate::weights::Weight, + info: DispatchInfo, len: usize, ) -> Result; } diff --git a/core/sr-primitives/src/weights.rs b/core/sr-primitives/src/weights.rs index fa60737bb89e9..872a091a84523 100644 --- a/core/sr-primitives/src/weights.rs +++ b/core/sr-primitives/src/weights.rs @@ -16,26 +16,78 @@ //! Primitives for transaction weighting. //! -//! Each dispatch function within `decl_module!` can now have an optional -//! `#[weight = $x]` attribute. $x can be any object that implements the -//! `Weigh` trait. By default, All transactions are annotated by -//! `#[weight = TransactionWeight::default()]`. +//! Each dispatch function within `decl_module!` can have an optional `#[weight = $x]` attribute. +//! `$x` can be any type that implements the `ClassifyDispatch` and `WeighData` traits. By +//! default, All transactions are annotated with `#[weight = SimpleDispatchInfo::default()]`. //! //! Note that the decl_module macro _cannot_ enforce this and will simply fail //! if an invalid struct is passed in. -/// The final type that each `#[weight = $x:expr]`'s -/// expression must evaluate to. +pub use crate::transaction_validity::TransactionPriority; +use crate::traits::Bounded; + +/// Numeric range of a transaction weight. pub type Weight = u32; -/// A `Call` enum (aka transaction) that can be weighted using the custom weight attribute of -/// its dispatchable functions. Is implemented by default in the `decl_module!`. -/// -/// Both the outer Call enum and the per-module individual ones will implement this. -/// The outer enum simply calls the inner ones based on call type. -pub trait Weigh { - /// Return the weight of this call. This is done independently of its encoded size. - fn weigh(&self) -> Weight; +/// A broad range of dispatch types. This is only distinguishing normal, user-triggered transactions +/// (`Normal`) and anything beyond which serves a higher purpose to the system (`Operational`). +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum DispatchClass { + /// A normal dispatch. + Normal, + /// An operational dispatch. + Operational, +} + +impl Default for DispatchClass { + fn default() -> Self { + DispatchClass::Normal + } +} + +impl From for DispatchClass { + fn from(tx: SimpleDispatchInfo) -> Self { + match tx { + SimpleDispatchInfo::FixedOperational(_) => DispatchClass::Operational, + SimpleDispatchInfo::MaxOperational => DispatchClass::Operational, + SimpleDispatchInfo::FreeOperational => DispatchClass::Operational, + + SimpleDispatchInfo::FixedNormal(_) => DispatchClass::Normal, + SimpleDispatchInfo::MaxNormal => DispatchClass::Normal, + SimpleDispatchInfo::FreeNormal => DispatchClass::Normal, + } + } +} + +/// A bundle of static information collected from the `#[weight = $x]` attributes. +#[cfg_attr(feature = "std", derive(PartialEq, Eq, Debug))] +#[derive(Clone, Copy, Default)] +pub struct DispatchInfo { + /// Weight of this transaction. + pub weight: Weight, + /// Class of this transaction. + pub class: DispatchClass, +} + +impl DispatchInfo { + /// Determine if this dispatch should pay the base length-related fee or not. + pub fn pay_length_fee(&self) -> bool { + match self.class { + DispatchClass::Normal => true, + // For now we assume all operational transactions don't pay the length fee. + DispatchClass::Operational => false, + } + } +} + +/// A `Dispatchable` function (aka transaction) that can carry some static information along with it, using the +/// `#[weight]` attribute. +pub trait GetDispatchInfo { + /// Return a `DispatchInfo`, containing relevant information of this dispatch. + /// + /// This is done independently of its encoded size. + fn get_dispatch_info(&self) -> DispatchInfo; } /// Means of weighing some particular kind of data (`T`). @@ -44,38 +96,73 @@ pub trait WeighData { fn weigh_data(&self, target: T) -> Weight; } -/// Default type used as the weight representative in a `#[weight = x]` attribute. +/// Means of classifying a dispatchable function. +pub trait ClassifyDispatch { + /// Classify the dispatch function based on input data `target` of type `T`. + fn classify_dispatch(&self, target: T) -> DispatchClass; +} + +/// Default type used with the `#[weight = x]` attribute in a substrate chain. /// -/// A user may pass in any other type that implements [`Weigh`]. If not, the `Default` -/// implementation of [`TransactionWeight`] is used. -pub enum TransactionWeight { - /// Basic weight (base, byte). - /// The values contained are the base weight and byte weight respectively. - Fixed(Weight), - /// Maximum fee. This implies that this transaction _might_ get included but - /// no more transaction can be added. This can be done by setting the - /// implementation to _maximum block weight_. - Max, - /// Free. The transaction does not increase the total weight - /// (i.e. is not included in weight calculation). - Free, +/// A user may pass in any other type that implements the correct traits. If not, the `Default` +/// implementation of [`SimpleDispatchInfo`] is used. +/// +/// For each broad group (`Normal` and `Operation`): +/// - A `Fixed` variant means weight fee is charged normally and the weight is the number +/// specified in the inner value of the variant. +/// - A `Free` variant is equal to `::Fixed(0)`. Note that this does not guarantee inclusion. +/// - A `Max` variant is equal to `::Fixed(Weight::max_value())`. +/// +/// Based on the final weight value, based on the above variants: +/// - A _weight-fee_ is deducted. +/// - The block weight is consumed proportionally. +/// +/// As for the broad groups themselves: +/// - `Normal` variants will be assigned a priority proportional to their weight. They can only +/// consume a portion (1/4) of the maximum block resource limits. +/// - `Operational` variants will be assigned the maximum priority. They can potentially consume +/// the entire block resource limit. +#[derive(Clone, Copy)] +pub enum SimpleDispatchInfo { + /// A normal dispatch with fixed weight. + FixedNormal(Weight), + /// A normal dispatch with the maximum weight. + MaxNormal, + /// A normal dispatch with no weight. + FreeNormal, + /// An operational dispatch with fixed weight. + FixedOperational(Weight), + /// An operational dispatch with the maximum weight. + MaxOperational, + /// An operational dispatch with no weight. + FreeOperational, } -impl WeighData for TransactionWeight { +impl WeighData for SimpleDispatchInfo { fn weigh_data(&self, _: T) -> Weight { match self { - TransactionWeight::Fixed(w) => *w, - TransactionWeight::Max => 3 * 1024 * 1024, - TransactionWeight::Free => 0, + SimpleDispatchInfo::FixedNormal(w) => *w, + SimpleDispatchInfo::MaxNormal => Bounded::max_value(), + SimpleDispatchInfo::FreeNormal => Bounded::min_value(), + + SimpleDispatchInfo::FixedOperational(w) => *w, + SimpleDispatchInfo::MaxOperational => Bounded::max_value(), + SimpleDispatchInfo::FreeOperational => Bounded::min_value(), } } } -impl Default for TransactionWeight { +impl ClassifyDispatch for SimpleDispatchInfo { + fn classify_dispatch(&self, _: T) -> DispatchClass { + DispatchClass::from(*self) + } +} + +impl Default for SimpleDispatchInfo { fn default() -> Self { // This implies that the weight is currently equal to 100, nothing more // for all substrate transactions that do NOT explicitly annotate weight. // TODO #2431 needs to be updated with proper max values. - TransactionWeight::Fixed(1) + SimpleDispatchInfo::FixedNormal(1) } } diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 92e64e2ea9ca9..b86af75ea8c8b 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -113,6 +113,7 @@ pub fn native_version() -> NativeVersion { parameter_types! { pub const BlockHashCount: BlockNumber = 250; pub const MaximumBlockWeight: Weight = 4 * 1024 * 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Runtime { @@ -138,6 +139,8 @@ impl system::Trait for Runtime { type BlockHashCount = BlockHashCount; /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. type MaximumBlockWeight = MaximumBlockWeight; + /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. + type MaximumBlockLength = MaximumBlockLength; } impl aura::Trait for Runtime { diff --git a/node-template/runtime/src/template.rs b/node-template/runtime/src/template.rs index 97466d2b6dce5..961ffaea4ffa6 100644 --- a/node-template/runtime/src/template.rs +++ b/node-template/runtime/src/template.rs @@ -87,6 +87,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -100,6 +101,7 @@ mod tests { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Test { type Event = (); diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 0f61a4ceade53..5e142a64b681f 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -80,7 +80,6 @@ mod tests { type TestExternalities = CoreTestExternalities; - // TODO: fix for being charged based on weight now. fn transfer_fee(extrinsic: &E) -> Balance { >::get() + >::get() * diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 04bc02e46e3b4..2b4f9322c7773 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -112,7 +112,8 @@ pub const DAYS: Moment = HOURS * 24; parameter_types! { pub const BlockHashCount: BlockNumber = 250; - pub const MaximumBlockWeight: Weight = 4 * 1024 * 1024; + pub const MaximumBlockWeight: Weight = 4 * 1024; + pub const MaximumBlockLength: u32 = 4 * 1024 * 1024; } impl system::Trait for Runtime { @@ -127,6 +128,7 @@ impl system::Trait for Runtime { type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl aura::Trait for Runtime { diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index 1e4c06700abe1..962f4cfb4f0e0 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -258,6 +258,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -271,6 +272,7 @@ mod tests { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Test { type Event = (); diff --git a/srml/aura/src/mock.rs b/srml/aura/src/mock.rs index 7664405eb37e3..ffa0385761c68 100644 --- a/srml/aura/src/mock.rs +++ b/srml/aura/src/mock.rs @@ -38,6 +38,7 @@ pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; pub const MinimumPeriod: u64 = 1; } @@ -53,6 +54,7 @@ impl system::Trait for Test { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl timestamp::Trait for Test { diff --git a/srml/authorship/src/lib.rs b/srml/authorship/src/lib.rs index 5ba82f5daf2c2..8561a8428303b 100644 --- a/srml/authorship/src/lib.rs +++ b/srml/authorship/src/lib.rs @@ -338,6 +338,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { @@ -352,6 +353,7 @@ mod tests { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Test { diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 24b414ee0a9eb..b8a0ffe6c51cc 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -156,13 +156,15 @@ use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage use srml_support::traits::{ UpdateBalanceOutcome, Currency, OnFreeBalanceZero, OnUnbalanced, WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement, - Imbalance, SignedImbalance, ReservableCurrency + Imbalance, SignedImbalance, ReservableCurrency, Get, }; -use srml_support::{dispatch::Result, traits::Get}; -use primitives::{transaction_validity::TransactionPriority, traits::{ - Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, - MaybeSerializeDebug, Saturating, Bounded, SignedExtension -}}; +use srml_support::dispatch::Result; +use primitives::traits::{ + Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug, + Saturating, Bounded, SignedExtension, SaturatedConversion, DispatchError +}; +use primitives::transaction_validity::{TransactionPriority, ValidTransaction}; +use primitives::weights::DispatchInfo; use system::{IsDeadAccount, OnNewAccount, ensure_signed, ensure_root}; mod mock; @@ -762,6 +764,7 @@ impl, I: Instance> system::Trait for ElevatedTrait { type Event = (); type BlockHashCount = T::BlockHashCount; type MaximumBlockWeight = T::MaximumBlockWeight; + type MaximumBlockLength = T::MaximumBlockLength; } impl, I: Instance> Trait for ElevatedTrait { type Balance = T::Balance; @@ -1150,12 +1153,38 @@ where #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct TakeFees, I: Instance = DefaultInstance>(#[codec(compact)] T::Balance); -#[cfg(feature = "std")] impl, I: Instance> TakeFees { /// utility constructor. Used only in client/factory code. + #[cfg(feature = "std")] pub fn from(fee: T::Balance) -> Self { Self(fee) } + + /// Compute the final fee value for a particular transaction. + /// + /// The final fee is composed of: + /// - _length-fee_: This is the amount paid merely to pay for size of the transaction. + /// - _weight-fee_: This amount is computed based on the weight of the transaction. Unlike + /// size-fee, this is not input dependent and reflects the _complexity_ of the execution + /// and the time it consumes. + /// - (optional) _tip_: if included in the transaction, it will be added on top. Only signed + /// transactions can have a tip. + fn compute_fee(len: usize, info: DispatchInfo, tip: T::Balance) -> T::Balance { + // length fee + let len_fee = if info.pay_length_fee() { + let len = T::Balance::from(len as u32); + let base = T::TransactionBaseFee::get(); + let byte = T::TransactionByteFee::get(); + base.saturating_add(byte.saturating_mul(len)) + } else { + Zero::zero() + }; + + // weight fee + let _weight_fee = T::Balance::from(0); // TODO: should be weight_and_size_to_fee(weight, _len) #2854 + + len_fee.saturating_add(_weight_fee).saturating_add(tip) + } } #[cfg(feature = "std")] @@ -1165,10 +1194,6 @@ impl, I: Instance> rstd::fmt::Debug for TakeFees { } } -use primitives::traits::{DispatchError, SaturatedConversion}; -use primitives::transaction_validity::ValidTransaction; -use primitives::weights::Weight; - impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { type AccountId = T::AccountId; type AdditionalSigned = (); @@ -1177,22 +1202,22 @@ impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { fn validate( &self, who: &Self::AccountId, - weight: Weight, - _len: usize, + info: DispatchInfo, + len: usize, ) -> rstd::result::Result { - let fee_x = T::Balance::from(weight); - // TODO: should be weight_and_size_to_fee(weight, _len) - let fee = T::TransactionBaseFee::get() + T::TransactionByteFee::get() * fee_x; - let fee = fee + self.0.clone(); + // pay any fees. + let fee = Self::compute_fee(len, info, self.0); let imbalance = >::withdraw( who, - fee.clone(), + fee, WithdrawReason::TransactionPayment, - ExistenceRequirement::KeepAlive + ExistenceRequirement::KeepAlive, ).map_err(|_| DispatchError::Payment)?; T::TransactionPayment::on_unbalanced(imbalance); let mut r = ValidTransaction::default(); + // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which + // will be a bit more than setting the priority to tip. For now, this is enough. r.priority = fee.saturated_into::(); Ok(r) } diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index 3fb29058439d5..12eae9724172e 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -18,10 +18,11 @@ #![cfg(test)] -use primitives::{traits::{IdentityLookup}, testing::Header}; +use primitives::{traits::{IdentityLookup}, testing::Header, weights::{DispatchInfo, Weight}}; use substrate_primitives::{H256, Blake2Hasher}; use runtime_io; -use srml_support::{impl_outer_origin, parameter_types, traits::Get}; +use srml_support::{impl_outer_origin, parameter_types}; +use srml_support::traits::Get; use std::cell::RefCell; use crate::{GenesisConfig, Module, Trait}; @@ -34,7 +35,7 @@ thread_local! { static TRANSFER_FEE: RefCell = RefCell::new(0); static CREATION_FEE: RefCell = RefCell::new(0); static TRANSACTION_BASE_FEE: RefCell = RefCell::new(0); - static TRANSACTION_BYTE_FEE: RefCell = RefCell::new(0); + static TRANSACTION_BYTE_FEE: RefCell = RefCell::new(1); } pub struct ExistentialDeposit; @@ -68,6 +69,7 @@ pub struct Runtime; parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Runtime { type Origin = Origin; @@ -81,6 +83,7 @@ impl system::Trait for Runtime { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Runtime { type Balance = u64; @@ -187,3 +190,8 @@ impl ExtBuilder { pub type System = system::Module; pub type Balances = Module; + +/// create a transaction info struct from weight. Handy to avoid building the whole struct. +pub fn info_from_weight(w: Weight) -> DispatchInfo { + DispatchInfo { weight: w, ..Default::default() } +} diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 2174176b7af5c..2828d40e63ff8 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -19,7 +19,7 @@ #![cfg(test)] use super::*; -use mock::{Balances, ExtBuilder, Runtime, System}; +use mock::{Balances, ExtBuilder, Runtime, System, info_from_weight}; use runtime_io::with_externalities; use srml_support::{ assert_noop, assert_ok, assert_err, @@ -124,7 +124,12 @@ fn lock_reasons_should_work() { ); assert_ok!(>::reserve(&1, 1)); // NOTE: this causes a fee payment. - assert!( as SignedExtension>::validate(&TakeFees::from(1), &1, 1).is_ok()); + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + info_from_weight(1), + 0, + ).is_ok()); Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into()); assert_ok!(>::transfer(&1, &2, 1)); @@ -132,12 +137,22 @@ fn lock_reasons_should_work() { >::reserve(&1, 1), "account liquidity restrictions prevent withdrawal" ); - assert!( as SignedExtension>::validate(&TakeFees::from(1), &1, 1).is_ok()); + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + info_from_weight(1), + 0, + ).is_ok()); Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into()); assert_ok!(>::transfer(&1, &2, 1)); assert_ok!(>::reserve(&1, 1)); - assert!( as SignedExtension>::validate(&TakeFees::from(1), &1, 1).is_err()); + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + info_from_weight(1), + 0, + ).is_err()); } ); } @@ -741,9 +756,10 @@ fn signed_extension_take_fees_work() { .monied(true) .build(), || { - assert!(TakeFees::::from(0).validate(&1, 10).is_ok()); + let len = 10; + assert!(TakeFees::::from(0).pre_dispatch(&1, info_from_weight(0), len).is_ok()); assert_eq!(Balances::free_balance(&1), 100 - 20); - assert!(TakeFees::::from(5 /* tipped */).validate(&1, 10).is_ok()); + assert!(TakeFees::::from(5 /* tipped */).pre_dispatch(&1, info_from_weight(0), len).is_ok()); assert_eq!(Balances::free_balance(&1), 100 - 20 - 25); } ); diff --git a/srml/collective/src/lib.rs b/srml/collective/src/lib.rs index a8303501d46ea..6e39d58e8d8b2 100644 --- a/srml/collective/src/lib.rs +++ b/srml/collective/src/lib.rs @@ -403,6 +403,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -416,6 +417,7 @@ mod tests { type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Test { type Origin = Origin; diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index adcc90342662e..71c6a7a31d7ed 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -97,6 +97,7 @@ pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -110,6 +111,7 @@ impl system::Trait for Test { type Event = MetaEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const BalancesTransactionBaseFee: u64 = 0; diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs index 1baeb6fdfd34b..72feead5e1e33 100644 --- a/srml/council/src/lib.rs +++ b/srml/council/src/lib.rs @@ -99,6 +99,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -112,6 +113,7 @@ mod tests { type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index 5c49f9519013d..ab5d068928258 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -999,6 +999,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -1012,6 +1013,7 @@ mod tests { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/srml/elections/src/lib.rs b/srml/elections/src/lib.rs index a1c139349ccee..1060c7b9a7bf0 100644 --- a/srml/elections/src/lib.rs +++ b/srml/elections/src/lib.rs @@ -1109,6 +1109,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -1122,6 +1123,7 @@ mod tests { type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index 37d091279e479..cd93d3a6b0735 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -255,7 +255,7 @@ use srml_support::{StorageValue, dispatch::Result, decl_module, decl_storage, decl_event}; use system::{ensure_signed, ensure_root}; -use sr_primitives::weights::TransactionWeight; +use sr_primitives::weights::SimpleDispatchInfo; /// 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 @@ -396,19 +396,18 @@ decl_module! { // // If you don't respect these rules, it is likely that your chain will be attackable. // - // Each transaction can optionally indicate a weight. The weight is passed in as a - // custom attribute and the value can be anything that implements the `Weigh` - // trait. Most often using substrate's default `TransactionWeight` is enough for you. + // Each transaction can define an optional `#[weight]` attribute to convey a set of static + // information about its dispatch. The `system` and `executive` module then use this + // information to properly execute the transaction, whilst keeping the total load of the + // chain in a moderate rate. // - // A basic weight is a tuple of `(base_weight, byte_weight)`. Upon including each transaction - // in a block, the final weight is calculated as `base_weight + byte_weight * tx_size`. - // If this value, added to the weight of all included transactions, exceeds `MAX_TRANSACTION_WEIGHT`, - // the transaction is not included. If no weight attribute is provided, the `::default()` - // implementation of `TransactionWeight` is used. - // - // The example below showcases a transaction which is relatively costly, but less dependent on - // the input, hence `byte_weight` is configured smaller. - #[weight = TransactionWeight::Basic(100_000, 10)] + // The _right-hand-side_ value of the `#[weight]` attribute can be any type that implements + // a set of traits, namely [`WeighData`] and [`ClassifyDispatch`]. The former conveys the + // weight (a numeric representation of pure execution time and difficulty) of the + // transaction and the latter demonstrates the `DispatchClass` of the call. A higher weight + // means a larger transaction (less of which can be placed in a single block). See the + // `CheckWeight` signed extension struct in the `system` module for more information. + #[weight = SimpleDispatchInfo::FixedNormal(10_000)] fn accumulate_dummy(origin, increase_by: T::Balance) -> Result { // This is a public call, so we ensure that the origin is some signed account. let _sender = ensure_signed(origin)?; @@ -526,6 +525,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -539,6 +539,7 @@ mod tests { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 47422d50f182f..0b2d9142a7d11 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -86,7 +86,7 @@ use parity_codec::{Codec, Encode}; use system::{extrinsics_root, DigestOf}; use primitives::{ApplyOutcome, ApplyError}; use primitives::transaction_validity::TransactionValidity; -use primitives::weights::Weigh; +use primitives::weights::GetDispatchInfo; mod internal { use primitives::traits::DispatchError; @@ -141,7 +141,7 @@ impl< > ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, - CheckedOf: Applyable + Weigh, + CheckedOf: Applyable + GetDispatchInfo, CallOf: Dispatchable, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, @@ -160,7 +160,7 @@ impl< > Executive where Block::Extrinsic: Checkable + Codec, - CheckedOf: Applyable + Weigh, + CheckedOf: Applyable + GetDispatchInfo, CallOf: Dispatchable, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, @@ -284,8 +284,8 @@ where // AUDIT: Under no circumstances may this function panic from here onwards. // Decode parameters and dispatch - let weight = xt.weigh(); - let r = Applyable::dispatch(xt, weight, encoded_len) + let dispatch_info = xt.get_dispatch_info(); + let r = Applyable::dispatch(xt, dispatch_info, encoded_len) .map_err(internal::ApplyError::from)?; >::note_applied_extrinsic(&r, encoded_len as u32); @@ -339,8 +339,8 @@ where Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), }; - let weight = xt.weigh(); - xt.validate::(weight, encoded_len) + let dispatch_info = xt.get_dispatch_info(); + xt.validate::(dispatch_info, encoded_len) } /// Start an offchain worker and generate extrinsics. @@ -380,6 +380,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Runtime { type Origin = Origin; @@ -393,6 +394,7 @@ mod tests { type Event = MetaEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const ExistentialDeposit: u64 = 0; @@ -548,57 +550,60 @@ mod tests { #[test] fn block_weight_limit_enforced() { - let run_test = |should_fail: bool| { - let mut t = new_test_ext(); - let xt = primitives::testing::TestXt(Some(1), Call::transfer(33, 69), extra(0, 0)); - let xt2 = primitives::testing::TestXt(Some(1), Call::transfer(33, 69), extra(1, 0)); - let encoded = xt2.encode(); - let len = if should_fail { ( ::MaximumBlockWeight::get() - 1) as usize } else { encoded.len() }; - let encoded_len = encoded.len() as u32; - with_externalities(&mut t, || { - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - assert_eq!(>::all_extrinsics_weight(), 0); - - Executive::apply_extrinsic(xt).unwrap(); - let res = Executive::apply_extrinsic_with_len(xt2, len, Some(encoded)); - - if should_fail { - assert!(res.is_err()); - assert_eq!(>::all_extrinsics_weight(), encoded_len); - assert_eq!(>::extrinsic_index(), Some(1)); + let mut t = new_test_ext(); + // given: TestXt uses the encoded len as fixed Len: + let xt = primitives::testing::TestXt(Some(1), Call::transfer::(33, 0), extra(0, 0)); + let encoded = xt.encode(); + let encoded_len = encoded.len() as u32; + let limit = >::get() / 4; + let num_to_exhaust_block = limit / encoded_len; + with_externalities(&mut t, || { + Executive::initialize_block(&Header::new( + 1, + H256::default(), + H256::default(), + [69u8; 32].into(), + Digest::default(), + )); + assert_eq!(>::all_extrinsics_weight(), 0); + + for nonce in 0..=num_to_exhaust_block { + let xt = primitives::testing::TestXt(Some(1), Call::transfer::(33, 0), extra(nonce.into(), 0)); + let res = Executive::apply_extrinsic(xt); + if nonce != num_to_exhaust_block { + assert_eq!(res.unwrap(), ApplyOutcome::Success); + assert_eq!(>::all_extrinsics_weight(), encoded_len * (nonce + 1)); + assert_eq!(>::extrinsic_index(), Some(nonce + 1)); } else { - assert!(res.is_ok()); - assert_eq!(>::all_extrinsics_weight(), encoded_len * 2); - assert_eq!(>::extrinsic_index(), Some(2)); + assert_eq!(res, Err(ApplyError::CantPay)); } - }); - }; - - run_test(false); - run_test(true); + } + }); } #[test] - fn default_block_weight_is_stored() { + fn block_weight_and_size_is_stored_per_tx() { let xt = primitives::testing::TestXt(Some(1), Call::transfer(33, 0), extra(0, 0)); let x1 = primitives::testing::TestXt(Some(1), Call::transfer(33, 0), extra(1, 0)); let x2 = primitives::testing::TestXt(Some(1), Call::transfer(33, 0), extra(2, 0)); let len = xt.clone().encode().len() as u32; let mut t = new_test_ext(); with_externalities(&mut t, || { - Executive::apply_extrinsic(xt.clone()).unwrap(); - Executive::apply_extrinsic(x1.clone()).unwrap(); - Executive::apply_extrinsic(x2.clone()).unwrap(); - assert_eq!( - >::all_extrinsics_weight(), - 3 * (0 /*base*/ + len /*len*/ * 1 /*byte*/) - ); + assert_eq!(>::all_extrinsics_weight(), 0); + assert_eq!(>::all_extrinsics_weight(), 0); + + assert_eq!(Executive::apply_extrinsic(xt.clone()).unwrap(), ApplyOutcome::Success); + assert_eq!(Executive::apply_extrinsic(x1.clone()).unwrap(), ApplyOutcome::Success); + assert_eq!(Executive::apply_extrinsic(x2.clone()).unwrap(), ApplyOutcome::Success); + + // default weight for `TestXt` == encoded length. + assert_eq!( >::all_extrinsics_weight(), 3 * len); + assert_eq!(>::all_extrinsics_len(), 3 * len); + + let _ = >::finalize(); + + assert_eq!(>::all_extrinsics_weight(), 0); + assert_eq!(>::all_extrinsics_weight(), 0); }); } diff --git a/srml/finality-tracker/src/lib.rs b/srml/finality-tracker/src/lib.rs index 2e17d9688f1c9..b6d59be474972 100644 --- a/srml/finality-tracker/src/lib.rs +++ b/srml/finality-tracker/src/lib.rs @@ -300,6 +300,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -313,6 +314,7 @@ mod tests { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const WindowSize: u64 = 11; diff --git a/srml/generic-asset/src/lib.rs b/srml/generic-asset/src/lib.rs index 60370600a69a6..d4a341c6ffd90 100644 --- a/srml/generic-asset/src/lib.rs +++ b/srml/generic-asset/src/lib.rs @@ -1056,6 +1056,8 @@ impl system::Trait for ElevatedTrait { type Lookup = T::Lookup; type Header = T::Header; type Event = (); + type MaximumBlockWeight = T::MaximumBlockWeight; + type MaximumBlockLength = T::MaximumBlockLength; type BlockHashCount = T::BlockHashCount; } impl Trait for ElevatedTrait { diff --git a/srml/generic-asset/src/mock.rs b/srml/generic-asset/src/mock.rs index 02e18fc335839..04c5b2d2ce157 100644 --- a/srml/generic-asset/src/mock.rs +++ b/srml/generic-asset/src/mock.rs @@ -40,6 +40,8 @@ impl_outer_origin! { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -51,6 +53,8 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = TestEvent; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; type BlockHashCount = BlockHashCount; } diff --git a/srml/grandpa/src/mock.rs b/srml/grandpa/src/mock.rs index 1420d65eb5576..71992655d1450 100644 --- a/srml/grandpa/src/mock.rs +++ b/srml/grandpa/src/mock.rs @@ -44,6 +44,7 @@ impl Trait for Test { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -57,6 +58,7 @@ impl system::Trait for Test { type Event = TestEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } mod grandpa { diff --git a/srml/indices/src/mock.rs b/srml/indices/src/mock.rs index 938aec2bc3311..ae6b31bb8cc86 100644 --- a/srml/indices/src/mock.rs +++ b/srml/indices/src/mock.rs @@ -67,6 +67,7 @@ pub struct Runtime; parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Runtime { type Origin = Origin; @@ -80,6 +81,7 @@ impl system::Trait for Runtime { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl Trait for Runtime { type AccountIndex = u64; diff --git a/srml/session/src/mock.rs b/srml/session/src/mock.rs index 915c315b1eba6..137642ca4cc69 100644 --- a/srml/session/src/mock.rs +++ b/srml/session/src/mock.rs @@ -110,6 +110,7 @@ pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; pub const MinimumPeriod: u64 = 5; } impl system::Trait for Test { @@ -124,6 +125,7 @@ impl system::Trait for Test { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl timestamp::Trait for Test { type Moment = u64; diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index 1c420f6e1c47b..2bf95bbf8b44b 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -88,6 +88,7 @@ pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -101,6 +102,7 @@ impl system::Trait for Test { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const TransferFee: u64 = 0; diff --git a/srml/support/Cargo.toml b/srml/support/Cargo.toml index 88cad9651a81e..6abe1fb336481 100644 --- a/srml/support/Cargo.toml +++ b/srml/support/Cargo.toml @@ -20,7 +20,7 @@ bitmask = { version = "0.5", default-features = false } [dev-dependencies] pretty_assertions = "0.6.1" -srml-system = { path = "../system", default-features = false } +srml-system = { path = "../system" } [features] default = ["std"] diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index ac5e0604446e4..476c82a1136c7 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -25,9 +25,11 @@ pub use srml_metadata::{ FunctionMetadata, DecodeDifferent, DecodeDifferentArray, FunctionArgumentMetadata, ModuleConstantMetadata, DefaultByte, DefaultByteGetter, }; -pub use sr_primitives::{ - weights::{TransactionWeight, Weigh, Weight, WeighData}, traits::{Dispatchable, DispatchResult} +pub use sr_primitives::weights::{SimpleDispatchInfo, GetDispatchInfo, DispatchInfo, WeighData, + ClassifyDispatch, + TransactionPriority }; +pub use sr_primitives::traits::{Dispatchable, DispatchResult}; /// A type that cannot be instantiated. pub enum Never {} @@ -584,7 +586,7 @@ macro_rules! decl_module { { $( $constants )* } [ $( $dispatchables )* ] $(#[doc = $doc_attr])* - #[weight = $crate::dispatch::TransactionWeight::default()] + #[weight = $crate::dispatch::SimpleDispatchInfo::default()] $fn_vis fn $fn_name( $from $(, $(#[$codec_attr])* $param_name : $param )* ) $( -> $result )* { $( $impl )* } @@ -1108,15 +1110,38 @@ macro_rules! decl_module { } // Implement weight calculation function for Call - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Weigh + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::GetDispatchInfo for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { - fn weigh(&self) -> $crate::dispatch::Weight { - match self { - $( $call_type::$fn_name($( ref $param_name ),*) => - <$crate::dispatch::WeighData<( $( & $param, )* )>>::weigh_data(&$weight, ($( $param_name, )*)), )* - $call_type::__PhantomItem(_, _) => { unreachable!("__PhantomItem should never be used.") }, - } + fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo { + $( + if let $call_type::$fn_name($( ref $param_name ),*) = self { + let weight = >::weigh_data( + &$weight, + ($( $param_name, )*) + ); + let class = >::classify_dispatch( + &$weight, + ($( $param_name, )*) + ); + return $crate::dispatch::DispatchInfo { weight, class }; + } + if let $call_type::__PhantomItem(_, _) = self { unreachable!("__PhantomItem should never be used.") } + )* + // Defensive only: this function must have already returned at this point. + // all dispatchable function will have a weight which has the `::default` + // implementation of `SimpleDispatchInfo`. Nonetheless, we create one if it does + // not exist. + let weight = >::weigh_data( + &$crate::dispatch::SimpleDispatchInfo::default(), + () + ); + let class = >::classify_dispatch( + &$crate::dispatch::SimpleDispatchInfo::default(), + () + ); + $crate::dispatch::DispatchInfo { weight, class } + } } @@ -1261,10 +1286,10 @@ macro_rules! impl_outer_dispatch { $camelcase ( $crate::dispatch::CallableCallFor<$camelcase, $runtime> ) ,)* } - impl $crate::dispatch::Weigh for $call_type { - fn weigh(&self) -> $crate::dispatch::Weight { + impl $crate::dispatch::GetDispatchInfo for $call_type { + fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo { match self { - $( $call_type::$camelcase(call) => call.weigh(), )* + $( $call_type::$camelcase(call) => call.get_dispatch_info(), )* } } } @@ -1571,6 +1596,7 @@ macro_rules! __check_reserved_fn_name { mod tests { use super::*; use crate::runtime_primitives::traits::{OnInitialize, OnFinalize}; + use sr_primitives::weights::{DispatchInfo, DispatchClass}; pub trait Trait: system::Trait + Sized where Self::AccountId: From { type Origin; @@ -1596,7 +1622,7 @@ mod tests { fn aux_0(_origin) -> Result { unreachable!() } fn aux_1(_origin, #[compact] _data: u32) -> Result { unreachable!() } fn aux_2(_origin, _data: i32, _data2: String) -> Result { unreachable!() } - #[weight = TransactionWeight::Basic(10, 100)] + #[weight = SimpleDispatchInfo::FixedNormal(10)] fn aux_3(_origin) -> Result { unreachable!() } fn aux_4(_origin, _data: i32) -> Result { unreachable!() } fn aux_5(_origin, _data: i32, #[compact] _data2: u32) -> Result { unreachable!() } @@ -1605,8 +1631,8 @@ mod tests { fn on_finalize(n: T::BlockNumber) { if n.into() == 42 { panic!("on_finalize") } } fn offchain_worker() {} - #[weight = TransactionWeight::Max] - fn weighted(_origin) { unreachable!() } + #[weight = SimpleDispatchInfo::FixedOperational(5)] + fn operational(_origin) { unreachable!() } } } @@ -1672,7 +1698,7 @@ mod tests { documentation: DecodeDifferent::Encode(&[]), }, FunctionMetadata { - name: DecodeDifferent::Encode("weighted"), + name: DecodeDifferent::Encode("operational"), arguments: DecodeDifferent::Encode(&[]), documentation: DecodeDifferent::Encode(&[]), }, @@ -1747,10 +1773,19 @@ mod tests { #[test] fn weight_should_attach_to_call_enum() { // max weight. not dependent on input. - assert_eq!(Call::::weighted().weight(100), 3 * 1024 * 1024); + assert_eq!( + Call::::operational().get_dispatch_info(), + DispatchInfo { weight: 5, class: DispatchClass::Operational }, + ); // default weight. - assert_eq!(Call::::aux_0().weight(5), 5 /*tx-len*/); + assert_eq!( + Call::::aux_0().get_dispatch_info(), + DispatchInfo { weight: 1, class: DispatchClass::Normal }, + ); // custom basic - assert_eq!(Call::::aux_3().weight(5), 10 + 100 * 5 ); + assert_eq!( + Call::::aux_3().get_dispatch_info(), + DispatchInfo { weight: 10, class: DispatchClass::Normal }, + ); } } diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 897d7c727635f..3ed20f247f82f 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -78,7 +78,7 @@ use rstd::prelude::*; use rstd::map; use rstd::marker::PhantomData; use primitives::{ - generic::{self, Era}, weights::Weight, traits::{ + generic::{self, Era}, weights::{Weight, DispatchInfo, DispatchClass} , traits::{ self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash, MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, @@ -194,6 +194,9 @@ pub trait Trait: 'static + Eq + Clone { /// The maximum weight of a block. type MaximumBlockWeight: Get; + + /// The maximum length of a block (in bytes). + type MaximumBlockLength: Get; } pub type DigestOf = generic::Digest<::Hash>; @@ -324,7 +327,9 @@ decl_storage! { /// Total extrinsics count for the current block. ExtrinsicCount: Option; /// Total weight for all extrinsics put together, for the current block. - AllExtrinsicsWeight: Option; + AllExtrinsicsWeight: Option; + /// Total length (in bytes) for all extrinsics put together, for the current block. + AllExtrinsicsLen: Option; /// Map of block numbers to block hashes. pub BlockHash get(block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]): map T::BlockNumber => T::Hash; /// Extrinsics data for the current block (maps an extrinsic's index to its data). @@ -546,6 +551,11 @@ impl Module { AllExtrinsicsWeight::get().unwrap_or_default() } + /// Gets a total length of all executed extrinsics. + pub fn all_extrinsics_len() -> u32 { + AllExtrinsicsLen::get().unwrap_or_default() + } + /// Start the execution of a particular block. pub fn initialize( number: &T::BlockNumber, @@ -575,6 +585,7 @@ impl Module { pub fn finalize() -> T::Header { ExtrinsicCount::kill(); AllExtrinsicsWeight::kill(); + AllExtrinsicsLen::kill(); let number = >::take(); let parent_hash = >::take(); @@ -755,19 +766,63 @@ impl Module { } } -/// Weight limit check and increment. +/// resource limit check. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckWeight(PhantomData); impl CheckWeight { - fn internal_check_weight(weight: Weight) -> Result<(), DispatchError> { + + /// Get the quota divisor of each dispatch class type. This indicates that all operational + /// dispatches can use the full capacity of any resource, while user-triggered ones can consume + /// a quarter. + fn get_dispatch_limit_divisor(class: DispatchClass) -> Weight { + match class { + DispatchClass::Operational => 1, + DispatchClass::Normal => 4, + } + } + /// Checks if the current extrinsic can fit into the block with respect to block weight limits. + /// + /// Upon successes, it returns the new block weight as a `Result`. + fn check_weight(info: DispatchInfo) -> Result { let current_weight = Module::::all_extrinsics_weight(); - let next_weight = current_weight.saturating_add(weight); - if next_weight > T::MaximumBlockWeight::get() { - return Err(DispatchError::Payment) + let maximum_weight = T::MaximumBlockWeight::get(); + let limit = maximum_weight / Self::get_dispatch_limit_divisor(info.class); + let added_weight = info.weight.min(limit); + let next_weight = current_weight.saturating_add(added_weight); + if next_weight > limit { + return Err(DispatchError::BadState) } - AllExtrinsicsWeight::put(next_weight); - Ok(()) + Ok(next_weight) + } + + /// Checks if the current extrinsic can fit into the block with respect to block length limits. + /// + /// Upon successes, it returns the new block length as a `Result`. + fn check_block_length(info: DispatchInfo, len: usize) -> Result { + let current_len = Module::::all_extrinsics_len(); + let maximum_len = T::MaximumBlockLength::get(); + let limit = maximum_len / Self::get_dispatch_limit_divisor(info.class); + let added_len = len as u32; + let next_len = current_len.saturating_add(added_len); + if next_len > limit { + return Err(DispatchError::BadState) + } + Ok(next_len) + } + + /// get the priority of an extrinsic denoted by `info`. + fn get_priority(info: DispatchInfo) -> TransactionPriority { + match info.class { + DispatchClass::Normal => info.weight.into(), + DispatchClass::Operational => Bounded::max_value() + } + } + + /// Utility constructor for tests and client code. + #[cfg(feature = "std")] + pub fn from() -> Self { + Self(PhantomData) } } @@ -780,28 +835,28 @@ impl SignedExtension for CheckWeight { fn pre_dispatch( self, _who: &Self::AccountId, - weight: Weight, - _len: usize, + info: DispatchInfo, + len: usize, ) -> Result<(), DispatchError> { - Self::internal_check_weight(weight) + let next_len = Self::check_block_length(info, len)?; + AllExtrinsicsLen::put(next_len); + let next_weight = Self::check_weight(info)?; + AllExtrinsicsWeight::put(next_weight); + Ok(()) } fn validate( &self, _who: &Self::AccountId, - _weight: Weight, - _len: usize, + info: DispatchInfo, + len: usize, ) -> Result { - // TODO: check for a maximum size and weight here as well. - // write priority based on tx weight type + tip. - Ok(ValidTransaction::default()) - } -} - -#[cfg(feature = "std")] -impl CheckWeight { - pub fn from() -> Self { - Self(PhantomData) + // There is no point in writing to storage here since changes are discarded. This basically + // discards any transaction which is bigger than the length or weight limit alone, which is + // a guarantee that it will fail in the pre-dispatch phase. + let _ = Self::check_block_length(info, len)?; + let _ = Self::check_weight(info)?; + Ok(ValidTransaction { priority: Self::get_priority(info), ..Default::default() }) } } @@ -840,7 +895,7 @@ impl SignedExtension for CheckNonce { fn pre_dispatch( self, who: &Self::AccountId, - _weight: Weight, + _info: DispatchInfo, _len: usize, ) -> Result<(), DispatchError> { let expected = >::get(who); @@ -856,7 +911,7 @@ impl SignedExtension for CheckNonce { fn validate( &self, who: &Self::AccountId, - _weight: Weight, + info: DispatchInfo, _len: usize, ) -> Result { // check index @@ -873,7 +928,7 @@ impl SignedExtension for CheckNonce { }; Ok(ValidTransaction { - priority: _weight as TransactionPriority, + priority: info.weight as TransactionPriority, requires, provides, longevity: TransactionLongevity::max_value(), @@ -960,6 +1015,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 10; pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl Trait for Test { @@ -974,6 +1030,7 @@ mod tests { type Event = u16; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } impl From for u16 { @@ -1129,31 +1186,126 @@ mod tests { fn signed_ext_check_nonce_works() { with_externalities(&mut new_test_ext(), || { >::insert(1, 1); + let info = DispatchInfo::default(); + let len = 0_usize; // stale - assert!(CheckNonce::(0).validate(&1, 0).is_err()); - assert!(CheckNonce::(0).pre_dispatch(&1, 0).is_err()); + assert!(CheckNonce::(0).validate(&1, info, len).is_err()); + assert!(CheckNonce::(0).pre_dispatch(&1, info, len).is_err()); // correct - assert!(CheckNonce::(1).validate(&1, 0).is_ok()); - assert!(CheckNonce::(1).pre_dispatch(&1, 0).is_ok()); + assert!(CheckNonce::(1).validate(&1, info, len).is_ok()); + assert!(CheckNonce::(1).pre_dispatch(&1, info, len).is_ok()); // future - assert!(CheckNonce::(5).validate(&1, 0).is_ok()); - assert!(CheckNonce::(5).pre_dispatch(&1, 0).is_err()); + assert!(CheckNonce::(5).validate(&1, info, len).is_ok()); + assert!(CheckNonce::(5).pre_dispatch(&1, info, len).is_err()); + }) + } + + #[test] + fn signed_ext_check_weight_works_user_tx() { + with_externalities(&mut new_test_ext(), || { + let small = DispatchInfo { weight: 100, ..Default::default() }; + let medium = DispatchInfo { + weight: >::get() / 4 - 1, + ..Default::default() + }; + let big = DispatchInfo { + weight: >::get() / 4 + 1, + ..Default::default() + }; + let len = 0_usize; + + let reset_check_weight = |i, f, s| { + AllExtrinsicsWeight::put(s); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, i, len); + if f { assert!(r.is_err()) } else { assert!(r.is_ok()) } + }; + + reset_check_weight(small, false, 0); + reset_check_weight(medium, false, 0); + reset_check_weight(big, true, 1); + }) + } + + #[test] + fn signed_ext_check_weight_fee_works() { + with_externalities(&mut new_test_ext(), || { + let free = DispatchInfo { weight: 0, ..Default::default() }; + let len = 0_usize; + + assert_eq!(System::all_extrinsics_weight(), 0); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, free, len); + assert!(r.is_ok()); + assert_eq!(System::all_extrinsics_weight(), 0); + }) + } + + #[test] + fn signed_ext_check_weight_max_works() { + with_externalities(&mut new_test_ext(), || { + let max = DispatchInfo { weight: Weight::max_value(), ..Default::default() }; + let len = 0_usize; + + assert_eq!(System::all_extrinsics_weight(), 0); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, max, len); + assert!(r.is_ok()); + assert_eq!(System::all_extrinsics_weight(), >::get() / 4); }) } #[test] - fn signed_ext_check_weight_works() { + fn signed_ext_check_weight_works_operational_tx() { with_externalities(&mut new_test_ext(), || { - // small - AllExtrinsicsWeight::put(512); - assert!(CheckWeight::(PhantomData).pre_dispatch(&1, 100).is_ok()); - // almost - AllExtrinsicsWeight::put(512); - assert!(CheckWeight::(PhantomData).pre_dispatch(&1, 512).is_ok()); - // big - AllExtrinsicsWeight::put(512); - assert!(CheckWeight::(PhantomData).pre_dispatch(&1, 513).is_err()); + let normal = DispatchInfo { weight: 100, ..Default::default() }; + let op = DispatchInfo { weight: 100, class: DispatchClass::Operational }; + let len = 0_usize; + + // given almost full block + AllExtrinsicsWeight::put(>::get() / 4); + // will not fit. + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, normal, len).is_err()); + // will fit. + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, op, len).is_ok()); + + // likewise for length limit. + let len = 100_usize; + AllExtrinsicsLen::put(>::get() / 4); + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, normal, len).is_err()); + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, op, len).is_ok()); + }) + } + + #[test] + fn signed_ext_check_weight_priority_works() { + with_externalities(&mut new_test_ext(), || { + let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal }; + let op = DispatchInfo { weight: 100, class: DispatchClass::Operational }; + let len = 0_usize; + + assert_eq!( + CheckWeight::(PhantomData).validate(&1, normal, len).unwrap().priority, + 100, + ); + assert_eq!( + CheckWeight::(PhantomData).validate(&1, op, len).unwrap().priority, + Bounded::max_value(), + ); + }) + } + + #[test] + fn signed_ext_check_weight_block_size_works() { + with_externalities(&mut new_test_ext(), || { + let tx = DispatchInfo::default(); + + let reset_check_weight = |s, f| { + AllExtrinsicsLen::put(0); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, tx, s); + if f { assert!(r.is_err()) } else { assert!(r.is_ok()) } + }; + reset_check_weight(128, false); + reset_check_weight(512, false); + reset_check_weight(513, true); }) } diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index b25cab3e03156..9fdc05ee2a248 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -339,6 +339,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -352,6 +353,7 @@ mod tests { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const MinimumPeriod: u64 = 5; diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index aa49190d5abb2..85edc89a30c83 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -371,6 +371,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; } impl system::Trait for Test { type Origin = Origin; @@ -384,6 +385,7 @@ mod tests { type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; } parameter_types! { pub const ExistentialDeposit: u64 = 0;