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)]
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> {
fn is_signed(&self) -> Option<bool> {
None
Some(self.0.is_some())
}
}
impl<Call> Applyable for TestXt<Call> where
@@ -23,6 +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, HasCompact};
use crate::transaction_validity::TransactionValidity;
pub use integer_sqrt::IntegerSquareRoot;
pub use num_traits::{
Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
@@ -786,3 +787,17 @@ pub trait RuntimeApiInfo {
/// The version of the runtime api.
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.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Nonce, Call>;
/// 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.
impl_runtime_apis! {
+4 -4
View File
@@ -34,7 +34,7 @@ use client::{
use runtime_primitives::{ApplyResult, generic, create_runtime_str};
use runtime_primitives::transaction_validity::TransactionValidity;
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 council::{motions as council_motions, voting as council_voting};
@@ -58,8 +58,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
spec_version: 74,
impl_version: 74,
spec_version: 75,
impl_version: 75,
apis: RUNTIME_API_VERSIONS,
};
@@ -249,7 +249,7 @@ pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic<Address,
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Index, Call>;
/// 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 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;
//!
//! # fn main() {}
//!```
//! ```
//!
//! 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 primitives::traits::{MaybeSerializeDebug, Member};
use substrate_primitives::storage::well_known_keys;
use system::{ensure_signed, ensure_inherent};
use system::{ensure_signed, ensure_none};
use inherents::{
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.
fn note_offline(origin, offline: <T::InherentOfflineReport as InherentOfflineReport>::Inherent) {
ensure_inherent(origin)?;
ensure_none(origin)?;
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() => {
(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"))
},
_ => return Err("Invalid surcharge claim: origin must be signed or \
+5 -5
View File
@@ -754,7 +754,7 @@ fn call_contract_removals() {
#[test]
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]
@@ -765,10 +765,10 @@ fn signed_claim_surcharge_contract_removals() {
#[test]
fn claim_surcharge_malus() {
// Test surcharge malus for inherent
claim_surcharge(4, || Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok(), true);
claim_surcharge(3, || Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok(), true);
claim_surcharge(2, || Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok(), true);
claim_surcharge(1, || Contract::claim_surcharge(Origin::INHERENT, BOB, Some(ALICE)).is_ok(), false);
claim_surcharge(4, || Contract::claim_surcharge(Origin::NONE, 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::NONE, BOB, Some(ALICE)).is_ok(), true);
claim_surcharge(1, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false);
// Test surcharge malus for signed
claim_surcharge(4, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true);
+8 -6
View File
@@ -107,9 +107,9 @@
//!
//! \### 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. 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
//!
//! <code>\`inherent\`</code> <INSERT_DESCRIPTION>
@@ -322,7 +322,9 @@ decl_event!(
// Generally you'll want to split these into three groups:
// - Public calls that are signed by an external account.
// - 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
// "origin". As such functions must always look like:
@@ -337,10 +339,10 @@ decl_event!(
// `fn foo(origin: T::Origin, bar: Bar, baz: Baz) { ... }`
//
// 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
// 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! {
// 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 {
@@ -448,7 +450,7 @@ decl_module! {
// For instance you can generate extrinsics for the upcoming produced block.
fn offchain_worker(_n: T::BlockNumber) {
// 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
}
}
+88 -40
View File
@@ -59,8 +59,17 @@
//! # pub type Balances = u64;
//! # pub type AllModules = u64;
//! # 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.
//! 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)]
@@ -70,7 +79,8 @@ use rstd::marker::PhantomData;
use rstd::result;
use primitives::traits::{
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 parity_codec::{Codec, Encode};
@@ -101,8 +111,8 @@ pub trait ExecuteBlock<Block: BlockT> {
fn execute_block(block: Block);
}
pub struct Executive<System, Block, Context, Payment, AllModules>(
PhantomData<(System, Block, Context, Payment, AllModules)>
pub struct Executive<System, Block, Context, Payment, UnsignedValidator, AllModules>(
PhantomData<(System, Block, Context, Payment, UnsignedValidator, AllModules)>
);
impl<
@@ -110,15 +120,18 @@ impl<
Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Context: Default,
Payment: MakePayment<System::AccountId>,
UnsignedValidator,
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 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 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) {
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>,
Context: Default,
Payment: MakePayment<System::AccountId>,
UnsignedValidator,
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 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 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.
pub fn initialize_block(header: &System::Header) {
@@ -290,7 +306,7 @@ impl<
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.
///
/// Changes made to storage should be discarded.
@@ -313,38 +329,37 @@ impl<
Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR),
};
if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) {
// pay any fees
if Payment::make_payment(sender, encoded_len).is_err() {
return TransactionValidity::Invalid(ApplyError::CantPay as i8)
}
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 = <system::Module<System>>::account_nonce(sender);
if index < &expected_index {
return TransactionValidity::Invalid(ApplyError::Stale as i8)
}
// check index
let expected_index = <system::Module<System>>::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![]
};
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(),
}
} else {
return TransactionValidity::Invalid(if xt.sender().is_none() {
MISSING_SENDER
} else {
INVALID_INDEX
})
TransactionValidity::Valid {
priority: encoded_len as TransactionPriority,
requires,
provides,
longevity: TransactionLongevity::max_value(),
}
},
(None, None) => UnsignedValidator::validate_unsigned(&xt.deconstruct().0),
(Some(_), None) => TransactionValidity::Invalid(INVALID_INDEX),
(None, Some(_)) => TransactionValidity::Invalid(MISSING_SENDER),
}
}
@@ -404,8 +419,24 @@ mod tests {
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 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]
fn balance_transfer_dispatch_works() {
@@ -527,4 +558,21 @@ mod tests {
run_test(false);
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 rstd::{prelude::*, result, cmp, vec};
use parity_codec::Decode;
use srml_system::{ensure_inherent, Trait as SystemTrait};
use srml_system::{ensure_none, Trait as SystemTrait};
#[cfg(feature = "std")]
use parity_codec::Encode;
@@ -117,7 +117,7 @@ decl_module! {
/// Hint that the author of this block thinks the best finalized
/// block is the given number.
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!(
srml_system::Module::<T>::block_number() >= hint,
@@ -372,7 +372,7 @@ mod tests {
System::initialize(&i, &parent_hash, &Default::default());
assert_ok!(FinalityTracker::dispatch(
Call::final_hint(i-1),
Origin::INHERENT,
Origin::NONE,
));
FinalityTracker::on_finalize(i);
let hdr = System::finalize();
+1 -1
View File
@@ -29,7 +29,7 @@ pub use inherents::{InherentData, ProvideInherent, CheckInherentsResult, IsFatal
///
/// ```nocompile
/// impl_outer_inherent! {
/// pub struct InherentData where Block = Block, UncheckedExtrinsic = UncheckedExtrinsic {
/// impl Inherents where Block = Block, UncheckedExtrinsic = UncheckedExtrinsic {
/// timestamp: Timestamp,
/// consensus: Consensus,
/// /// Aura module using the `Timestamp` call.
+2
View File
@@ -52,6 +52,8 @@ pub mod metadata;
mod runtime;
#[macro_use]
pub mod inherent;
#[macro_use]
pub mod unsigned;
mod double_map;
pub mod traits;
+2 -2
View File
@@ -265,14 +265,14 @@ mod tests {
pub enum RawOrigin<AccountId> {
Root,
Signed(AccountId),
Inherent,
None,
}
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
match s {
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)]
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 fn signed(by: <$runtime as $system::Trait>::AccountId) -> Self {
$name::system($system::RawOrigin::Signed(by))
@@ -156,14 +156,14 @@ mod tests {
pub enum RawOrigin<AccountId> {
Root,
Signed(AccountId),
Inherent,
None,
}
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
match s {
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
/// is for modules that use a `Call` from a different module as
/// inherent.
/// - `ValidateUnsigned` - If the module validates unsigned extrinsics.
///
/// # Note
///
@@ -286,6 +287,13 @@ macro_rules! construct_runtime {
$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 srml_support::{StorageValue, StorageMap, StorageDoubleMap};
use srml_support::storage::unhashed;
@@ -75,14 +75,14 @@ mod system {
pub enum RawOrigin<AccountId> {
Root,
Signed(AccountId),
Inherent,
None,
}
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
match s {
Some(who) => RawOrigin::Signed(who),
None => RawOrigin::Inherent,
None => RawOrigin::None,
}
}
}
+8 -6
View File
@@ -233,15 +233,17 @@ pub enum RawOrigin<AccountId> {
Root,
/// It is signed by some public key and we provide the `AccountId`.
Signed(AccountId),
/// It is signed by nobody but included and agreed upon by the validators anyway: it's "inherently" true.
Inherent,
/// It is signed by nobody, can be either:
/// * included and agreed upon by the validators anyway,
/// * or unsigned transaction validated by a module.
None,
}
impl<AccountId> From<Option<AccountId>> for RawOrigin<AccountId> {
fn from(s: Option<AccountId>) -> RawOrigin<AccountId> {
match s {
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.
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>>>
{
match o.into() {
Some(RawOrigin::Inherent) => Ok(()),
_ => Err("bad origin: expected to be an inherent origin"),
Some(RawOrigin::None) => Ok(()),
_ => 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::for_each_tuple;
use runtime_primitives::traits::{As, SimpleArithmetic, Zero};
use system::ensure_inherent;
use system::ensure_none;
use rstd::{result, ops::{Mul, Div}, cmp};
use inherents::{RuntimeString, InherentIdentifier, ProvideInherent, IsFatalError, InherentData};
@@ -221,7 +221,7 @@ decl_module! {
///
/// The dispatch origin for this call must be `Inherent`.
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::now().is_zero() || now >= Self::now() + <MinimumPeriod<T>>::get(),
@@ -369,7 +369,7 @@ mod tests {
with_externalities(&mut TestExternalities::new(t), || {
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);
});
}
@@ -384,8 +384,8 @@ mod tests {
with_externalities(&mut TestExternalities::new(t), || {
Timestamp::set_timestamp(42);
assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT));
let _ = Timestamp::dispatch(Call::set(70), Origin::INHERENT);
assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE));
let _ = Timestamp::dispatch(Call::set(70), Origin::NONE);
});
}
@@ -399,7 +399,7 @@ mod tests {
with_externalities(&mut TestExternalities::new(t), || {
Timestamp::set_timestamp(42);
let _ = Timestamp::dispatch(Call::set(46), Origin::INHERENT);
let _ = Timestamp::dispatch(Call::set(46), Origin::NONE);
});
}
}