mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 19:07:57 +00:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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! {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
//!
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 )*
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user