From b042a93f67050a8e3cbea565883df62a08996cbf Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 11 Jul 2019 13:24:53 +0200 Subject: [PATCH 01/20] Make extrinsics extensible. Also Remove old extrinsic types. --- .../src/generic/checked_extrinsic.rs | 34 +- core/sr-primitives/src/generic/mod.rs | 4 - .../src/generic/unchecked_extrinsic.rs | 339 +++++++++++++----- .../unchecked_mortal_compact_extrinsic.rs | 306 ---------------- .../src/generic/unchecked_mortal_extrinsic.rs | 307 ---------------- core/sr-primitives/src/traits.rs | 71 ++++ node-template/runtime/src/lib.rs | 2 +- node/runtime/src/lib.rs | 2 +- srml/collective/src/lib.rs | 2 +- srml/elections/src/lib.rs | 2 +- srml/executive/src/lib.rs | 49 ++- srml/support/test/tests/instance.rs | 4 +- srml/support/test/tests/issue2219.rs | 4 +- 13 files changed, 398 insertions(+), 728 deletions(-) delete mode 100644 core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs delete mode 100644 core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs diff --git a/core/sr-primitives/src/generic/checked_extrinsic.rs b/core/sr-primitives/src/generic/checked_extrinsic.rs index ee43b3af2e951..c1d22d7c09186 100644 --- a/core/sr-primitives/src/generic/checked_extrinsic.rs +++ b/core/sr-primitives/src/generic/checked_extrinsic.rs @@ -17,7 +17,8 @@ //! Generic implementation of an extrinsic that has passed the verification //! stage. -use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay}; +use rstd::result::Result; +use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, SignedExtension, DispatchError}; use crate::weights::{Weighable, Weight}; /// Definition of something that the external world might want to say; its @@ -25,23 +26,27 @@ use crate::weights::{Weighable, Weight}; /// regards to the signature. #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Debug))] -pub struct CheckedExtrinsic { +pub struct CheckedExtrinsic { /// Who this purports to be from and the number of extrinsics have come before /// from the same signer, if anyone (note this is not a signature). - pub signed: Option<(AccountId, Index)>, + pub signed: Option<(AccountId, Index, Extra)>, + /// The function that should be called. pub function: Call, } -impl traits::Applyable for CheckedExtrinsic +impl traits::Applyable +for + CheckedExtrinsic where AccountId: Member + MaybeDisplay, Index: Member + MaybeDisplay + SimpleArithmetic, Call: Member, + Extra: SignedExtension, { - type Index = Index; type AccountId = AccountId; type Call = Call; + type Index = Index; fn index(&self) -> Option<&Self::Index> { self.signed.as_ref().map(|x| &x.1) @@ -52,11 +57,26 @@ where } fn deconstruct(self) -> (Self::Call, Option) { - (self.function, self.signed.map(|x| x.0)) + if let Some((id, _, extra)) = self.signed { + (self.function, Some(id)) + } else { + (self.function, None) + } + } + + fn pre_dispatch(self, + weight: crate::weights::Weight, + ) -> Result<(Self::Call, Option), DispatchError> { + if let Some((id, index, extra)) = self.signed { + Extra::pre_dispatch(extra, &id, &index, &self.function, weight)?; + Ok((self.function, Some(id))) + } else { + Ok((self.function, None)) + } } } -impl Weighable for CheckedExtrinsic +impl Weighable for CheckedExtrinsic where Call: Weighable, { diff --git a/core/sr-primitives/src/generic/mod.rs b/core/sr-primitives/src/generic/mod.rs index a4e4106780efc..1511753d2c524 100644 --- a/core/sr-primitives/src/generic/mod.rs +++ b/core/sr-primitives/src/generic/mod.rs @@ -19,8 +19,6 @@ // end::description[] mod unchecked_extrinsic; -mod unchecked_mortal_extrinsic; -mod unchecked_mortal_compact_extrinsic; mod era; mod checked_extrinsic; mod header; @@ -30,8 +28,6 @@ mod digest; mod tests; pub use self::unchecked_extrinsic::UncheckedExtrinsic; -pub use self::unchecked_mortal_extrinsic::UncheckedMortalExtrinsic; -pub use self::unchecked_mortal_compact_extrinsic::UncheckedMortalCompactExtrinsic; pub use self::era::{Era, Phase}; pub use self::checked_extrinsic::CheckedExtrinsic; pub use self::header::Header; diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index d6e0d60e2c218..7eb31e28fd608 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -20,48 +20,73 @@ use std::fmt; use rstd::prelude::*; -use crate::codec::{Decode, Encode, Codec, Input, HasCompact}; -use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, Lookup, Extrinsic}; -use super::CheckedExtrinsic; +use runtime_io::blake2_256; +use crate::codec::{Decode, Encode, Codec, Input, Compact}; +use crate::traits::{ + self, Member, SimpleArithmetic, MaybeDebug, MaybeDisplay, CurrentHeight, SignedExtension, + BlockNumberToHash, Lookup, Checkable, Extrinsic, SaturatedConversion, DispatchError +}; +use super::{CheckedExtrinsic, Era}; -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -pub struct SignatureContent -where - Address: Codec, - Index: HasCompact + Codec, - Signature: Codec, -{ - signed: Address, - signature: Signature, - index: Index, +const TRANSACTION_VERSION: u8 = 1; +/* +/// A type with which the signed payload of a transaction may be extended. +pub trait OSignedExtension: Codec + MaybeDebug {} + +impl SignedExtra for T {} + +/// A type that names a `SignedExtension` and knows how to deal with it. +pub trait OExtension { + /// An extra data field that should go into the signed part of the payload. + type SignedPayload: OSignedExtension; + + /// Anything that should be executed immediately before dispatch. + fn pre_dispatch( + a: &AccountId, + c: &Call, + e: Self::SignedPayload + ) -> Result<(), DispatchError>; } +impl OExtension for () { + type SignedPayload = (); + + fn pre_dispatch( + _: &AccountId, + _: &Call, + _: () + ) -> Result<(), DispatchError> { Ok(()) } +} +*/ /// A extrinsic right from the external world. This is unchecked and so /// can contain a signature. #[derive(PartialEq, Eq, Clone)] -pub struct UncheckedExtrinsic +pub struct UncheckedExtrinsic where - Address: Codec, - Index: HasCompact + Codec, - Signature: Codec, + Extra: SignedExtension { - /// The signature, address and number of extrinsics have come before from - /// the same signer, if this is a signed extrinsic. - pub signature: Option>, + /// The signature, address, number of extrinsics have come before from + /// the same signer and an era describing the longevity of this transaction, + /// if this is a signed extrinsic. + pub signature: Option<(Address, Signature, Compact, Era, Extra)>, /// The function that should be called. pub function: Call, } -impl UncheckedExtrinsic -where - Address: Codec, - Index: HasCompact + Codec, - Signature: Codec, +impl + UncheckedExtrinsic { /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature) -> Self { + pub fn new_signed( + index: Index, + function: Call, + signed: Address, + signature: Signature, + era: Era, + extra: Extra + ) -> Self { UncheckedExtrinsic { - signature: Some(SignatureContent{signed, signature, index}), + signature: Some((signed, signature, index.into(), era, extra)), function, } } @@ -75,29 +100,54 @@ where } } -impl traits::Checkable - for UncheckedExtrinsic +impl Extrinsic + for UncheckedExtrinsic +{ + fn is_signed(&self) -> Option { + Some(self.signature.is_some()) + } +} + +impl + Checkable +for + UncheckedExtrinsic where - Address: Member + MaybeDisplay + Codec, - Index: Member + MaybeDisplay + SimpleArithmetic + Codec, + Address: Member + MaybeDisplay, + Index: Member + MaybeDisplay + SimpleArithmetic, + Compact: Encode, Call: Encode + Member, - Signature: Member + traits::Verify + Codec, + Signature: Member + traits::Verify, + Extra: SignedExtension, AccountId: Member + MaybeDisplay, - Context: Lookup, + BlockNumber: SimpleArithmetic, + Hash: Encode, + Context: Lookup + + CurrentHeight + + BlockNumberToHash, { - type Checked = CheckedExtrinsic; + type Checked = CheckedExtrinsic; fn check(self, context: &Context) -> Result { Ok(match self.signature { - Some(SignatureContent{signed, signature, index}) => { - let payload = (index, self.function); + Some((signed, signature, index, era, extra)) => { + let current_u64 = context.current_height().saturated_into::(); + let h = context.block_number_to_hash(era.birth(current_u64).saturated_into()) + .ok_or("transaction birth block ancient")?; let signed = context.lookup(signed)?; - if !crate::verify_encoded_lazy(&signature, &payload, &signed) { + let raw_payload = (index, self.function, era, h, extra); + if !raw_payload.using_encoded(|payload| { + if payload.len() > 256 { + signature.verify(&blake2_256(payload)[..], &signed) + } else { + signature.verify(payload, &signed) + } + }) { return Err(crate::BAD_SIGNATURE) } CheckedExtrinsic { - signed: Some((signed, payload.0)), - function: payload.1, + signed: Some((signed, (raw_payload.0).0, raw_payload.4)), + function: raw_payload.1, } } None => CheckedExtrinsic { @@ -108,19 +158,14 @@ where } } -impl< - Address: Codec, - Index: HasCompact + Codec, - Signature: Codec, - Call, -> Extrinsic for UncheckedExtrinsic { - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) - } -} - -impl Decode - for UncheckedExtrinsic +impl Decode + for UncheckedExtrinsic +where + Address: Decode, + Signature: Decode, + Compact: Decode, + Call: Decode, + Extra: SignedExtension, { fn decode(input: &mut I) -> Option { // This is a little more complicated than usual since the binary format must be compatible @@ -129,70 +174,202 @@ impl // to use this). let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?; + let version = input.read_byte()?; + + let is_signed = version & 0b1000_0000 != 0; + let version = version & 0b0111_1111; + if version != TRANSACTION_VERSION { + return None + } + Some(UncheckedExtrinsic { - signature: Decode::decode(input)?, + signature: if is_signed { Some(Decode::decode(input)?) } else { None }, function: Decode::decode(input)?, }) } } -impl Encode - for UncheckedExtrinsic +impl Encode + for UncheckedExtrinsic +where + Address: Encode, + Signature: Encode, + Compact: Encode, + Call: Encode, + Extra: SignedExtension, { fn encode(&self) -> Vec { super::encode_with_vec_prefix::(|v| { - self.signature.encode_to(v); + // 1 byte version id. + match self.signature.as_ref() { + Some(s) => { + v.push(TRANSACTION_VERSION | 0b1000_0000); + s.encode_to(v); + } + None => { + v.push(TRANSACTION_VERSION & 0b0111_1111); + } + } self.function.encode_to(v); }) } } #[cfg(feature = "std")] -impl serde::Serialize - for UncheckedExtrinsic +impl serde::Serialize + for UncheckedExtrinsic + where Compact: Encode { fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { - self.using_encoded(|bytes| ::substrate_primitives::bytes::serialize(bytes, seq)) + self.using_encoded(|bytes| seq.serialize_bytes(bytes)) } } #[cfg(feature = "std")] -impl fmt::Debug - for UncheckedExtrinsic +impl fmt::Debug + for UncheckedExtrinsic where - Address: fmt::Debug + Codec, - Index: fmt::Debug + HasCompact + Codec, - Signature: Codec, + Address: fmt::Debug, + Index: fmt::Debug, Call: fmt::Debug, + Extra: SignedExtension, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.signed, &x.index)), self.function) + write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2, &x.4)), self.function) } } #[cfg(test)] -mod test { - use crate::codec::{Decode, Encode}; - use super::UncheckedExtrinsic; +mod tests { + use super::*; + use runtime_io::blake2_256; + use crate::codec::{Encode, Decode}; + use crate::traits::{SignedExtension, DispatchError}; + use serde::{Serialize, Deserialize}; + + struct TestContext; + impl Lookup for TestContext { + type Source = u64; + type Target = u64; + fn lookup(&self, s: u64) -> Result { Ok(s) } + } + impl CurrentHeight for TestContext { + type BlockNumber = u64; + fn current_height(&self) -> u64 { 42 } + } + impl BlockNumberToHash for TestContext { + type BlockNumber = u64; + type Hash = u64; + fn block_number_to_hash(&self, n: u64) -> Option { Some(n) } + } + + #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)] + struct TestSig(u64, Vec); + impl traits::Verify for TestSig { + type Signer = u64; + fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { + *signer == self.0 && msg.get() == &self.1[..] + } + } + + const DUMMY_ACCOUNTID: u64 = 0; + + #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd)] + struct TestExtra; + impl SignedExtension for TestExtra { + type AccountId = u64; + type Index = u64; + type Call = Vec; + fn pre_dispatch( + self, + _a: &Self::AccountId, + _i: &Self::Index, + _c: &Self::Call, + _w: crate::weights::Weight, + ) -> Result<(), DispatchError> { + Ok(()) + } + } + type Ex = UncheckedExtrinsic, TestSig, TestExtra>; + type CEx = CheckedExtrinsic, TestExtra>; #[test] - fn encoding_matches_vec() { - type Extrinsic = UncheckedExtrinsic; - let ex = Extrinsic::new_unsigned(42); - let encoded = ex.encode(); - let decoded = Extrinsic::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(decoded, ex); - let as_vec: Vec = Decode::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(as_vec.encode(), encoded); + fn unsigned_codec_should_work() { + let ux = Ex::new_unsigned(vec![0u8;0]); + let encoded = ux.encode(); + assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); + } + + #[test] + fn signed_codec_should_work() { + let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal(), TestExtra); + let encoded = ux.encode(); + assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); + } + + #[test] + fn large_signed_codec_should_work() { + let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8; 257], Era::immortal(), 0u64).using_encoded(blake2_256)[..].to_owned()), Era::immortal(), TestExtra); + let encoded = ux.encode(); + assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); + } + + #[test] + fn unsigned_check_should_work() { + let ux = Ex::new_unsigned(vec![0u8;0]); + assert!(!ux.is_signed().unwrap_or(false)); + assert!(>::check(ux, &TestContext).is_ok()); + } + + #[test] + fn badly_signed_check_should_fail() { + let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal(), TestExtra); + assert!(ux.is_signed().unwrap_or(false)); + assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); } + #[test] + fn immortal_signed_check_should_work() { + let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal(), TestExtra); + assert!(ux.is_signed().unwrap_or(false)); + assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0, TestExtra)), function: vec![0u8;0] })); + } + + #[test] + fn mortal_signed_check_should_work() { + let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::mortal(32, 42), 42u64).encode()), Era::mortal(32, 42), TestExtra); + assert!(ux.is_signed().unwrap_or(false)); + assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0, TestExtra)), function: vec![0u8;0] })); + } + + #[test] + fn later_mortal_signed_check_should_work() { + let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::mortal(32, 11), 11u64).encode()), Era::mortal(32, 11), TestExtra); + assert!(ux.is_signed().unwrap_or(false)); + assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0, TestExtra)), function: vec![0u8;0] })); + } + + #[test] + fn too_late_mortal_signed_check_should_fail() { + let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10), TestExtra); + assert!(ux.is_signed().unwrap_or(false)); + assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + } #[test] - #[cfg(feature = "std")] - fn serialization_of_unchecked_extrinsics() { - type Extrinsic = UncheckedExtrinsic; - let ex = Extrinsic::new_unsigned(42); + fn too_early_mortal_signed_check_should_fail() { + let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43), TestExtra); + assert!(ux.is_signed().unwrap_or(false)); + assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); + } - assert_eq!(serde_json::to_string(&ex).unwrap(), "\"0x14002a000000\""); + #[test] + fn encoding_matches_vec() { + let ex = Ex::new_unsigned(vec![0u8;0]); + let encoded = ex.encode(); + let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); + assert_eq!(decoded, ex); + let as_vec: Vec = Decode::decode(&mut encoded.as_slice()).unwrap(); + assert_eq!(as_vec.encode(), encoded); } } diff --git a/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs deleted file mode 100644 index 36e17fc277cde..0000000000000 --- a/core/sr-primitives/src/generic/unchecked_mortal_compact_extrinsic.rs +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2017-2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate 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. If not, see . - -//! Generic implementation of an unchecked (pre-verification) extrinsic. - -#[cfg(feature = "std")] -use std::fmt; - -use rstd::prelude::*; -use runtime_io::blake2_256; -use crate::codec::{Decode, Encode, Input, Compact}; -use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, BlockNumberToHash, - Lookup, Checkable, Extrinsic, SaturatedConversion}; -use super::{CheckedExtrinsic, Era}; - -const TRANSACTION_VERSION: u8 = 1; - -/// A extrinsic right from the external world. This is unchecked and so -/// can contain a signature. -#[derive(PartialEq, Eq, Clone)] -pub struct UncheckedMortalCompactExtrinsic { - /// The signature, address, number of extrinsics have come before from - /// the same signer and an era describing the longevity of this transaction, - /// if this is a signed extrinsic. - pub signature: Option<(Address, Signature, Compact, Era)>, - /// The function that should be called. - pub function: Call, -} - -impl UncheckedMortalCompactExtrinsic { - /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature, era: Era) -> Self { - UncheckedMortalCompactExtrinsic { - signature: Some((signed, signature, index.into(), era)), - function, - } - } - - /// New instance of an unsigned extrinsic aka "inherent". - pub fn new_unsigned(function: Call) -> Self { - UncheckedMortalCompactExtrinsic { - signature: None, - function, - } - } -} - -impl Extrinsic for UncheckedMortalCompactExtrinsic { - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) - } -} - -impl Checkable - for UncheckedMortalCompactExtrinsic -where - Address: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Compact: Encode, - Call: Encode + Member, - Signature: Member + traits::Verify, - AccountId: Member + MaybeDisplay, - BlockNumber: SimpleArithmetic, - Hash: Encode, - Context: Lookup - + CurrentHeight - + BlockNumberToHash, -{ - type Checked = CheckedExtrinsic; - - fn check(self, context: &Context) -> Result { - Ok(match self.signature { - Some((signed, signature, index, era)) => { - let current_u64 = context.current_height().saturated_into::(); - let h = context.block_number_to_hash(era.birth(current_u64).saturated_into()) - .ok_or("transaction birth block ancient")?; - let signed = context.lookup(signed)?; - let raw_payload = (index, self.function, era, h); - if !raw_payload.using_encoded(|payload| { - if payload.len() > 256 { - signature.verify(&blake2_256(payload)[..], &signed) - } else { - signature.verify(payload, &signed) - } - }) { - return Err(crate::BAD_SIGNATURE) - } - CheckedExtrinsic { - signed: Some((signed, (raw_payload.0).0)), - function: raw_payload.1, - } - } - None => CheckedExtrinsic { - signed: None, - function: self.function, - }, - }) - } -} - -impl Decode - for UncheckedMortalCompactExtrinsic -where - Address: Decode, - Signature: Decode, - Compact: Decode, - Call: Decode, -{ - fn decode(input: &mut I) -> Option { - // This is a little more complicated than usual since the binary format must be compatible - // with substrate's generic `Vec` type. Basically this just means accepting that there - // will be a prefix of vector length (we don't need - // to use this). - let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?; - - let version = input.read_byte()?; - - let is_signed = version & 0b1000_0000 != 0; - let version = version & 0b0111_1111; - if version != TRANSACTION_VERSION { - return None - } - - Some(UncheckedMortalCompactExtrinsic { - signature: if is_signed { Some(Decode::decode(input)?) } else { None }, - function: Decode::decode(input)?, - }) - } -} - -impl Encode - for UncheckedMortalCompactExtrinsic -where - Address: Encode, - Signature: Encode, - Compact: Encode, - Call: Encode, -{ - fn encode(&self) -> Vec { - super::encode_with_vec_prefix::(|v| { - // 1 byte version id. - match self.signature.as_ref() { - Some(s) => { - v.push(TRANSACTION_VERSION | 0b1000_0000); - s.encode_to(v); - } - None => { - v.push(TRANSACTION_VERSION & 0b0111_1111); - } - } - self.function.encode_to(v); - }) - } -} - -#[cfg(feature = "std")] -impl serde::Serialize - for UncheckedMortalCompactExtrinsic - where Compact: Encode -{ - fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} - -#[cfg(feature = "std")] -impl fmt::Debug for UncheckedMortalCompactExtrinsic where - Address: fmt::Debug, - Index: fmt::Debug, - Call: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedMortalCompactExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2)), self.function) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::blake2_256; - use crate::codec::{Encode, Decode}; - use serde::{Serialize, Deserialize}; - - struct TestContext; - impl Lookup for TestContext { - type Source = u64; - type Target = u64; - fn lookup(&self, s: u64) -> Result { Ok(s) } - } - impl CurrentHeight for TestContext { - type BlockNumber = u64; - fn current_height(&self) -> u64 { 42 } - } - impl BlockNumberToHash for TestContext { - type BlockNumber = u64; - type Hash = u64; - fn block_number_to_hash(&self, n: u64) -> Option { Some(n) } - } - - #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)] - struct TestSig(u64, Vec); - impl traits::Verify for TestSig { - type Signer = u64; - fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { - *signer == self.0 && msg.get() == &self.1[..] - } - } - - const DUMMY_ACCOUNTID: u64 = 0; - - type Ex = UncheckedMortalCompactExtrinsic, TestSig>; - type CEx = CheckedExtrinsic>; - - #[test] - fn unsigned_codec_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal()); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn large_signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8; 257], Era::immortal(), 0u64).using_encoded(blake2_256)[..].to_owned()), Era::immortal()); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn unsigned_check_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); - assert!(!ux.is_signed().unwrap_or(false)); - assert!(>::check(ux, &TestContext).is_ok()); - } - - #[test] - fn badly_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal()); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn immortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal()); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::mortal(32, 42), 42u64).encode()), Era::mortal(32, 42)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn later_mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::mortal(32, 11), 11u64).encode()), Era::mortal(32, 11)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn too_late_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn too_early_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn encoding_matches_vec() { - let ex = Ex::new_unsigned(vec![0u8;0]); - let encoded = ex.encode(); - let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(decoded, ex); - let as_vec: Vec = Decode::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(as_vec.encode(), encoded); - } -} diff --git a/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs deleted file mode 100644 index 7f92b20edd0c3..0000000000000 --- a/core/sr-primitives/src/generic/unchecked_mortal_extrinsic.rs +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2017-2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate 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. If not, see . - -//! Generic implementation of an unchecked (pre-verification) extrinsic. - -#[cfg(feature = "std")] -use std::fmt; - -use rstd::prelude::*; -use runtime_io::blake2_256; -use crate::codec::{Decode, Encode, Input}; -use crate::traits::{ - self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, BlockNumberToHash, - Lookup, Checkable, Extrinsic, SaturatedConversion -}; -use super::{CheckedExtrinsic, Era}; - -const TRANSACTION_VERSION: u8 = 1; - -/// A extrinsic right from the external world. This is unchecked and so -/// can contain a signature. -#[derive(PartialEq, Eq, Clone)] -pub struct UncheckedMortalExtrinsic { - /// The signature, address, number of extrinsics have come before from - /// the same signer and an era describing the longevity of this transaction, - /// if this is a signed extrinsic. - pub signature: Option<(Address, Signature, Index, Era)>, - /// The function that should be called. - pub function: Call, -} - -impl UncheckedMortalExtrinsic { - /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature, era: Era) -> Self { - UncheckedMortalExtrinsic { - signature: Some((signed, signature, index, era)), - function, - } - } - - /// New instance of an unsigned extrinsic aka "inherent". - pub fn new_unsigned(function: Call) -> Self { - UncheckedMortalExtrinsic { - signature: None, - function, - } - } -} - -impl Extrinsic for UncheckedMortalExtrinsic { - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) - } -} - -impl Checkable - for UncheckedMortalExtrinsic -where - Address: Member + MaybeDisplay, - Index: Encode + Member + MaybeDisplay + SimpleArithmetic, - Call: Encode + Member, - Signature: Member + traits::Verify, - AccountId: Member + MaybeDisplay, - BlockNumber: SimpleArithmetic, - Hash: Encode, - Context: Lookup - + CurrentHeight - + BlockNumberToHash, -{ - type Checked = CheckedExtrinsic; - - fn check(self, context: &Context) -> Result { - Ok(match self.signature { - Some((signed, signature, index, era)) => { - let current_u64 = context.current_height().saturated_into::(); - let h = context.block_number_to_hash(era.birth(current_u64).saturated_into()) - .ok_or("transaction birth block ancient")?; - let signed = context.lookup(signed)?; - let raw_payload = (index, self.function, era, h); - - if !raw_payload.using_encoded(|payload| { - if payload.len() > 256 { - signature.verify(&blake2_256(payload)[..], &signed) - } else { - signature.verify(payload, &signed) - } - }) { - return Err(crate::BAD_SIGNATURE) - } - CheckedExtrinsic { - signed: Some((signed, raw_payload.0)), - function: raw_payload.1, - } - } - None => CheckedExtrinsic { - signed: None, - function: self.function, - }, - }) - } -} - -impl Decode - for UncheckedMortalExtrinsic -where - Address: Decode, - Signature: Decode, - Index: Decode, - Call: Decode, -{ - fn decode(input: &mut I) -> Option { - // This is a little more complicated than usual since the binary format must be compatible - // with substrate's generic `Vec` type. Basically this just means accepting that there - // will be a prefix of vector length (we don't need - // to use this). - let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?; - - let version = input.read_byte()?; - - let is_signed = version & 0b1000_0000 != 0; - let version = version & 0b0111_1111; - if version != TRANSACTION_VERSION { - return None - } - - Some(UncheckedMortalExtrinsic { - signature: if is_signed { Some(Decode::decode(input)?) } else { None }, - function: Decode::decode(input)?, - }) - } -} - -impl Encode - for UncheckedMortalExtrinsic -where - Address: Encode, - Signature: Encode, - Index: Encode, - Call: Encode, -{ - fn encode(&self) -> Vec { - super::encode_with_vec_prefix::(|v| { - // 1 byte version id. - match self.signature.as_ref() { - Some(s) => { - v.push(TRANSACTION_VERSION | 0b1000_0000); - s.encode_to(v); - } - None => { - v.push(TRANSACTION_VERSION & 0b0111_1111); - } - } - self.function.encode_to(v); - }) - } -} - -#[cfg(feature = "std")] -impl serde::Serialize - for UncheckedMortalExtrinsic -{ - fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} - -#[cfg(feature = "std")] -impl fmt::Debug for UncheckedMortalExtrinsic where - Address: fmt::Debug, - Index: fmt::Debug, - Call: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedMortalExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2)), self.function) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::blake2_256; - use crate::codec::{Encode, Decode}; - use serde::{Serialize, Deserialize}; - - struct TestContext; - impl Lookup for TestContext { - type Source = u64; - type Target = u64; - fn lookup(&self, s: u64) -> Result { Ok(s) } - } - impl CurrentHeight for TestContext { - type BlockNumber = u64; - fn current_height(&self) -> u64 { 42 } - } - impl BlockNumberToHash for TestContext { - type BlockNumber = u64; - type Hash = u64; - fn block_number_to_hash(&self, n: u64) -> Option { Some(n) } - } - - #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)] - struct TestSig(u64, Vec); - impl traits::Verify for TestSig { - type Signer = u64; - fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { - *signer == self.0 && msg.get() == &self.1[..] - } - } - - const DUMMY_ACCOUNTID: u64 = 0; - - type Ex = UncheckedMortalExtrinsic, TestSig>; - type CEx = CheckedExtrinsic>; - - #[test] - fn unsigned_codec_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal()); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn large_signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8; 257], Era::immortal(), 0u64).using_encoded(blake2_256)[..].to_owned()), Era::immortal()); - let encoded = ux.encode(); - assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); - } - - #[test] - fn unsigned_check_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); - assert!(!ux.is_signed().unwrap_or(false)); - assert!(>::check(ux, &TestContext).is_ok()); - } - - #[test] - fn badly_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal()); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn immortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal()); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 42), 42u64).encode()), Era::mortal(32, 42)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn later_mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 11), 11u64).encode()), Era::mortal(32, 11)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0)), function: vec![0u8;0] })); - } - - #[test] - fn too_late_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn too_early_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43)); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn encoding_matches_vec() { - let ex = Ex::new_unsigned(vec![0u8;0]); - let encoded = ex.encode(); - let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(decoded, ex); - let as_vec: Vec = Decode::decode(&mut encoded.as_slice()).unwrap(); - assert_eq!(as_vec.encode(), encoded); - } -} diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index a6d94babbe577..8cf8449c03553 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -749,6 +749,68 @@ 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. +pub enum DispatchError { + /// General error to do with the inability to pay some fees (e.g. account balance too low). + Payment, + + /// General error to do with the permissions of the sender. + NoPermission, + + /// General error to do with the state of the system in general. + BadState, + + /// General error to do with the transaction being outdated (e.g. nonce too low). + Stale, + + /// General error to do with the transaction not yet being valid (e.g. nonce too high). + Future, + + /// General error to do with the transaction's proofs (e.g. signature). + BadProof, +} + +/// Means by which a transaction may be extended. This type embodies both the data and the logic +/// that should be additionally associated with the transaction. It should be plain old data. +pub trait SignedExtension: + Codec + MaybeDebug + Sync + Send + Clone + Eq + PartialEq + Ord + PartialOrd +{ + type AccountId; + type Call; + type Index; + fn pre_dispatch( + self, + account: &Self::AccountId, + index: &Self::Index, + call: &Self::Call, + weight: crate::weights::Weight, + ) -> Result<(), DispatchError>; +} + +impl< + AccountId, + Call, + Index, + A: SignedExtension, + B: SignedExtension +> SignedExtension for (A, B) { + type AccountId = AccountId; + type Call = Call; + type Index = Index; + fn pre_dispatch( + self, + account: &Self::AccountId, + index: &Self::Index, + call: &Self::Call, + weight: crate::weights::Weight, + ) -> Result<(), DispatchError> { + self.0.pre_dispatch(account, index, call, weight)?; + self.1.pre_dispatch(account, index, call, weight) + } +} + /// An "executable" piece of information, used by the standard Substrate Executive in order to /// enact a piece of extrinsic information by marshalling and dispatching to a named function /// call. @@ -767,7 +829,16 @@ pub trait Applyable: Sized + Send + Sync { /// Returns a reference to the sender if any. fn sender(&self) -> Option<&Self::AccountId>; /// Deconstructs into function call and sender. + /// + /// @deprecated - avoid using this and instead refactor into pre_dispatch. fn deconstruct(self) -> (Self::Call, Option); + /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, + /// index and sender. + fn pre_dispatch(self, + weight: crate::weights::Weight + ) -> Result<(Self::Call, Option), DispatchError> { + Ok(self.deconstruct()) + } } /// Auxiliary wrapper that holds an api instance and binds it to the given lifetime. diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 5cf774a5e9015..8879b163ff763 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -228,7 +228,7 @@ pub type Block = generic::Block; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 04bc98cecb021..0b66424576181 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -436,7 +436,7 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. diff --git a/srml/collective/src/lib.rs b/srml/collective/src/lib.rs index 153a5df00ae8d..23b156c718e7f 100644 --- a/srml/collective/src/lib.rs +++ b/srml/collective/src/lib.rs @@ -422,7 +422,7 @@ mod tests { } pub type Block = primitives::generic::Block; - pub type UncheckedExtrinsic = primitives::generic::UncheckedMortalCompactExtrinsic; + pub type UncheckedExtrinsic = primitives::generic::UncheckedExtrinsic; srml_support::construct_runtime!( pub enum Test where diff --git a/srml/elections/src/lib.rs b/srml/elections/src/lib.rs index 359407d222c18..77597591775d7 100644 --- a/srml/elections/src/lib.rs +++ b/srml/elections/src/lib.rs @@ -1211,7 +1211,7 @@ mod tests { } pub type Block = primitives::generic::Block; - pub type UncheckedExtrinsic = primitives::generic::UncheckedMortalCompactExtrinsic; + pub type UncheckedExtrinsic = primitives::generic::UncheckedExtrinsic; srml_support::construct_runtime!( pub enum Test where diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 49d4addb3bc2b..dbfe02e00bc41 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -79,8 +79,7 @@ use rstd::marker::PhantomData; use rstd::result; use primitives::{generic::Digest, traits::{ self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, - OnInitialize, NumberFor, Block as BlockT, OffchainWorker, - ValidateUnsigned, + OnInitialize, NumberFor, Block as BlockT, OffchainWorker, ValidateUnsigned }}; use srml_support::{Dispatchable, traits::MakePayment}; use parity_codec::{Codec, Encode}; @@ -90,6 +89,8 @@ use primitives::transaction_validity::{TransactionValidity, TransactionPriority, use primitives::weights::Weighable; mod internal { + use primitives::traits::DispatchError; + pub const MAX_TRANSACTIONS_WEIGHT: u32 = 4 * 1024 * 1024; pub enum ApplyError { @@ -104,6 +105,19 @@ mod internal { Success, Fail(&'static str), } + + impl From for ApplyError { + fn from(d: DispatchError) -> Self { + match d { + DispatchError::Payment => ApplyError::CantPay, + DispatchError::NoPermission => ApplyError::CantPay, + DispatchError::BadState => ApplyError::CantPay, + DispatchError::Stale => ApplyError::Stale, + DispatchError::Future => ApplyError::Future, + DispatchError::BadProof => ApplyError::BadSignature(""), + } + } + } } /// Trait that can be used to execute a block. @@ -264,36 +278,40 @@ where // Verify that the signature is good. let xt = uxt.check(&Default::default()).map_err(internal::ApplyError::BadSignature)?; + // We don't need to make sure to `note_extrinsic` only after we know it's going to be + // executed to prevent it from leaking in storage since at this point, it will either + // execute or panic (and revert storage changes). + if let Some(encoded) = to_note { + >::note_extrinsic(encoded); + } + // Check the weight of the block if that extrinsic is applied. let weight = xt.weight(encoded_len); + + // TODO: Consider placing into a transaction extension. if >::all_extrinsics_weight() + weight > internal::MAX_TRANSACTIONS_WEIGHT { return Err(internal::ApplyError::FullBlock); } if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) { - // check index + // TODO: Place into a transaction extension. + // check nonce and increment let expected_index = >::account_nonce(sender); if index != &expected_index { return Err( if index < &expected_index { internal::ApplyError::Stale } else { internal::ApplyError::Future } ) } + >::inc_account_nonce(sender); + + // TODO: Place into a transaction extension. // pay any fees Payment::make_payment(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?; - - // AUDIT: Under no circumstances may this function panic from here onwards. - // FIXME: ensure this at compile-time (such as by not defining a panic function, forcing - // a linker error unless the compiler can prove it cannot be called). - // increment nonce in storage - >::inc_account_nonce(sender); } - // Make sure to `note_extrinsic` only after we know it's going to be executed - // to prevent it from leaking in storage. - if let Some(encoded) = to_note { - >::note_extrinsic(encoded); - } + // AUDIT: Under no circumstances may this function panic from here onwards. // Decode parameters and dispatch - let (f, s) = xt.deconstruct(); + let (f, s) = Applyable::pre_dispatch(xt, weight) + .map_err(internal::ApplyError::from)?; let r = f.dispatch(s.into()); >::note_applied_extrinsic(&r, encoded_len as u32); @@ -348,6 +366,7 @@ where Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), }; + // TODO: remove this block next block into the transaction extension. match (xt.sender(), xt.index()) { (Some(sender), Some(index)) => { // pay any fees diff --git a/srml/support/test/tests/instance.rs b/srml/support/test/tests/instance.rs index 62e7263b511be..46edcd7df323b 100644 --- a/srml/support/test/tests/instance.rs +++ b/srml/support/test/tests/instance.rs @@ -269,7 +269,7 @@ srml_support::construct_runtime!( pub type Header = generic::Header; pub type Block = generic::Block; -pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; fn new_test_ext() -> runtime_io::TestExternalities { GenesisConfig{ @@ -407,4 +407,4 @@ fn storage_with_instance_basic_operation() { DoubleMap::remove(key1, key2); assert_eq!(DoubleMap::get(key1, key2), 0); }); -} \ No newline at end of file +} diff --git a/srml/support/test/tests/issue2219.rs b/srml/support/test/tests/issue2219.rs index 185b5e24807a9..9bae9bcde5549 100644 --- a/srml/support/test/tests/issue2219.rs +++ b/srml/support/test/tests/issue2219.rs @@ -152,7 +152,7 @@ pub type BlockNumber = u64; pub type Index = u64; pub type Header = generic::Header; pub type Block = generic::Block; -pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; impl system::Trait for Runtime { type Hash = H256; @@ -183,4 +183,4 @@ fn create_genesis_config() { enable_storage_role: true, }) }; -} \ No newline at end of file +} From 37f6ae00822da7adfef81514d5bf3edaed1831c6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 11 Jul 2019 15:56:40 +0200 Subject: [PATCH 02/20] Rest of mockup. Add tips. --- .../src/generic/checked_extrinsic.rs | 57 ++++---- .../src/generic/unchecked_extrinsic.rs | 104 ++++----------- core/sr-primitives/src/testing.rs | 23 +++- core/sr-primitives/src/traits.rs | 126 +++++++++++++----- .../sr-primitives/src/transaction_validity.rs | 112 +++++++++++----- srml/balances/src/lib.rs | 44 +++++- srml/executive/src/lib.rs | 54 +------- srml/support/src/dispatch.rs | 17 +-- srml/system/src/lib.rs | 73 +++++++++- 9 files changed, 359 insertions(+), 251 deletions(-) diff --git a/core/sr-primitives/src/generic/checked_extrinsic.rs b/core/sr-primitives/src/generic/checked_extrinsic.rs index c1d22d7c09186..5a54bf1afb1b6 100644 --- a/core/sr-primitives/src/generic/checked_extrinsic.rs +++ b/core/sr-primitives/src/generic/checked_extrinsic.rs @@ -18,65 +18,72 @@ //! stage. use rstd::result::Result; -use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, SignedExtension, DispatchError}; +use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, SignedExtension, DispatchError, Dispatchable, DispatchResult, ValidateUnsigned}; use crate::weights::{Weighable, Weight}; +use crate::transaction_validity::{ValidTransaction, TransactionValidity}; /// Definition of something that the external world might want to say; its /// existence implies that it has been checked and is good, particularly with /// regards to the signature. #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Debug))] -pub struct CheckedExtrinsic { +pub struct CheckedExtrinsic { /// Who this purports to be from and the number of extrinsics have come before /// from the same signer, if anyone (note this is not a signature). - pub signed: Option<(AccountId, Index, Extra)>, + pub signed: Option<(AccountId, Extra)>, /// The function that should be called. pub function: Call, } -impl traits::Applyable +impl traits::Applyable for - CheckedExtrinsic + CheckedExtrinsic where AccountId: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Call: Member, - Extra: SignedExtension, + Call: Member + Dispatchable, + Extra: SignedExtension, + Origin: From>, { type AccountId = AccountId; - type Call = Call; - type Index = Index; - fn index(&self) -> Option<&Self::Index> { - self.signed.as_ref().map(|x| &x.1) - } + type Call = Call; fn sender(&self) -> Option<&Self::AccountId> { self.signed.as_ref().map(|x| &x.0) } - fn deconstruct(self) -> (Self::Call, Option) { - if let Some((id, _, extra)) = self.signed { - (self.function, Some(id)) + fn validate>(&self, + weight: crate::weights::Weight, + ) -> TransactionValidity { + if let Some((ref id, ref extra)) = self.signed { + Extra::validate(extra, id, weight).into() } else { - (self.function, None) + match Extra::validate_unsigned(weight) { + Ok(v) => match UnsignedValidator::validate_unsigned(&self.function) { + TransactionValidity::Valid(v) => Ok(TransactionValidity(v.combine_with(extra))), + x => x, + }, + x => x.into(), + } } } - fn pre_dispatch(self, + fn dispatch(self, weight: crate::weights::Weight, - ) -> Result<(Self::Call, Option), DispatchError> { - if let Some((id, index, extra)) = self.signed { - Extra::pre_dispatch(extra, &id, &index, &self.function, weight)?; - Ok((self.function, Some(id))) + ) -> Result { + let maybe_who = if let Some((id, extra)) = self.signed { + Extra::pre_dispatch(extra, &id, weight)?; + Some(id) } else { - Ok((self.function, None)) - } + Extra::pre_dispatch_unsigned(weight)?; + None + }; + Ok(self.function.dispatch(Origin::from(maybe_who))) } } -impl Weighable for CheckedExtrinsic +impl Weighable for CheckedExtrinsic where Call: Weighable, { diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index 7eb31e28fd608..39c426e3fc39e 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -21,64 +21,35 @@ use std::fmt; use rstd::prelude::*; use runtime_io::blake2_256; -use crate::codec::{Decode, Encode, Codec, Input, Compact}; +use crate::codec::{Decode, Encode, Input, Compact}; use crate::traits::{ - self, Member, SimpleArithmetic, MaybeDebug, MaybeDisplay, CurrentHeight, SignedExtension, - BlockNumberToHash, Lookup, Checkable, Extrinsic, SaturatedConversion, DispatchError + self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, SignedExtension, + BlockNumberToHash, Lookup, Checkable, Extrinsic, SaturatedConversion }; use super::{CheckedExtrinsic, Era}; const TRANSACTION_VERSION: u8 = 1; -/* -/// A type with which the signed payload of a transaction may be extended. -pub trait OSignedExtension: Codec + MaybeDebug {} - -impl SignedExtra for T {} - -/// A type that names a `SignedExtension` and knows how to deal with it. -pub trait OExtension { - /// An extra data field that should go into the signed part of the payload. - type SignedPayload: OSignedExtension; - - /// Anything that should be executed immediately before dispatch. - fn pre_dispatch( - a: &AccountId, - c: &Call, - e: Self::SignedPayload - ) -> Result<(), DispatchError>; -} - -impl OExtension for () { - type SignedPayload = (); - fn pre_dispatch( - _: &AccountId, - _: &Call, - _: () - ) -> Result<(), DispatchError> { Ok(()) } -} -*/ /// A extrinsic right from the external world. This is unchecked and so /// can contain a signature. #[derive(PartialEq, Eq, Clone)] -pub struct UncheckedExtrinsic +pub struct UncheckedExtrinsic where Extra: SignedExtension { /// The signature, address, number of extrinsics have come before from /// the same signer and an era describing the longevity of this transaction, /// if this is a signed extrinsic. - pub signature: Option<(Address, Signature, Compact, Era, Extra)>, + pub signature: Option<(Address, Signature, Era, Extra)>, /// The function that should be called. pub function: Call, } -impl - UncheckedExtrinsic +impl + UncheckedExtrinsic { /// New instance of a signed extrinsic aka "transaction". pub fn new_signed( - index: Index, function: Call, signed: Address, signature: Signature, @@ -86,7 +57,7 @@ impl extra: Extra ) -> Self { UncheckedExtrinsic { - signature: Some((signed, signature, index.into(), era, extra)), + signature: Some((signed, signature, era, extra)), function, } } @@ -100,25 +71,23 @@ impl } } -impl Extrinsic - for UncheckedExtrinsic +impl Extrinsic + for UncheckedExtrinsic { fn is_signed(&self) -> Option { Some(self.signature.is_some()) } } -impl +impl Checkable for - UncheckedExtrinsic + UncheckedExtrinsic where Address: Member + MaybeDisplay, - Index: Member + MaybeDisplay + SimpleArithmetic, - Compact: Encode, Call: Encode + Member, Signature: Member + traits::Verify, - Extra: SignedExtension, + Extra: SignedExtension, AccountId: Member + MaybeDisplay, BlockNumber: SimpleArithmetic, Hash: Encode, @@ -126,16 +95,16 @@ where + CurrentHeight + BlockNumberToHash, { - type Checked = CheckedExtrinsic; + type Checked = CheckedExtrinsic; fn check(self, context: &Context) -> Result { Ok(match self.signature { - Some((signed, signature, index, era, extra)) => { + Some((signed, signature, era, extra)) => { let current_u64 = context.current_height().saturated_into::(); let h = context.block_number_to_hash(era.birth(current_u64).saturated_into()) .ok_or("transaction birth block ancient")?; let signed = context.lookup(signed)?; - let raw_payload = (index, self.function, era, h, extra); + let raw_payload = (self.function, era, h, extra); if !raw_payload.using_encoded(|payload| { if payload.len() > 256 { signature.verify(&blake2_256(payload)[..], &signed) @@ -146,8 +115,8 @@ where return Err(crate::BAD_SIGNATURE) } CheckedExtrinsic { - signed: Some((signed, (raw_payload.0).0, raw_payload.4)), - function: raw_payload.1, + signed: Some((signed, raw_payload.3)), + function: raw_payload.0, } } None => CheckedExtrinsic { @@ -158,12 +127,11 @@ where } } -impl Decode - for UncheckedExtrinsic +impl Decode + for UncheckedExtrinsic where Address: Decode, Signature: Decode, - Compact: Decode, Call: Decode, Extra: SignedExtension, { @@ -189,12 +157,11 @@ where } } -impl Encode - for UncheckedExtrinsic +impl Encode + for UncheckedExtrinsic where Address: Encode, Signature: Encode, - Compact: Encode, Call: Encode, Extra: SignedExtension, { @@ -216,9 +183,8 @@ where } #[cfg(feature = "std")] -impl serde::Serialize - for UncheckedExtrinsic - where Compact: Encode +impl serde::Serialize + for UncheckedExtrinsic { fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { self.using_encoded(|bytes| seq.serialize_bytes(bytes)) @@ -226,16 +192,15 @@ impl fmt::Debug - for UncheckedExtrinsic +impl fmt::Debug + for UncheckedExtrinsic where Address: fmt::Debug, - Index: fmt::Debug, Call: fmt::Debug, Extra: SignedExtension, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2, &x.4)), self.function) + write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2, &x.3)), self.function) } } @@ -278,20 +243,9 @@ mod tests { struct TestExtra; impl SignedExtension for TestExtra { type AccountId = u64; - type Index = u64; - type Call = Vec; - fn pre_dispatch( - self, - _a: &Self::AccountId, - _i: &Self::Index, - _c: &Self::Call, - _w: crate::weights::Weight, - ) -> Result<(), DispatchError> { - Ok(()) - } } - type Ex = UncheckedExtrinsic, TestSig, TestExtra>; - type CEx = CheckedExtrinsic, TestExtra>; + type Ex = UncheckedExtrinsic, TestSig, TestExtra>; + type CEx = CheckedExtrinsic, TestExtra>; #[test] fn unsigned_codec_should_work() { diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index f8df25ec596b0..3a5bf8af9f4bf 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -19,8 +19,8 @@ use serde::{Serialize, Serializer, Deserialize, de::Error as DeError, Deserializer}; use std::{fmt::Debug, ops::Deref, fmt}; use crate::codec::{Codec, Encode, Decode}; -use crate::traits::{self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, TypedKey}; -use crate::{generic, KeyTypeId}; +use crate::traits::{self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, TypedKey, DispatchError, DispatchResult}; +use crate::{generic, KeyTypeId, transaction_validity::ValidTransaction}; use crate::weights::{Weighable, Weight}; pub use substrate_primitives::H256; use substrate_primitives::U256; @@ -227,12 +227,21 @@ impl Applyable for TestXt where Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug, { type AccountId = u64; - type Index = u64; - type Call = Call; fn sender(&self) -> Option<&u64> { self.0.as_ref() } - fn index(&self) -> Option<&u64> { self.0.as_ref().map(|_| &self.1) } - fn deconstruct(self) -> (Self::Call, Option) { - (self.2, self.0) + + /// Checks to see if this is a valid *transaction*. It returns information on it if so. + fn validate(&self, + weight: crate::weights::Weight + ) -> Result { + Ok(Default::default()) + } + + /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, + /// index and sender. + fn dispatch(self, + weight: crate::weights::Weight + ) -> Result { + Ok(Ok(())) } } impl Weighable for TestXt { diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index 8cf8449c03553..d3c5afa68de59 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -23,7 +23,7 @@ use runtime_io; #[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; use substrate_primitives::{self, Hasher, Blake2Hasher}; use crate::codec::{Codec, Encode, Decode, HasCompact}; -use crate::transaction_validity::TransactionValidity; +use crate::transaction_validity::{ValidTransaction, TransactionValidity}; use crate::generic::{Digest, DigestItem}; pub use substrate_primitives::crypto::TypedKey; pub use integer_sqrt::IntegerSquareRoot; @@ -770,44 +770,106 @@ pub enum DispatchError { /// General error to do with the transaction's proofs (e.g. signature). BadProof, + +/* /// General error to do with actually executing the dispatched logic. + User(&'static str),*/ +} + +impl From for i8 { + fn from(e: DispatchError) -> i8 { + match e { + DispatchError::Payment => -64, + DispatchError::NoPermission => -65, + DispatchError::BadState => -66, + DispatchError::Stale => -67, + DispatchError::Future => -68, + DispatchError::BadProof => -69, + } + } +} + +/// Result of a module function call; either nothing (functions are only called for "side effects") +/// or an error message. +pub type DispatchResult = result::Result<(), &'static str>; + +/// A lazy call (module function and argument values) that can be executed via its `dispatch` +/// method. +pub trait Dispatchable { + /// Every function call from your runtime has an origin, which specifies where the extrinsic was + /// generated from. In the case of a signed extrinsic (transaction), the origin contains an + /// identifier for the caller. The origin can be empty in the case of an inherent extrinsic. + type Origin; + /// ... + type Trait; + /// Actually dispatch this call and result the result of it. + fn dispatch(self, origin: Self::Origin) -> DispatchResult; } /// Means by which a transaction may be extended. This type embodies both the data and the logic /// that should be additionally associated with the transaction. It should be plain old data. pub trait SignedExtension: - Codec + MaybeDebug + Sync + Send + Clone + Eq + PartialEq + Ord + PartialOrd + Codec + MaybeDebug + Sync + Send + Clone + Eq + PartialEq { type AccountId; - type Call; - type Index; + + fn validate( + &self, + who: &Self::AccountId, + _weight: crate::weights::Weight, + ) -> Result { Ok(Default::default()) } + fn pre_dispatch( self, - account: &Self::AccountId, - index: &Self::Index, - call: &Self::Call, + who: &Self::AccountId, weight: crate::weights::Weight, - ) -> Result<(), DispatchError>; + ) -> Result<(), DispatchError> { self.validate(who, weight).map(|_| ()) } + + fn validate_unsigned( + _weight: crate::weights::Weight, + ) -> Result { Ok(Default::default()) } + + fn pre_dispatch_unsigned( + weight: crate::weights::Weight, + ) -> Result<(), DispatchError> { Self::validate_unsigned(weight).map(|_| ()) } } impl< AccountId, - Call, - Index, - A: SignedExtension, - B: SignedExtension + A: SignedExtension, + B: SignedExtension, > SignedExtension for (A, B) { type AccountId = AccountId; - type Call = Call; - type Index = Index; + fn validate( + &self, + who: &Self::AccountId, + weight: crate::weights::Weight, + ) -> Result { + let a = self.0.validate(who, weight)?; + let b = self.1.validate(who, weight)?; + Ok(a.combine_with(b)) + } fn pre_dispatch( self, - account: &Self::AccountId, - index: &Self::Index, - call: &Self::Call, + who: &Self::AccountId, weight: crate::weights::Weight, ) -> Result<(), DispatchError> { - self.0.pre_dispatch(account, index, call, weight)?; - self.1.pre_dispatch(account, index, call, weight) + self.0.pre_dispatch(who, weight)?; + self.1.pre_dispatch(who, weight)?; + Ok(()) + } + fn validate_unsigned( + weight: crate::weights::Weight, + ) -> Result { + let a = A::validate_unsigned(weight)?; + let b = B::validate_unsigned(weight)?; + Ok(a.combine_with(b)) + } + fn pre_dispatch_unsigned( + weight: crate::weights::Weight, + ) -> Result<(), DispatchError> { + A::pre_dispatch_unsigned(weight)?; + B::pre_dispatch_unsigned(weight)?; + Ok(()) } } @@ -820,25 +882,23 @@ impl< pub trait Applyable: Sized + Send + Sync { /// Id of the account that is responsible for this piece of information (sender). type AccountId: Member + MaybeDisplay; - /// Index allowing to disambiguate other `Applyable`s from the same `AccountId`. - type Index: Member + MaybeDisplay + SimpleArithmetic; - /// Function call. - type Call: Member; - /// Returns a reference to the index if any. - fn index(&self) -> Option<&Self::Index>; + + /// Type by which we can dispatch. Restricts the UnsignedValidator type. + type Call; + /// Returns a reference to the sender if any. fn sender(&self) -> Option<&Self::AccountId>; - /// Deconstructs into function call and sender. - /// - /// @deprecated - avoid using this and instead refactor into pre_dispatch. - fn deconstruct(self) -> (Self::Call, Option); + + /// Checks to see if this is a valid *transaction*. It returns information on it if so. + fn validate>(&self, + weight: crate::weights::Weight + ) -> TransactionValidity; + /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, /// index and sender. - fn pre_dispatch(self, + fn dispatch(self, weight: crate::weights::Weight - ) -> Result<(Self::Call, Option), DispatchError> { - Ok(self.deconstruct()) - } + ) -> Result; } /// Auxiliary wrapper that holds an api instance and binds it to the given lifetime. diff --git a/core/sr-primitives/src/transaction_validity.rs b/core/sr-primitives/src/transaction_validity.rs index f36599b67b42c..d2e3b27bcab11 100644 --- a/core/sr-primitives/src/transaction_validity.rs +++ b/core/sr-primitives/src/transaction_validity.rs @@ -18,6 +18,8 @@ use rstd::prelude::*; use crate::codec::{Encode, Decode}; +use crate::transaction_validity::TransactionValidity::Valid; +use crate::traits::DispatchError; /// Priority for a transaction. Additive. Higher is better. pub type TransactionPriority = u64; @@ -36,40 +38,78 @@ pub enum TransactionValidity { /// Transaction is invalid. Details are described by the error code. Invalid(i8), /// Transaction is valid. - Valid { - /// Priority of the transaction. - /// - /// Priority determines the ordering of two transactions that have all - /// their dependencies (required tags) satisfied. - priority: TransactionPriority, - /// Transaction dependencies - /// - /// A non-empty list signifies that some other transactions which provide - /// given tags are required to be included before that one. - requires: Vec, - /// Provided tags - /// - /// A list of tags this transaction provides. Successfully importing the transaction - /// will enable other transactions that depend on (require) those tags to be included as well. - /// Provided and required tags allow Substrate to build a dependency graph of transactions - /// and import them in the right (linear) order. - provides: Vec, - /// Transaction longevity - /// - /// Longevity describes minimum number of blocks the validity is correct. - /// After this period transaction should be removed from the pool or revalidated. - longevity: TransactionLongevity, - /// A flag indicating if the transaction should be propagated to other peers. - /// - /// By setting `false` here the transaction will still be considered for - /// including in blocks that are authored on the current node, but will - /// never be sent to other peers. - propagate: bool, - }, + Valid(ValidTransaction), /// Transaction validity can't be determined. Unknown(i8), } +impl From> for TransactionValidity { + fn from(r: Result) -> Self { + match r { + Ok(v) => TransactionValidity::Valid(v), + Err(e) => TransactionValidity::Invalid(e.into()), + } + } +} + +/// Information concerning a valid transaction. +#[derive(Clone, PartialEq, Eq, Encode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct ValidTransaction { + /// Priority of the transaction. + /// + /// Priority determines the ordering of two transactions that have all + /// their dependencies (required tags) satisfied. + pub priority: TransactionPriority, + /// Transaction dependencies + /// + /// A non-empty list signifies that some other transactions which provide + /// given tags are required to be included before that one. + pub requires: Vec, + /// Provided tags + /// + /// A list of tags this transaction provides. Successfully importing the transaction + /// will enable other transactions that depend on (require) those tags to be included as well. + /// Provided and required tags allow Substrate to build a dependency graph of transactions + /// and import them in the right (linear) order. + pub provides: Vec, + /// Transaction longevity + /// + /// Longevity describes minimum number of blocks the validity is correct. + /// After this period transaction should be removed from the pool or revalidated. + pub longevity: TransactionLongevity, + /// A flag indicating if the transaction should be propagated to other peers. + /// + /// By setting `false` here the transaction will still be considered for + /// including in blocks that are authored on the current node, but will + /// never be sent to other peers. + pub propagate: bool, +} + +impl Default for ValidTransaction { + fn default() -> Self { + ValidTransaction { + priority: 0, + requires: vec![], + provides: vec![], + longevity: TransactionLongevity::max_value(), + propagate: true, + } + } +} + +impl ValidTransaction { + pub fn combine_with(mut self, mut other: ValidTransaction) -> Self { + ValidTransaction { + priority: self.priority + other.priority, + requires: { self.requires.append(&mut other.requires); self.requires }, + provides: { self.provides.append(&mut other.provides); self.provides }, + longevity: self.longevity.min(other.longevity), + propagate: self.propagate && other.propagate, + } + } +} + impl Decode for TransactionValidity { fn decode(value: &mut I) -> Option { match value.read_byte()? { @@ -81,9 +121,9 @@ impl Decode for TransactionValidity { let longevity = TransactionLongevity::decode(value)?; let propagate = bool::decode(value).unwrap_or(true); - Some(TransactionValidity::Valid { + Some(TransactionValidity::Valid(ValidTransaction { priority, requires, provides, longevity, propagate, - }) + })) }, 2 => Some(TransactionValidity::Unknown(i8::decode(value)?)), _ => None, @@ -101,24 +141,24 @@ mod tests { 1, 5, 0, 0, 0, 0, 0, 0, 0, 4, 16, 1, 2, 3, 4, 4, 12, 4, 5, 6, 42, 0, 0, 0, 0, 0, 0, 0 ]; - assert_eq!(TransactionValidity::decode(&mut &*old_encoding), Some(TransactionValidity::Valid { + assert_eq!(TransactionValidity::decode(&mut &*old_encoding), Some(TransactionValidity::Valid(ValidTransaction { priority: 5, requires: vec![vec![1, 2, 3, 4]], provides: vec![vec![4, 5, 6]], longevity: 42, propagate: true, - })); + }))); } #[test] fn should_encode_and_decode() { - let v = TransactionValidity::Valid { + let v = TransactionValidity::Valid(ValidTransaction { priority: 5, requires: vec![vec![1, 2, 3, 4]], provides: vec![vec![4, 5, 6]], longevity: 42, propagate: false, - }; + }); let encoded = v.encode(); assert_eq!( diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 84e023ee5b2aa..16b7bcc3a185d 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -1130,18 +1130,50 @@ where } } -impl, I: Instance> MakePayment for Module { - fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { - let encoded_len = T::Balance::from(encoded_len as u32); - let transaction_fee = T::TransactionBaseFee::get() + T::TransactionByteFee::get() * encoded_len; +/// Require the transactor pay for themselves and maybe include a tip to gain additional priority +/// in the queue. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct TakeFees(T::Balance); + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for TakeFees { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + self.0.fmt(f) + } +} + +// TODO: wire this and CheckNonce in to the runtime. + +use primitives::traits::DispatchError; +use primitives::transaction_validity::ValidTransaction; +use primitives::weights::Weight; + +impl SignedExtension for TakeFees { + type AccountId = T::AccountId; + + fn validate( + &self, + who: &Self::AccountId, + weight: Weight, + ) -> rstd::result::Result { + let fee_x = T::Balance::from(weight as u32); + // should be weight_to_fee(weight) + let transaction_fee = T::TransactionBaseFee::get() + T::TransactionByteFee::get() * fee_x; let imbalance = Self::withdraw( transactor, transaction_fee, WithdrawReason::TransactionPayment, ExistenceRequirement::KeepAlive - )?; + ).map_err(|_| DispatchError::Payment)?; T::TransactionPayment::on_unbalanced(imbalance); - Ok(()) + + Ok(ValidTransaction { + priority: _weight as TransactionPriority + self.0.clone() as TransactionPriority, // TODO: overflow?? + requires: vec![], + provides: vec![], + longevity: TransactionLongevity::max_value(), + propagate: true, + }) } } diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index dbfe02e00bc41..443ba947b3e44 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -293,26 +293,12 @@ where return Err(internal::ApplyError::FullBlock); } - if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) { - // TODO: Place into a transaction extension. - // check nonce and increment - let expected_index = >::account_nonce(sender); - if index != &expected_index { return Err( - if index < &expected_index { internal::ApplyError::Stale } else { internal::ApplyError::Future } - ) } - >::inc_account_nonce(sender); - - // TODO: Place into a transaction extension. - // pay any fees - Payment::make_payment(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?; - } - // AUDIT: Under no circumstances may this function panic from here onwards. // Decode parameters and dispatch - let (f, s) = Applyable::pre_dispatch(xt, weight) + let (f, s) = Applyable::dispatch(xt, weight) .map_err(internal::ApplyError::from)?; - let r = f.dispatch(s.into()); + >::note_applied_extrinsic(&r, encoded_len as u32); r.map(|_| internal::ApplyOutcome::Success).or_else(|e| match e { @@ -352,6 +338,7 @@ where const UNKNOWN_ERROR: i8 = -127; const MISSING_SENDER: i8 = -20; const INVALID_INDEX: i8 = -10; + const BAD_DISPATCH: i8 = -15; let encoded_len = uxt.encode().len(); @@ -366,40 +353,7 @@ where Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), }; - // TODO: remove this block next block into the transaction extension. - match (xt.sender(), xt.index()) { - (Some(sender), Some(index)) => { - // pay any fees - if Payment::make_payment(sender, encoded_len).is_err() { - return TransactionValidity::Invalid(ApplyError::CantPay as i8) - } - - // check index - let expected_index = >::account_nonce(sender); - if index < &expected_index { - return TransactionValidity::Invalid(ApplyError::Stale as i8) - } - - let index = *index; - let provides = vec![(sender, index).encode()]; - let requires = if expected_index < index { - vec![(sender, index - One::one()).encode()] - } else { - vec![] - }; - - TransactionValidity::Valid { - priority: encoded_len as TransactionPriority, - requires, - provides, - longevity: TransactionLongevity::max_value(), - propagate: true, - } - }, - (None, None) => UnsignedValidator::validate_unsigned(&xt.deconstruct().0), - (Some(_), None) => TransactionValidity::Invalid(INVALID_INDEX), - (None, Some(_)) => TransactionValidity::Invalid(MISSING_SENDER), - } + xt.validate::() } /// Start an offchain worker and generate extrinsics. diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index f990cbd8d5a1e..00c21a31ba47f 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -25,25 +25,16 @@ pub use srml_metadata::{ FunctionMetadata, DecodeDifferent, DecodeDifferentArray, FunctionArgumentMetadata, ModuleConstantMetadata, DefaultByte, DefaultByteGetter, }; -pub use sr_primitives::weights::{TransactionWeight, Weighable, Weight}; +pub use sr_primitives::{ + weights::{TransactionWeight, Weighable, Weight}, traits::{Dispatchable, DispatchResult} +}; /// A type that cannot be instantiated. pub enum Never {} /// Result of a module function call; either nothing (functions are only called for "side effects") /// or an error message. -pub type Result = result::Result<(), &'static str>; - -/// A lazy call (module function and argument values) that can be executed via its `dispatch` -/// method. -pub trait Dispatchable { - /// Every function call from your runtime has an origin, which specifies where the extrinsic was - /// generated from. In the case of a signed extrinsic (transaction), the origin contains an - /// identifier for the caller. The origin can be empty in the case of an inherent extrinsic. - type Origin; - type Trait; - fn dispatch(self, origin: Self::Origin) -> Result; -} +pub type Result = DispatchResult; /// Serializable version of Dispatchable. /// This value can be used as a "function" in an extrinsic. diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index a55cb9be86ea4..8961c98a717ad 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -76,15 +76,20 @@ use serde::Serialize; use rstd::prelude::*; #[cfg(any(feature = "std", test))] use rstd::map; -use primitives::{generic, traits::{self, CheckEqual, SimpleArithmetic, - SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash, - MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, Lookup, - Zero, -}}; +use primitives::{ + generic, weights::Weight, traits::{ + self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, + SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash, + MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, + Lookup, DispatchError + }, transaction_validity::{ + ValidTransaction, TransactionPriority, TransactionLongevity + }, +}; use substrate_primitives::storage::well_known_keys; use srml_support::{ storage, decl_module, decl_event, decl_storage, StorageDoubleMap, StorageValue, - StorageMap, Parameter, for_each_tuple, traits::{Contains, Get}, + StorageMap, Parameter, for_each_tuple, traits::{Contains, Get}, Dispatchable }; use safe_mix::TripletMix; use parity_codec::{Encode, Decode}; @@ -748,6 +753,62 @@ impl Module { } } +/// Nonce check and increment to give replay protection for transactions. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct CheckNonce(T::Index); + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for CheckNonce { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + self.0.fmt(f) + } +} + +impl SignedExtension for CheckNonce { + type AccountId = T::AccountId; + fn pre_dispatch( + self, + who: &Self::AccountId, + _weight: Weight, + ) -> Result<(), DispatchError> { + let expected = >::get(who); + if self.0 != expected { + return Err( + if self.0 < expected { DispatchError::Stale } else { DispatchError::Future } + ) + } + >::insert(who, expected + T::Index::one()); + Ok(()) + } + + fn validate( + &self, + who: &Self::AccountId, + _weight: Weight, + ) -> Result { + // check index + let expected = >::get(who); + if self.0 < expected { + return Err(DispatchError::Stale) + } + + let provides = vec![Encode::encode(&(who, self.0))]; + let requires = if expected < self.0 { + vec![Encode::encode(&(who, self.0 - One::one()))] + } else { + vec![] + }; + + Ok(ValidTransaction { + priority: _weight as TransactionPriority, + requires, + provides, + longevity: TransactionLongevity::max_value(), + propagate: true, + }) + } +} + pub struct ChainContext(::rstd::marker::PhantomData); impl Default for ChainContext { fn default() -> Self { From 2e5b1f4628a65a6fec87f80789d15cb37ac44d81 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 11 Jul 2019 23:02:52 +0200 Subject: [PATCH 03/20] Fix some build issues --- .../src/generic/checked_extrinsic.rs | 12 ++++--- .../src/generic/unchecked_extrinsic.rs | 2 +- core/sr-primitives/src/testing.rs | 21 ++++++----- core/sr-primitives/src/traits.rs | 9 ++++- .../sr-primitives/src/transaction_validity.rs | 4 ++- srml/balances/src/lib.rs | 35 +++++++++---------- srml/executive/src/lib.rs | 14 ++++---- srml/support/src/traits.rs | 13 ------- srml/system/src/lib.rs | 2 +- 9 files changed, 57 insertions(+), 55 deletions(-) diff --git a/core/sr-primitives/src/generic/checked_extrinsic.rs b/core/sr-primitives/src/generic/checked_extrinsic.rs index 5a54bf1afb1b6..bd9a711fc0e54 100644 --- a/core/sr-primitives/src/generic/checked_extrinsic.rs +++ b/core/sr-primitives/src/generic/checked_extrinsic.rs @@ -18,9 +18,12 @@ //! stage. use rstd::result::Result; -use crate::traits::{self, Member, SimpleArithmetic, MaybeDisplay, SignedExtension, DispatchError, Dispatchable, DispatchResult, ValidateUnsigned}; +use crate::traits::{ + self, Member, MaybeDisplay, SignedExtension, DispatchError, Dispatchable, DispatchResult, + ValidateUnsigned +}; use crate::weights::{Weighable, Weight}; -use crate::transaction_validity::{ValidTransaction, TransactionValidity}; +use crate::transaction_validity::TransactionValidity; /// Definition of something that the external world might want to say; its /// existence implies that it has been checked and is good, particularly with @@ -60,8 +63,9 @@ where Extra::validate(extra, id, weight).into() } else { match Extra::validate_unsigned(weight) { - Ok(v) => match UnsignedValidator::validate_unsigned(&self.function) { - TransactionValidity::Valid(v) => Ok(TransactionValidity(v.combine_with(extra))), + Ok(extra) => match U::validate_unsigned(&self.function) { + TransactionValidity::Valid(v) => + TransactionValidity::Valid(v.combine_with(extra)), x => x, }, x => x.into(), diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index 39c426e3fc39e..a500283b7a947 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -21,7 +21,7 @@ use std::fmt; use rstd::prelude::*; use runtime_io::blake2_256; -use crate::codec::{Decode, Encode, Input, Compact}; +use crate::codec::{Decode, Encode, Input}; use crate::traits::{ self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, SignedExtension, BlockNumberToHash, Lookup, Checkable, Extrinsic, SaturatedConversion diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index 3a5bf8af9f4bf..76d37a0580a3a 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -19,12 +19,16 @@ use serde::{Serialize, Serializer, Deserialize, de::Error as DeError, Deserializer}; use std::{fmt::Debug, ops::Deref, fmt}; use crate::codec::{Codec, Encode, Decode}; -use crate::traits::{self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, TypedKey, DispatchError, DispatchResult}; -use crate::{generic, KeyTypeId, transaction_validity::ValidTransaction}; +use crate::traits::{ + self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, TypedKey, DispatchError, DispatchResult, + ValidateUnsigned +}; +use crate::{generic, KeyTypeId}; use crate::weights::{Weighable, Weight}; pub use substrate_primitives::H256; use substrate_primitives::U256; use substrate_primitives::ed25519::{Public as AuthorityId}; +use crate::transaction_validity::TransactionValidity; /// Authority Id #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug)] @@ -199,7 +203,7 @@ impl<'a, Xt> Deserialize<'a> for Block where Block: Decode { /// /// If sender is some then the transaction is signed otherwise it is unsigned. #[derive(PartialEq, Eq, Clone, Encode, Decode)] -pub struct TestXt(pub Option, pub u64, pub Call); +pub struct TestXt(pub Option, u64, pub Call); impl Serialize for TestXt where TestXt: Encode { @@ -227,19 +231,20 @@ impl Applyable for TestXt where Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug, { type AccountId = u64; + type Call = Call; fn sender(&self) -> Option<&u64> { self.0.as_ref() } /// Checks to see if this is a valid *transaction*. It returns information on it if so. - fn validate(&self, - weight: crate::weights::Weight - ) -> Result { - Ok(Default::default()) + fn validate>(&self, + _weight: crate::weights::Weight + ) -> TransactionValidity { + TransactionValidity::Valid(Default::default()) } /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, /// index and sender. fn dispatch(self, - weight: crate::weights::Weight + _weight: crate::weights::Weight ) -> Result { Ok(Ok(())) } diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index d3c5afa68de59..ccd9c06ea33ec 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -810,24 +810,31 @@ pub trait Dispatchable { pub trait SignedExtension: Codec + MaybeDebug + Sync + Send + Clone + Eq + PartialEq { + /// The type which encodes the sender identity. type AccountId; + /// Validate a signed transaction for the transaction queue. fn validate( &self, - who: &Self::AccountId, + _who: &Self::AccountId, _weight: crate::weights::Weight, ) -> Result { Ok(Default::default()) } + /// Do any pre-flight stuff for a signed transaction. fn pre_dispatch( self, who: &Self::AccountId, weight: crate::weights::Weight, ) -> Result<(), DispatchError> { self.validate(who, weight).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, ) -> Result { Ok(Default::default()) } + /// Do any pre-flight stuff for a unsigned transaction. fn pre_dispatch_unsigned( weight: crate::weights::Weight, ) -> Result<(), DispatchError> { Self::validate_unsigned(weight).map(|_| ()) } diff --git a/core/sr-primitives/src/transaction_validity.rs b/core/sr-primitives/src/transaction_validity.rs index d2e3b27bcab11..66e66c0042db9 100644 --- a/core/sr-primitives/src/transaction_validity.rs +++ b/core/sr-primitives/src/transaction_validity.rs @@ -18,7 +18,6 @@ use rstd::prelude::*; use crate::codec::{Encode, Decode}; -use crate::transaction_validity::TransactionValidity::Valid; use crate::traits::DispatchError; /// Priority for a transaction. Additive. Higher is better. @@ -99,6 +98,9 @@ impl Default for ValidTransaction { } impl ValidTransaction { + /// Combine two instances into one, as a best effort. This will take the superset of each of the + /// `provides` and `requires` tags, it will sum the priorities, take the minimum longevity and + /// the logic *And* of the propagate flags. pub fn combine_with(mut self, mut other: ValidTransaction) -> Self { ValidTransaction { priority: self.priority + other.priority, diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 16b7bcc3a185d..d894b589dc1d1 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -154,15 +154,15 @@ use rstd::{cmp, result, mem}; use parity_codec::{Codec, Encode, Decode}; use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module}; use srml_support::traits::{ - UpdateBalanceOutcome, Currency, OnFreeBalanceZero, MakePayment, OnUnbalanced, + UpdateBalanceOutcome, Currency, OnFreeBalanceZero, OnUnbalanced, WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement, Imbalance, SignedImbalance, ReservableCurrency }; use srml_support::{dispatch::Result, traits::Get}; -use primitives::traits::{ +use primitives::{transaction_validity::TransactionPriority, traits::{ Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, - MaybeSerializeDebug, Saturating, Bounded -}; + MaybeSerializeDebug, Saturating, Bounded, SignedExtension +}}; use system::{IsDeadAccount, OnNewAccount, ensure_signed, ensure_root}; mod mock; @@ -1133,10 +1133,10 @@ where /// Require the transactor pay for themselves and maybe include a tip to gain additional priority /// in the queue. #[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct TakeFees(T::Balance); +pub struct TakeFees, I: Instance>(T::Balance); #[cfg(feature = "std")] -impl rstd::fmt::Debug for TakeFees { +impl, I: Instance> rstd::fmt::Debug for TakeFees { fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { self.0.fmt(f) } @@ -1144,11 +1144,11 @@ impl rstd::fmt::Debug for TakeFees { // TODO: wire this and CheckNonce in to the runtime. -use primitives::traits::DispatchError; +use primitives::traits::{DispatchError, SaturatedConversion}; use primitives::transaction_validity::ValidTransaction; use primitives::weights::Weight; -impl SignedExtension for TakeFees { +impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { type AccountId = T::AccountId; fn validate( @@ -1158,22 +1158,19 @@ impl SignedExtension for TakeFees { ) -> rstd::result::Result { let fee_x = T::Balance::from(weight as u32); // should be weight_to_fee(weight) - let transaction_fee = T::TransactionBaseFee::get() + T::TransactionByteFee::get() * fee_x; - let imbalance = Self::withdraw( - transactor, - transaction_fee, + let fee = T::TransactionBaseFee::get() + T::TransactionByteFee::get() * fee_x; + let fee = fee + self.0.clone(); + let imbalance = >::withdraw( + who, + fee.clone(), WithdrawReason::TransactionPayment, ExistenceRequirement::KeepAlive ).map_err(|_| DispatchError::Payment)?; T::TransactionPayment::on_unbalanced(imbalance); - Ok(ValidTransaction { - priority: _weight as TransactionPriority + self.0.clone() as TransactionPriority, // TODO: overflow?? - requires: vec![], - provides: vec![], - longevity: TransactionLongevity::max_value(), - propagate: true, - }) + let mut r = ValidTransaction::default(); + r.priority = fee.saturated_into::(); + Ok(r) } } diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 443ba947b3e44..41492b2e26ec8 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -85,7 +85,7 @@ use srml_support::{Dispatchable, traits::MakePayment}; use parity_codec::{Codec, Encode}; use system::{extrinsics_root, DigestOf}; use primitives::{ApplyOutcome, ApplyError}; -use primitives::transaction_validity::{TransactionValidity, TransactionPriority, TransactionLongevity}; +use primitives::transaction_validity::TransactionValidity; use primitives::weights::Weighable; mod internal { @@ -144,7 +144,7 @@ impl< > ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, - CheckedOf: Applyable + Weighable, + CheckedOf: Applyable + Weighable, CallOf: Dispatchable, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, @@ -164,7 +164,7 @@ impl< > Executive where Block::Extrinsic: Checkable + Codec, - CheckedOf: Applyable + Weighable, + CheckedOf: Applyable + Weighable, CallOf: Dispatchable, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, @@ -296,7 +296,7 @@ where // AUDIT: Under no circumstances may this function panic from here onwards. // Decode parameters and dispatch - let (f, s) = Applyable::dispatch(xt, weight) + let r = Applyable::dispatch(xt, weight) .map_err(internal::ApplyError::from)?; >::note_applied_extrinsic(&r, encoded_len as u32); @@ -336,9 +336,7 @@ where pub fn validate_transaction(uxt: Block::Extrinsic) -> TransactionValidity { // Note errors > 0 are from ApplyError const UNKNOWN_ERROR: i8 = -127; - const MISSING_SENDER: i8 = -20; const INVALID_INDEX: i8 = -10; - const BAD_DISPATCH: i8 = -15; let encoded_len = uxt.encode().len(); @@ -353,7 +351,9 @@ where Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), }; - xt.validate::() + let weight = xt.weight(encoded_len);† + + xt.validate::(weight) } /// Start an offchain worker and generate extrinsics. diff --git a/srml/support/src/traits.rs b/srml/support/src/traits.rs index 86071a37a2720..f6c09190ebad6 100644 --- a/srml/support/src/traits.rs +++ b/srml/support/src/traits.rs @@ -91,19 +91,6 @@ pub enum UpdateBalanceOutcome { AccountKilled, } -/// Simple trait designed for hooking into a transaction payment. -/// -/// It operates over a single generic `AccountId` type. -pub trait MakePayment { - /// Make transaction payment from `who` for an extrinsic of encoded length - /// `encoded_len` bytes. Return `Ok` iff the payment was successful. - fn make_payment(who: &AccountId, encoded_len: usize) -> Result<(), &'static str>; -} - -impl MakePayment for () { - fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) } -} - /// A trait for finding the author of a block header based on the `PreRuntime` digests contained /// within it. pub trait FindAuthor { diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 8961c98a717ad..d60524dc42c8b 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -89,7 +89,7 @@ use primitives::{ use substrate_primitives::storage::well_known_keys; use srml_support::{ storage, decl_module, decl_event, decl_storage, StorageDoubleMap, StorageValue, - StorageMap, Parameter, for_each_tuple, traits::{Contains, Get}, Dispatchable + StorageMap, Parameter, for_each_tuple, traits::{Contains, Get} }; use safe_mix::TripletMix; use parity_codec::{Encode, Decode}; From b7646ec9b0a0c3f40ef2d35391b31f0ce57c30e4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 11 Jul 2019 23:16:18 +0200 Subject: [PATCH 04/20] Runtiem builds :) --- node/runtime/src/lib.rs | 8 +++++--- srml/balances/src/lib.rs | 4 +--- srml/executive/src/lib.rs | 16 +++++++--------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 0b66424576181..4daab741406b4 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -435,12 +435,14 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +type SignedExtra = (system::CheckNonce, balances::TakeFees); /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive, Balances, Runtime, AllModules>; +pub type Executive = executive::Executive, Runtime, AllModules>; impl_runtime_apis! { impl client_api::Core for Runtime { diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index d894b589dc1d1..1e5ec3afc7a23 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -1133,7 +1133,7 @@ where /// Require the transactor pay for themselves and maybe include a tip to gain additional priority /// in the queue. #[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct TakeFees, I: Instance>(T::Balance); +pub struct TakeFees, I: Instance = DefaultInstance>(T::Balance); #[cfg(feature = "std")] impl, I: Instance> rstd::fmt::Debug for TakeFees { @@ -1142,8 +1142,6 @@ impl, I: Instance> rstd::fmt::Debug for TakeFees { } } -// TODO: wire this and CheckNonce in to the runtime. - use primitives::traits::{DispatchError, SaturatedConversion}; use primitives::transaction_validity::ValidTransaction; use primitives::weights::Weight; diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 41492b2e26ec8..6c83f92850ab6 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -81,7 +81,7 @@ use primitives::{generic::Digest, traits::{ self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, OnInitialize, NumberFor, Block as BlockT, OffchainWorker, ValidateUnsigned }}; -use srml_support::{Dispatchable, traits::MakePayment}; +use srml_support::Dispatchable; use parity_codec::{Codec, Encode}; use system::{extrinsics_root, DigestOf}; use primitives::{ApplyOutcome, ApplyError}; @@ -130,18 +130,17 @@ pub type CheckedOf = >::Checked; pub type CallOf = as Applyable>::Call; pub type OriginOf = as Dispatchable>::Origin; -pub struct Executive( - PhantomData<(System, Block, Context, Payment, UnsignedValidator, AllModules)> +pub struct Executive( + PhantomData<(System, Block, Context, UnsignedValidator, AllModules)> ); impl< System: system::Trait, Block: traits::Block, Context: Default, - Payment: MakePayment, UnsignedValidator, AllModules: OnInitialize + OnFinalize + OffchainWorker, -> ExecuteBlock for Executive +> ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, CheckedOf: Applyable + Weighable, @@ -150,7 +149,7 @@ where UnsignedValidator: ValidateUnsigned>, { fn execute_block(block: Block) { - Executive::::execute_block(block); + Executive::::execute_block(block); } } @@ -158,10 +157,9 @@ impl< System: system::Trait, Block: traits::Block, Context: Default, - Payment: MakePayment, UnsignedValidator, AllModules: OnInitialize + OnFinalize + OffchainWorker, -> Executive +> Executive where Block::Extrinsic: Checkable + Codec, CheckedOf: Applyable + Weighable, @@ -351,7 +349,7 @@ where Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), }; - let weight = xt.weight(encoded_len);† + let weight = xt.weight(encoded_len); xt.validate::(weight) } From a871c9f4bc872d9abff4256408003bc5f09fd088 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 13 Jul 2019 01:56:11 +0200 Subject: [PATCH 05/20] Substrate builds. --- Cargo.lock | 4 + .../src/generic/unchecked_extrinsic.rs | 98 +++++++++++++++---- core/sr-primitives/src/testing.rs | 42 +++++--- core/sr-primitives/src/traits.rs | 6 ++ core/test-runtime/src/lib.rs | 10 +- core/test-runtime/src/system.rs | 7 +- core/transaction-pool/graph/src/pool.rs | 28 +++--- core/transaction-pool/src/tests.rs | 6 +- node-template/runtime/src/lib.rs | 8 +- node/cli/Cargo.toml | 2 + node/cli/src/factory_impl.rs | 16 +-- node/cli/src/service.rs | 12 ++- node/executor/src/lib.rs | 76 +++++++------- node/runtime/src/lib.rs | 2 +- srml/balances/src/lib.rs | 12 ++- srml/balances/src/tests.rs | 12 +-- srml/executive/src/lib.rs | 58 +++++------ srml/support/test/tests/instance.rs | 2 +- srml/support/test/tests/issue2219.rs | 2 +- srml/system/src/lib.rs | 8 ++ subkey/Cargo.toml | 2 + subkey/src/main.rs | 20 ++-- 22 files changed, 270 insertions(+), 163 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f389625eae788..d925435e138c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2198,9 +2198,11 @@ dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", + "srml-balances 2.0.0", "srml-contracts 2.0.0", "srml-finality-tracker 2.0.0", "srml-indices 2.0.0", + "srml-system 2.0.0", "srml-timestamp 2.0.0", "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-basic-authorship 2.0.0", @@ -4055,6 +4057,8 @@ dependencies = [ "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", + "srml-balances 2.0.0", + "srml-system 2.0.0", "substrate-bip39 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-primitives 2.0.0", "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index a500283b7a947..12e96ba2f1832 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -209,7 +209,7 @@ mod tests { use super::*; use runtime_io::blake2_256; use crate::codec::{Encode, Decode}; - use crate::traits::{SignedExtension, DispatchError}; + use crate::traits::SignedExtension; use serde::{Serialize, Deserialize}; struct TestContext; @@ -237,89 +237,151 @@ mod tests { } } - const DUMMY_ACCOUNTID: u64 = 0; + type TestAccountId = u64; + type TestCall = Vec; + const TEST_ACCOUNT: TestAccountId = 0; + + // NOTE: this is demonstration. One can simply use `()` for testing. #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd)] struct TestExtra; impl SignedExtension for TestExtra { type AccountId = u64; } - type Ex = UncheckedExtrinsic, TestSig, TestExtra>; - type CEx = CheckedExtrinsic, TestExtra>; + + type Ex = UncheckedExtrinsic; + type CEx = CheckedExtrinsic; #[test] fn unsigned_codec_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); + let ux = Ex::new_unsigned(vec![0u8; 0]); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); } #[test] fn signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal(), TestExtra); + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (TEST_ACCOUNT, vec![0u8; 0], Era::immortal(), 0u64).encode()), + Era::immortal(), + TestExtra + ); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); } #[test] fn large_signed_codec_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8; 257], Era::immortal(), 0u64).using_encoded(blake2_256)[..].to_owned()), Era::immortal(), TestExtra); + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (TEST_ACCOUNT, vec![0u8; 257], Era::immortal(), 0u64) + .using_encoded(blake2_256)[..].to_owned()), + Era::immortal(), + TestExtra + ); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Some(ux)); } #[test] fn unsigned_check_should_work() { - let ux = Ex::new_unsigned(vec![0u8;0]); + let ux = Ex::new_unsigned(vec![0u8; 0]); assert!(!ux.is_signed().unwrap_or(false)); assert!(>::check(ux, &TestContext).is_ok()); } #[test] fn badly_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, vec![0u8]), Era::immortal(), TestExtra); + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, vec![0u8; 0]), + Era::immortal(), + TestExtra + ); assert!(ux.is_signed().unwrap_or(false)); assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); } #[test] fn immortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::immortal(), 0u64).encode()), Era::immortal(), TestExtra); + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (vec![0u8; 0], Era::immortal(), 0u64, TestExtra).encode()), + Era::immortal(), + TestExtra + ); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0, TestExtra)), function: vec![0u8;0] })); + assert_eq!( + >::check(ux, &TestContext), + Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }) + ); } #[test] fn mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::mortal(32, 42), 42u64).encode()), Era::mortal(32, 42), TestExtra); + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (vec![0u8; 0], Era::mortal(32, 42), 42u64, TestExtra).encode()), + Era::mortal(32, 42), + TestExtra + ); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0, TestExtra)), function: vec![0u8;0] })); + assert_eq!( + >::check(ux, &TestContext), + Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }) + ); } #[test] fn later_mortal_signed_check_should_work() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (Compact::from(DUMMY_ACCOUNTID), vec![0u8;0], Era::mortal(32, 11), 11u64).encode()), Era::mortal(32, 11), TestExtra); + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (vec![0u8; 0], Era::mortal(32, 11), 11u64, TestExtra).encode()), + Era::mortal(32, 11), + TestExtra + ); assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Ok(CEx { signed: Some((DUMMY_ACCOUNTID, 0, TestExtra)), function: vec![0u8;0] })); + assert_eq!( + >::check(ux, &TestContext), + Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] })); } #[test] fn too_late_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 10), 10u64).encode()), Era::mortal(32, 10), TestExtra); + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (TEST_ACCOUNT, vec![0u8; 0], Era::mortal(32, 10), 10u64).encode()), + Era::mortal(32, 10), + TestExtra + ); assert!(ux.is_signed().unwrap_or(false)); assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); } #[test] fn too_early_mortal_signed_check_should_fail() { - let ux = Ex::new_signed(0, vec![0u8;0], DUMMY_ACCOUNTID, TestSig(DUMMY_ACCOUNTID, (DUMMY_ACCOUNTID, vec![0u8;0], Era::mortal(32, 43), 43u64).encode()), Era::mortal(32, 43), TestExtra); + let ux = Ex::new_signed( + vec![0u8; 0], + TEST_ACCOUNT, + TestSig(TEST_ACCOUNT, (TEST_ACCOUNT, vec![0u8; 0], Era::mortal(32, 43), 43u64).encode()), + Era::mortal(32, 43), + TestExtra + ); assert!(ux.is_signed().unwrap_or(false)); assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); } #[test] fn encoding_matches_vec() { - let ex = Ex::new_unsigned(vec![0u8;0]); + let ex = Ex::new_unsigned(vec![0u8; 0]); let encoded = ex.encode(); let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); assert_eq!(decoded, ex); diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index 76d37a0580a3a..a101c3db94212 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -21,7 +21,7 @@ use std::{fmt::Debug, ops::Deref, fmt}; use crate::codec::{Codec, Encode, Decode}; use crate::traits::{ self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, TypedKey, DispatchError, DispatchResult, - ValidateUnsigned + ValidateUnsigned, SignedExtension, Dispatchable, }; use crate::{generic, KeyTypeId}; use crate::weights::{Weighable, Weight}; @@ -198,45 +198,49 @@ impl<'a, Xt> Deserialize<'a> for Block where Block: Decode { } } -/// Test transaction, tuple of (sender, index, call) +/// Test transaction, tuple of (sender, call, signed_extra) /// with index only used if sender is some. /// /// If sender is some then the transaction is signed otherwise it is unsigned. #[derive(PartialEq, Eq, Clone, Encode, Decode)] -pub struct TestXt(pub Option, u64, pub Call); +pub struct TestXt(pub Option, pub Call, pub Extra); -impl Serialize for TestXt where TestXt: Encode -{ +impl Serialize for TestXt where TestXt: Encode { fn serialize(&self, seq: S) -> Result where S: Serializer { self.using_encoded(|bytes| seq.serialize_bytes(bytes)) } } -impl Debug for TestXt { +impl Debug for TestXt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TestXt({:?}, {:?})", self.0, self.1) + write!(f, "TestXt({:?}, ...)", self.0) } } -impl Checkable for TestXt { +impl Checkable for TestXt { type Checked = Self; fn check(self, _: &Context) -> Result { Ok(self) } } -impl traits::Extrinsic for TestXt { + +impl traits::Extrinsic for TestXt { fn is_signed(&self) -> Option { Some(self.0.is_some()) } } -impl Applyable for TestXt where - Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug, + +impl Applyable for TestXt where + Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug + Dispatchable, + Extra: SignedExtension, + Origin: From> { type AccountId = u64; type Call = Call; + fn sender(&self) -> Option<&u64> { self.0.as_ref() } /// Checks to see if this is a valid *transaction*. It returns information on it if so. fn validate>(&self, - _weight: crate::weights::Weight + _weight: Weight ) -> TransactionValidity { TransactionValidity::Valid(Default::default()) } @@ -244,12 +248,20 @@ 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: crate::weights::Weight + weight: Weight ) -> Result { - Ok(Ok(())) + let maybe_who = if let Some(who) = self.0 { + Extra::pre_dispatch(self.2, &who, weight)?; + Some(who) + } else { + Extra::pre_dispatch_unsigned(weight)?; + None + }; + Ok(self.1.dispatch(maybe_who.into())) } } -impl Weighable for TestXt { + +impl Weighable for TestXt { fn weight(&self, len: usize) -> Weight { // for testing: weight == size. len as Weight diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index ccd9c06ea33ec..91667ecce4ae7 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -880,6 +880,12 @@ impl< } } +/// To be used only for testing. +#[cfg(feature = "std")] +impl SignedExtension for () { + type AccountId = u64; +} + /// An "executable" piece of information, used by the standard Substrate Executive in order to /// enact a piece of extrinsic information by marshalling and dispatching to a named function /// call. diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs index b72d2af62a9ac..196336a2e91e8 100644 --- a/core/test-runtime/src/lib.rs +++ b/core/test-runtime/src/lib.rs @@ -36,7 +36,7 @@ use substrate_client::{ use runtime_primitives::{ ApplyResult, create_runtime_str, - transaction_validity::TransactionValidity, + transaction_validity::{TransactionValidity, ValidTransaction}, traits::{ BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT, GetNodeBlockType, GetRuntimeBlockType, Verify @@ -371,13 +371,13 @@ cfg_if! { impl client_api::TaggedTransactionQueue for Runtime { fn validate_transaction(utx: ::Extrinsic) -> TransactionValidity { if let Extrinsic::IncludeData(data) = utx { - return TransactionValidity::Valid { + return TransactionValidity::Valid(ValidTransaction { priority: data.len() as u64, requires: vec![], provides: vec![data], longevity: 1, propagate: false, - }; + }); } system::validate_transaction(utx) @@ -512,13 +512,13 @@ cfg_if! { impl client_api::TaggedTransactionQueue for Runtime { fn validate_transaction(utx: ::Extrinsic) -> TransactionValidity { if let Extrinsic::IncludeData(data) = utx { - return TransactionValidity::Valid { + return TransactionValidity::Valid(ValidTransaction{ priority: data.len() as u64, requires: vec![], provides: vec![data], longevity: 1, propagate: false, - }; + }); } system::validate_transaction(utx) diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index f4433e391c7df..01b032f59925a 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -23,7 +23,8 @@ use runtime_support::storage::{self, StorageValue, StorageMap}; use runtime_support::storage_items; use runtime_primitives::traits::{Hash as HashT, BlakeTwo256, Header as _}; use runtime_primitives::generic; -use runtime_primitives::{ApplyError, ApplyOutcome, ApplyResult, transaction_validity::TransactionValidity}; +use runtime_primitives::{ApplyError, ApplyOutcome, ApplyResult}; +use runtime_primitives::transaction_validity::{TransactionValidity, ValidTransaction}; use parity_codec::{KeyedVec, Encode}; use super::{ AccountId, BlockNumber, Extrinsic, Transfer, H256 as Hash, Block, Header, Digest, AuthorityId @@ -175,13 +176,13 @@ pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity { p }; - TransactionValidity::Valid { + TransactionValidity::Valid(ValidTransaction { priority: tx.amount, requires, provides, longevity: 64, propagate: true, - } + }) } /// Execute a transaction outside of the block execution function. diff --git a/core/transaction-pool/graph/src/pool.rs b/core/transaction-pool/graph/src/pool.rs index 4498598aee9ca..8f76a17ed9809 100644 --- a/core/transaction-pool/graph/src/pool.rs +++ b/core/transaction-pool/graph/src/pool.rs @@ -129,18 +129,19 @@ impl Pool { } match self.api.validate_transaction(at, xt.clone())? { - TransactionValidity::Valid { priority, requires, provides, longevity, propagate } => { + TransactionValidity::Valid(validity) => { Ok(base::Transaction { data: xt, - bytes, + bytes + , hash, - priority, - requires, - provides, - propagate, + priority: validity.priority, + requires: validity.requires, + provides: validity.provides, + propagate: validity.propagate, valid_till: block_number .saturated_into::() - .saturating_add(longevity), + .saturating_add(validity.longevity), }) }, TransactionValidity::Invalid(e) => { @@ -233,7 +234,7 @@ impl Pool { for (extrinsic, existing_in_pool) in all { match *existing_in_pool { - // reuse the tags for extrinsis that were found in the pool + // reuse the tags for extrinsics that were found in the pool Some(ref transaction) => { tags.extend(transaction.provides.iter().cloned()); }, @@ -242,8 +243,8 @@ impl Pool { None => { let validity = self.api.validate_transaction(parent, extrinsic.clone()); match validity { - Ok(TransactionValidity::Valid { mut provides, .. }) => { - tags.append(&mut provides); + Ok(TransactionValidity::Valid(mut validity)) => { + tags.append(&mut validity.provides); }, // silently ignore invalid extrinsics, // cause they might just be inherent @@ -306,7 +307,7 @@ impl Pool { let hashes = status.pruned.iter().map(|tx| tx.hash.clone()).collect::>(); let results = self.submit_at(at, status.pruned.into_iter().map(|tx| tx.data.clone()))?; - // Collect the hashes of transactions that now became invalid (meaning that they are succesfully pruned). + // Collect the hashes of transactions that now became invalid (meaning that they are successfully pruned). let hashes = results.into_iter().enumerate().filter_map(|(idx, r)| match r.map_err(error::IntoPoolError::into_pool_error) { Err(Ok(error::Error::InvalidTransaction(_))) => Some(hashes[idx].clone()), _ => None, @@ -451,6 +452,7 @@ fn fire_events( #[cfg(test)] mod tests { use super::*; + use sr_primitives::transaction_validity::ValidTransaction; use futures::Stream; use parity_codec::Encode; use test_runtime::{Block, Extrinsic, Transfer, H256, AccountId}; @@ -486,13 +488,13 @@ mod tests { if nonce < block_number { Ok(TransactionValidity::Invalid(0)) } else { - Ok(TransactionValidity::Valid { + Ok(TransactionValidity::Valid(ValidTransaction { priority: 4, requires: if nonce > block_number { vec![vec![nonce as u8 - 1]] } else { vec![] }, provides: vec![vec![nonce as u8]], longevity: 3, propagate: true, - }) + })) } } diff --git a/core/transaction-pool/src/tests.rs b/core/transaction-pool/src/tests.rs index a1ee4a50df332..7fb94936d2b62 100644 --- a/core/transaction-pool/src/tests.rs +++ b/core/transaction-pool/src/tests.rs @@ -23,7 +23,7 @@ use test_client::{runtime::{AccountId, Block, Hash, Index, Extrinsic, Transfer}, use sr_primitives::{ generic::{self, BlockId}, traits::{Hash as HashT, BlakeTwo256}, - transaction_validity::TransactionValidity, + transaction_validity::{TransactionValidity, ValidTransaction}, }; struct TestApi; @@ -48,13 +48,13 @@ impl txpool::ChainApi for TestApi { }; let provides = vec![vec![uxt.transfer().nonce as u8]]; - Ok(TransactionValidity::Valid { + Ok(TransactionValidity::Valid(ValidTransaction { priority: 1, requires, provides, longevity: 64, propagate: true, - }) + })) } fn block_id_to_number(&self, at: &BlockId) -> error::Result>> { diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 8879b163ff763..1dcc65d00b5e4 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -227,12 +227,14 @@ pub type Header = generic::Header; pub type Block = generic::Block; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = (system::CheckNonce, balances::TakeFees); /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; +pub type Executive = executive::Executive; // Implement our runtime API endpoints. This is just a bunch of proxying. impl_runtime_apis! { diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index ad49cbe36cd90..17efcc400f35b 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -40,6 +40,8 @@ timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default rand = "0.6" finality_tracker = { package = "srml-finality-tracker", path = "../../srml/finality-tracker", default-features = false } contracts = { package = "srml-contracts", path = "../../srml/contracts" } +system = { package = "srml-system", path = "../../srml/system" } +balances = { package = "srml-balances", path = "../../srml/balances" } [dev-dependencies] consensus-common = { package = "substrate-consensus-common", path = "../../core/consensus/common" } diff --git a/node/cli/src/factory_impl.rs b/node/cli/src/factory_impl.rs index 0d94610362fdc..40f4f602ce712 100644 --- a/node/cli/src/factory_impl.rs +++ b/node/cli/src/factory_impl.rs @@ -130,14 +130,14 @@ impl RuntimeAdapter for FactoryState { ) -> ::Extrinsic { let index = self.extract_index(&sender, prior_block_hash); let phase = self.extract_phase(*prior_block_hash); + let check_nonce = system::CheckNonce::from(index); + let take_fees = balances::TakeFees::from(0); sign::(CheckedExtrinsic { - signed: Some((sender.clone(), index)), + signed: Some((sender.clone(), (check_nonce, take_fees))), function: Call::Balances( BalancesCall::transfer( - indices::address::Address::Id( - destination.clone().into() - ), + indices::address::Address::Id(destination.clone().into()), (*amount).into() ) ) @@ -233,9 +233,9 @@ fn sign( phase: u64, ) -> ::Extrinsic { let s = match xt.signed { - Some((signed, index)) => { + Some((signed, extra)) => { let era = Era::mortal(256, phase); - let payload = (index.into(), xt.function, era, prior_block_hash); + let payload = (xt.function, era, prior_block_hash, extra.clone()); let signature = payload.using_encoded(|b| { if b.len() > 256 { key.sign(&sr_io::blake2_256(b)) @@ -244,8 +244,8 @@ fn sign( } }).into(); UncheckedExtrinsic { - signature: Some((indices::address::Address::Id(signed), signature, payload.0, era)), - function: payload.1, + signature: Some((indices::address::Address::Id(signed), signature, era, extra)), + function: payload.0, } } None => UncheckedExtrinsic { diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs index 3dcaf85d6a80b..ce054f9f798a2 100644 --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -219,7 +219,7 @@ mod tests { use consensus_common::{Environment, Proposer, ImportBlock, BlockOrigin, ForkChoiceStrategy}; use node_primitives::DigestItem; use node_runtime::{BalancesCall, Call, CENTS, UncheckedExtrinsic}; - use parity_codec::{Compact, Encode, Decode}; + use parity_codec::{Encode, Decode}; use primitives::{ crypto::Pair as CryptoPair, ed25519::Pair, blake2_256, sr25519::Public as AddressPublic, H256, @@ -358,18 +358,22 @@ mod tests { let function = Call::Balances(BalancesCall::transfer(to.into(), amount)); let era = Era::immortal(); - let raw_payload = (Compact(index), function, era, genesis_hash); + let check_nonce = system::CheckNonce::from(index); + let take_fees = balances::TakeFees::from(0); + let extra = (check_nonce, take_fees); + + let raw_payload = (function, era, genesis_hash, extra.clone()); let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { signer.sign(&blake2_256(payload)[..]) } else { signer.sign(payload) }); let xt = UncheckedExtrinsic::new_signed( - index, - raw_payload.1, + raw_payload.0, from.into(), signature.into(), era, + extra, ).encode(); let v: Vec = Decode::decode(&mut xt.as_slice()).unwrap(); diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index ae306d33e5bd3..87c4d4a88e4bc 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -40,13 +40,13 @@ mod tests { use substrate_executor::{WasmExecutor, NativeExecutionDispatch}; use parity_codec::{Encode, Decode, Joiner}; use keyring::{AuthorityKeyring, AccountKeyring}; - use runtime_support::{Hashable, StorageValue, StorageMap, traits::Currency}; + use runtime_support::{Hashable, StorageValue, StorageMap, traits::{Currency, Get}}; use state_machine::{CodeExecutor, Externalities, TestExternalities as CoreTestExternalities}; use primitives::{ twox_128, blake2_256, Blake2Hasher, ChangesTrieConfiguration, NeverNativeValue, NativeOrEncoded }; - use node_primitives::{Hash, BlockNumber, AccountId}; + use node_primitives::{Hash, BlockNumber, AccountId, Balance, Index}; use runtime_primitives::traits::{Header as HeaderT, Hash as HashT}; use runtime_primitives::{generic::Era, ApplyOutcome, ApplyError, ApplyResult, Perbill}; use {balances, contracts, indices, staking, system, timestamp}; @@ -55,8 +55,8 @@ mod tests { use node_runtime::{ Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System, SystemConfig, - GrandpaConfig, IndicesConfig, ContractsConfig, Event, SessionKeys, - CENTS, DOLLARS, MILLICENTS, + GrandpaConfig, IndicesConfig, ContractsConfig, Event, SessionKeys, CreationFee, + CENTS, DOLLARS, MILLICENTS, SignedExtra, TransactionBaseFee, TransactionByteFee, }; use wabt; use primitives::map; @@ -78,10 +78,16 @@ mod tests { const GENESIS_HASH: [u8; 32] = [69u8; 32]; - const TX_FEE: u128 = 3 * CENTS + 460 * MILLICENTS; - type TestExternalities = CoreTestExternalities; + fn transfer_fee(bytes: Balance) -> Balance { + >::get() + >::get() * bytes + } + + fn creation_fee() -> Balance { + >::get() + } + fn alice() -> AccountId { AccountKeyring::Alice.into() } @@ -108,9 +114,9 @@ mod tests { fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { match xt.signed { - Some((signed, index)) => { + Some((signed, extra)) => { let era = Era::mortal(256, 0); - let payload = (index.into(), xt.function, era, GENESIS_HASH); + let payload = (xt.function, era, GENESIS_HASH, extra.clone()); let key = AccountKeyring::from_public(&signed).unwrap(); let signature = payload.using_encoded(|b| { if b.len() > 256 { @@ -120,8 +126,8 @@ mod tests { } }).into(); UncheckedExtrinsic { - signature: Some((indices::address::Address::Id(signed), signature, payload.0, era)), - function: payload.1, + signature: Some((indices::address::Address::Id(signed), signature, era, extra)), + function: payload.0, } } None => UncheckedExtrinsic { @@ -131,9 +137,13 @@ mod tests { } } + fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra { + (system::CheckNonce::from(nonce), balances::TakeFees::from(extra_fee)) + } + fn xt() -> UncheckedExtrinsic { sign(CheckedExtrinsic { - signed: Some((alice(), 0)), + signed: Some((alice(), signed_extra(0, 0))), function: Call::Balances(balances::Call::transfer::(bob().into(), 69 * DOLLARS)), }) } @@ -249,7 +259,7 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(169) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -285,7 +295,7 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(169) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -430,7 +440,7 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { - signed: Some((alice(), 0)), + signed: Some((alice(), signed_extra(0, 0))), function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), }, ] @@ -452,7 +462,7 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { - signed: Some((alice(), 0)), + signed: Some((alice(), signed_extra(0, 0))), function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), }, ] @@ -467,11 +477,11 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(52)), }, CheckedExtrinsic { - signed: Some((bob(), 0)), + signed: Some((bob(), signed_extra(0, 0))), function: Call::Balances(balances::Call::transfer(alice().into(), 5 * DOLLARS)), }, CheckedExtrinsic { - signed: Some((alice(), 1)), + signed: Some((alice(), signed_extra(1, 0))), function: Call::Balances(balances::Call::transfer(bob().into(), 15 * DOLLARS)), } ] @@ -496,7 +506,7 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { - signed: Some((alice(), 0)), + signed: Some((alice(), signed_extra(0, 0))), function: Call::System(system::Call::remark(vec![0; 120000])), } ] @@ -518,9 +528,7 @@ mod tests { ).0.unwrap(); runtime_io::with_externalities(&mut t, || { - // block1 transfers from alice 69 to bob. - // -1 is the default fee - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(169) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); let events = vec![ EventRecord { @@ -556,11 +564,9 @@ mod tests { ).0.unwrap(); runtime_io::with_externalities(&mut t, || { - // bob sends 5, alice sends 15 | bob += 10, alice -= 10 - // 111 - 69 - 10 = 32 - assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * TX_FEE); - // 100 + 69 + 10 = 179 - assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * TX_FEE); + // TODO TODO: this needs investigating: why are we deducting creation fee twice here? and why bob also pays it? + assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(169) - 2 * creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - transfer_fee(169) - creation_fee()); let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), @@ -615,19 +621,15 @@ mod tests { WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap(); runtime_io::with_externalities(&mut t, || { - // block1 transfers from alice 69 to bob. - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(169) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); }); WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block2.0).unwrap(); runtime_io::with_externalities(&mut t, || { - // bob sends 5, alice sends 15 | bob += 10, alice -= 10 - // 111 - 69 - 10 = 32 - assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * TX_FEE); - // 100 + 69 + 10 = 179 - assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * TX_FEE); + assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(169) - 2 * creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * transfer_fee(169) - creation_fee()); }); } @@ -745,19 +747,19 @@ mod tests { function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { - signed: Some((charlie(), 0)), + signed: Some((charlie(), signed_extra(0, 0))), function: Call::Contracts( contracts::Call::put_code::(10_000, transfer_code) ), }, CheckedExtrinsic { - signed: Some((charlie(), 1)), + signed: Some((charlie(), signed_extra(1, 0))), function: Call::Contracts( contracts::Call::create::(1 * DOLLARS, 10_000, transfer_ch, Vec::new()) ), }, CheckedExtrinsic { - signed: Some((charlie(), 2)), + signed: Some((charlie(), signed_extra(2, 0))), function: Call::Contracts( contracts::Call::call::( indices::address::Address::Id(addr.clone()), @@ -873,7 +875,7 @@ mod tests { assert_eq!(r, Ok(ApplyOutcome::Success)); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(169) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 4daab741406b4..71784959e5ef8 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -436,7 +436,7 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; /// The SignedExtension to the basic transaction logic. -type SignedExtra = (system::CheckNonce, balances::TakeFees); +pub type SignedExtra = (system::CheckNonce, balances::TakeFees); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 1e5ec3afc7a23..8c04e6f7d29d5 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -1135,6 +1135,14 @@ where #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct TakeFees, I: Instance = DefaultInstance>(T::Balance); +#[cfg(feature = "std")] +impl, I: Instance> TakeFees { + /// utility constructor. Used only in client/factory code. + pub fn from(fee: T::Balance) -> Self { + Self(fee) + } +} + #[cfg(feature = "std")] impl, I: Instance> rstd::fmt::Debug for TakeFees { fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { @@ -1154,8 +1162,8 @@ impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { who: &Self::AccountId, weight: Weight, ) -> rstd::result::Result { - let fee_x = T::Balance::from(weight as u32); - // should be weight_to_fee(weight) + let fee_x = T::Balance::from(weight); + // TODO TODO: should be weight_to_fee(weight) let fee = T::TransactionBaseFee::get() + T::TransactionByteFee::get() * fee_x; let fee = fee + self.0.clone(); let imbalance = >::withdraw( diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 0a5a4b5bb70a6..10e6f94693682 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -24,7 +24,7 @@ use runtime_io::with_externalities; use srml_support::{ assert_noop, assert_ok, assert_err, traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, - Currency, MakePayment, ReservableCurrency} + Currency, ReservableCurrency} }; const ID_1: LockIdentifier = *b"1 "; @@ -118,7 +118,8 @@ fn lock_reasons_should_work() { "account liquidity restrictions prevent withdrawal" ); assert_ok!(>::reserve(&1, 1)); - assert_ok!(>::make_payment(&1, 1)); + // NOTE: this causes a fee payment. + assert!( as SignedExtension>::validate(&TakeFees::from(1), &1, 1).is_ok()); Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into()); assert_ok!(>::transfer(&1, &2, 1)); @@ -126,15 +127,12 @@ fn lock_reasons_should_work() { >::reserve(&1, 1), "account liquidity restrictions prevent withdrawal" ); - assert_ok!(>::make_payment(&1, 1)); + assert!( as SignedExtension>::validate(&TakeFees::from(1), &1, 1).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_noop!( - >::make_payment(&1, 1), - "account liquidity restrictions prevent withdrawal" - ); + assert!( as SignedExtension>::validate(&TakeFees::from(1), &1, 1).is_err()); }); } diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 6c83f92850ab6..a389da6ac093c 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -429,27 +429,19 @@ mod tests { fn validate_unsigned(call: &Self::Call) -> TransactionValidity { match call { - Call::set_balance(_, _, _) => TransactionValidity::Valid { - priority: 0, - requires: vec![], - provides: vec![], - longevity: std::u64::MAX, - propagate: false, - }, + Call::set_balance(_, _, _) => TransactionValidity::Valid(Default::default()), _ => TransactionValidity::Invalid(0), } } } - type TestXt = primitives::testing::TestXt>; - type Executive = super::Executive< - Runtime, - Block, - system::ChainContext, - balances::Module, - Runtime, - () - >; + type SignedExtra = (system::CheckNonce, balances::TakeFees); + type TestXt = primitives::testing::TestXt, SignedExtra>; + type Executive = super::Executive, system::ChainContext, Runtime, ()>; + + fn extra(nonce: u64, fee: u64) -> SignedExtra { + (system::CheckNonce::from(nonce), balances::TakeFees::from(fee)) + } #[test] fn balance_transfer_dispatch_works() { @@ -458,7 +450,7 @@ mod tests { balances: vec![(1, 111)], vesting: vec![], }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); - let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2, 69)); + let xt = primitives::testing::TestXt(Some(1), Call::transfer(2, 69), extra(0, 0)); let mut t = runtime_io::TestExternalities::::new_with_children(t); with_externalities(&mut t, || { Executive::initialize_block(&Header::new( @@ -468,7 +460,8 @@ mod tests { [69u8; 32].into(), Digest::default(), )); - Executive::apply_extrinsic(xt).unwrap(); + let r = Executive::apply_extrinsic(xt); + assert_eq!(r, Ok(ApplyOutcome::Success)); assert_eq!(>::total_balance(&1), 42 - 10); assert_eq!(>::total_balance(&2), 69); }); @@ -536,7 +529,8 @@ mod tests { #[test] fn bad_extrinsic_not_inserted() { let mut t = new_test_ext(); - let xt = primitives::testing::TestXt(Some(1), 42, Call::transfer(33, 69)); + // bad nonce check! + let xt = primitives::testing::TestXt(Some(1), Call::transfer(33, 69), extra(30, 0)); with_externalities(&mut t, || { Executive::initialize_block(&Header::new( 1, @@ -554,10 +548,11 @@ mod tests { fn block_weight_limit_enforced() { let run_test = |should_fail: bool| { let mut t = new_test_ext(); - let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(33, 69)); - let xt2 = primitives::testing::TestXt(Some(1), 1, Call::transfer(33, 69)); + 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 { (internal::MAX_TRANSACTIONS_WEIGHT - 1) as usize } else { encoded.len() }; + let encoded_len = encoded.len() as u32; with_externalities(&mut t, || { Executive::initialize_block(&Header::new( 1, @@ -573,11 +568,11 @@ mod tests { if should_fail { assert!(res.is_err()); - assert_eq!(>::all_extrinsics_weight(), 28); + assert_eq!(>::all_extrinsics_weight(), encoded_len); assert_eq!(>::extrinsic_index(), Some(1)); } else { assert!(res.is_ok()); - assert_eq!(>::all_extrinsics_weight(), 56); + assert_eq!(>::all_extrinsics_weight(), encoded_len * 2); assert_eq!(>::extrinsic_index(), Some(2)); } }); @@ -589,7 +584,8 @@ mod tests { #[test] fn default_block_weight() { - let xt = primitives::testing::TestXt(None, 0, Call::set_balance(33, 69, 69)); + let xt = primitives::testing::TestXt(None, Call::set_balance(33, 69, 69), extra(0, 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(); @@ -597,21 +593,15 @@ mod tests { Executive::apply_extrinsic(xt.clone()).unwrap(); assert_eq!( >::all_extrinsics_weight(), - 3 * (0 /*base*/ + 22 /*len*/ * 1 /*byte*/) + 3 * (0 /*base*/ + len /*len*/ * 1 /*byte*/) ); }); } #[test] fn validate_unsigned() { - let xt = primitives::testing::TestXt(None, 0, Call::set_balance(33, 69, 69)); - let valid = TransactionValidity::Valid { - priority: 0, - requires: vec![], - provides: vec![], - longevity: 18446744073709551615, - propagate: false, - }; + let xt = primitives::testing::TestXt(None, Call::set_balance(33, 69, 69), extra(0, 0)); + let valid = TransactionValidity::Valid(Default::default()); let mut t = new_test_ext(); with_externalities(&mut t, || { @@ -633,7 +623,7 @@ mod tests { 10, lock, ); - let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2, 10)); + let xt = primitives::testing::TestXt(Some(1), Call::transfer(2, 10), extra(0, 0)); Executive::initialize_block(&Header::new( 1, H256::default(), diff --git a/srml/support/test/tests/instance.rs b/srml/support/test/tests/instance.rs index 46edcd7df323b..a5f40878e057b 100644 --- a/srml/support/test/tests/instance.rs +++ b/srml/support/test/tests/instance.rs @@ -269,7 +269,7 @@ srml_support::construct_runtime!( pub type Header = generic::Header; pub type Block = generic::Block; -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; fn new_test_ext() -> runtime_io::TestExternalities { GenesisConfig{ diff --git a/srml/support/test/tests/issue2219.rs b/srml/support/test/tests/issue2219.rs index 9bae9bcde5549..54ad62cc937ab 100644 --- a/srml/support/test/tests/issue2219.rs +++ b/srml/support/test/tests/issue2219.rs @@ -152,7 +152,7 @@ pub type BlockNumber = u64; pub type Index = u64; pub type Header = generic::Header; pub type Block = generic::Block; -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; impl system::Trait for Runtime { type Hash = H256; diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index d60524dc42c8b..9e98aaffa017f 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -757,6 +757,14 @@ impl Module { #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckNonce(T::Index); +#[cfg(feature = "std")] +impl CheckNonce { + /// utility constructor. Used only in client/factory code. + pub fn from(nonce: T::Index) -> Self { + Self(nonce) + } +} + #[cfg(feature = "std")] impl rstd::fmt::Debug for CheckNonce { fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { diff --git a/subkey/Cargo.toml b/subkey/Cargo.toml index 699448e17407c..6ecede88edaec 100644 --- a/subkey/Cargo.toml +++ b/subkey/Cargo.toml @@ -18,6 +18,8 @@ schnorrkel = "0.1.1" hex = "0.3" hex-literal = "0.2" parity-codec = "4.1.1" +system = { package = "srml-system", path = "../srml/system" } +balances = { package = "srml-balances", path = "../srml/balances" } [features] bench = [] diff --git a/subkey/src/main.rs b/subkey/src/main.rs index 7cff0d6414f26..dfd923e993149 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -23,10 +23,10 @@ use hex_literal::hex; use clap::load_yaml; use bip39::{Mnemonic, Language, MnemonicType}; use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay, Pair, Public, crypto::Ss58Codec, blake2_256}; -use parity_codec::{Encode, Decode, Compact}; +use parity_codec::{Encode, Decode}; use sr_primitives::generic::Era; use node_primitives::{Balance, Index, Hash}; -use node_runtime::{Call, UncheckedExtrinsic, BalancesCall}; +use node_runtime::{Call, UncheckedExtrinsic, BalancesCall, Runtime}; mod vanity; @@ -86,6 +86,9 @@ fn execute(matches: clap::ArgMatches) where <::Pair as Pair>::Signature: AsRef<[u8]> + AsMut<[u8]> + Default, <::Pair as Pair>::Public: Sized + AsRef<[u8]> + Ss58Codec + AsRef<<::Pair as Pair>::Public>, { + let extra = |i: Index, f: Balance| { + (system::CheckNonce::::from(i), balances::TakeFees::::from(f)) + }; let password = matches.value_of("password"); match matches.subcommand() { ("generate", Some(matches)) => { @@ -153,7 +156,8 @@ fn execute(matches: clap::ArgMatches) where println!("Using a genesis hash of {}", HexDisplay::from(&genesis_hash.as_ref())); let era = Era::immortal(); - let raw_payload = (Compact(index), function, era, genesis_hash); + + let raw_payload = (function, era, genesis_hash, extra(index, 0)); let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { signer.sign(&blake2_256(payload)[..]) } else { @@ -161,11 +165,11 @@ fn execute(matches: clap::ArgMatches) where signer.sign(payload) }); let extrinsic = UncheckedExtrinsic::new_signed( - index, - raw_payload.1, + raw_payload.0, signer.public().into(), signature.into(), era, + extra(index, 0), ); println!("0x{}", hex::encode(&extrinsic.encode())); } @@ -192,7 +196,7 @@ fn execute(matches: clap::ArgMatches) where let era = Era::immortal(); - let raw_payload = (Compact(index), function, era, prior_block_hash); + let raw_payload = (function, era, prior_block_hash, extra(index, 0)); let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { signer.sign(&blake2_256(payload)[..]) @@ -202,11 +206,11 @@ fn execute(matches: clap::ArgMatches) where ); let extrinsic = UncheckedExtrinsic::new_signed( - index, - raw_payload.1, + raw_payload.0, signer.public().into(), signature.into(), era, + extra(index, 0), ); println!("0x{}", hex::encode(&extrinsic.encode())); From 8e7c803c0ffdedf0c9775c01fe124ac5c30e0a30 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 13 Jul 2019 13:30:52 +0200 Subject: [PATCH 06/20] Fix a doc test --- srml/executive/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index a389da6ac093c..512b9086c9cbb 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -69,7 +69,7 @@ //! # } //! # } //! /// Executive: handles dispatch to the various modules. -//! pub type Executive = executive::Executive; +//! pub type Executive = executive::Executive; //! ``` #![cfg_attr(not(feature = "std"), no_std)] From a824b561e371faf8eefadc23ed527ddc93d286f8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 13 Jul 2019 14:03:10 +0200 Subject: [PATCH 07/20] Compact encoding --- srml/balances/src/lib.rs | 2 +- srml/system/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 8c04e6f7d29d5..493090ed0b3c2 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -1133,7 +1133,7 @@ where /// Require the transactor pay for themselves and maybe include a tip to gain additional priority /// in the queue. #[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct TakeFees, I: Instance = DefaultInstance>(T::Balance); +pub struct TakeFees, I: Instance = DefaultInstance>(#[codec(compact)] T::Balance); #[cfg(feature = "std")] impl, I: Instance> TakeFees { diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 9e98aaffa017f..bc2b217a49eeb 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -755,7 +755,7 @@ impl Module { /// Nonce check and increment to give replay protection for transactions. #[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct CheckNonce(T::Index); +pub struct CheckNonce(#[codec(compact)] T::Index); #[cfg(feature = "std")] impl CheckNonce { From 5de080f671e0290729d9ae0f54b73b9a97e318f9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 15 Jul 2019 15:42:53 +0900 Subject: [PATCH 08/20] Extract out the era logic into an extension --- .../src/generic/unchecked_extrinsic.rs | 38 +++++++------------ core/sr-primitives/src/traits.rs | 18 ++++++++- node/cli/src/factory_impl.rs | 22 +++++------ node/executor/src/lib.rs | 1 + node/runtime/src/lib.rs | 2 +- srml/balances/src/lib.rs | 5 ++- srml/system/src/lib.rs | 38 ++++++++++++++++++- 7 files changed, 82 insertions(+), 42 deletions(-) diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index 12e96ba2f1832..190fff168c770 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -22,11 +22,8 @@ use std::fmt; use rstd::prelude::*; use runtime_io::blake2_256; use crate::codec::{Decode, Encode, Input}; -use crate::traits::{ - self, Member, SimpleArithmetic, MaybeDisplay, CurrentHeight, SignedExtension, - BlockNumberToHash, Lookup, Checkable, Extrinsic, SaturatedConversion -}; -use super::{CheckedExtrinsic, Era}; +use crate::traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic}; +use super::CheckedExtrinsic; const TRANSACTION_VERSION: u8 = 1; @@ -40,7 +37,7 @@ where /// The signature, address, number of extrinsics have come before from /// the same signer and an era describing the longevity of this transaction, /// if this is a signed extrinsic. - pub signature: Option<(Address, Signature, Era, Extra)>, + pub signature: Option<(Address, Signature, Extra)>, /// The function that should be called. pub function: Call, } @@ -53,11 +50,10 @@ impl function: Call, signed: Address, signature: Signature, - era: Era, extra: Extra ) -> Self { UncheckedExtrinsic { - signature: Some((signed, signature, era, extra)), + signature: Some((signed, signature, extra)), function, } } @@ -79,8 +75,8 @@ impl Extrinsic } } -impl - Checkable +impl + Checkable for UncheckedExtrinsic where @@ -89,22 +85,16 @@ where Signature: Member + traits::Verify, Extra: SignedExtension, AccountId: Member + MaybeDisplay, - BlockNumber: SimpleArithmetic, - Hash: Encode, - Context: Lookup - + CurrentHeight - + BlockNumberToHash, + Lookup: traits::Lookup { type Checked = CheckedExtrinsic; - fn check(self, context: &Context) -> Result { + fn check(self, lookup: &Lookup) -> Result { Ok(match self.signature { - Some((signed, signature, era, extra)) => { - let current_u64 = context.current_height().saturated_into::(); - let h = context.block_number_to_hash(era.birth(current_u64).saturated_into()) - .ok_or("transaction birth block ancient")?; - let signed = context.lookup(signed)?; - let raw_payload = (self.function, era, h, extra); + Some((signed, signature, extra)) => { + let additional_signed = extra.additional_signed()?; + let raw_payload = (self.function, extra, additional_signed); + let signed = lookup.lookup(signed)?; if !raw_payload.using_encoded(|payload| { if payload.len() > 256 { signature.verify(&blake2_256(payload)[..], &signed) @@ -115,7 +105,7 @@ where return Err(crate::BAD_SIGNATURE) } CheckedExtrinsic { - signed: Some((signed, raw_payload.3)), + signed: Some((signed, raw_payload.1)), function: raw_payload.0, } } @@ -200,7 +190,7 @@ where Extra: SignedExtension, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2, &x.3)), self.function) + write!(f, "UncheckedExtrinsic({:?}, {:?})", self.signature.as_ref().map(|x| (&x.0, &x.2)), self.function) } } diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index 91667ecce4ae7..98b3ceb0ef15d 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -813,7 +813,15 @@ pub trait SignedExtension: /// The type which encodes the sender identity. type AccountId; - /// Validate a signed transaction for the transaction queue. + /// Any additional data that will go into the signed payload. This may be created dynamically + /// from the transaction using the `additional_signed` function. + type AdditionalSigned: Encode; + + /// Construct any additional data that should be in the signed payload of the transaction. Can + /// also perform any pre-signature-verification checks and return an error if needed. + fn additional_signed(&self) -> Result; + + /// Validate a signed transaction for the transaction queue. fn validate( &self, _who: &Self::AccountId, @@ -846,6 +854,12 @@ impl< B: SignedExtension, > SignedExtension for (A, B) { type AccountId = AccountId; + type AdditionalSigned = (A::AdditionalSigned, B::AdditionalSigned); + + fn additional_signed(&self) -> Result { + Ok((self.0.additional_signed()?, self.1.additional_signed()?)) + } + fn validate( &self, who: &Self::AccountId, @@ -884,6 +898,8 @@ impl< #[cfg(feature = "std")] impl SignedExtension for () { type AccountId = u64; + type AdditionalSigned = (); + fn additional_signed(&self) -> result::Result<(), &'static str> { Ok(()) } } /// An "executable" piece of information, used by the standard Substrate Executive in order to diff --git a/node/cli/src/factory_impl.rs b/node/cli/src/factory_impl.rs index 40f4f602ce712..ed45d4cf9545a 100644 --- a/node/cli/src/factory_impl.rs +++ b/node/cli/src/factory_impl.rs @@ -23,13 +23,10 @@ use rand::rngs::StdRng; use parity_codec::Decode; use keyring::sr25519::Keyring; -use node_primitives::Hash; -use node_runtime::{Call, CheckedExtrinsic, UncheckedExtrinsic, BalancesCall}; -use primitives::sr25519; -use primitives::crypto::Pair; +use node_runtime::{Call, CheckedExtrinsic, UncheckedExtrinsic, SignedExtra, BalancesCall}; +use primitives::{sr25519, crypto::Pair}; use parity_codec::Encode; -use sr_primitives::generic::Era; -use sr_primitives::traits::{Block as BlockT, Header as HeaderT}; +use sr_primitives::{generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension}}; use substrate_service::ServiceFactory; use transaction_factory::RuntimeAdapter; use transaction_factory::modes::Mode; @@ -130,18 +127,19 @@ impl RuntimeAdapter for FactoryState { ) -> ::Extrinsic { let index = self.extract_index(&sender, prior_block_hash); let phase = self.extract_phase(*prior_block_hash); + let era = system::CheckEra::from(Era::mortal(256, phase)); let check_nonce = system::CheckNonce::from(index); let take_fees = balances::TakeFees::from(0); sign::(CheckedExtrinsic { - signed: Some((sender.clone(), (check_nonce, take_fees))), + signed: Some((sender.clone(), (era, (check_nonce, take_fees)))), function: Call::Balances( BalancesCall::transfer( indices::address::Address::Id(destination.clone().into()), (*amount).into() ) ) - }, key, &prior_block_hash, phase) + }, key, (prior_block_hash.clone(), ((), ()))) } fn inherent_extrinsics(&self) -> InherentData { @@ -229,13 +227,11 @@ fn gen_seed_bytes(seed: u64) -> [u8; 32] { fn sign( xt: CheckedExtrinsic, key: &sr25519::Pair, - prior_block_hash: &Hash, - phase: u64, + additional_signed: ::AdditionalSigned, ) -> ::Extrinsic { let s = match xt.signed { Some((signed, extra)) => { - let era = Era::mortal(256, phase); - let payload = (xt.function, era, prior_block_hash, extra.clone()); + let payload = (xt.function, extra.clone(), additional_signed); let signature = payload.using_encoded(|b| { if b.len() > 256 { key.sign(&sr_io::blake2_256(b)) @@ -244,7 +240,7 @@ fn sign( } }).into(); UncheckedExtrinsic { - signature: Some((indices::address::Address::Id(signed), signature, era, extra)), + signature: Some((indices::address::Address::Id(signed), signature, extra)), function: payload.0, } } diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 87c4d4a88e4bc..f09853ca10e1e 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -80,6 +80,7 @@ mod tests { type TestExternalities = CoreTestExternalities; + // TODO: fix for being charged based on weight now. fn transfer_fee(bytes: Balance) -> Balance { >::get() + >::get() * bytes } diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 71784959e5ef8..f6c6707e03cfd 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -436,7 +436,7 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; /// The SignedExtension to the basic transaction logic. -pub type SignedExtra = (system::CheckNonce, balances::TakeFees); +pub type SignedExtra = (system::CheckEra, (system::CheckNonce, balances::TakeFees)); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 493090ed0b3c2..9266b44a4ee42 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -1157,13 +1157,16 @@ use primitives::weights::Weight; impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { type AccountId = T::AccountId; + type AdditionalSigned = (); + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } + fn validate( &self, who: &Self::AccountId, weight: Weight, ) -> rstd::result::Result { let fee_x = T::Balance::from(weight); - // TODO TODO: should be weight_to_fee(weight) + // TODO: should be weight_to_fee(weight) let fee = T::TransactionBaseFee::get() + T::TransactionByteFee::get() * fee_x; let fee = fee + self.0.clone(); let imbalance = >::withdraw( diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index bc2b217a49eeb..684dc24ae7339 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -77,11 +77,11 @@ use rstd::prelude::*; #[cfg(any(feature = "std", test))] use rstd::map; use primitives::{ - generic, weights::Weight, traits::{ + generic::{self, Era}, weights::Weight, traits::{ self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash, MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, - Lookup, DispatchError + Lookup, DispatchError, SaturatedConversion }, transaction_validity::{ ValidTransaction, TransactionPriority, TransactionLongevity }, @@ -774,6 +774,8 @@ impl rstd::fmt::Debug for CheckNonce { impl SignedExtension for CheckNonce { type AccountId = T::AccountId; + type AdditionalSigned = (); + fn additional_signed(&self) -> Result<(), &'static str> { Ok(()) } fn pre_dispatch( self, who: &Self::AccountId, @@ -817,6 +819,38 @@ impl SignedExtension for CheckNonce { } } + +/// Nonce check and increment to give replay protection for transactions. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct CheckEra((Era, rstd::marker::PhantomData)); + +#[cfg(feature = "std")] +impl CheckEra { + /// utility constructor. Used only in client/factory code. + pub fn from(era: Era) -> Self { + Self((era, rstd::marker::PhantomData)) + } +} + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for CheckEra { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + self.0.fmt(f) + } +} + +impl SignedExtension for CheckEra { + type AccountId = T::AccountId; + type AdditionalSigned = T::Hash; + fn additional_signed(&self) -> Result { + let current_u64 = >::block_number().saturated_into::(); + let n = (self.0).0.birth(current_u64).saturated_into::(); + if !>::exists(n) { Err("transaction birth block ancient")? } + Ok(>::block_hash(n)) + } +} + + pub struct ChainContext(::rstd::marker::PhantomData); impl Default for ChainContext { fn default() -> Self { From a8789b92382cd78d1dfec0f7f386f80727a37e6f Mon Sep 17 00:00:00 2001 From: Kian Peymani Date: Tue, 16 Jul 2019 12:42:42 +0200 Subject: [PATCH 09/20] Weight Check signed extension. (#3115) * Weight signed extension. * Revert a bit + test for check era. * Update Cargo.toml * Update node/cli/src/factory_impl.rs * Update node/executor/src/lib.rs * Update node/executor/src/lib.rs --- Cargo.lock | 1 + .../src/generic/unchecked_extrinsic.rs | 73 ++--------- core/sr-primitives/src/traits.rs | 106 +++++++++------- .../sr-primitives/src/transaction_validity.rs | 2 +- node-template/runtime/src/lib.rs | 7 +- node-template/runtime/src/template.rs | 3 + node/cli/src/factory_impl.rs | 18 ++- node/cli/src/service.rs | 9 +- node/executor/src/lib.rs | 37 +++--- node/runtime/src/lib.rs | 10 +- srml/assets/src/lib.rs | 2 + srml/aura/src/mock.rs | 2 + srml/authorship/src/lib.rs | 2 + srml/balances/src/lib.rs | 2 +- srml/balances/src/mock.rs | 2 + srml/balances/src/tests.rs | 17 +++ srml/collective/src/lib.rs | 2 + srml/contracts/src/tests.rs | 2 + srml/council/src/lib.rs | 2 + srml/democracy/src/lib.rs | 2 + srml/elections/src/lib.rs | 2 + srml/example/src/lib.rs | 2 + srml/executive/src/lib.rs | 42 ++++--- srml/finality-tracker/src/lib.rs | 2 + srml/grandpa/src/mock.rs | 2 + srml/indices/src/mock.rs | 2 + srml/session/src/mock.rs | 2 + srml/staking/src/mock.rs | 2 + srml/system/src/lib.rs | 119 +++++++++++++++++- srml/timestamp/src/lib.rs | 2 + srml/treasury/src/lib.rs | 2 + subkey/src/main.rs | 13 +- 32 files changed, 322 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d925435e138c4..33d34db349ec1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3738,6 +3738,7 @@ name = "srml-executive" version = "2.0.0" dependencies = [ "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "node-runtime 2.0.0", "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index 190fff168c770..65c0f20182e5e 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -199,7 +199,7 @@ mod tests { use super::*; use runtime_io::blake2_256; use crate::codec::{Encode, Decode}; - use crate::traits::SignedExtension; + use crate::traits::{SignedExtension, BlockNumberToHash, Lookup, CurrentHeight}; use serde::{Serialize, Deserialize}; struct TestContext; @@ -237,6 +237,8 @@ mod tests { struct TestExtra; impl SignedExtension for TestExtra { type AccountId = u64; + type AdditionalSigned = (); + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } } type Ex = UncheckedExtrinsic; @@ -254,8 +256,7 @@ mod tests { let ux = Ex::new_signed( vec![0u8; 0], TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (TEST_ACCOUNT, vec![0u8; 0], Era::immortal(), 0u64).encode()), - Era::immortal(), + TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()), TestExtra ); let encoded = ux.encode(); @@ -267,9 +268,8 @@ mod tests { let ux = Ex::new_signed( vec![0u8; 0], TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (TEST_ACCOUNT, vec![0u8; 257], Era::immortal(), 0u64) + TestSig(TEST_ACCOUNT, (vec![0u8; 257], TestExtra) .using_encoded(blake2_256)[..].to_owned()), - Era::immortal(), TestExtra ); let encoded = ux.encode(); @@ -289,7 +289,6 @@ mod tests { vec![0u8; 0], TEST_ACCOUNT, TestSig(TEST_ACCOUNT, vec![0u8; 0]), - Era::immortal(), TestExtra ); assert!(ux.is_signed().unwrap_or(false)); @@ -297,12 +296,11 @@ mod tests { } #[test] - fn immortal_signed_check_should_work() { + fn signed_check_should_work() { let ux = Ex::new_signed( vec![0u8; 0], TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (vec![0u8; 0], Era::immortal(), 0u64, TestExtra).encode()), - Era::immortal(), + TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()), TestExtra ); assert!(ux.is_signed().unwrap_or(false)); @@ -312,63 +310,6 @@ mod tests { ); } - #[test] - fn mortal_signed_check_should_work() { - let ux = Ex::new_signed( - vec![0u8; 0], - TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (vec![0u8; 0], Era::mortal(32, 42), 42u64, TestExtra).encode()), - Era::mortal(32, 42), - TestExtra - ); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!( - >::check(ux, &TestContext), - Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }) - ); - } - - #[test] - fn later_mortal_signed_check_should_work() { - let ux = Ex::new_signed( - vec![0u8; 0], - TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (vec![0u8; 0], Era::mortal(32, 11), 11u64, TestExtra).encode()), - Era::mortal(32, 11), - TestExtra - ); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!( - >::check(ux, &TestContext), - Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] })); - } - - #[test] - fn too_late_mortal_signed_check_should_fail() { - let ux = Ex::new_signed( - vec![0u8; 0], - TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (TEST_ACCOUNT, vec![0u8; 0], Era::mortal(32, 10), 10u64).encode()), - Era::mortal(32, 10), - TestExtra - ); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - - #[test] - fn too_early_mortal_signed_check_should_fail() { - let ux = Ex::new_signed( - vec![0u8; 0], - TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (TEST_ACCOUNT, vec![0u8; 0], Era::mortal(32, 43), 43u64).encode()), - Era::mortal(32, 43), - TestExtra - ); - assert!(ux.is_signed().unwrap_or(false)); - assert_eq!(>::check(ux, &TestContext), Err(crate::BAD_SIGNATURE)); - } - #[test] fn encoding_matches_vec() { let ex = Ex::new_unsigned(vec![0u8; 0]); diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index 98b3ceb0ef15d..1afe65fa274b2 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -848,58 +848,74 @@ pub trait SignedExtension: ) -> Result<(), DispatchError> { Self::validate_unsigned(weight).map(|_| ()) } } -impl< - AccountId, - A: SignedExtension, - B: SignedExtension, -> SignedExtension for (A, B) { - type AccountId = AccountId; - type AdditionalSigned = (A::AdditionalSigned, B::AdditionalSigned); - - fn additional_signed(&self) -> Result { - Ok((self.0.additional_signed()?, self.1.additional_signed()?)) - } +macro_rules! tuple_impl_indexed { + ($first:ident, $($rest:ident,)+ ; $first_index:tt, $($rest_index:tt,)+) => { + tuple_impl_indexed!([$first] [$($rest)+] ; [$first_index,] [$($rest_index,)+]); + }; + ([$($direct:ident)+] ; [$($index:tt,)+]) => { + impl< + AccountId, + $($direct: SignedExtension),+ + > SignedExtension for ($($direct),+,) { + type AccountId = AccountId; + type AdditionalSigned = ($($direct::AdditionalSigned,)+); + fn additional_signed(&self) -> Result { + Ok(( $(self.$index.additional_signed()?,)+ )) + } + fn validate( + &self, + who: &Self::AccountId, + weight: crate::weights::Weight, + ) -> Result { + let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, weight)?),+]; + Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) + } + fn pre_dispatch( + self, + who: &Self::AccountId, + weight: crate::weights::Weight, + ) -> Result<(), DispatchError> { + $(self.$index.pre_dispatch(who, weight)?;)+ + Ok(()) + } + fn validate_unsigned( + weight: crate::weights::Weight, + ) -> Result { + let aggregator = vec![$($direct::validate_unsigned(weight)?),+]; + Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) + } + fn pre_dispatch_unsigned( + weight: crate::weights::Weight, + ) -> Result<(), DispatchError> { + $($direct::pre_dispatch_unsigned(weight)?;)+ + Ok(()) + } + } - fn validate( - &self, - who: &Self::AccountId, - weight: crate::weights::Weight, - ) -> Result { - let a = self.0.validate(who, weight)?; - let b = self.1.validate(who, weight)?; - Ok(a.combine_with(b)) - } - fn pre_dispatch( - self, - who: &Self::AccountId, - weight: crate::weights::Weight, - ) -> Result<(), DispatchError> { - self.0.pre_dispatch(who, weight)?; - self.1.pre_dispatch(who, weight)?; - Ok(()) - } - fn validate_unsigned( - weight: crate::weights::Weight, - ) -> Result { - let a = A::validate_unsigned(weight)?; - let b = B::validate_unsigned(weight)?; - Ok(a.combine_with(b)) - } - fn pre_dispatch_unsigned( - weight: crate::weights::Weight, - ) -> Result<(), DispatchError> { - A::pre_dispatch_unsigned(weight)?; - B::pre_dispatch_unsigned(weight)?; - Ok(()) - } + }; + ([$($direct:ident)+] [] ; [$($index:tt,)+] []) => { + tuple_impl_indexed!([$($direct)+] ; [$($index,)+]); + }; + ( + [$($direct:ident)+] [$first:ident $($rest:ident)*] + ; + [$($index:tt,)+] [$first_index:tt, $($rest_index:tt,)*] + ) => { + tuple_impl_indexed!([$($direct)+] ; [$($index,)+]); + tuple_impl_indexed!([$($direct)+ $first] [$($rest)*] ; [$($index,)+ $first_index,] [$($rest_index,)*]); + }; } -/// To be used only for testing. +// TODO: merge this into `tuple_impl` once codec supports `trait Codec` for longer tuple lengths. +#[allow(non_snake_case)] +tuple_impl_indexed!(A, B, C, D, E, F, G, H, I, J, ; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,); + +/// Only for base bone testing when you don't care about signed extensions at all.\ #[cfg(feature = "std")] impl SignedExtension for () { type AccountId = u64; type AdditionalSigned = (); - fn additional_signed(&self) -> result::Result<(), &'static str> { Ok(()) } + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } } /// An "executable" piece of information, used by the standard Substrate Executive in order to diff --git a/core/sr-primitives/src/transaction_validity.rs b/core/sr-primitives/src/transaction_validity.rs index 66e66c0042db9..a6cc43c5a365d 100644 --- a/core/sr-primitives/src/transaction_validity.rs +++ b/core/sr-primitives/src/transaction_validity.rs @@ -103,7 +103,7 @@ impl ValidTransaction { /// the logic *And* of the propagate flags. pub fn combine_with(mut self, mut other: ValidTransaction) -> Self { ValidTransaction { - priority: self.priority + other.priority, + priority: self.priority.saturating_add(other.priority), requires: { self.requires.append(&mut other.requires); self.requires }, provides: { self.provides.append(&mut other.provides); self.provides }, longevity: self.longevity.min(other.longevity), diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 1dcc65d00b5e4..3a6eb1886fc34 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -17,7 +17,7 @@ use primitives::bytes; use primitives::{ed25519, sr25519, OpaqueMetadata}; use runtime_primitives::{ ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, - traits::{self, NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify} + traits::{self, NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify}, weights::Weight, }; use client::{ block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api}, @@ -113,6 +113,7 @@ pub fn native_version() -> NativeVersion { parameter_types! { pub const BlockHashCount: BlockNumber = 250; + pub const MaximumBlockWeight: Weight = 4 * 1024 * 1024; } impl system::Trait for Runtime { @@ -136,6 +137,8 @@ impl system::Trait for Runtime { type Origin = Origin; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount = BlockHashCount; + /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. + type MaximumBlockWeight = MaximumBlockWeight; } impl aura::Trait for Runtime { @@ -228,7 +231,7 @@ pub type Block = generic::Block; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; /// The SignedExtension to the basic transaction logic. -pub type SignedExtra = (system::CheckNonce, balances::TakeFees); +pub type SignedExtra = (system::CheckNonce, system::CheckWeight, balances::TakeFees); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. diff --git a/node-template/runtime/src/template.rs b/node-template/runtime/src/template.rs index 05257d2a53341..97466d2b6dce5 100644 --- a/node-template/runtime/src/template.rs +++ b/node-template/runtime/src/template.rs @@ -73,6 +73,7 @@ mod tests { use primitives::{H256, Blake2Hasher}; use support::{impl_outer_origin, assert_ok, parameter_types}; use runtime_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; + use runtime_primitives::weights::Weight; impl_outer_origin! { pub enum Origin for Test {} @@ -85,6 +86,7 @@ mod tests { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -97,6 +99,7 @@ mod tests { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl Trait for Test { type Event = (); diff --git a/node/cli/src/factory_impl.rs b/node/cli/src/factory_impl.rs index ed45d4cf9545a..6f2ef00ad74a6 100644 --- a/node/cli/src/factory_impl.rs +++ b/node/cli/src/factory_impl.rs @@ -51,6 +51,17 @@ pub struct FactoryState { type Number = <::Header as HeaderT>::Number; +impl FactoryState { + fn build_extra(index: node_primitives::Index, phase: u64) -> node_runtime::SignedExtra { + ( + system::CheckEra::from(Era::mortal(256, phase)), + system::CheckNonce::from(index), + system::CheckWeight::from(), + balances::TakeFees::from(0) + ) + } +} + impl RuntimeAdapter for FactoryState { type AccountId = node_primitives::AccountId; type Balance = node_primitives::Balance; @@ -127,19 +138,16 @@ impl RuntimeAdapter for FactoryState { ) -> ::Extrinsic { let index = self.extract_index(&sender, prior_block_hash); let phase = self.extract_phase(*prior_block_hash); - let era = system::CheckEra::from(Era::mortal(256, phase)); - let check_nonce = system::CheckNonce::from(index); - let take_fees = balances::TakeFees::from(0); sign::(CheckedExtrinsic { - signed: Some((sender.clone(), (era, (check_nonce, take_fees)))), + signed: Some((sender.clone(), Self::build_extra(index, phase))), function: Call::Balances( BalancesCall::transfer( indices::address::Address::Id(destination.clone().into()), (*amount).into() ) ) - }, key, (prior_block_hash.clone(), ((), ()))) + }, key, (prior_block_hash.clone(), (), (), ())) } fn inherent_extrinsics(&self) -> InherentData { diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs index ce054f9f798a2..6302a6a4815e5 100644 --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -357,12 +357,14 @@ mod tests { let signer = charlie.clone(); let function = Call::Balances(BalancesCall::transfer(to.into(), amount)); - let era = Era::immortal(); + + let check_era = system::CheckEra::from(Era::Immortal); let check_nonce = system::CheckNonce::from(index); + let check_weight = system::CheckWeight::from(); let take_fees = balances::TakeFees::from(0); - let extra = (check_nonce, take_fees); + let extra = (check_era, check_nonce, check_weight, take_fees); - let raw_payload = (function, era, genesis_hash, extra.clone()); + let raw_payload = (function, extra.clone(), genesis_hash); let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { signer.sign(&blake2_256(payload)[..]) } else { @@ -372,7 +374,6 @@ mod tests { raw_payload.0, from.into(), signature.into(), - era, extra, ).encode(); let v: Vec = Decode::decode(&mut xt.as_slice()).unwrap(); diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index f09853ca10e1e..e0617ff9d1324 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -81,8 +81,10 @@ mod tests { type TestExternalities = CoreTestExternalities; // TODO: fix for being charged based on weight now. - fn transfer_fee(bytes: Balance) -> Balance { - >::get() + >::get() * bytes + fn transfer_fee(extrinsic: &E) -> Balance { + >::get() + + >::get() * + (extrinsic.encode().len() as Balance) } fn creation_fee() -> Balance { @@ -116,8 +118,7 @@ mod tests { fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { match xt.signed { Some((signed, extra)) => { - let era = Era::mortal(256, 0); - let payload = (xt.function, era, GENESIS_HASH, extra.clone()); + let payload = (xt.function, extra.clone(), GENESIS_HASH); let key = AccountKeyring::from_public(&signed).unwrap(); let signature = payload.using_encoded(|b| { if b.len() > 256 { @@ -127,7 +128,7 @@ mod tests { } }).into(); UncheckedExtrinsic { - signature: Some((indices::address::Address::Id(signed), signature, era, extra)), + signature: Some((indices::address::Address::Id(signed), signature, extra)), function: payload.0, } } @@ -139,7 +140,12 @@ mod tests { } fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra { - (system::CheckNonce::from(nonce), balances::TakeFees::from(extra_fee)) + ( + system::CheckEra::from(Era::mortal(256, 0)), + system::CheckNonce::from(nonce), + system::CheckWeight::from(), + balances::TakeFees::from(extra_fee) + ) } fn xt() -> UncheckedExtrinsic { @@ -260,7 +266,7 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(169) - creation_fee()); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -296,7 +302,7 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(169) - creation_fee()); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -491,7 +497,6 @@ mod tests { // session change => consensus authorities change => authorities change digest item appears let digest = Header::decode(&mut &block2.0[..]).unwrap().digest; assert_eq!(digest.logs().len(), 0); -// assert!(digest.logs()[0].as_consensus().is_some()); (block1, block2) } @@ -529,7 +534,7 @@ mod tests { ).0.unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(169) - creation_fee()); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); let events = vec![ EventRecord { @@ -566,8 +571,8 @@ mod tests { runtime_io::with_externalities(&mut t, || { // TODO TODO: this needs investigating: why are we deducting creation fee twice here? and why bob also pays it? - assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(169) - 2 * creation_fee()); - assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - transfer_fee(169) - creation_fee()); + assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(&xt()) - 2 * creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - transfer_fee(&xt()) - creation_fee()); let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), @@ -622,15 +627,15 @@ mod tests { WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(169) - creation_fee()); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); }); WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block2.0).unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(169) - 2 * creation_fee()); - assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * transfer_fee(169) - creation_fee()); + assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(&xt()) - 2 * creation_fee()); + assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * transfer_fee(&xt()) - creation_fee()); }); } @@ -876,7 +881,7 @@ mod tests { assert_eq!(r, Ok(ApplyOutcome::Success)); runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(169) - creation_fee()); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index f6c6707e03cfd..a8130dee0ea17 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -36,6 +36,7 @@ use client::{ }; use runtime_primitives::{ApplyResult, impl_opaque_keys, generic, create_runtime_str, key_types}; use runtime_primitives::transaction_validity::TransactionValidity; +use runtime_primitives::weights::Weight; use runtime_primitives::traits::{ BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, Convert, }; @@ -111,6 +112,7 @@ pub const DAYS: Moment = HOURS * 24; parameter_types! { pub const BlockHashCount: BlockNumber = 250; + pub const MaximumBlockWeight: Weight = 4 * 1024 * 1024; } impl system::Trait for Runtime { @@ -124,6 +126,7 @@ impl system::Trait for Runtime { type Header = generic::Header; type Event = Event; type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl aura::Trait for Runtime { @@ -436,7 +439,12 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; /// The SignedExtension to the basic transaction logic. -pub type SignedExtra = (system::CheckEra, (system::CheckNonce, balances::TakeFees)); +pub type SignedExtra = ( + system::CheckEra, + system::CheckNonce, + system::CheckWeight, + balances::TakeFees +); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index 19159bf60fba3..1e4c06700abe1 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -257,6 +257,7 @@ mod tests { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -269,6 +270,7 @@ mod tests { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl Trait for Test { type Event = (); diff --git a/srml/aura/src/mock.rs b/srml/aura/src/mock.rs index fad511baba331..ba6a66cdace68 100644 --- a/srml/aura/src/mock.rs +++ b/srml/aura/src/mock.rs @@ -37,6 +37,7 @@ pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { @@ -50,6 +51,7 @@ impl system::Trait for Test { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl timestamp::Trait for Test { diff --git a/srml/authorship/src/lib.rs b/srml/authorship/src/lib.rs index 08a0279284871..42fa0630a4855 100644 --- a/srml/authorship/src/lib.rs +++ b/srml/authorship/src/lib.rs @@ -337,6 +337,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { @@ -350,6 +351,7 @@ mod tests { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl Trait for Test { diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 9266b44a4ee42..004ea41a035e9 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -747,6 +747,7 @@ impl, I: Instance> system::Trait for ElevatedTrait { type Header = T::Header; type Event = (); type BlockHashCount = T::BlockHashCount; + type MaximumBlockWeight = T::MaximumBlockWeight; } impl, I: Instance> Trait for ElevatedTrait { type Balance = T::Balance; @@ -1156,7 +1157,6 @@ use primitives::weights::Weight; impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { type AccountId = T::AccountId; - type AdditionalSigned = (); fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index 26f4439f4608d..274f093386823 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -67,6 +67,7 @@ impl Get for TransactionByteFee { pub struct Runtime; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Runtime { type Origin = Origin; @@ -79,6 +80,7 @@ impl system::Trait for Runtime { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl Trait for Runtime { type Balance = u64; diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 10e6f94693682..c5e36ed6571f2 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -658,3 +658,20 @@ fn extra_balance_should_transfer() { } ); } + +#[test] +fn signed_extension_take_fees_work() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(10) + .transaction_fees(10, 1) + .monied(true) + .build(), + || { + assert!(TakeFees::::from(0).validate(&1, 10).is_ok()); + assert_eq!(Balances::free_balance(&1), 100 - 20); + assert!(TakeFees::::from(5 /* tipped */).validate(&1, 10).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 23b156c718e7f..a8303501d46ea 100644 --- a/srml/collective/src/lib.rs +++ b/srml/collective/src/lib.rs @@ -402,6 +402,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -414,6 +415,7 @@ mod tests { type Header = Header; type Event = Event; type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl Trait for Test { type Origin = Origin; diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index b195a74bf429b..c3c6a595a3442 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -96,6 +96,7 @@ impl Get for BlockGasLimit { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -108,6 +109,7 @@ impl system::Trait for Test { type Header = Header; type Event = MetaEvent; type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const BalancesTransactionBaseFee: u64 = 0; diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs index 8a52f9ec004a9..1baeb6fdfd34b 100644 --- a/srml/council/src/lib.rs +++ b/srml/council/src/lib.rs @@ -98,6 +98,7 @@ mod tests { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -110,6 +111,7 @@ mod tests { type Header = Header; type Event = Event; type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index d13feb4db531d..5c49f9519013d 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -998,6 +998,7 @@ mod tests { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -1010,6 +1011,7 @@ mod tests { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/srml/elections/src/lib.rs b/srml/elections/src/lib.rs index 77597591775d7..324c1a78fbf1e 100644 --- a/srml/elections/src/lib.rs +++ b/srml/elections/src/lib.rs @@ -1108,6 +1108,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -1120,6 +1121,7 @@ mod tests { type Header = Header; type Event = Event; type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index 20ee1c6ba114f..b2bb0a0af47e4 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -525,6 +525,7 @@ mod tests { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -537,6 +538,7 @@ mod tests { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 512b9086c9cbb..fa08a1017b94f 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -91,8 +91,6 @@ use primitives::weights::Weighable; mod internal { use primitives::traits::DispatchError; - pub const MAX_TRANSACTIONS_WEIGHT: u32 = 4 * 1024 * 1024; - pub enum ApplyError { BadSignature(&'static str), Stale, @@ -283,17 +281,10 @@ where >::note_extrinsic(encoded); } - // Check the weight of the block if that extrinsic is applied. - let weight = xt.weight(encoded_len); - - // TODO: Consider placing into a transaction extension. - if >::all_extrinsics_weight() + weight > internal::MAX_TRANSACTIONS_WEIGHT { - return Err(internal::ApplyError::FullBlock); - } - // AUDIT: Under no circumstances may this function panic from here onwards. // Decode parameters and dispatch + let weight = xt.weight(encoded_len); let r = Applyable::dispatch(xt, weight) .map_err(internal::ApplyError::from)?; @@ -366,10 +357,11 @@ mod tests { use balances::Call; use runtime_io::with_externalities; use substrate_primitives::{H256, Blake2Hasher}; + use primitives::generic::Era; use primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup}; use primitives::testing::{Digest, Header, Block}; use srml_support::{impl_outer_event, impl_outer_origin, parameter_types}; - use srml_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason}; + use srml_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason, Get}; use system; use hex_literal::hex; @@ -389,6 +381,7 @@ mod tests { pub struct Runtime; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Runtime { type Origin = Origin; @@ -401,6 +394,7 @@ mod tests { type Header = Header; type Event = MetaEvent; type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const ExistentialDeposit: u64 = 0; @@ -435,12 +429,22 @@ mod tests { } } - type SignedExtra = (system::CheckNonce, balances::TakeFees); + type SignedExtra = ( + system::CheckEra, + system::CheckNonce, + system::CheckWeight, + balances::TakeFees + ); type TestXt = primitives::testing::TestXt, SignedExtra>; type Executive = super::Executive, system::ChainContext, Runtime, ()>; fn extra(nonce: u64, fee: u64) -> SignedExtra { - (system::CheckNonce::from(nonce), balances::TakeFees::from(fee)) + ( + system::CheckEra::from(Era::Immortal), + system::CheckNonce::from(nonce), + system::CheckWeight::from(), + balances::TakeFees::from(fee) + ) } #[test] @@ -551,7 +555,7 @@ mod tests { 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 { (internal::MAX_TRANSACTIONS_WEIGHT - 1) as usize } else { encoded.len() }; + 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( @@ -583,14 +587,16 @@ mod tests { } #[test] - fn default_block_weight() { - let xt = primitives::testing::TestXt(None, Call::set_balance(33, 69, 69), extra(0, 0)); + fn default_block_weight_is_stored() { + 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(xt.clone()).unwrap(); - 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*/) diff --git a/srml/finality-tracker/src/lib.rs b/srml/finality-tracker/src/lib.rs index f9ccc36346222..2e17d9688f1c9 100644 --- a/srml/finality-tracker/src/lib.rs +++ b/srml/finality-tracker/src/lib.rs @@ -299,6 +299,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -311,6 +312,7 @@ mod tests { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const WindowSize: u64 = 11; diff --git a/srml/grandpa/src/mock.rs b/srml/grandpa/src/mock.rs index 733b2deaf1493..1420d65eb5576 100644 --- a/srml/grandpa/src/mock.rs +++ b/srml/grandpa/src/mock.rs @@ -43,6 +43,7 @@ impl Trait for Test { } parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -55,6 +56,7 @@ impl system::Trait for Test { type Header = Header; type Event = TestEvent; type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } mod grandpa { diff --git a/srml/indices/src/mock.rs b/srml/indices/src/mock.rs index 53e8f314c94bb..938aec2bc3311 100644 --- a/srml/indices/src/mock.rs +++ b/srml/indices/src/mock.rs @@ -66,6 +66,7 @@ impl ResolveHint for TestResolveHint { pub struct Runtime; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Runtime { type Origin = Origin; @@ -78,6 +79,7 @@ impl system::Trait for Runtime { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl Trait for Runtime { type AccountIndex = u64; diff --git a/srml/session/src/mock.rs b/srml/session/src/mock.rs index adb37720519a8..e6228e21042ed 100644 --- a/srml/session/src/mock.rs +++ b/srml/session/src/mock.rs @@ -109,6 +109,7 @@ pub fn set_next_validators(next: Vec) { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -121,6 +122,7 @@ impl system::Trait for Test { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl timestamp::Trait for Test { type Moment = u64; diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index abad1752a6ec6..b767f446df1e8 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -87,6 +87,7 @@ impl_outer_origin!{ pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -99,6 +100,7 @@ impl system::Trait for Test { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const TransferFee: u64 = 0; diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 684dc24ae7339..be7a3ec492252 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -76,6 +76,7 @@ use serde::Serialize; use rstd::prelude::*; #[cfg(any(feature = "std", test))] use rstd::map; +use rstd::marker::PhantomData; use primitives::{ generic::{self, Era}, weights::Weight, traits::{ self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, @@ -190,6 +191,9 @@ pub trait Trait: 'static + Eq + Clone { /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount: Get; + + /// The maximum weight of a block. + type MaximumBlockWeight: Get; } pub type DigestOf = generic::Digest<::Hash>; @@ -725,17 +729,15 @@ impl Module { } /// To be called immediately after an extrinsic has been applied. - pub fn note_applied_extrinsic(r: &Result<(), &'static str>, encoded_len: u32) { + pub fn note_applied_extrinsic(r: &Result<(), &'static str>, _encoded_len: u32) { Self::deposit_event(match r { Ok(_) => Event::ExtrinsicSuccess, Err(_) => Event::ExtrinsicFailed, }.into()); let next_extrinsic_index = Self::extrinsic_index().unwrap_or_default() + 1u32; - let total_length = encoded_len.saturating_add(Self::all_extrinsics_weight()); storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &next_extrinsic_index); - AllExtrinsicsWeight::put(&total_length); } /// To be called immediately after `note_applied_extrinsic` of the last extrinsic of the block @@ -753,6 +755,61 @@ impl Module { } } +/// Weight limit check and increment. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct CheckWeight(PhantomData); + +impl CheckWeight { + fn internal_check_weight(weight: Weight) -> Result<(), DispatchError> { + 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) + } + AllExtrinsicsWeight::put(next_weight); + Ok(()) + } +} + +impl SignedExtension for CheckWeight { + type AccountId = T::AccountId; + type AdditionalSigned = (); + + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + weight: Weight, + ) -> Result<(), DispatchError> { + Self::internal_check_weight(weight) + } + + fn validate( + &self, + _who: &Self::AccountId, + _weight: Weight, + ) -> 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) + } +} + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for CheckWeight { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "CheckWeight") + } +} + /// Nonce check and increment to give replay protection for transactions. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckNonce(#[codec(compact)] T::Index); @@ -775,7 +832,9 @@ impl rstd::fmt::Debug for CheckNonce { impl SignedExtension for CheckNonce { type AccountId = T::AccountId; type AdditionalSigned = (); - fn additional_signed(&self) -> Result<(), &'static str> { Ok(()) } + + fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } + fn pre_dispatch( self, who: &Self::AccountId, @@ -819,7 +878,6 @@ impl SignedExtension for CheckNonce { } } - /// Nonce check and increment to give replay protection for transactions. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckEra((Era, rstd::marker::PhantomData)); @@ -850,7 +908,6 @@ impl SignedExtension for CheckEra { } } - pub struct ChainContext(::rstd::marker::PhantomData); impl Default for ChainContext { fn default() -> Self { @@ -898,6 +955,7 @@ mod tests { parameter_types! { pub const BlockHashCount: u64 = 10; + pub const MaximumBlockWeight: Weight = 1024; } impl Trait for Test { @@ -911,6 +969,7 @@ mod tests { type Header = Header; type Event = u16; type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl From for u16 { @@ -1061,4 +1120,52 @@ mod tests { } }) } + + #[test] + fn signed_ext_check_nonce_works() { + with_externalities(&mut new_test_ext(), || { + >::insert(1, 1); + // stale + assert!(CheckNonce::(0).validate(&1, 0).is_err()); + assert!(CheckNonce::(0).pre_dispatch(&1, 0).is_err()); + // correct + assert!(CheckNonce::(1).validate(&1, 0).is_ok()); + assert!(CheckNonce::(1).pre_dispatch(&1, 0).is_ok()); + // future + assert!(CheckNonce::(5).validate(&1, 0).is_ok()); + assert!(CheckNonce::(5).pre_dispatch(&1, 0).is_err()); + }) + } + + #[test] + fn signed_ext_check_weight_works() { + 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()); + + }) + } + + #[test] + fn signed_ext_check_era_should_work() { + with_externalities(&mut new_test_ext(), || { + // future + assert_eq!( + CheckEra::::from(Era::mortal(4, 2)).additional_signed().err().unwrap(), + "transaction birth block ancient" + ); + + // correct + System::set_block_number(13); + >::insert(12, H256::repeat_byte(1)); + assert!(CheckEra::::from(Era::mortal(4, 12)).additional_signed().is_ok()); + }) + } } diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index 7bffac0db9007..a1c0b2f69fcaa 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -343,6 +343,7 @@ mod tests { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -355,6 +356,7 @@ mod tests { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } impl Trait for Test { type Moment = u64; diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index 7c4d7b10f21cf..aa49190d5abb2 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -370,6 +370,7 @@ mod tests { pub struct Test; parameter_types! { pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; } impl system::Trait for Test { type Origin = Origin; @@ -382,6 +383,7 @@ mod tests { type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; } parameter_types! { pub const ExistentialDeposit: u64 = 0; diff --git a/subkey/src/main.rs b/subkey/src/main.rs index dfd923e993149..6a74391b00815 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -87,7 +87,12 @@ fn execute(matches: clap::ArgMatches) where <::Pair as Pair>::Public: Sized + AsRef<[u8]> + Ss58Codec + AsRef<<::Pair as Pair>::Public>, { let extra = |i: Index, f: Balance| { - (system::CheckNonce::::from(i), balances::TakeFees::::from(f)) + ( + system::CheckEra::::from(Era::Immortal), + system::CheckNonce::::from(i), + system::CheckWeight::::from(), + balances::TakeFees::::from(f), + ) }; let password = matches.value_of("password"); match matches.subcommand() { @@ -155,9 +160,7 @@ fn execute(matches: clap::ArgMatches) where println!("Using a genesis hash of {}", HexDisplay::from(&genesis_hash.as_ref())); - let era = Era::immortal(); - - let raw_payload = (function, era, genesis_hash, extra(index, 0)); + let raw_payload = (function, extra(index, 0), genesis_hash); let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { signer.sign(&blake2_256(payload)[..]) } else { @@ -168,7 +171,6 @@ fn execute(matches: clap::ArgMatches) where raw_payload.0, signer.public().into(), signature.into(), - era, extra(index, 0), ); println!("0x{}", hex::encode(&extrinsic.encode())); @@ -209,7 +211,6 @@ fn execute(matches: clap::ArgMatches) where raw_payload.0, signer.public().into(), signature.into(), - era, extra(index, 0), ); From 30b4ba7c200eeefc445b762942d74a23f6557b55 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 16 Jul 2019 21:47:23 +0900 Subject: [PATCH 10/20] Don't use len for weight - use data. --- .../src/generic/checked_extrinsic.rs | 20 +++++++------ core/sr-primitives/src/testing.rs | 18 +++++++----- core/sr-primitives/src/traits.rs | 26 ++++++++++++----- core/sr-primitives/src/weights.rs | 29 +++++++++++-------- srml/balances/src/lib.rs | 3 +- srml/example/src/lib.rs | 2 +- srml/executive/src/lib.rs | 18 +++++------- srml/support/src/dispatch.rs | 15 +++++----- srml/system/src/lib.rs | 4 +++ 9 files changed, 79 insertions(+), 56 deletions(-) diff --git a/core/sr-primitives/src/generic/checked_extrinsic.rs b/core/sr-primitives/src/generic/checked_extrinsic.rs index bd9a711fc0e54..3f73bca07a5f8 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::{Weighable, Weight}; +use crate::weights::{Weigh, Weight}; use crate::transaction_validity::TransactionValidity; /// Definition of something that the external world might want to say; its @@ -58,11 +58,12 @@ where fn validate>(&self, weight: crate::weights::Weight, + len: usize, ) -> TransactionValidity { if let Some((ref id, ref extra)) = self.signed { - Extra::validate(extra, id, weight).into() + Extra::validate(extra, id, weight, len).into() } else { - match Extra::validate_unsigned(weight) { + match Extra::validate_unsigned(weight, len) { Ok(extra) => match U::validate_unsigned(&self.function) { TransactionValidity::Valid(v) => TransactionValidity::Valid(v.combine_with(extra)), @@ -75,23 +76,24 @@ where fn dispatch(self, weight: crate::weights::Weight, + len: usize, ) -> Result { let maybe_who = if let Some((id, extra)) = self.signed { - Extra::pre_dispatch(extra, &id, weight)?; + Extra::pre_dispatch(extra, &id, weight, len)?; Some(id) } else { - Extra::pre_dispatch_unsigned(weight)?; + Extra::pre_dispatch_unsigned(weight, len)?; None }; Ok(self.function.dispatch(Origin::from(maybe_who))) } } -impl Weighable for CheckedExtrinsic +impl Weigh for CheckedExtrinsic where - Call: Weighable, + Call: Weigh, { - fn weight(&self, len: usize) -> Weight { - self.function.weight(len) + fn weigh(&self) -> Weight { + self.function.weigh() } } diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index a101c3db94212..61e65a21d5b61 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::{Weighable, Weight}; +use crate::weights::{Weigh, Weight}; pub use substrate_primitives::H256; use substrate_primitives::U256; use substrate_primitives::ed25519::{Public as AuthorityId}; @@ -240,7 +240,8 @@ 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 + _weight: Weight, + _len: usize, ) -> TransactionValidity { TransactionValidity::Valid(Default::default()) } @@ -248,22 +249,23 @@ 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 + weight: Weight, + len: usize, ) -> Result { let maybe_who = if let Some(who) = self.0 { - Extra::pre_dispatch(self.2, &who, weight)?; + Extra::pre_dispatch(self.2, &who, weight, len)?; Some(who) } else { - Extra::pre_dispatch_unsigned(weight)?; + Extra::pre_dispatch_unsigned(weight, len)?; None }; Ok(self.1.dispatch(maybe_who.into())) } } -impl Weighable for TestXt { - fn weight(&self, len: usize) -> Weight { +impl Weigh for TestXt { + fn weigh(&self) -> Weight { // for testing: weight == size. - len as Weight + self.0.using_encoded(|d| d.len() as Weight) } } diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index 1afe65fa274b2..0708e561f6c2a 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -826,6 +826,7 @@ pub trait SignedExtension: &self, _who: &Self::AccountId, _weight: crate::weights::Weight, + _len: usize, ) -> Result { Ok(Default::default()) } /// Do any pre-flight stuff for a signed transaction. @@ -833,19 +834,22 @@ pub trait SignedExtension: self, who: &Self::AccountId, weight: crate::weights::Weight, - ) -> Result<(), DispatchError> { self.validate(who, weight).map(|_| ()) } + len: usize, + ) -> Result<(), DispatchError> { self.validate(who, weight, 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, + _len: usize, ) -> Result { Ok(Default::default()) } /// Do any pre-flight stuff for a unsigned transaction. fn pre_dispatch_unsigned( weight: crate::weights::Weight, - ) -> Result<(), DispatchError> { Self::validate_unsigned(weight).map(|_| ()) } + len: usize, + ) -> Result<(), DispatchError> { Self::validate_unsigned(weight, len).map(|_| ()) } } macro_rules! tuple_impl_indexed { @@ -866,28 +870,32 @@ macro_rules! tuple_impl_indexed { &self, who: &Self::AccountId, weight: crate::weights::Weight, + len: usize, ) -> Result { - let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, weight)?),+]; + let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, weight, 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, + len: usize, ) -> Result<(), DispatchError> { - $(self.$index.pre_dispatch(who, weight)?;)+ + $(self.$index.pre_dispatch(who, weight, len)?;)+ Ok(()) } fn validate_unsigned( weight: crate::weights::Weight, + len: usize, ) -> Result { - let aggregator = vec![$($direct::validate_unsigned(weight)?),+]; + let aggregator = vec![$($direct::validate_unsigned(weight, len)?),+]; Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) } fn pre_dispatch_unsigned( weight: crate::weights::Weight, + len: usize, ) -> Result<(), DispatchError> { - $($direct::pre_dispatch_unsigned(weight)?;)+ + $($direct::pre_dispatch_unsigned(weight, len)?;)+ Ok(()) } } @@ -936,13 +944,15 @@ 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 + weight: crate::weights::Weight, + 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 + weight: crate::weights::Weight, + len: usize, ) -> Result; } diff --git a/core/sr-primitives/src/weights.rs b/core/sr-primitives/src/weights.rs index 3443992c7396b..fa60737bb89e9 100644 --- a/core/sr-primitives/src/weights.rs +++ b/core/sr-primitives/src/weights.rs @@ -18,7 +18,7 @@ //! //! Each dispatch function within `decl_module!` can now have an optional //! `#[weight = $x]` attribute. $x can be any object that implements the -//! `Weighable` trait. By default, All transactions are annotated by +//! `Weigh` trait. By default, All transactions are annotated by //! `#[weight = TransactionWeight::default()]`. //! //! Note that the decl_module macro _cannot_ enforce this and will simply fail @@ -33,20 +33,25 @@ pub type Weight = u32; /// /// 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 Weighable { - /// Return the weight of this call. - /// The `len` argument is the encoded length of the transaction/call. - fn weight(&self, len: usize) -> Weight; +pub trait Weigh { + /// Return the weight of this call. This is done independently of its encoded size. + fn weigh(&self) -> Weight; +} + +/// Means of weighing some particular kind of data (`T`). +pub trait WeighData { + /// Weigh the data `T` given by `target`. + fn weigh_data(&self, target: T) -> Weight; } /// Default type used as the weight representative in a `#[weight = x]` attribute. /// -/// A user may pass in any other type that implements [`Weighable`]. If not, the `Default` +/// 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. - Basic(Weight, Weight), + 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_. @@ -56,10 +61,10 @@ pub enum TransactionWeight { Free, } -impl Weighable for TransactionWeight { - fn weight(&self, len: usize) -> Weight { +impl WeighData for TransactionWeight { + fn weigh_data(&self, _: T) -> Weight { match self { - TransactionWeight::Basic(base, byte) => base + byte * len as Weight, + TransactionWeight::Fixed(w) => *w, TransactionWeight::Max => 3 * 1024 * 1024, TransactionWeight::Free => 0, } @@ -68,9 +73,9 @@ impl Weighable for TransactionWeight { impl Default for TransactionWeight { fn default() -> Self { - // This implies that the weight is currently equal to tx-size, nothing more + // 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::Basic(0, 1) + TransactionWeight::Fixed(1) } } diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index bba3162ab9b5e..24b414ee0a9eb 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -1178,9 +1178,10 @@ impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { &self, who: &Self::AccountId, weight: Weight, + _len: usize, ) -> rstd::result::Result { let fee_x = T::Balance::from(weight); - // TODO: should be weight_to_fee(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(); let imbalance = >::withdraw( diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index b2bb0a0af47e4..37d091279e479 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -397,7 +397,7 @@ 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 `Weighable` + // custom attribute and the value can be anything that implements the `Weigh` // trait. Most often using substrate's default `TransactionWeight` is enough for you. // // A basic weight is a tuple of `(base_weight, byte_weight)`. Upon including each transaction diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index fa08a1017b94f..47422d50f182f 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::Weighable; +use primitives::weights::Weigh; mod internal { use primitives::traits::DispatchError; @@ -141,7 +141,7 @@ impl< > ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, - CheckedOf: Applyable + Weighable, + CheckedOf: Applyable + Weigh, CallOf: Dispatchable, OriginOf: From>, UnsignedValidator: ValidateUnsigned>, @@ -160,7 +160,7 @@ impl< > Executive where Block::Extrinsic: Checkable + Codec, - CheckedOf: Applyable + Weighable, + CheckedOf: Applyable + Weigh, 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.weight(encoded_len); - let r = Applyable::dispatch(xt, weight) + let weight = xt.weigh(); + let r = Applyable::dispatch(xt, weight, encoded_len) .map_err(internal::ApplyError::from)?; >::note_applied_extrinsic(&r, encoded_len as u32); @@ -327,8 +327,7 @@ where const UNKNOWN_ERROR: i8 = -127; const INVALID_INDEX: i8 = -10; - let encoded_len = uxt.encode().len(); - + let encoded_len = uxt.using_encoded(|d| d.len()); let xt = match uxt.check(&Default::default()) { // Checks out. Carry on. Ok(xt) => xt, @@ -340,9 +339,8 @@ where Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), }; - let weight = xt.weight(encoded_len); - - xt.validate::(weight) + let weight = xt.weigh(); + xt.validate::(weight, encoded_len) } /// Start an offchain worker and generate extrinsics. diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index 00c21a31ba47f..ac5e0604446e4 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -26,7 +26,7 @@ pub use srml_metadata::{ ModuleConstantMetadata, DefaultByte, DefaultByteGetter, }; pub use sr_primitives::{ - weights::{TransactionWeight, Weighable, Weight}, traits::{Dispatchable, DispatchResult} + weights::{TransactionWeight, Weigh, Weight, WeighData}, traits::{Dispatchable, DispatchResult} }; /// A type that cannot be instantiated. @@ -1108,12 +1108,13 @@ macro_rules! decl_module { } // Implement weight calculation function for Call - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Weighable + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Weigh for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { - fn weight(&self, _len: usize) -> $crate::dispatch::Weight { + fn weigh(&self) -> $crate::dispatch::Weight { match self { - $( $call_type::$fn_name(..) => $crate::dispatch::Weighable::weight(&$weight, _len), )* + $( $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.") }, } } @@ -1260,10 +1261,10 @@ macro_rules! impl_outer_dispatch { $camelcase ( $crate::dispatch::CallableCallFor<$camelcase, $runtime> ) ,)* } - impl $crate::dispatch::Weighable for $call_type { - fn weight(&self, len: usize) -> $crate::dispatch::Weight { + impl $crate::dispatch::Weigh for $call_type { + fn weigh(&self) -> $crate::dispatch::Weight { match self { - $( $call_type::$camelcase(call) => call.weight(len), )* + $( $call_type::$camelcase(call) => call.weigh(), )* } } } diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index be7a3ec492252..897d7c727635f 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -781,6 +781,7 @@ impl SignedExtension for CheckWeight { self, _who: &Self::AccountId, weight: Weight, + _len: usize, ) -> Result<(), DispatchError> { Self::internal_check_weight(weight) } @@ -789,6 +790,7 @@ impl SignedExtension for CheckWeight { &self, _who: &Self::AccountId, _weight: Weight, + _len: usize, ) -> Result { // TODO: check for a maximum size and weight here as well. // write priority based on tx weight type + tip. @@ -839,6 +841,7 @@ impl SignedExtension for CheckNonce { self, who: &Self::AccountId, _weight: Weight, + _len: usize, ) -> Result<(), DispatchError> { let expected = >::get(who); if self.0 != expected { @@ -854,6 +857,7 @@ impl SignedExtension for CheckNonce { &self, who: &Self::AccountId, _weight: Weight, + _len: usize, ) -> Result { // check index let expected = >::get(who); From 342efb514a00a9c877a70564d064e4c72c84db0c Mon Sep 17 00:00:00 2001 From: Kian Peymani Date: Fri, 19 Jul 2019 14:22:14 +0200 Subject: [PATCH 11/20] Operational Transaction; second attempt (#3138) * working poc added. * some fixes. * Update doc. * Fix all tests + final logic. * more refactoring. * nits. * System block limit in bytes. * Silent the storage macro warnings. * More logic more tests. * Fix import. * Refactor names. * Fix build. * Update srml/balances/src/lib.rs * Final refactor. --- .../src/generic/checked_extrinsic.rs | 22 +- core/sr-primitives/src/testing.rs | 19 +- core/sr-primitives/src/traits.rs | 34 +-- core/sr-primitives/src/weights.rs | 155 +++++++++--- node-template/runtime/src/lib.rs | 3 + node-template/runtime/src/template.rs | 2 + node/executor/src/lib.rs | 1 - node/runtime/src/lib.rs | 4 +- srml/assets/src/lib.rs | 2 + srml/aura/src/mock.rs | 2 + srml/authorship/src/lib.rs | 2 + srml/balances/src/lib.rs | 63 +++-- srml/balances/src/mock.rs | 14 +- srml/balances/src/tests.rs | 28 ++- srml/collective/src/lib.rs | 2 + srml/contracts/src/tests.rs | 2 + srml/council/src/lib.rs | 2 + srml/democracy/src/lib.rs | 2 + srml/elections/src/lib.rs | 2 + srml/example/src/lib.rs | 27 +- srml/executive/src/lib.rs | 99 ++++---- srml/finality-tracker/src/lib.rs | 2 + srml/generic-asset/src/lib.rs | 2 + srml/generic-asset/src/mock.rs | 4 + srml/grandpa/src/mock.rs | 2 + srml/indices/src/mock.rs | 2 + srml/session/src/mock.rs | 2 + srml/staking/src/mock.rs | 2 + srml/support/Cargo.toml | 2 +- srml/support/src/dispatch.rs | 75 ++++-- srml/system/src/lib.rs | 238 ++++++++++++++---- srml/timestamp/src/lib.rs | 2 + srml/treasury/src/lib.rs | 2 + 33 files changed, 599 insertions(+), 223 deletions(-) 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 97a265c1f9b0a..2c652a027785e 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 b7f57cd1234b2..c2d146e0b6484 100644 --- a/srml/collective/src/lib.rs +++ b/srml/collective/src/lib.rs @@ -400,6 +400,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; @@ -413,6 +414,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 785840e9352c6..ffc7b9998d191 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; From b8f564e7d268e3eef52723e7a94cf0ca30df378b Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 19 Jul 2019 14:31:28 +0200 Subject: [PATCH 12/20] Bump transaction version --- core/sr-primitives/src/generic/unchecked_extrinsic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index 190fff168c770..c7b83110c88e8 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -25,7 +25,7 @@ use crate::codec::{Decode, Encode, Input}; use crate::traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic}; use super::CheckedExtrinsic; -const TRANSACTION_VERSION: u8 = 1; +const TRANSACTION_VERSION: u8 = 2; /// A extrinsic right from the external world. This is unchecked and so /// can contain a signature. From 7a0fbc949df08b85fff6a692d86a0ae9e70094b1 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 19 Jul 2019 17:57:55 +0200 Subject: [PATCH 13/20] Fix weight mult test. --- node/runtime/src/impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/runtime/src/impls.rs b/node/runtime/src/impls.rs index ec7f596605fe4..85365b6a7e73e 100644 --- a/node/runtime/src/impls.rs +++ b/node/runtime/src/impls.rs @@ -108,7 +108,7 @@ mod tests { } fn ideal() -> Weight { - max() / 4 + max() / 4 / 4 } // poc reference implementation. From 84fa279ece1b210be0032fee9a4fd613a5678312 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 19 Jul 2019 18:18:40 +0200 Subject: [PATCH 14/20] Fix more tests and improve doc. --- core/sr-primitives/src/weights.rs | 6 +++--- node-template/runtime/src/lib.rs | 2 +- srml/support/src/dispatch.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/sr-primitives/src/weights.rs b/core/sr-primitives/src/weights.rs index 68bf1a5b06195..c592d9e19546c 100644 --- a/core/sr-primitives/src/weights.rs +++ b/core/sr-primitives/src/weights.rs @@ -32,7 +32,7 @@ use crate::traits::Bounded; /// Numeric range of a transaction weight. pub type Weight = u32; -/// A broad range of dispatch types. This is only distinguishing normal, user-triggered transactions +/// A generalized group 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)] @@ -110,7 +110,7 @@ pub trait ClassifyDispatch { /// 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`): +/// For each generalized 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. @@ -120,7 +120,7 @@ pub trait ClassifyDispatch { /// - A _weight-fee_ is deducted. /// - The block weight is consumed proportionally. /// -/// As for the broad groups themselves: +/// As for the generalized 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 diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 3a03add715a04..77407e696d669 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -116,7 +116,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; + pub const MaximumBlockLength: u32 = 4 * 1024 * 1024; } impl system::Trait for Runtime { diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index 476c82a1136c7..7afbd11cd1d0d 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -1780,7 +1780,7 @@ mod tests { // default weight. assert_eq!( Call::::aux_0().get_dispatch_info(), - DispatchInfo { weight: 1, class: DispatchClass::Normal }, + DispatchInfo { weight: 100, class: DispatchClass::Normal }, ); // custom basic assert_eq!( From f4d457921ad000482bee082c42ab9c47f85775aa Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 19 Jul 2019 18:22:03 +0200 Subject: [PATCH 15/20] Bump. --- node/runtime/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 92ca0a691c591..a9aa77bb07762 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -75,8 +75,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 114, - impl_version: 114, + spec_version: 115, + impl_version: 115, apis: RUNTIME_API_VERSIONS, }; @@ -117,7 +117,7 @@ pub const DAYS: Moment = HOURS * 24; parameter_types! { pub const BlockHashCount: BlockNumber = 250; - pub const MaximumBlockWeight: Weight = 4 * 1024; + pub const MaximumBlockWeight: Weight = 4 * 1024 * 1024; pub const MaximumBlockLength: u32 = 4 * 1024 * 1024; } From d12713a151ffd2775d85bb89d0e219d3aa2cb2ee Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 20 Jul 2019 15:21:04 +0200 Subject: [PATCH 16/20] Make some tests work again. --- srml/executive/src/lib.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 2bed6a446e507..326c3d7ab95d9 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -449,6 +449,10 @@ mod tests { ) } + fn sign_extra(who: u64, nonce: u64, fee: u64) -> Option<(u64, SignedExtra)> { + Some((who, extra(nonce, fee))) + } + #[test] fn balance_transfer_dispatch_works() { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); @@ -456,7 +460,7 @@ mod tests { balances: vec![(1, 111)], vesting: vec![], }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); - let xt = primitives::testing::TestXt(Some(1), Call::transfer(2, 69), extra(0, 0)); + let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(2, 69)); let weight = xt.get_dispatch_info().weight as u64; let mut t = runtime_io::TestExternalities::::new_with_children(t); with_externalities(&mut t, || { @@ -537,7 +541,7 @@ mod tests { fn bad_extrinsic_not_inserted() { let mut t = new_test_ext(1); // bad nonce check! - let xt = primitives::testing::TestXt(Some(1), Call::transfer(33, 69), extra(30, 0)); + let xt = primitives::testing::TestXt(sign_extra(1, 30, 0), Call::transfer(33, 69)); with_externalities(&mut t, || { Executive::initialize_block(&Header::new( 1, @@ -555,7 +559,7 @@ mod tests { fn block_weight_limit_enforced() { let mut t = new_test_ext(10000); // given: TestXt uses the encoded len as fixed Len: - let xt = primitives::testing::TestXt(Some(1), Call::transfer::(33, 0), extra(0, 0)); + let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer::(33, 0)); let encoded = xt.encode(); let encoded_len = encoded.len() as u32; let limit = >::get() / 4; @@ -571,7 +575,7 @@ mod tests { 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 xt = primitives::testing::TestXt(sign_extra(1, nonce.into(), 0), Call::transfer::(33, 0)); let res = Executive::apply_extrinsic(xt); if nonce != num_to_exhaust_block { assert_eq!(res.unwrap(), ApplyOutcome::Success); @@ -586,9 +590,9 @@ mod tests { #[test] 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 xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(33, 0)); + let x1 = primitives::testing::TestXt(sign_extra(1, 1, 0), Call::transfer(33, 0)); + let x2 = primitives::testing::TestXt(sign_extra(1, 2, 0), Call::transfer(33, 0)); let len = xt.clone().encode().len() as u32; let mut t = new_test_ext(1); with_externalities(&mut t, || { @@ -612,7 +616,7 @@ mod tests { #[test] fn validate_unsigned() { - let xt = primitives::testing::TestXt(None, Call::set_balance(33, 69, 69), extra(0, 0)); + let xt = primitives::testing::TestXt(None, Call::set_balance(33, 69, 69)); let valid = TransactionValidity::Valid(Default::default()); let mut t = new_test_ext(1); @@ -635,7 +639,7 @@ mod tests { 10, lock, ); - let xt = primitives::testing::TestXt(Some(1), Call::transfer(2, 10), extra(0, 0)); + let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(2, 10)); let weight = xt.get_dispatch_info().weight as u64; Executive::initialize_block(&Header::new( 1, From 3350f9c6bbab21159e10a178acf1a01285db1fc5 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 20 Jul 2019 16:31:30 +0200 Subject: [PATCH 17/20] Fix subkey. --- subkey/src/main.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/subkey/src/main.rs b/subkey/src/main.rs index ea72acd8b4d61..f9432b42c12b3 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -26,7 +26,6 @@ use substrate_primitives::{ ed25519, sr25519, hexdisplay::HexDisplay, Pair, Public, blake2_256, crypto::{Ss58Codec, set_default_ss58_version, Ss58AddressFormat} }; -use parity_codec::{Encode, Decode}; use parity_codec::{Encode, Decode, Compact}; use sr_primitives::generic::Era; use node_primitives::{Balance, Index, Hash}; @@ -91,14 +90,14 @@ fn execute(matches: clap::ArgMatches) where <::Pair as Pair>::Signature: AsRef<[u8]> + AsMut<[u8]> + Default, <::Pair as Pair>::Public: Sized + AsRef<[u8]> + Ss58Codec + AsRef<<::Pair as Pair>::Public>, { - let extra = |i: Index, f: Balance| { - ( - system::CheckEra::::from(Era::Immortal), - system::CheckNonce::::from(i), - system::CheckWeight::::from(), - balances::TakeFees::::from(f), - ) - }; + // let extra = |i: Index, f: Balance| { + // ( + // system::CheckEra::::from(Era::Immortal), + // system::CheckNonce::::from(i), + // system::CheckWeight::::from(), + // balances::TakeFees::::from(f), + // ) + // }; let password = matches.value_of("password"); let maybe_network = matches.value_of("network"); if let Some(network) = maybe_network { From b7885073ce948f72a22b28b0288c5d28fdbb45f4 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 20 Jul 2019 16:43:37 +0200 Subject: [PATCH 18/20] Remove todos + bump. --- core/sr-primitives/src/traits.rs | 2 +- node/runtime/src/lib.rs | 4 ++-- srml/system/src/lib.rs | 5 ----- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index a460b4b785d8b..eccf9751322ca 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -929,7 +929,7 @@ macro_rules! tuple_impl_indexed { }; } -// TODO: merge this into `tuple_impl` once codec supports `trait Codec` for longer tuple lengths. +// TODO: merge this into `tuple_impl` once codec supports `trait Codec` for longer tuple lengths. #3152 #[allow(non_snake_case)] tuple_impl_indexed!(A, B, C, D, E, F, G, H, I, J, ; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,); diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 84f7e73730f43..a0a8af0b5c586 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -75,8 +75,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 115, - impl_version: 115, + spec_version: 116, + impl_version: 116, apis: RUNTIME_API_VERSIONS, }; diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 0c58002f939e9..bd1f76299e49c 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -201,10 +201,6 @@ pub trait Trait: 'static + Eq + Clone { /// The maximum weight of a block. type MaximumBlockWeight: Get; - /// portion of the block weight that is allowed to be consumed by normal extrinsics. The weight - /// multiplier also updates proportional to this. - // type IdealBlockWeightRatio: Get; - /// The maximum length of a block (in bytes). type MaximumBlockLength: Get; } @@ -802,7 +798,6 @@ impl CheckWeight { fn get_dispatch_limit_divisor(class: DispatchClass) -> Weight { match class { DispatchClass::Operational => 1, - // TODO: make this an associated const. DispatchClass::Normal => 4, } } From 383185d9ed6dde7103b22a2033366be982710125 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 22 Jul 2019 18:12:42 +0200 Subject: [PATCH 19/20] Ignore expensive test. --- node/executor/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index cc050b015f080..b55461492625d 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -936,6 +936,7 @@ mod tests { #[test] + #[ignore] fn weight_multiplier_increases_and_decreases_on_big_weight() { let mut t = new_test_ext(COMPACT_CODE, false); From ec829fdb336faf7201fc01a505c7c15578a9dc38 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 22 Jul 2019 18:14:29 +0200 Subject: [PATCH 20/20] Bump. --- node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index a0a8af0b5c586..71af5d9417e81 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -75,8 +75,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 116, - impl_version: 116, + spec_version: 117, + impl_version: 117, apis: RUNTIME_API_VERSIONS, };