Allow modules to validate transaction, second attempt (#2463)

* first impl

* rename origin::inherent to none

* fix

* fix

* Apply suggestions from code review

Co-Authored-By: thiolliere <gui.thiolliere@gmail.com>

* comment

* better error

* doc

* (add unsigned module 🤦)

* doc

* fix

* implement for node-template as well

* add validated unsigned to executor

* fix

* fix

* bump version

* testing xt

* remove extraneous logic

* licence

* impl test
This commit is contained in:
thiolliere
2019-05-10 14:13:05 +02:00
committed by Gavin Wood
parent 4aa44ab280
commit dfbaedd535
22 changed files with 414 additions and 85 deletions
+5 -2
View File
@@ -199,7 +199,10 @@ impl<'a, Xt> Deserialize<'a> for Block<Xt> where Block<Xt>: Decode {
} }
} }
/// Test transaction /// Test transaction, tuple of (sender, index, call)
/// 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)] #[derive(PartialEq, Eq, Clone, Encode, Decode)]
pub struct TestXt<Call>(pub Option<u64>, pub u64, pub Call); pub struct TestXt<Call>(pub Option<u64>, pub u64, pub Call);
@@ -222,7 +225,7 @@ impl<Call: Codec + Sync + Send, Context> Checkable<Context> for TestXt<Call> {
} }
impl<Call: Codec + Sync + Send> traits::Extrinsic for TestXt<Call> { impl<Call: Codec + Sync + Send> traits::Extrinsic for TestXt<Call> {
fn is_signed(&self) -> Option<bool> { fn is_signed(&self) -> Option<bool> {
None Some(self.0.is_some())
} }
} }
impl<Call> Applyable for TestXt<Call> where impl<Call> Applyable for TestXt<Call> where
@@ -23,6 +23,7 @@ use runtime_io;
#[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; #[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned};
use substrate_primitives::{self, Hasher, Blake2Hasher}; use substrate_primitives::{self, Hasher, Blake2Hasher};
use crate::codec::{Codec, Encode, HasCompact}; use crate::codec::{Codec, Encode, HasCompact};
use crate::transaction_validity::TransactionValidity;
pub use integer_sqrt::IntegerSquareRoot; pub use integer_sqrt::IntegerSquareRoot;
pub use num_traits::{ pub use num_traits::{
Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
@@ -786,3 +787,17 @@ pub trait RuntimeApiInfo {
/// The version of the runtime api. /// The version of the runtime api.
const VERSION: u32; const VERSION: u32;
} }
/// Something that can validate unsigned extrinsics.
pub trait ValidateUnsigned {
/// The call to validate
type Call;
/// Return the validity of the call
///
/// This doesn't execute any side-effects; it merely checks
/// whether the transaction would panic if it were included or not.
///
/// Changes made to storage should be discarded by caller.
fn validate_unsigned(call: &Self::Call) -> TransactionValidity;
}
+1 -1
View File
@@ -224,7 +224,7 @@ pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic<Address,
/// Extrinsic type that has already been checked. /// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Nonce, Call>; pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Nonce, Call>;
/// Executive: handles dispatch to the various modules. /// Executive: handles dispatch to the various modules.
pub type Executive = executive::Executive<Runtime, Block, Context, Balances, AllModules>; pub type Executive = executive::Executive<Runtime, Block, Context, Balances, Runtime, AllModules>;
// Implement our runtime API endpoints. This is just a bunch of proxying. // Implement our runtime API endpoints. This is just a bunch of proxying.
impl_runtime_apis! { impl_runtime_apis! {
+4 -4
View File
@@ -34,7 +34,7 @@ use client::{
use runtime_primitives::{ApplyResult, generic, create_runtime_str}; use runtime_primitives::{ApplyResult, generic, create_runtime_str};
use runtime_primitives::transaction_validity::TransactionValidity; use runtime_primitives::transaction_validity::TransactionValidity;
use runtime_primitives::traits::{ use runtime_primitives::traits::{
BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, AuthorityIdFor, Convert BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, AuthorityIdFor, Convert,
}; };
use version::RuntimeVersion; use version::RuntimeVersion;
use council::{motions as council_motions, voting as council_voting}; use council::{motions as council_motions, voting as council_voting};
@@ -58,8 +58,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"), spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"), impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10, authoring_version: 10,
spec_version: 74, spec_version: 75,
impl_version: 74, impl_version: 75,
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
}; };
@@ -249,7 +249,7 @@ pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic<Address,
/// Extrinsic type that has already been checked. /// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Index, Call>; pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Index, Call>;
/// Executive: handles dispatch to the various modules. /// Executive: handles dispatch to the various modules.
pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Balances, AllModules>; pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Balances, Runtime, AllModules>;
impl_runtime_apis! { impl_runtime_apis! {
impl client_api::Core<Block> for Runtime { impl client_api::Core<Block> for Runtime {
+1 -1
View File
@@ -105,7 +105,7 @@
//! pub type NegativeImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance; //! pub type NegativeImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
//! //!
//! # fn main() {} //! # fn main() {}
//!``` //! ```
//! //!
//! The Staking module uses the `LockableCurrency` trait to lock a stash account's funds: //! The Staking module uses the `LockableCurrency` trait to lock a stash account's funds:
//! //!
+2 -2
View File
@@ -136,7 +136,7 @@ use srml_support::storage::StorageValue;
use srml_support::storage::unhashed::StorageVec; use srml_support::storage::unhashed::StorageVec;
use primitives::traits::{MaybeSerializeDebug, Member}; use primitives::traits::{MaybeSerializeDebug, Member};
use substrate_primitives::storage::well_known_keys; use substrate_primitives::storage::well_known_keys;
use system::{ensure_signed, ensure_inherent}; use system::{ensure_signed, ensure_none};
use inherents::{ use inherents::{
ProvideInherent, InherentData, InherentIdentifier, RuntimeString, MakeFatalError ProvideInherent, InherentData, InherentIdentifier, RuntimeString, MakeFatalError
}; };
@@ -298,7 +298,7 @@ decl_module! {
/// Note that the previous block's validator missed its opportunity to propose a block. /// Note that the previous block's validator missed its opportunity to propose a block.
fn note_offline(origin, offline: <T::InherentOfflineReport as InherentOfflineReport>::Inherent) { fn note_offline(origin, offline: <T::InherentOfflineReport as InherentOfflineReport>::Inherent) {
ensure_inherent(origin)?; ensure_none(origin)?;
T::InherentOfflineReport::handle_report(offline); T::InherentOfflineReport::handle_report(offline);
} }
+1 -1
View File
@@ -476,7 +476,7 @@ decl_module! {
Some(system::RawOrigin::Signed(ref account)) if aux_sender.is_none() => { Some(system::RawOrigin::Signed(ref account)) if aux_sender.is_none() => {
(true, account) (true, account)
}, },
Some(system::RawOrigin::Inherent) if aux_sender.is_some() => { Some(system::RawOrigin::None) if aux_sender.is_some() => {
(false, aux_sender.as_ref().expect("checked above")) (false, aux_sender.as_ref().expect("checked above"))
}, },
_ => return Err("Invalid surcharge claim: origin must be signed or \ _ => return Err("Invalid surcharge claim: origin must be signed or \
+5 -5
View File
@@ -754,7 +754,7 @@ fn call_contract_removals() {
#[test] #[test]
fn inherent_claim_surcharge_contract_removals() { fn inherent_claim_surcharge_contract_removals() {
removals(|| Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok()); removals(|| Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok());
} }
#[test] #[test]
@@ -765,10 +765,10 @@ fn signed_claim_surcharge_contract_removals() {
#[test] #[test]
fn claim_surcharge_malus() { fn claim_surcharge_malus() {
// Test surcharge malus for inherent // Test surcharge malus for inherent
claim_surcharge(4, || Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok(), true); claim_surcharge(4, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
claim_surcharge(3, || Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok(), true); claim_surcharge(3, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
claim_surcharge(2, || Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok(), true); claim_surcharge(2, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
claim_surcharge(1, || Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok(), false); claim_surcharge(1, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false);
// Test surcharge malus for signed // Test surcharge malus for signed
claim_surcharge(4, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true); claim_surcharge(4, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true);
+8 -6
View File
@@ -107,9 +107,9 @@
//! //!
//! \### Supported Origins //! \### Supported Origins
//! //!
//! // What origins are used and supported in this module (root, signed, inherent) //! // What origins are used and supported in this module (root, signed, none)
//! // i.e. root when <code>\`ensure_root\`</code> used //! // i.e. root when <code>\`ensure_root\`</code> used
//! // i.e. inherent when <code>\`ensure_inherent\`</code> used //! // i.e. none when <code>\`ensure_none\`</code> used
//! // i.e. signed when <code>\`ensure_signed\`</code> used //! // i.e. signed when <code>\`ensure_signed\`</code> used
//! //!
//! <code>\`inherent\`</code> <INSERT_DESCRIPTION> //! <code>\`inherent\`</code> <INSERT_DESCRIPTION>
@@ -322,7 +322,9 @@ decl_event!(
// Generally you'll want to split these into three groups: // Generally you'll want to split these into three groups:
// - Public calls that are signed by an external account. // - Public calls that are signed by an external account.
// - Root calls that are allowed to be made only by the governance system. // - Root calls that are allowed to be made only by the governance system.
// - Inherent calls that are allowed to be made only by the block authors and validators. // - Unsigned calls that can be of two kinds:
// * "Inherent extrinsics" that are opinions generally held by the block authors that build child blocks.
// * Unsigned Transactions that are of intrinsic recognisable utility to the network, and are validated by the runtime.
// //
// Information about where this dispatch initiated from is provided as the first argument // Information about where this dispatch initiated from is provided as the first argument
// "origin". As such functions must always look like: // "origin". As such functions must always look like:
@@ -337,10 +339,10 @@ decl_event!(
// `fn foo(origin: T::Origin, bar: Bar, baz: Baz) { ... }` // `fn foo(origin: T::Origin, bar: Bar, baz: Baz) { ... }`
// //
// There are three entries in the `system::Origin` enum that correspond // There are three entries in the `system::Origin` enum that correspond
// to the above bullets: `::Signed(AccountId)`, `::Root` and `::Inherent`. You should always match // to the above bullets: `::Signed(AccountId)`, `::Root` and `::None`. You should always match
// against them as the first thing you do in your function. There are three convenience calls // against them as the first thing you do in your function. There are three convenience calls
// in system that do the matching for you and return a convenient result: `ensure_signed`, // in system that do the matching for you and return a convenient result: `ensure_signed`,
// `ensure_root` and `ensure_inherent`. // `ensure_root` and `ensure_none`.
decl_module! { decl_module! {
// Simple declaration of the `Module` type. Lets the macro know what its working on. // Simple declaration of the `Module` type. Lets the macro know what its working on.
pub struct Module<T: Trait> for enum Call where origin: T::Origin { pub struct Module<T: Trait> for enum Call where origin: T::Origin {
@@ -448,7 +450,7 @@ decl_module! {
// For instance you can generate extrinsics for the upcoming produced block. // For instance you can generate extrinsics for the upcoming produced block.
fn offchain_worker(_n: T::BlockNumber) { fn offchain_worker(_n: T::BlockNumber) {
// We don't do anything here. // We don't do anything here.
// but we could dispatch extrinsic (transaction/inherent) using // but we could dispatch extrinsic (transaction/unsigned/inherent) using
// runtime_io::submit_extrinsic // runtime_io::submit_extrinsic
} }
} }
+88 -40
View File
@@ -59,8 +59,17 @@
//! # pub type Balances = u64; //! # pub type Balances = u64;
//! # pub type AllModules = u64; //! # pub type AllModules = u64;
//! # pub enum Runtime {}; //! # pub enum Runtime {};
//! # use primitives::transaction_validity::TransactionValidity;
//! # use primitives::traits::ValidateUnsigned;
//! # impl ValidateUnsigned for Runtime {
//! # type Call = ();
//! #
//! # fn validate_unsigned(_call: &Self::Call) -> TransactionValidity {
//! # TransactionValidity::Invalid(0)
//! # }
//! # }
//! /// Executive: handles dispatch to the various modules. //! /// Executive: handles dispatch to the various modules.
//! pub type Executive = executive::Executive<Runtime, Block, Context, Balances, AllModules>; //! pub type Executive = executive::Executive<Runtime, Block, Context, Balances, Runtime, AllModules>;
//! ``` //! ```
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
@@ -70,7 +79,8 @@ use rstd::marker::PhantomData;
use rstd::result; use rstd::result;
use primitives::traits::{ use primitives::traits::{
self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize,
OnInitialize, Digest, NumberFor, Block as BlockT, OffchainWorker OnInitialize, Digest, NumberFor, Block as BlockT, OffchainWorker,
ValidateUnsigned,
}; };
use srml_support::{Dispatchable, traits::MakePayment}; use srml_support::{Dispatchable, traits::MakePayment};
use parity_codec::{Codec, Encode}; use parity_codec::{Codec, Encode};
@@ -101,8 +111,8 @@ pub trait ExecuteBlock<Block: BlockT> {
fn execute_block(block: Block); fn execute_block(block: Block);
} }
pub struct Executive<System, Block, Context, Payment, AllModules>( pub struct Executive<System, Block, Context, Payment, UnsignedValidator, AllModules>(
PhantomData<(System, Block, Context, Payment, AllModules)> PhantomData<(System, Block, Context, Payment, UnsignedValidator, AllModules)>
); );
impl< impl<
@@ -110,15 +120,18 @@ impl<
Block: traits::Block<Header=System::Header, Hash=System::Hash>, Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Context: Default, Context: Default,
Payment: MakePayment<System::AccountId>, Payment: MakePayment<System::AccountId>,
UnsignedValidator,
AllModules: OnInitialize<System::BlockNumber> + OnFinalize<System::BlockNumber> + OffchainWorker<System::BlockNumber>, AllModules: OnInitialize<System::BlockNumber> + OnFinalize<System::BlockNumber> + OffchainWorker<System::BlockNumber>,
> ExecuteBlock<Block> for Executive<System, Block, Context, Payment, AllModules> where > ExecuteBlock<Block> for Executive<System, Block, Context, Payment, UnsignedValidator, AllModules>
where
Block::Extrinsic: Checkable<Context> + Codec, Block::Extrinsic: Checkable<Context> + Codec,
<Block::Extrinsic as Checkable<Context>>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>, <Block::Extrinsic as Checkable<Context>>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>,
<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call: Dispatchable, <<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call: Dispatchable,
<<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call as Dispatchable>::Origin: From<Option<System::AccountId>> <<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call as Dispatchable>::Origin: From<Option<System::AccountId>>,
UnsignedValidator: ValidateUnsigned<Call=<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call>
{ {
fn execute_block(block: Block) { fn execute_block(block: Block) {
Executive::<System, Block, Context, Payment, AllModules>::execute_block(block); Executive::<System, Block, Context, Payment, UnsignedValidator, AllModules>::execute_block(block);
} }
} }
@@ -127,12 +140,15 @@ impl<
Block: traits::Block<Header=System::Header, Hash=System::Hash>, Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Context: Default, Context: Default,
Payment: MakePayment<System::AccountId>, Payment: MakePayment<System::AccountId>,
UnsignedValidator,
AllModules: OnInitialize<System::BlockNumber> + OnFinalize<System::BlockNumber> + OffchainWorker<System::BlockNumber>, AllModules: OnInitialize<System::BlockNumber> + OnFinalize<System::BlockNumber> + OffchainWorker<System::BlockNumber>,
> Executive<System, Block, Context, Payment, AllModules> where > Executive<System, Block, Context, Payment, UnsignedValidator, AllModules>
where
Block::Extrinsic: Checkable<Context> + Codec, Block::Extrinsic: Checkable<Context> + Codec,
<Block::Extrinsic as Checkable<Context>>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>, <Block::Extrinsic as Checkable<Context>>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>,
<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call: Dispatchable, <<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call: Dispatchable,
<<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call as Dispatchable>::Origin: From<Option<System::AccountId>> <<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call as Dispatchable>::Origin: From<Option<System::AccountId>>,
UnsignedValidator: ValidateUnsigned<Call=<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call>
{ {
/// Start the execution of a particular block. /// Start the execution of a particular block.
pub fn initialize_block(header: &System::Header) { pub fn initialize_block(header: &System::Header) {
@@ -290,7 +306,7 @@ impl<
assert!(header.state_root() == storage_root, "Storage root must match that calculated."); assert!(header.state_root() == storage_root, "Storage root must match that calculated.");
} }
/// Check a given transaction for validity. This doesn't execute any /// Check a given signed transaction for validity. This doesn't execute any
/// side-effects; it merely checks whether the transaction would panic if it were included or not. /// side-effects; it merely checks whether the transaction would panic if it were included or not.
/// ///
/// Changes made to storage should be discarded. /// Changes made to storage should be discarded.
@@ -313,38 +329,37 @@ impl<
Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR), Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR),
}; };
if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) { match (xt.sender(), xt.index()) {
// pay any fees (Some(sender), Some(index)) => {
if Payment::make_payment(sender, encoded_len).is_err() { // pay any fees
return TransactionValidity::Invalid(ApplyError::CantPay as i8) if Payment::make_payment(sender, encoded_len).is_err() {
} return TransactionValidity::Invalid(ApplyError::CantPay as i8)
}
// check index // check index
let expected_index = <system::Module<System>>::account_nonce(sender); let expected_index = <system::Module<System>>::account_nonce(sender);
if index < &expected_index { if index < &expected_index {
return TransactionValidity::Invalid(ApplyError::Stale as i8) return TransactionValidity::Invalid(ApplyError::Stale as i8)
} }
let index = *index; let index = *index;
let provides = vec![(sender, index).encode()]; let provides = vec![(sender, index).encode()];
let requires = if expected_index < index { let requires = if expected_index < index {
vec![(sender, index - One::one()).encode()] vec![(sender, index - One::one()).encode()]
} else { } else {
vec![] vec![]
}; };
TransactionValidity::Valid { TransactionValidity::Valid {
priority: encoded_len as TransactionPriority, priority: encoded_len as TransactionPriority,
requires, requires,
provides, provides,
longevity: TransactionLongevity::max_value(), longevity: TransactionLongevity::max_value(),
} }
} else { },
return TransactionValidity::Invalid(if xt.sender().is_none() { (None, None) => UnsignedValidator::validate_unsigned(&xt.deconstruct().0),
MISSING_SENDER (Some(_), None) => TransactionValidity::Invalid(INVALID_INDEX),
} else { (None, Some(_)) => TransactionValidity::Invalid(MISSING_SENDER),
INVALID_INDEX
})
} }
} }
@@ -404,8 +419,24 @@ mod tests {
type TransferPayment = (); type TransferPayment = ();
} }
impl ValidateUnsigned for Runtime {
type Call = Call<Runtime>;
fn validate_unsigned(call: &Self::Call) -> TransactionValidity {
match call {
Call::set_balance(_, _, _) => TransactionValidity::Valid {
priority: 0,
requires: vec![],
provides: vec![],
longevity: std::u64::MAX,
},
_ => TransactionValidity::Invalid(0),
}
}
}
type TestXt = primitives::testing::TestXt<Call<Runtime>>; type TestXt = primitives::testing::TestXt<Call<Runtime>>;
type Executive = super::Executive<Runtime, Block<TestXt>, system::ChainContext<Runtime>, balances::Module<Runtime>, ()>; type Executive = super::Executive<Runtime, Block<TestXt>, system::ChainContext<Runtime>, balances::Module<Runtime>, Runtime, ()>;
#[test] #[test]
fn balance_transfer_dispatch_works() { fn balance_transfer_dispatch_works() {
@@ -527,4 +558,21 @@ mod tests {
run_test(false); run_test(false);
run_test(true); run_test(true);
} }
#[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
};
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(Executive::validate_transaction(xt.clone()), valid);
assert_eq!(Executive::apply_extrinsic(xt), Ok(ApplyOutcome::Fail));
});
}
} }
+3 -3
View File
@@ -29,7 +29,7 @@ use srml_support::StorageValue;
use primitives::traits::{As, One, Zero}; use primitives::traits::{As, One, Zero};
use rstd::{prelude::*, result, cmp, vec}; use rstd::{prelude::*, result, cmp, vec};
use parity_codec::Decode; use parity_codec::Decode;
use srml_system::{ensure_inherent, Trait as SystemTrait}; use srml_system::{ensure_none, Trait as SystemTrait};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use parity_codec::Encode; use parity_codec::Encode;
@@ -117,7 +117,7 @@ decl_module! {
/// Hint that the author of this block thinks the best finalized /// Hint that the author of this block thinks the best finalized
/// block is the given number. /// block is the given number.
fn final_hint(origin, #[compact] hint: T::BlockNumber) { fn final_hint(origin, #[compact] hint: T::BlockNumber) {
ensure_inherent(origin)?; ensure_none(origin)?;
assert!(!<Self as Store>::Update::exists(), "Final hint must be updated only once in the block"); assert!(!<Self as Store>::Update::exists(), "Final hint must be updated only once in the block");
assert!( assert!(
srml_system::Module::<T>::block_number() >= hint, srml_system::Module::<T>::block_number() >= hint,
@@ -372,7 +372,7 @@ mod tests {
System::initialize(&i, &parent_hash, &Default::default()); System::initialize(&i, &parent_hash, &Default::default());
assert_ok!(FinalityTracker::dispatch( assert_ok!(FinalityTracker::dispatch(
Call::final_hint(i-1), Call::final_hint(i-1),
Origin::INHERENT, Origin::NONE,
)); ));
FinalityTracker::on_finalize(i); FinalityTracker::on_finalize(i);
let hdr = System::finalize(); let hdr = System::finalize();
+1 -1
View File
@@ -29,7 +29,7 @@ pub use inherents::{InherentData, ProvideInherent, CheckInherentsResult, IsFatal
/// ///
/// ```nocompile /// ```nocompile
/// impl_outer_inherent! { /// impl_outer_inherent! {
/// pub struct InherentData where Block = Block, UncheckedExtrinsic = UncheckedExtrinsic { /// impl Inherents where Block = Block, UncheckedExtrinsic = UncheckedExtrinsic {
/// timestamp: Timestamp, /// timestamp: Timestamp,
/// consensus: Consensus, /// consensus: Consensus,
/// /// Aura module using the `Timestamp` call. /// /// Aura module using the `Timestamp` call.
+2
View File
@@ -52,6 +52,8 @@ pub mod metadata;
mod runtime; mod runtime;
#[macro_use] #[macro_use]
pub mod inherent; pub mod inherent;
#[macro_use]
pub mod unsigned;
mod double_map; mod double_map;
pub mod traits; pub mod traits;
+2 -2
View File
@@ -265,14 +265,14 @@ mod tests {
pub enum RawOrigin<AccountId> { pub enum RawOrigin<AccountId> {
Root, Root,
Signed(AccountId), Signed(AccountId),
Inherent, None,
} }
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> { impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> { fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
match s { match s {
Some(who) => RawOrigin::Signed(who), Some(who) => RawOrigin::Signed(who),
None => RawOrigin::Inherent, None => RawOrigin::None,
} }
} }
} }
+3 -3
View File
@@ -101,7 +101,7 @@ macro_rules! impl_outer_origin {
} }
#[allow(dead_code)] #[allow(dead_code)]
impl $name { impl $name {
pub const INHERENT: Self = $name::system($system::RawOrigin::Inherent); pub const NONE: Self = $name::system($system::RawOrigin::None);
pub const ROOT: Self = $name::system($system::RawOrigin::Root); pub const ROOT: Self = $name::system($system::RawOrigin::Root);
pub fn signed(by: <$runtime as $system::Trait>::AccountId) -> Self { pub fn signed(by: <$runtime as $system::Trait>::AccountId) -> Self {
$name::system($system::RawOrigin::Signed(by)) $name::system($system::RawOrigin::Signed(by))
@@ -156,14 +156,14 @@ mod tests {
pub enum RawOrigin<AccountId> { pub enum RawOrigin<AccountId> {
Root, Root,
Signed(AccountId), Signed(AccountId),
Inherent, None,
} }
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> { impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> { fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
match s { match s {
Some(who) => RawOrigin::Signed(who), Some(who) => RawOrigin::Signed(who),
None => RawOrigin::Inherent, None => RawOrigin::None,
} }
} }
} }
+70
View File
@@ -66,6 +66,7 @@
/// - `Inherent $( (CALL) )*` - If the module provides/can check inherents. The optional parameter /// - `Inherent $( (CALL) )*` - If the module provides/can check inherents. The optional parameter
/// is for modules that use a `Call` from a different module as /// is for modules that use a `Call` from a different module as
/// inherent. /// inherent.
/// - `ValidateUnsigned` - If the module validates unsigned extrinsics.
/// ///
/// # Note /// # Note
/// ///
@@ -286,6 +287,13 @@ macro_rules! construct_runtime {
$name: $module::{ $( $modules $( ( $( $modules_args ),* ) )* ),* } $name: $module::{ $( $modules $( ( $( $modules_args ),* ) )* ),* }
),*; ),*;
); );
$crate::__impl_outer_validate_unsigned!(
$runtime;
{};
$(
$name: $module::{ $( $modules $( ( $( $modules_args )* ) )* )* }
)*
);
} }
} }
@@ -947,3 +955,65 @@ macro_rules! __decl_instance_import {
} }
}; };
} }
/// A private macro that calls impl_outer_validate_unsigned for Call.
#[macro_export]
#[doc(hidden)]
macro_rules! __impl_outer_validate_unsigned {
(
$runtime:ident;
{ $( $parsed:tt )* };
$name:ident: $module:ident:: $(<$module_instance:ident>::)? {
ValidateUnsigned $( $modules:ident $( ( $( $modules_args:ident )* ) )* )*
}
$( $rest:tt )*
) => {
$crate::__impl_outer_validate_unsigned!(
$runtime;
{ $( $parsed )* $name };
$( $rest )*
);
};
(
$runtime:ident;
{ $( $parsed:tt )* };
$name:ident: $module:ident:: $(<$module_instance:ident>::)? {
$ignore:ident $( ( $( $args_ignore:ident )* ) )*
$( $modules:ident $( ( $( $modules_args:ident )* ) )* )*
}
$( $rest:tt )*
) => {
$crate::__impl_outer_validate_unsigned!(
$runtime;
{ $( $parsed )* };
$name: $module:: $(<$module_instance>::)? {
$( $modules $( ( $( $modules_args )* ) )* )*
}
$( $rest )*
);
};
(
$runtime:ident;
{ $( $parsed:tt )* };
$name:ident: $module:ident:: $(<$module_instance:ident>::)? {}
$( $rest:tt )*
) => {
$crate::__impl_outer_validate_unsigned!(
$runtime;
{ $( $parsed )* };
$( $rest )*
);
};
(
$runtime:ident;
{ $(
$parsed_modules:ident
)* };
) => {
$crate::impl_outer_validate_unsigned!(
impl ValidateUnsigned for $runtime {
$( $parsed_modules )*
}
);
};
}
+153
View File
@@ -0,0 +1,153 @@
// Copyright 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 <http://www.gnu.org/licenses/>.
#[doc(hidden)]
pub use crate::runtime_primitives::traits::ValidateUnsigned;
#[doc(hidden)]
pub use crate::runtime_primitives::transaction_validity::TransactionValidity;
#[doc(hidden)]
pub use crate::runtime_primitives::ApplyError;
/// Implement `ValidateUnsigned` for `Runtime`.
/// All given modules need to implement `ValidateUnsigned`.
///
/// # Example
///
/// ```
/// # mod timestamp {
/// # pub struct Module;
/// #
/// # impl srml_support::unsigned::ValidateUnsigned for Module {
/// # type Call = Call;
/// #
/// # fn validate_unsigned(call: &Self::Call) -> srml_support::unsigned::TransactionValidity {
/// # unimplemented!();
/// # }
/// # }
/// #
/// # pub enum Call {
/// # }
/// # }
/// #
/// # pub type Timestamp = timestamp::Module;
/// #
/// #
/// # pub enum Call {
/// # Timestamp(timestamp::Call),
/// # }
/// # #[allow(unused)]
/// pub struct Runtime;
///
/// srml_support::impl_outer_validate_unsigned! {
/// impl ValidateUnsigned for Runtime {
/// Timestamp
/// }
/// }
/// ```
#[macro_export]
macro_rules! impl_outer_validate_unsigned {
(
impl ValidateUnsigned for $runtime:ident {
$( $module:ident )*
}
) => {
impl $crate::unsigned::ValidateUnsigned for $runtime {
type Call = Call;
fn validate_unsigned(call: &Self::Call) -> $crate::unsigned::TransactionValidity {
#[allow(unreachable_patterns)]
match call {
$( Call::$module(inner_call) => $module::validate_unsigned(inner_call), )*
_ => $crate::unsigned::TransactionValidity::Invalid($crate::unsigned::ApplyError::BadSignature as i8),
}
}
}
};
}
#[cfg(test)]
mod test_empty_call {
pub enum Call {
}
pub struct Runtime;
impl_outer_validate_unsigned! {
impl ValidateUnsigned for Runtime {
}
}
}
#[cfg(test)]
mod test_partial_and_full_call {
pub mod timestamp {
pub struct Module;
impl super::super::ValidateUnsigned for Module {
type Call = Call;
fn validate_unsigned(_call: &Self::Call) -> super::super::TransactionValidity {
unimplemented!();
}
}
pub enum Call {
Foo,
}
}
mod test_full_unsigned {
pub type Timestamp = super::timestamp::Module;
pub enum Call {
Timestamp(super::timestamp::Call),
}
pub struct Runtime;
impl_outer_validate_unsigned! {
impl ValidateUnsigned for Runtime {
Timestamp
}
}
#[test]
fn used() {
let _ = Call::Timestamp(super::timestamp::Call::Foo);
let _ = Runtime;
}
}
mod test_not_full_unsigned {
pub enum Call {
Timestamp(super::timestamp::Call),
}
pub struct Runtime;
impl_outer_validate_unsigned! {
impl ValidateUnsigned for Runtime {
}
}
#[test]
fn used() {
let _ = Call::Timestamp(super::timestamp::Call::Foo);
let _ = Runtime;
}
}
}
+18
View File
@@ -0,0 +1,18 @@
// Copyright 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 <http://www.gnu.org/licenses/>.
//! Test crate for srml_support. Allow to make use of `srml_support::decl_storage`.
//! See tests directory.
@@ -1,3 +1,19 @@
// Copyright 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 <http://www.gnu.org/licenses/>.
use runtime_io::{with_externalities, Blake2Hasher}; use runtime_io::{with_externalities, Blake2Hasher};
use srml_support::{StorageValue, StorageMap, StorageDoubleMap}; use srml_support::{StorageValue, StorageMap, StorageDoubleMap};
use srml_support::storage::unhashed; use srml_support::storage::unhashed;
@@ -75,14 +75,14 @@ mod system {
pub enum RawOrigin<AccountId> { pub enum RawOrigin<AccountId> {
Root, Root,
Signed(AccountId), Signed(AccountId),
Inherent, None,
} }
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> { impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> { fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
match s { match s {
Some(who) => RawOrigin::Signed(who), Some(who) => RawOrigin::Signed(who),
None => RawOrigin::Inherent, None => RawOrigin::None,
} }
} }
} }
+8 -6
View File
@@ -233,15 +233,17 @@ pub enum RawOrigin<AccountId> {
Root, Root,
/// It is signed by some public key and we provide the `AccountId`. /// It is signed by some public key and we provide the `AccountId`.
Signed(AccountId), Signed(AccountId),
/// It is signed by nobody but included and agreed upon by the validators anyway: it's "inherently" true. /// It is signed by nobody, can be either:
Inherent, /// * included and agreed upon by the validators anyway,
/// * or unsigned transaction validated by a module.
None,
} }
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> { impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> { fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
match s { match s {
Some(who) => RawOrigin::Signed(who), Some(who) => RawOrigin::Signed(who),
None => RawOrigin::Inherent, None => RawOrigin::None,
} }
} }
} }
@@ -363,12 +365,12 @@ pub fn ensure_root<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<(), &'stati
} }
/// Ensure that the origin `o` represents an unsigned extrinsic. Returns `Ok` or an `Err` otherwise. /// Ensure that the origin `o` represents an unsigned extrinsic. Returns `Ok` or an `Err` otherwise.
pub fn ensure_inherent<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<(), &'static str> pub fn ensure_none<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<(), &'static str>
where OuterOrigin: Into<Option<RawOrigin<AccountId>>> where OuterOrigin: Into<Option<RawOrigin<AccountId>>>
{ {
match o.into() { match o.into() {
Some(RawOrigin::Inherent) => Ok(()), Some(RawOrigin::None) => Ok(()),
_ => Err("bad origin: expected to be an inherent origin"), _ => Err("bad origin: expected to be no origin"),
} }
} }
+6 -6
View File
@@ -95,7 +95,7 @@ use inherents::ProvideInherentData;
use srml_support::{StorageValue, Parameter, decl_storage, decl_module}; use srml_support::{StorageValue, Parameter, decl_storage, decl_module};
use srml_support::for_each_tuple; use srml_support::for_each_tuple;
use runtime_primitives::traits::{As, SimpleArithmetic, Zero}; use runtime_primitives::traits::{As, SimpleArithmetic, Zero};
use system::ensure_inherent; use system::ensure_none;
use rstd::{result, ops::{Mul, Div}, cmp}; use rstd::{result, ops::{Mul, Div}, cmp};
use inherents::{RuntimeString, InherentIdentifier, ProvideInherent, IsFatalError, InherentData}; use inherents::{RuntimeString, InherentIdentifier, ProvideInherent, IsFatalError, InherentData};
@@ -221,7 +221,7 @@ decl_module! {
/// ///
/// The dispatch origin for this call must be `Inherent`. /// The dispatch origin for this call must be `Inherent`.
fn set(origin, #[compact] now: T::Moment) { fn set(origin, #[compact] now: T::Moment) {
ensure_inherent(origin)?; ensure_none(origin)?;
assert!(!<Self as Store>::DidUpdate::exists(), "Timestamp must be updated only once in the block"); assert!(!<Self as Store>::DidUpdate::exists(), "Timestamp must be updated only once in the block");
assert!( assert!(
Self::now().is_zero() || now >= Self::now() + <MinimumPeriod<T>>::get(), Self::now().is_zero() || now >= Self::now() + <MinimumPeriod<T>>::get(),
@@ -369,7 +369,7 @@ mod tests {
with_externalities(&mut TestExternalities::new(t), || { with_externalities(&mut TestExternalities::new(t), || {
Timestamp::set_timestamp(42); Timestamp::set_timestamp(42);
assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT)); assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE));
assert_eq!(Timestamp::now(), 69); assert_eq!(Timestamp::now(), 69);
}); });
} }
@@ -384,8 +384,8 @@ mod tests {
with_externalities(&mut TestExternalities::new(t), || { with_externalities(&mut TestExternalities::new(t), || {
Timestamp::set_timestamp(42); Timestamp::set_timestamp(42);
assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT)); assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE));
let _ = Timestamp::dispatch(Call::set(70), Origin::INHERENT); let _ = Timestamp::dispatch(Call::set(70), Origin::NONE);
}); });
} }
@@ -399,7 +399,7 @@ mod tests {
with_externalities(&mut TestExternalities::new(t), || { with_externalities(&mut TestExternalities::new(t), || {
Timestamp::set_timestamp(42); Timestamp::set_timestamp(42);
let _ = Timestamp::dispatch(Call::set(46), Origin::INHERENT); let _ = Timestamp::dispatch(Call::set(46), Origin::NONE);
}); });
} }
} }