mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 03:01:07 +00:00
New extrinsic dispatch model (#678)
* System and Balances switched to new call model * Fixed up executive for new pardigm * Consensus works with Origin * Timestamp ported over * Session now compatible * Ported staking * Treasury compatible * Expunge MaybeEmpty * Make democracy work * Council compatible * Remove some aux-dispatch stuff and fix balances * Rename aux -> origin, fix contracts * Fix test * Fix example * Update macro to remove superfluous cruft * Make work with new macro * Tests * Fix some tests * Tests * Fix grumbles * Final grumble
This commit is contained in:
Generated
-4
@@ -2769,15 +2769,11 @@ dependencies = [
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-consensus 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-session 0.1.0",
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -52,11 +52,11 @@ mod tests {
|
||||
use primitives::{twox_128, KeccakHasher};
|
||||
use demo_primitives::{Hash, BlockNumber, AccountId};
|
||||
use runtime_primitives::traits::Header as HeaderT;
|
||||
use runtime_primitives::{ApplyOutcome, ApplyError, ApplyResult, MaybeUnsigned};
|
||||
use runtime_primitives::{ApplyOutcome, ApplyError, ApplyResult};
|
||||
use {balances, staking, session, system, consensus};
|
||||
use system::{EventRecord, Phase};
|
||||
use demo_runtime::{Header, Block, UncheckedExtrinsic, Extrinsic, Call, Runtime, Balances,
|
||||
BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, BareExtrinsic, System, Event};
|
||||
use demo_runtime::{Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances,
|
||||
BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System, Event};
|
||||
use ed25519::{Public, Pair};
|
||||
|
||||
const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
|
||||
@@ -77,20 +77,32 @@ mod tests {
|
||||
AccountId::from(Keyring::Bob.to_raw_public())
|
||||
}
|
||||
|
||||
fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
||||
match xt.signed {
|
||||
Some(signed) => {
|
||||
let payload = (xt.index, xt.function);
|
||||
let pair = Pair::from(Keyring::from_public(Public::from_raw(signed.clone().into())).unwrap());
|
||||
let signature = pair.sign(&payload.encode()).into();
|
||||
UncheckedExtrinsic {
|
||||
signature: Some((balances::address::Address::Id(signed), signature)),
|
||||
index: payload.0,
|
||||
function: payload.1,
|
||||
}
|
||||
}
|
||||
None => UncheckedExtrinsic {
|
||||
signature: None,
|
||||
index: xt.index,
|
||||
function: xt.function,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn xt() -> UncheckedExtrinsic {
|
||||
let extrinsic = BareExtrinsic {
|
||||
signed: alice(),
|
||||
sign(CheckedExtrinsic {
|
||||
signed: Some(alice()),
|
||||
index: 0,
|
||||
function: Call::Balances(balances::Call::transfer::<Runtime>(bob().into(), 69)),
|
||||
};
|
||||
let signature = MaybeUnsigned(Keyring::from_raw_public(extrinsic.signed.0.clone()).unwrap()
|
||||
.sign(&extrinsic.encode()).into());
|
||||
let extrinsic = Extrinsic {
|
||||
signed: extrinsic.signed.into(),
|
||||
index: extrinsic.index,
|
||||
function: extrinsic.function,
|
||||
};
|
||||
UncheckedExtrinsic::new(extrinsic, signature)
|
||||
})
|
||||
}
|
||||
|
||||
fn from_block_number(n: u64) -> Header {
|
||||
@@ -225,19 +237,10 @@ mod tests {
|
||||
}.build_storage().unwrap().into()
|
||||
}
|
||||
|
||||
fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, extrinsics: Vec<BareExtrinsic>) -> (Vec<u8>, Hash) {
|
||||
fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, extrinsics: Vec<CheckedExtrinsic>) -> (Vec<u8>, Hash) {
|
||||
use triehash::ordered_trie_root;
|
||||
|
||||
let extrinsics = extrinsics.into_iter().map(|extrinsic| {
|
||||
let signature = MaybeUnsigned(Pair::from(Keyring::from_public(Public::from_raw(extrinsic.signed.0.clone())).unwrap())
|
||||
.sign(&extrinsic.encode()).into());
|
||||
let extrinsic = Extrinsic {
|
||||
signed: extrinsic.signed.into(),
|
||||
index: extrinsic.index,
|
||||
function: extrinsic.function,
|
||||
};
|
||||
UncheckedExtrinsic::new(extrinsic, signature)
|
||||
}).collect::<Vec<_>>();
|
||||
let extrinsics = extrinsics.into_iter().map(sign).collect::<Vec<_>>();
|
||||
|
||||
let extrinsics_root = ordered_trie_root::<KeccakHasher, _, _>(extrinsics.iter().map(Encode::encode)).0.into();
|
||||
|
||||
@@ -261,8 +264,8 @@ mod tests {
|
||||
// hex!("3437bf4b182ab17bb322af5c67e55f6be487a77084ad2b4e27ddac7242e4ad21").into(),
|
||||
// Keccak
|
||||
hex!("508a68a0918f614b86b2ccfd0975754f6d2abe1026a34e42d6d8d5abdf4db010").into(),
|
||||
vec![BareExtrinsic {
|
||||
signed: alice(),
|
||||
vec![CheckedExtrinsic {
|
||||
signed: Some(alice()),
|
||||
index: 0,
|
||||
function: Call::Balances(balances::Call::transfer(bob().into(), 69)),
|
||||
}]
|
||||
@@ -276,15 +279,15 @@ mod tests {
|
||||
// Blake
|
||||
// hex!("741fcb660e6fa9f625fbcd993b49f6c1cc4040f5e0cc8727afdedf11fd3c464b").into(),
|
||||
// Keccak
|
||||
hex!("171f1b2c01c9c616e40ee2d842a699286b50a5a74874b56d826094dadedffb27").into(),
|
||||
hex!("a72ec570c7642d9ad06ef0e5dd37be65fb04b71e0ab52b3927d760ed6c777a1f").into(),
|
||||
vec![
|
||||
BareExtrinsic {
|
||||
signed: bob(),
|
||||
CheckedExtrinsic {
|
||||
signed: Some(bob()),
|
||||
index: 0,
|
||||
function: Call::Balances(balances::Call::transfer(alice().into(), 5)),
|
||||
},
|
||||
BareExtrinsic {
|
||||
signed: alice(),
|
||||
CheckedExtrinsic {
|
||||
signed: Some(alice()),
|
||||
index: 1,
|
||||
function: Call::Balances(balances::Call::transfer(bob().into(), 15)),
|
||||
}
|
||||
@@ -300,10 +303,10 @@ mod tests {
|
||||
// hex!("2c7231a9c210a7aa4bea169d944bc4aaacd517862b244b8021236ffa7f697991").into(),
|
||||
// Keccak
|
||||
hex!("e45221804da3a3609454d4e09debe6364cc6af63c2ff067d802d1af62fea32ae").into(),
|
||||
vec![BareExtrinsic {
|
||||
signed: alice(),
|
||||
vec![CheckedExtrinsic {
|
||||
signed: Some(alice()),
|
||||
index: 0,
|
||||
function: Call::Consensus(consensus::Call::remark(vec![0; 60000])),
|
||||
function: Call::Consensus(consensus::Call::remark(vec![0; 60000000])),
|
||||
}]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,4 +53,4 @@ pub type Index = u64;
|
||||
pub type Hash = primitives::H256;
|
||||
|
||||
/// Alias to 512-bit hash when used in the context of a signature on the relay chain.
|
||||
pub type Signature = runtime_primitives::MaybeUnsigned<runtime_primitives::Ed25519Signature>;
|
||||
pub type Signature = runtime_primitives::Ed25519Signature;
|
||||
|
||||
@@ -64,7 +64,7 @@ pub use runtime_primitives::BuildStorage;
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
/// Runtime runtime type used to parameterize the various modules.
|
||||
/// Runtime type used to collate and parameterize the various modules.
|
||||
pub struct Runtime;
|
||||
|
||||
/// Runtime version.
|
||||
@@ -76,15 +76,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
impl_version: 0,
|
||||
};
|
||||
|
||||
/// Version module for this concrete runtime.
|
||||
pub type Version = version::Module<Runtime>;
|
||||
|
||||
impl version::Trait for Runtime {
|
||||
const VERSION: RuntimeVersion = VERSION;
|
||||
}
|
||||
|
||||
impl system::Trait for Runtime {
|
||||
type PublicAux = Self::AccountId;
|
||||
type Origin = Origin;
|
||||
type Index = Index;
|
||||
type BlockNumber = BlockNumber;
|
||||
type Hash = Hash;
|
||||
@@ -154,7 +147,7 @@ impl staking::Trait for Runtime {
|
||||
pub type Staking = staking::Module<Runtime>;
|
||||
|
||||
impl democracy::Trait for Runtime {
|
||||
type Proposal = PrivCall;
|
||||
type Proposal = Call;
|
||||
}
|
||||
|
||||
/// Democracy module for this concrete runtime.
|
||||
@@ -179,20 +172,13 @@ impl_outer_log! {
|
||||
}
|
||||
}
|
||||
|
||||
impl DigestItem for Log {
|
||||
type AuthoritiesChange = consensus::AuthoritiesChange<SessionKey>;
|
||||
|
||||
fn as_authorities_change(&self) -> Option<&Self::AuthoritiesChange> {
|
||||
match *self {
|
||||
Log::consensus(ref item) => item.as_authorities_change(),
|
||||
}
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Runtime {
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_dispatch! {
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum Call where aux: <Runtime as system::Trait>::PublicAux {
|
||||
pub enum Call where origin: Origin {
|
||||
Consensus,
|
||||
Balances,
|
||||
Session,
|
||||
@@ -202,38 +188,8 @@ impl_outer_dispatch! {
|
||||
Council,
|
||||
CouncilVoting,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
Consensus,
|
||||
Balances,
|
||||
Session,
|
||||
Staking,
|
||||
Democracy,
|
||||
Council,
|
||||
CouncilVoting,
|
||||
}
|
||||
}
|
||||
|
||||
/// The address format for describing accounts.
|
||||
pub type Address = balances::Address<Runtime>;
|
||||
/// Block header type as expected by this runtime.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256, Log>;
|
||||
/// Block type as expected by this runtime.
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
/// BlockId type as expected by this runtime.
|
||||
pub type BlockId = generic::BlockId<Block>;
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Index, Call, Signature>;
|
||||
/// Extrinsic type as expected by this runtime. This is not the type that is signed.
|
||||
pub type Extrinsic = generic::Extrinsic<Address, Index, Call>;
|
||||
/// Extrinsic type that is signed.
|
||||
pub type BareExtrinsic = generic::Extrinsic<AccountId, Index, Call>;
|
||||
/// Executive: handles dispatch to the various modules.
|
||||
pub type Executive = executive::Executive<Runtime, Block, Balances, Balances,
|
||||
(((((), Council), Democracy), Staking), Session)>;
|
||||
|
||||
impl_outer_config! {
|
||||
pub struct GenesisConfig for Runtime {
|
||||
ConsensusConfig => consensus,
|
||||
@@ -247,9 +203,35 @@ impl_outer_config! {
|
||||
}
|
||||
}
|
||||
|
||||
impl DigestItem for Log {
|
||||
type AuthoritiesChange = consensus::AuthoritiesChange<SessionKey>;
|
||||
|
||||
fn as_authorities_change(&self) -> Option<&Self::AuthoritiesChange> {
|
||||
match *self {
|
||||
Log::consensus(ref item) => item.as_authorities_change(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The address format for describing accounts.
|
||||
pub type Address = balances::Address<Runtime>;
|
||||
/// Block header type as expected by this runtime.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256, Log>;
|
||||
/// Block type as expected by this runtime.
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
/// BlockId type as expected by this runtime.
|
||||
pub type BlockId = generic::BlockId<Block>;
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Index, Call, Signature>;
|
||||
/// 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, Balances, Balances,
|
||||
(((((), Council), Democracy), Staking), Session)>;
|
||||
|
||||
pub mod api {
|
||||
impl_stubs!(
|
||||
version => |()| super::Version::version(),
|
||||
version => |()| super::VERSION,
|
||||
authorities => |()| super::Consensus::authorities(),
|
||||
events => |()| super::System::events(),
|
||||
initialise_block => |header| super::Executive::initialise_block(&header),
|
||||
|
||||
Generated
-1
@@ -688,7 +688,6 @@ dependencies = [
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -27,29 +27,11 @@ pub use codec::{Codec, Decode, Encode, Input, Output};
|
||||
pub type Result = result::Result<(), &'static str>;
|
||||
|
||||
pub trait Dispatchable {
|
||||
type Origin;
|
||||
type Trait;
|
||||
fn dispatch(self) -> Result;
|
||||
fn dispatch(self, origin: Self::Origin) -> Result;
|
||||
}
|
||||
|
||||
pub trait AuxDispatchable {
|
||||
type Aux;
|
||||
type Trait;
|
||||
fn dispatch(self, aux: &Self::Aux) -> Result;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait AuxCallable {
|
||||
type Call: AuxDispatchable + Codec + ::serde::Serialize + Clone + PartialEq + Eq;
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait AuxCallable {
|
||||
type Call: AuxDispatchable + Codec + Clone + PartialEq + Eq;
|
||||
}
|
||||
|
||||
// dirty hack to work around serde_derive issue
|
||||
// https://github.com/rust-lang/rust/issues/51331
|
||||
pub type AuxCallableCallFor<A> = <A as AuxCallable>::Call;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Callable {
|
||||
type Call: Dispatchable + Codec + ::serde::Serialize + Clone + PartialEq + Eq;
|
||||
@@ -59,9 +41,9 @@ pub trait Callable {
|
||||
type Call: Dispatchable + Codec + Clone + PartialEq + Eq;
|
||||
}
|
||||
|
||||
// dirty hack to work around serde_derive issue.
|
||||
// dirty hack to work around serde_derive issue
|
||||
// https://github.com/rust-lang/rust/issues/51331
|
||||
pub type CallableCallFor<C> = <C as Callable>::Call;
|
||||
pub type CallableCallFor<A> = <A as Callable>::Call;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Parameter: Codec + serde::Serialize + Clone + Eq + fmt::Debug {}
|
||||
@@ -81,8 +63,15 @@ impl<T> Parameter for T where T: Codec + Clone + Eq {}
|
||||
macro_rules! decl_module {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$($rest:tt)*
|
||||
pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>
|
||||
for enum $call_type:ident where origin: $origin_type:ty {$(
|
||||
$(#[doc = $doc_attr:tt])*
|
||||
fn $fn_name:ident(origin
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
) -> $result:ty;
|
||||
)*}
|
||||
) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
@@ -91,244 +80,20 @@ macro_rules! decl_module {
|
||||
// serde-derive for when we attempt to derive `Deserialize` on these types,
|
||||
// in a situation where we've imported `substrate_runtime_support` as another name.
|
||||
#[cfg(feature = "std")]
|
||||
$(#[$attr])*
|
||||
pub struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>);
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg(not(feature = "std"))]
|
||||
$(#[$attr])*
|
||||
pub struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>);
|
||||
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
|
||||
__impl_json_metadata! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg(feature = "std")]
|
||||
$(#[$attr])*
|
||||
struct $mod_type<$trait_instance: $trait_name>(::std::marker::PhantomData<$trait_instance>);
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg(not(feature = "std"))]
|
||||
$(#[$attr])*
|
||||
struct $mod_type<$trait_instance: $trait_name>(::core::marker::PhantomData<$trait_instance>);
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
|
||||
__impl_json_metadata! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement several dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
#[macro_export]
|
||||
macro_rules! decl_dispatch {
|
||||
// WITHOUT AUX
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident {
|
||||
$(
|
||||
$(#[$fn_attr:meta])*
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
) -> $result:ty;
|
||||
)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
__decl_dispatch_module_without_aux! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$(#[$attr])*
|
||||
pub enum $call_type;
|
||||
$(
|
||||
fn $fn_name( $( $param_name: $param ),* ) -> $result;
|
||||
)*
|
||||
}
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
// WITH AUX
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident where aux: $aux_type:ty {
|
||||
$(
|
||||
$(#[$fn_attr:meta])*
|
||||
fn $fn_name:ident(aux
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
) -> $result:ty;
|
||||
)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
__decl_dispatch_module_with_aux! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$(#[$attr])*
|
||||
pub enum $call_type where aux: $aux_type;
|
||||
$(
|
||||
fn $fn_name(aux $(, $param_name: $param )*) -> $result;
|
||||
)*
|
||||
}
|
||||
decl_dispatch! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
// BASE CASE
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
) => {
|
||||
impl<$trait_instance: $trait_name> $mod_type<$trait_instance> {
|
||||
pub fn aux_dispatch<D: $crate::dispatch::AuxDispatchable<Trait = $trait_instance>>(d: D, aux: &D::Aux) -> $crate::dispatch::Result {
|
||||
d.dispatch(aux)
|
||||
}
|
||||
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance>>(d: D) -> $crate::dispatch::Result {
|
||||
d.dispatch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
/// Implement a single dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
macro_rules! __decl_dispatch_module_without_aux {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident;
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
)
|
||||
-> $result:ty;
|
||||
)*
|
||||
) => {
|
||||
__decl_dispatch_module_common! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$(#[$attr])*
|
||||
pub enum $call_type;
|
||||
$( fn $fn_name( $( $param_name : $param ),* ) -> $result; )*
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Dispatchable
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
type Trait = $trait_instance;
|
||||
fn dispatch(self) -> $crate::dispatch::Result {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$fn_name( $( $param_name ),* ) =>
|
||||
<$mod_type<$trait_instance>>::$fn_name( $( $param_name ),* ),
|
||||
)*
|
||||
$call_type::__PhantomItem(_) => { panic!("__PhantomItem should never be used.") },
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Callable
|
||||
for $mod_type<$trait_instance>
|
||||
{
|
||||
type Call = $call_type<$trait_instance>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
/// Implement a single dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
macro_rules! __decl_dispatch_module_with_aux {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident where aux: $aux_type:ty;
|
||||
$(
|
||||
fn $fn_name:ident(aux
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
)
|
||||
-> $result:ty;
|
||||
)*
|
||||
) => {
|
||||
__decl_dispatch_module_common! {
|
||||
impl for $mod_type<$trait_instance: $trait_name>;
|
||||
$(#[$attr])*
|
||||
pub enum $call_type;
|
||||
$( fn $fn_name( $( $param_name : $param ),* ) -> $result; )*
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::AuxDispatchable
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
type Trait = $trait_instance;
|
||||
type Aux = $aux_type;
|
||||
fn dispatch(self, aux: &Self::Aux) -> $crate::dispatch::Result {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$fn_name( $( $param_name ),* ) =>
|
||||
<$mod_type<$trait_instance>>::$fn_name( aux $(, $param_name )* ),
|
||||
)*
|
||||
$call_type::__PhantomItem(_) => { panic!("__PhantomItem should never be used.") },
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::AuxCallable
|
||||
for $mod_type<$trait_instance>
|
||||
{
|
||||
type Call = $call_type<$trait_instance>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement a single dispatch modules to create a pairing of a dispatch trait and enum.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __decl_dispatch_module_common {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident;
|
||||
$(
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
)
|
||||
-> $result:ty;
|
||||
)*
|
||||
) => {
|
||||
#[cfg(feature = "std")]
|
||||
$(#[$attr])*
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum $call_type<$trait_instance: $trait_name> {
|
||||
__PhantomItem(::std::marker::PhantomData<$trait_instance>),
|
||||
__OtherPhantomItem(::std::marker::PhantomData<$trait_instance>),
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* ),
|
||||
@@ -337,8 +102,10 @@ macro_rules! __decl_dispatch_module_common {
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
$(#[$attr])*
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum $call_type<$trait_instance: $trait_name> {
|
||||
__PhantomItem(::core::marker::PhantomData<$trait_instance>),
|
||||
__OtherPhantomItem(::core::marker::PhantomData<$trait_instance>),
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$fn_name ( $( $param ),* ),
|
||||
@@ -356,30 +123,30 @@ macro_rules! __decl_dispatch_module_common {
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) =>
|
||||
$call_type::$fn_name( $( $param_name.clone() ),* )
|
||||
,)*
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::PartialEq
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) => {
|
||||
let self_params = ( $( $param_name, )* );
|
||||
if let $call_type::$fn_name( $( ref $param_name ),* ) = *other {
|
||||
if let $call_type::$fn_name( $( ref $param_name ),* ) = *_other {
|
||||
self_params == ( $( $param_name, )* )
|
||||
} else {
|
||||
if let $call_type::__PhantomItem(_) = *other {
|
||||
unreachable!()
|
||||
} else {
|
||||
false
|
||||
match *_other {
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
$call_type::__OtherPhantomItem(_) => unreachable!(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,33 +158,65 @@ macro_rules! __decl_dispatch_module_common {
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::fmt::Debug
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
fn fmt(&self, f: &mut $crate::dispatch::fmt::Formatter) -> $crate::dispatch::result::Result<(), $crate::dispatch::fmt::Error> {
|
||||
fn fmt(&self, _f: &mut $crate::dispatch::fmt::Formatter) -> $crate::dispatch::result::Result<(), $crate::dispatch::fmt::Error> {
|
||||
match *self {
|
||||
$(
|
||||
$call_type::$fn_name( $( ref $param_name ),* ) =>
|
||||
write!(f, "{}{:?}",
|
||||
write!(_f, "{}{:?}",
|
||||
stringify!($fn_name),
|
||||
( $( $param_name.clone(), )* )
|
||||
)
|
||||
,)*
|
||||
$call_type::__PhantomItem(_) => unreachable!(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Decode for $call_type<$trait_instance> {
|
||||
fn decode<I: $crate::dispatch::Input>(input: &mut I) -> Option<Self> {
|
||||
let input_id = input.read_byte()?;
|
||||
__impl_decode!(input; input_id; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*)
|
||||
let _input_id = input.read_byte()?;
|
||||
__impl_decode!(input; _input_id; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Encode for $call_type<$trait_instance> {
|
||||
fn encode_to<W: $crate::dispatch::Output>(&self, dest: &mut W) {
|
||||
__impl_encode!(dest; *self; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*);
|
||||
fn encode_to<W: $crate::dispatch::Output>(&self, _dest: &mut W) {
|
||||
__impl_encode!(_dest; *self; 0; $call_type; $( fn $fn_name( $( $param_name ),* ); )*);
|
||||
if let $call_type::__PhantomItem(_) = *self { unreachable!() }
|
||||
if let $call_type::__OtherPhantomItem(_) = *self { unreachable!() }
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Dispatchable
|
||||
for $call_type<$trait_instance>
|
||||
{
|
||||
type Trait = $trait_instance;
|
||||
type Origin = $origin_type;
|
||||
fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::Result {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$fn_name( $( $param_name ),* ) =>
|
||||
<$mod_type<$trait_instance>>::$fn_name( _origin $(, $param_name )* ),
|
||||
)*
|
||||
_ => { panic!("__PhantomItem should never be used.") },
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$trait_instance: $trait_name> $crate::dispatch::Callable
|
||||
for $mod_type<$trait_instance>
|
||||
{
|
||||
type Call = $call_type<$trait_instance>;
|
||||
}
|
||||
|
||||
impl<$trait_instance: $trait_name> $mod_type<$trait_instance> {
|
||||
pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance>>(d: D, origin: D::Origin) -> $crate::dispatch::Result {
|
||||
d.dispatch(origin)
|
||||
}
|
||||
}
|
||||
|
||||
__impl_json_metadata! {
|
||||
$mod_type $trait_instance $trait_name $call_type $origin_type
|
||||
{$( $(#[doc = $doc_attr])* fn $fn_name(origin $(, $param_name : $param )*) -> $result; )*}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,10 +291,7 @@ macro_rules! __impl_encode {
|
||||
}
|
||||
|
||||
pub trait IsSubType<T: Callable> {
|
||||
fn is_sub_type(&self) -> Option<&<T as Callable>::Call>;
|
||||
}
|
||||
pub trait IsAuxSubType<T: AuxCallable> {
|
||||
fn is_aux_sub_type(&self) -> Option<&<T as AuxCallable>::Call>;
|
||||
fn is_aux_sub_type(&self) -> Option<&<T as Callable>::Call>;
|
||||
}
|
||||
|
||||
/// Implement a meta-dispatch module to dispatch to other dispatchers.
|
||||
@@ -504,47 +300,7 @@ macro_rules! impl_outer_dispatch {
|
||||
() => ();
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident where aux: $aux:ty {
|
||||
$(
|
||||
$camelcase:ident,
|
||||
)*
|
||||
}
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
pub enum $call_type {
|
||||
$(
|
||||
$camelcase ( $crate::dispatch::AuxCallableCallFor<$camelcase> )
|
||||
,)*
|
||||
}
|
||||
__impl_outer_dispatch_common! { $call_type, $($camelcase,)* }
|
||||
impl $crate::dispatch::AuxDispatchable for $call_type {
|
||||
type Aux = $aux;
|
||||
type Trait = $call_type;
|
||||
fn dispatch(self, aux: &$aux) -> $crate::dispatch::Result {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$camelcase(call) => call.dispatch(&aux),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl $crate::dispatch::IsAuxSubType<$camelcase> for $call_type {
|
||||
fn is_aux_sub_type(&self) -> Option<&<$camelcase as $crate::dispatch::AuxCallable>::Call> {
|
||||
if let $call_type::$camelcase ( ref r ) = *self {
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
impl_outer_dispatch!{ $($rest)* }
|
||||
};
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident {
|
||||
pub enum $call_type:ident where origin: $origin:ty {
|
||||
$(
|
||||
$camelcase:ident,
|
||||
)*
|
||||
@@ -552,6 +308,8 @@ macro_rules! impl_outer_dispatch {
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum $call_type {
|
||||
$(
|
||||
$camelcase ( $crate::dispatch::CallableCallFor<$camelcase> )
|
||||
@@ -559,18 +317,19 @@ macro_rules! impl_outer_dispatch {
|
||||
}
|
||||
__impl_outer_dispatch_common! { $call_type, $($camelcase,)* }
|
||||
impl $crate::dispatch::Dispatchable for $call_type {
|
||||
type Origin = $origin;
|
||||
type Trait = $call_type;
|
||||
fn dispatch(self) -> $crate::dispatch::Result {
|
||||
fn dispatch(self, origin: $origin) -> $crate::dispatch::Result {
|
||||
match self {
|
||||
$(
|
||||
$call_type::$camelcase(call) => call.dispatch(),
|
||||
$call_type::$camelcase(call) => call.dispatch(origin),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl $crate::dispatch::IsSubType<$camelcase> for $call_type {
|
||||
fn is_sub_type(&self) -> Option<&<$camelcase as $crate::dispatch::Callable>::Call> {
|
||||
fn is_aux_sub_type(&self) -> Option<&<$camelcase as $crate::dispatch::Callable>::Call> {
|
||||
if let $call_type::$camelcase ( ref r ) = *self {
|
||||
Some(r)
|
||||
} else {
|
||||
@@ -611,13 +370,13 @@ macro_rules! __impl_outer_dispatch_common {
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_json_metadata {
|
||||
(
|
||||
impl for $mod_type:ident<$trait_instance:ident: $trait_name:ident>;
|
||||
$mod_type:ident $trait_instance:ident $trait_name:ident
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
impl<$trait_instance: $trait_name> $mod_type<$trait_instance> {
|
||||
pub fn json_metadata() -> &'static str {
|
||||
concat!(r#"{ "name": ""#, stringify!($mod_type), r#"", "calls": ["#,
|
||||
__calls_to_json!(""; $($rest)*), " ] }")
|
||||
concat!(r#"{ "name": ""#, stringify!($mod_type), r#"", "call": [ "#,
|
||||
__call_to_json!($($rest)*), " ] }")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -626,66 +385,31 @@ macro_rules! __impl_json_metadata {
|
||||
/// Convert the list of calls into their JSON representation, joined by ",".
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __calls_to_json {
|
||||
// WITHOUT AUX
|
||||
(
|
||||
$prefix_str:tt;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident {
|
||||
$(
|
||||
$(#[doc = $doc_attr:tt])*
|
||||
fn $fn_name:ident(
|
||||
$(
|
||||
$param_name:ident : $param:ty
|
||||
),*
|
||||
) -> $result:ty;
|
||||
)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
concat!($prefix_str, " ",
|
||||
r#"{ "name": ""#, stringify!($call_type),
|
||||
r#"", "functions": {"#,
|
||||
__functions_to_json!(""; 0; $(
|
||||
fn $fn_name( $( $param_name: $param ),* ) -> $result;
|
||||
__function_doc_to_json!(""; $($doc_attr)*);
|
||||
)*), " } }", __calls_to_json!(","; $($rest)*)
|
||||
)
|
||||
};
|
||||
macro_rules! __call_to_json {
|
||||
// WITH AUX
|
||||
(
|
||||
$prefix_str:tt;
|
||||
$(#[$attr:meta])*
|
||||
pub enum $call_type:ident where aux: $aux_type:ty {
|
||||
$(
|
||||
$call_type:ident $origin_type:ty
|
||||
{$(
|
||||
$(#[doc = $doc_attr:tt])*
|
||||
fn $fn_name:ident(aux
|
||||
fn $fn_name:ident(origin
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
) -> $result:ty;
|
||||
)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
)*}
|
||||
) => {
|
||||
concat!($prefix_str, " ",
|
||||
concat!(
|
||||
r#"{ "name": ""#, stringify!($call_type),
|
||||
r#"", "functions": {"#,
|
||||
__functions_to_json!(""; 0; $aux_type; $(
|
||||
fn $fn_name(aux $(, $param_name: $param )* ) -> $result;
|
||||
__functions_to_json!(""; 0; $origin_type; $(
|
||||
fn $fn_name(origin $(, $param_name: $param )* ) -> $result;
|
||||
__function_doc_to_json!(""; $($doc_attr)*);
|
||||
)*), " } }", __calls_to_json!(","; $($rest)*)
|
||||
)*), " } }"
|
||||
)
|
||||
};
|
||||
// BASE CASE
|
||||
(
|
||||
$prefix_str:tt;
|
||||
) => {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a list of function into their JSON representation, joined by ",".
|
||||
/// Convert a list of functions into their JSON representation, joined by ",".
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __functions_to_json {
|
||||
@@ -713,8 +437,8 @@ macro_rules! __functions_to_json {
|
||||
(
|
||||
$prefix_str:tt;
|
||||
$fn_id:expr;
|
||||
$aux_type:ty;
|
||||
fn $fn_name:ident(aux
|
||||
$origin_type:ty;
|
||||
fn $fn_name:ident(origin
|
||||
$(
|
||||
, $param_name:ident : $param:ty
|
||||
)*
|
||||
@@ -725,19 +449,19 @@ macro_rules! __functions_to_json {
|
||||
concat!($prefix_str, " ",
|
||||
__function_to_json!(
|
||||
fn $fn_name(
|
||||
aux: $aux_type
|
||||
origin: $origin_type
|
||||
$(, $param_name : $param)*
|
||||
) -> $result;
|
||||
$fn_doc;
|
||||
$fn_id;
|
||||
), __functions_to_json!(","; $fn_id + 1; $aux_type; $($rest)*)
|
||||
), __functions_to_json!(","; $fn_id + 1; $origin_type; $($rest)*)
|
||||
)
|
||||
};
|
||||
// BASE CASE
|
||||
(
|
||||
$prefix_str:tt;
|
||||
$fn_id:expr;
|
||||
$($aux_type:ty;)*
|
||||
$($origin_type:ty;)*
|
||||
) => {
|
||||
""
|
||||
}
|
||||
@@ -799,75 +523,47 @@ mod tests {
|
||||
use serde_json;
|
||||
|
||||
pub trait Trait {
|
||||
type PublicAux;
|
||||
type Origin;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
/// Hi, this is a comment.
|
||||
fn aux_0(aux) -> Result;
|
||||
fn aux_1(aux, data: i32) -> Result;
|
||||
fn aux_2(aux, data: i32, data2: String) -> Result;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum PrivCall {
|
||||
/// Hi, this is a comment.
|
||||
/// Hi, this is a second comment.
|
||||
fn priv_0(data: String) -> Result;
|
||||
fn priv_1(data: String, data2: u32) -> Result;
|
||||
fn aux_0(origin) -> Result;
|
||||
fn aux_1(origin, data: i32) -> Result;
|
||||
fn aux_2(origin, data: i32, data2: String) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: &str = concat!(
|
||||
r#"{ "name": "Module", "calls": [ "#,
|
||||
r#"{ "name": "Module", "call": [ "#,
|
||||
r#"{ "name": "Call", "functions": { "#,
|
||||
r#""0": { "name": "aux_0", "params": [ "#,
|
||||
r#"{ "name": "aux", "type": "T::PublicAux" }"#,
|
||||
r#"{ "name": "origin", "type": "T::Origin" }"#,
|
||||
r#" ], "description": [ " Hi, this is a comment." ] }, "#,
|
||||
r#""0 + 1": { "name": "aux_1", "params": [ "#,
|
||||
r#"{ "name": "aux", "type": "T::PublicAux" }, "#,
|
||||
r#"{ "name": "origin", "type": "T::Origin" }, "#,
|
||||
r#"{ "name": "data", "type": "i32" }"#,
|
||||
r#" ], "description": [ ] }, "#,
|
||||
r#""0 + 1 + 1": { "name": "aux_2", "params": [ "#,
|
||||
r#"{ "name": "aux", "type": "T::PublicAux" }, "#,
|
||||
r#"{ "name": "origin", "type": "T::Origin" }, "#,
|
||||
r#"{ "name": "data", "type": "i32" }, "#,
|
||||
r#"{ "name": "data2", "type": "String" }"#,
|
||||
r#" ], "description": [ ] }"#,
|
||||
r#" } }, "#,
|
||||
r#"{ "name": "PrivCall", "functions": { "#,
|
||||
r#""0": { "name": "priv_0", "params": [ "#,
|
||||
r#"{ "name": "data", "type": "String" }"#,
|
||||
r#" ], "description": [ " Hi, this is a comment.", " Hi, this is a second comment." ] }, "#,
|
||||
r#""0 + 1": { "name": "priv_1", "params": [ "#,
|
||||
r#"{ "name": "data", "type": "String" }, "#,
|
||||
r#"{ "name": "data2", "type": "u32" }"#,
|
||||
r#" ], "description": [ ] }"#,
|
||||
r#" } }"#,
|
||||
r#" ] }"#,
|
||||
);
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
fn aux_0(_: &T::PublicAux) -> Result {
|
||||
fn aux_0(_: T::Origin) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn aux_1(_: &T::PublicAux, _: i32) -> Result {
|
||||
fn aux_1(_: T::Origin, _: i32) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn aux_2(_: &T::PublicAux, _: i32, _: String) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn priv_0(_: String) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn priv_1(_: String, _: u32) -> Result {
|
||||
fn aux_2(_: T::Origin, _: i32, _: String) -> Result {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@@ -875,7 +571,7 @@ mod tests {
|
||||
struct TraitImpl {}
|
||||
|
||||
impl Trait for TraitImpl {
|
||||
type PublicAux = u32;
|
||||
type Origin = u32;
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -50,7 +50,7 @@ mod event;
|
||||
|
||||
pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap};
|
||||
pub use self::hashable::Hashable;
|
||||
pub use self::dispatch::{Parameter, Dispatchable, Callable, AuxDispatchable, AuxCallable, IsSubType, IsAuxSubType};
|
||||
pub use self::dispatch::{Parameter, Dispatchable, Callable, IsSubType};
|
||||
pub use runtime_io::print;
|
||||
|
||||
|
||||
@@ -99,6 +99,82 @@ macro_rules! assert_ok {
|
||||
}
|
||||
}
|
||||
|
||||
/// The void type - it cannot exist.
|
||||
// Oh rust, you crack me up...
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub enum Void {}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_origin {
|
||||
($(#[$attr:meta])* pub enum $name:ident for $trait:ident where system = $system:ident { $( $module:ident ),* }) => {
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum $name {
|
||||
system($system::Origin<$trait>),
|
||||
$(
|
||||
$module($module::Origin<$trait>),
|
||||
)*
|
||||
#[allow(dead_code)]
|
||||
Void($crate::Void)
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
impl $name {
|
||||
pub const INHERENT: Self = $name::system($system::RawOrigin::Inherent);
|
||||
pub const ROOT: Self = $name::system($system::RawOrigin::Root);
|
||||
pub fn signed(by: <$trait as $system::Trait>::AccountId) -> Self {
|
||||
$name::system($system::RawOrigin::Signed(by))
|
||||
}
|
||||
}
|
||||
impl From<$system::Origin<$trait>> for $name {
|
||||
fn from(x: $system::Origin<$trait>) -> Self {
|
||||
$name::system(x)
|
||||
}
|
||||
}
|
||||
impl Into<Option<$system::Origin<$trait>>> for $name {
|
||||
fn into(self) -> Option<$system::Origin<$trait>> {
|
||||
if let $name::system(l) = self {
|
||||
Some(l)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Option<<$trait as $system::Trait>::AccountId>> for $name {
|
||||
fn from(x: Option<<$trait as $system::Trait>::AccountId>) -> Self {
|
||||
<$system::Origin<$trait>>::from(x).into()
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl From<$module::Origin<$trait>> for $name {
|
||||
fn from(x: $module::Origin<$trait>) -> Self {
|
||||
$name::$module(x)
|
||||
}
|
||||
}
|
||||
impl<T: Trait> Into<Option<$module::Origin<T>>> for $name<T> {
|
||||
fn into(self) -> Option<$module::Origin<T>> {
|
||||
if let $name::$module(l) = self {
|
||||
Some(l)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
($(#[$attr:meta])* pub enum $name:ident for $trait:ident { $( $module:ident ),* }) => {
|
||||
impl_outer_origin! {
|
||||
$(#[$attr])*
|
||||
pub enum $name for $trait where system = system {
|
||||
$( $module ),*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_log {
|
||||
|
||||
|
||||
@@ -1456,11 +1456,11 @@ mod tests {
|
||||
}
|
||||
|
||||
pub trait Trait {
|
||||
type PublicAux;
|
||||
type Origin;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
@@ -1500,7 +1500,7 @@ mod tests {
|
||||
struct TraitImpl {}
|
||||
|
||||
impl Trait for TraitImpl {
|
||||
type PublicAux = u32;
|
||||
type Origin = u32;
|
||||
}
|
||||
|
||||
const EXPECTED_METADATA: &str = concat!(
|
||||
|
||||
@@ -45,9 +45,10 @@ use rstd::{cmp, result};
|
||||
use codec::{Encode, Decode, Codec, Input, Output};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use runtime_support::dispatch::Result;
|
||||
use primitives::traits::{Zero, One, RefInto, SimpleArithmetic, OnFinalise, MakePayment,
|
||||
As, AuxLookup, Member, CheckedAdd, CheckedSub};
|
||||
use primitives::traits::{Zero, One, SimpleArithmetic, OnFinalise, MakePayment,
|
||||
As, Lookup, Member, CheckedAdd, CheckedSub};
|
||||
use address::Address as RawAddress;
|
||||
use system::{ensure_signed, ensure_root};
|
||||
|
||||
mod mock;
|
||||
|
||||
@@ -132,16 +133,9 @@ pub trait Trait: system::Trait {
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn transfer(aux, dest: RawAddress<T::AccountId, T::AccountIndex>, value: T::Balance) -> Result;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_balance(who: RawAddress<T::AccountId, T::AccountIndex>, free: T::Balance, reserved: T::Balance) -> Result;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn transfer(origin, dest: RawAddress<T::AccountId, T::AccountIndex>, value: T::Balance) -> Result;
|
||||
fn set_balance(origin, who: RawAddress<T::AccountId, T::AccountIndex>, free: T::Balance, reserved: T::Balance) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +227,11 @@ pub enum UpdateBalanceOutcome {
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
|
||||
/// Deposit one of this module's events.
|
||||
fn deposit_event(event: Event<T>) {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
// PUBLIC IMMUTABLES
|
||||
|
||||
/// The combined balance of `who`.
|
||||
@@ -283,11 +282,11 @@ impl<T: Trait> Module<T> {
|
||||
// PUBLIC DISPATCH
|
||||
|
||||
/// Transfer some liquid free balance to another staker.
|
||||
pub fn transfer(aux: &T::PublicAux, dest: Address<T>, value: T::Balance) -> Result {
|
||||
let dest = Self::lookup(dest)?;
|
||||
pub fn transfer(origin: T::Origin, dest: Address<T>, value: T::Balance) -> Result {
|
||||
let transactor = ensure_signed(origin)?;
|
||||
|
||||
let transactor = aux.ref_into();
|
||||
let from_balance = Self::free_balance(transactor);
|
||||
let dest = Self::lookup(dest)?;
|
||||
let from_balance = Self::free_balance(&transactor);
|
||||
let would_create = from_balance.is_zero();
|
||||
let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() };
|
||||
let liability = value + fee;
|
||||
@@ -299,7 +298,7 @@ impl<T: Trait> Module<T> {
|
||||
if would_create && value < Self::existential_deposit() {
|
||||
return Err("value too low to create account");
|
||||
}
|
||||
T::EnsureAccountLiquid::ensure_account_liquid(transactor)?;
|
||||
T::EnsureAccountLiquid::ensure_account_liquid(&transactor)?;
|
||||
|
||||
let to_balance = Self::free_balance(&dest);
|
||||
// NOTE: total stake being stored in the same type means that this could never overflow
|
||||
@@ -309,8 +308,8 @@ impl<T: Trait> Module<T> {
|
||||
None => return Err("destination balance too high to receive value"),
|
||||
};
|
||||
|
||||
if transactor != &dest {
|
||||
Self::set_free_balance(transactor, new_from_balance);
|
||||
if transactor != dest {
|
||||
Self::set_free_balance(&transactor, new_from_balance);
|
||||
Self::decrease_total_stake_by(fee);
|
||||
Self::set_free_balance_creating(&dest, new_to_balance);
|
||||
}
|
||||
@@ -318,15 +317,9 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// PRIV DISPATCH
|
||||
|
||||
/// Deposit one of this module's events.
|
||||
fn deposit_event(event: Event<T>) {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
/// Set the balances of a given account.
|
||||
fn set_balance(who: Address<T>, free: T::Balance, reserved: T::Balance) -> Result {
|
||||
fn set_balance(origin: T::Origin, who: Address<T>, free: T::Balance, reserved: T::Balance) -> Result {
|
||||
ensure_root(origin)?;
|
||||
let who = Self::lookup(who)?;
|
||||
Self::set_free_balance(&who, free);
|
||||
Self::set_reserved_balance(&who, reserved);
|
||||
@@ -647,7 +640,7 @@ impl<T: Trait> OnFinalise<T::BlockNumber> for Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> AuxLookup for Module<T> {
|
||||
impl<T: Trait> Lookup for Module<T> {
|
||||
type Source = address::Address<T::AccountId, T::AccountIndex>;
|
||||
type Target = T::AccountId;
|
||||
fn lookup(a: Self::Source) -> result::Result<Self::Target, &'static str> {
|
||||
|
||||
@@ -24,11 +24,15 @@ use substrate_primitives::{H256, KeccakHasher};
|
||||
use runtime_io;
|
||||
use {GenesisConfig, Module, Trait, system};
|
||||
|
||||
impl_outer_origin!{
|
||||
pub enum Origin for Runtime {}
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
pub struct Test;
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
pub struct Runtime;
|
||||
impl system::Trait for Runtime {
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -38,7 +42,7 @@ impl system::Trait for Test {
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
impl Trait for Runtime {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = ();
|
||||
@@ -47,13 +51,13 @@ impl Trait for Test {
|
||||
}
|
||||
|
||||
pub fn new_test_ext(ext_deposit: u64, monied: bool) -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
let mut t = system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
|
||||
let balance_factor = if ext_deposit > 0 {
|
||||
256
|
||||
} else {
|
||||
1
|
||||
};
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
t.extend(GenesisConfig::<Runtime>{
|
||||
balances: if monied {
|
||||
vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)]
|
||||
} else {
|
||||
@@ -69,5 +73,5 @@ pub fn new_test_ext(ext_deposit: u64, monied: bool) -> runtime_io::TestExternali
|
||||
t.into()
|
||||
}
|
||||
|
||||
pub type System = system::Module<Test>;
|
||||
pub type Balances = Module<Test>;
|
||||
pub type System = system::Module<Runtime>;
|
||||
pub type Balances = Module<Runtime>;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
use super::*;
|
||||
use runtime_io::with_externalities;
|
||||
use mock::{Balances, System, Test, new_test_ext};
|
||||
use mock::{Balances, System, Runtime, new_test_ext};
|
||||
|
||||
#[test]
|
||||
fn reward_should_work() {
|
||||
@@ -28,7 +28,7 @@ fn reward_should_work() {
|
||||
assert_eq!(Balances::total_balance(&1), 10);
|
||||
assert_ok!(Balances::reward(&1, 10));
|
||||
assert_eq!(Balances::total_balance(&1), 20);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 110);
|
||||
assert_eq!(<TotalIssuance<Runtime>>::get(), 110);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ fn indexing_lookup_should_work() {
|
||||
fn default_indexing_on_new_accounts_should_work() {
|
||||
with_externalities(&mut new_test_ext(10, true), || {
|
||||
assert_eq!(Balances::lookup_index(4), None);
|
||||
assert_ok!(Balances::transfer(&1, 5.into(), 10));
|
||||
assert_ok!(Balances::transfer(Some(1).into(), 5.into(), 10));
|
||||
assert_eq!(Balances::lookup_index(4), Some(5));
|
||||
});
|
||||
}
|
||||
@@ -59,7 +59,7 @@ fn dust_account_removal_should_work() {
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
assert_eq!(Balances::total_balance(&2), 256 * 20);
|
||||
|
||||
assert_ok!(Balances::transfer(&2, 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie
|
||||
assert_ok!(Balances::transfer(Some(2).into(), 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie
|
||||
assert_eq!(Balances::total_balance(&2), 0);
|
||||
assert_eq!(Balances::total_balance(&5), 256 * 10 + 1);
|
||||
assert_eq!(System::account_nonce(&2), 0);
|
||||
@@ -73,10 +73,10 @@ fn reclaim_indexing_on_new_accounts_should_work() {
|
||||
assert_eq!(Balances::lookup_index(4), None);
|
||||
assert_eq!(Balances::total_balance(&2), 256 * 20);
|
||||
|
||||
assert_ok!(Balances::transfer(&2, 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim)
|
||||
assert_ok!(Balances::transfer(Some(2).into(), 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim)
|
||||
assert_eq!(Balances::total_balance(&2), 0);
|
||||
|
||||
assert_ok!(Balances::transfer(&5, 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1.
|
||||
assert_ok!(Balances::transfer(Some(5).into(), 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1.
|
||||
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
|
||||
assert_eq!(Balances::lookup_index(1), Some(6));
|
||||
});
|
||||
@@ -95,7 +95,7 @@ fn reserved_balance_should_prevent_reclaim_count() {
|
||||
assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists.
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
|
||||
assert_ok!(Balances::transfer(&4, 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5.
|
||||
assert_ok!(Balances::transfer(Some(4).into(), 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5.
|
||||
assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69);
|
||||
assert_eq!(Balances::lookup_index(1), Some(2)); // but fails.
|
||||
assert_eq!(System::account_nonce(&2), 1);
|
||||
@@ -104,7 +104,7 @@ fn reserved_balance_should_prevent_reclaim_count() {
|
||||
assert_eq!(Balances::total_balance(&2), 0); // "free" account deleted."
|
||||
assert_eq!(System::account_nonce(&2), 0);
|
||||
|
||||
assert_ok!(Balances::transfer(&4, 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6.
|
||||
assert_ok!(Balances::transfer(Some(4).into(), 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6.
|
||||
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
|
||||
assert_eq!(Balances::lookup_index(1), Some(6)); // and succeeds.
|
||||
});
|
||||
@@ -128,7 +128,7 @@ fn balance_transfer_works() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
Balances::increase_total_stake_by(111);
|
||||
assert_ok!(Balances::transfer(&1, 2.into(), 69));
|
||||
assert_ok!(Balances::transfer(Some(1).into(), 2.into(), 69));
|
||||
assert_eq!(Balances::total_balance(&1), 42);
|
||||
assert_eq!(Balances::total_balance(&2), 69);
|
||||
});
|
||||
@@ -156,7 +156,7 @@ fn balance_transfer_when_reserved_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
assert_ok!(Balances::reserve(&1, 69));
|
||||
assert_noop!(Balances::transfer(&1, 2.into(), 69), "balance too low to send value");
|
||||
assert_noop!(Balances::transfer(Some(1).into(), 2.into(), 69), "balance too low to send value");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ fn slashing_balance_should_work() {
|
||||
assert!(Balances::slash(&1, 69).is_none());
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
assert_eq!(Balances::reserved_balance(&1), 42);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 44);
|
||||
assert_eq!(<TotalIssuance<Runtime>>::get(), 44);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ fn slashing_incomplete_balance_should_work() {
|
||||
assert!(Balances::slash(&1, 69).is_some());
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 2);
|
||||
assert_eq!(<TotalIssuance<Runtime>>::get(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ fn slashing_reserved_balance_should_work() {
|
||||
assert!(Balances::slash_reserved(&1, 42).is_none());
|
||||
assert_eq!(Balances::reserved_balance(&1), 69);
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 71);
|
||||
assert_eq!(<TotalIssuance<Runtime>>::get(), 71);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ fn slashing_incomplete_reserved_balance_should_work() {
|
||||
assert!(Balances::slash_reserved(&1, 69).is_some());
|
||||
assert_eq!(Balances::free_balance(&1), 69);
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 71);
|
||||
assert_eq!(<TotalIssuance<Runtime>>::get(), 71);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -283,11 +283,11 @@ fn transferring_incomplete_reserved_balance_should_work() {
|
||||
#[test]
|
||||
fn transferring_too_high_value_should_not_panic() {
|
||||
with_externalities(&mut new_test_ext(0, false), || {
|
||||
<FreeBalance<Test>>::insert(1, u64::max_value());
|
||||
<FreeBalance<Test>>::insert(2, 1);
|
||||
<FreeBalance<Runtime>>::insert(1, u64::max_value());
|
||||
<FreeBalance<Runtime>>::insert(2, 1);
|
||||
|
||||
assert_err!(
|
||||
Balances::transfer(&1, 2.into(), u64::max_value()),
|
||||
Balances::transfer(Some(1).into(), 2.into(), u64::max_value()),
|
||||
"destination balance too high to receive value"
|
||||
);
|
||||
|
||||
@@ -307,18 +307,18 @@ fn account_removal_on_free_too_low() {
|
||||
Balances::set_free_balance(&2, 110);
|
||||
Balances::increase_total_stake_by(110);
|
||||
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 732);
|
||||
assert_eq!(<TotalIssuance<Runtime>>::get(), 732);
|
||||
}
|
||||
|
||||
// Transfer funds from account 1 of such amount that after this transfer
|
||||
// the balance of account 1 will be below the exsistential threshold.
|
||||
// This should lead to the removal of all balance of this account.
|
||||
assert_ok!(Balances::transfer(&1, 2.into(), 20));
|
||||
assert_ok!(Balances::transfer(Some(1).into(), 2.into(), 20));
|
||||
|
||||
// Verify free balance removal of account 1.
|
||||
assert_eq!(Balances::free_balance(&1), 0);
|
||||
|
||||
// Verify that TotalIssuance tracks balance removal when free balance is too low.
|
||||
assert_eq!(<TotalIssuance<Test>>::get(), 642);
|
||||
assert_eq!(<TotalIssuance<Runtime>>::get(), 642);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,8 +46,9 @@ use runtime_support::{storage, Parameter};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_support::storage::StorageValue;
|
||||
use runtime_support::storage::unhashed::StorageVec;
|
||||
use primitives::traits::{MaybeSerializeDebug, MaybeEmpty, OnFinalise, Member, AuthoritiesChangeDigest};
|
||||
use primitives::traits::{MaybeSerializeDebug, OnFinalise, Member, AuthoritiesChangeDigest};
|
||||
use primitives::bft::MisbehaviorReport;
|
||||
use system::{ensure_signed, ensure_inherent, ensure_root};
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use substrate_primitives::KeccakHasher;
|
||||
@@ -141,19 +142,12 @@ decl_storage! {
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn report_misbehavior(aux, report: MisbehaviorReport<T::Hash, T::BlockNumber>) -> Result;
|
||||
fn note_offline(aux, offline_val_indices: Vec<u32>) -> Result;
|
||||
fn remark(aux, remark: Vec<u8>) -> Result;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_code(new: Vec<u8>) -> Result;
|
||||
fn set_storage(items: Vec<KeyValue>) -> Result;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn report_misbehavior(origin, report: MisbehaviorReport<T::Hash, T::BlockNumber>) -> Result;
|
||||
fn note_offline(origin, offline_val_indices: Vec<u32>) -> Result;
|
||||
fn remark(origin, remark: Vec<u8>) -> Result;
|
||||
fn set_code(origin, new: Vec<u8>) -> Result;
|
||||
fn set_storage(origin, items: Vec<KeyValue>) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,13 +158,15 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Set the new code.
|
||||
fn set_code(new: Vec<u8>) -> Result {
|
||||
fn set_code(origin: T::Origin, new: Vec<u8>) -> Result {
|
||||
ensure_root(origin)?;
|
||||
storage::unhashed::put_raw(CODE, &new);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set some items of storage.
|
||||
fn set_storage(items: Vec<KeyValue>) -> Result {
|
||||
fn set_storage(origin: T::Origin, items: Vec<KeyValue>) -> Result {
|
||||
ensure_root(origin)?;
|
||||
for i in &items {
|
||||
storage::unhashed::put_raw(&i.0, &i.1);
|
||||
}
|
||||
@@ -178,7 +174,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Report some misbehaviour.
|
||||
fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport<T::Hash, T::BlockNumber>) -> Result {
|
||||
fn report_misbehavior(origin: T::Origin, _report: MisbehaviorReport<T::Hash, T::BlockNumber>) -> Result {
|
||||
ensure_signed(origin)?;
|
||||
// TODO.
|
||||
Ok(())
|
||||
}
|
||||
@@ -186,8 +183,8 @@ impl<T: Trait> Module<T> {
|
||||
/// Note the previous block's validator missed their opportunity to propose a block. This only comes in
|
||||
/// if 2/3+1 of the validators agree that no proposal was submitted. It's only relevant
|
||||
/// for the previous block.
|
||||
fn note_offline(aux: &T::PublicAux, offline_val_indices: Vec<u32>) -> Result {
|
||||
assert!(aux.is_empty());
|
||||
fn note_offline(origin: T::Origin, offline_val_indices: Vec<u32>) -> Result {
|
||||
ensure_inherent(origin)?;
|
||||
assert!(
|
||||
<system::Module<T>>::extrinsic_index() == Some(T::NOTE_OFFLINE_POSITION),
|
||||
"note_offline extrinsic must be at position {} in the block",
|
||||
@@ -202,7 +199,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Make some on-chain remark.
|
||||
fn remark(_aux: &T::PublicAux, _remark: Vec<u8>) -> Result {
|
||||
fn remark(origin: T::Origin, _remark: Vec<u8>) -> Result {
|
||||
ensure_signed(origin)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives" }
|
||||
substrate-runtime-primitives = { path = "../../runtime/primitives" }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-primitives = { path = "../../runtime/primitives", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-sandbox = { path = "../../runtime-sandbox", default_features = false }
|
||||
@@ -28,6 +28,7 @@ std = [
|
||||
"serde_derive",
|
||||
"serde/std",
|
||||
"substrate-codec/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-std/std",
|
||||
|
||||
@@ -101,9 +101,10 @@ use account_db::{AccountDb, OverlayAccountDb};
|
||||
use double_map::StorageDoubleMap;
|
||||
|
||||
use codec::Codec;
|
||||
use runtime_primitives::traits::{As, RefInto, SimpleArithmetic, OnFinalise};
|
||||
use runtime_primitives::traits::{As, SimpleArithmetic, OnFinalise};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_support::{Parameter, StorageMap, StorageValue};
|
||||
use system::ensure_signed;
|
||||
|
||||
pub trait Trait: balances::Trait {
|
||||
/// Function type to get the contract address given the creator.
|
||||
@@ -119,13 +120,10 @@ pub trait ContractAddressFor<AccountId: Sized> {
|
||||
|
||||
decl_module! {
|
||||
/// Contracts module.
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
// TODO: Change AccountId to staking::Address
|
||||
fn call(
|
||||
aux,
|
||||
origin,
|
||||
dest: T::AccountId,
|
||||
value: T::Balance,
|
||||
gas_limit: T::Gas,
|
||||
@@ -133,7 +131,7 @@ decl_module! {
|
||||
) -> Result;
|
||||
|
||||
fn create(
|
||||
aux,
|
||||
origin,
|
||||
value: T::Balance,
|
||||
gas_limit: T::Gas,
|
||||
ctor: Vec<u8>,
|
||||
@@ -181,26 +179,26 @@ impl<T: Trait> double_map::StorageDoubleMap for StorageOf<T> {
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Make a call to a specified account, optionally transferring some balance.
|
||||
fn call(
|
||||
aux: &<T as system::Trait>::PublicAux,
|
||||
origin: <T as system::Trait>::Origin,
|
||||
dest: T::AccountId,
|
||||
value: T::Balance,
|
||||
gas_limit: T::Gas,
|
||||
data: Vec<u8>,
|
||||
) -> Result {
|
||||
let aux = aux.ref_into();
|
||||
let origin = ensure_signed(origin)?;
|
||||
|
||||
// Pay for the gas upfront.
|
||||
//
|
||||
// NOTE: it is very important to avoid any state changes before
|
||||
// paying for the gas.
|
||||
let mut gas_meter = gas::buy_gas::<T>(aux, gas_limit)?;
|
||||
let mut gas_meter = gas::buy_gas::<T>(&origin, gas_limit)?;
|
||||
|
||||
let mut ctx = ExecutionContext {
|
||||
self_account: aux.clone(),
|
||||
self_account: origin.clone(),
|
||||
depth: 0,
|
||||
overlay: OverlayAccountDb::<T>::new(&account_db::DirectAccountDb),
|
||||
};
|
||||
let result = ctx.call(aux.clone(), dest, value, &mut gas_meter, &data);
|
||||
let result = ctx.call(origin.clone(), dest, value, &mut gas_meter, &data);
|
||||
|
||||
if let Ok(_) = result {
|
||||
// Commit all changes that made it thus far into the persistant storage.
|
||||
@@ -211,7 +209,7 @@ impl<T: Trait> Module<T> {
|
||||
//
|
||||
// NOTE: this should go after the commit to the storage, since the storage changes
|
||||
// can alter the balance of the caller.
|
||||
gas::refund_unused_gas::<T>(aux, gas_meter);
|
||||
gas::refund_unused_gas::<T>(&origin, gas_meter);
|
||||
|
||||
result.map(|_| ())
|
||||
}
|
||||
@@ -226,26 +224,26 @@ impl<T: Trait> Module<T> {
|
||||
/// after the execution is saved as the `code` of the account. That code will be invoked
|
||||
/// upon any message received by this account.
|
||||
fn create(
|
||||
aux: &<T as system::Trait>::PublicAux,
|
||||
origin: <T as system::Trait>::Origin,
|
||||
endowment: T::Balance,
|
||||
gas_limit: T::Gas,
|
||||
ctor_code: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
) -> Result {
|
||||
let aux = aux.ref_into();
|
||||
let origin = ensure_signed(origin)?;
|
||||
|
||||
// Pay for the gas upfront.
|
||||
//
|
||||
// NOTE: it is very important to avoid any state changes before
|
||||
// paying for the gas.
|
||||
let mut gas_meter = gas::buy_gas::<T>(aux, gas_limit)?;
|
||||
let mut gas_meter = gas::buy_gas::<T>(&origin, gas_limit)?;
|
||||
|
||||
let mut ctx = ExecutionContext {
|
||||
self_account: aux.clone(),
|
||||
self_account: origin.clone(),
|
||||
depth: 0,
|
||||
overlay: OverlayAccountDb::<T>::new(&account_db::DirectAccountDb),
|
||||
};
|
||||
let result = ctx.create(aux.clone(), endowment, &mut gas_meter, &ctor_code, &data);
|
||||
let result = ctx.create(origin.clone(), endowment, &mut gas_meter, &ctor_code, &data);
|
||||
|
||||
if let Ok(_) = result {
|
||||
// Commit all changes that made it thus far into the persistant storage.
|
||||
@@ -256,7 +254,7 @@ impl<T: Trait> Module<T> {
|
||||
//
|
||||
// NOTE: this should go after the commit to the storage, since the storage changes
|
||||
// can alter the balance of the caller.
|
||||
gas::refund_unused_gas::<T>(aux, gas_meter);
|
||||
gas::refund_unused_gas::<T>(&origin, gas_meter);
|
||||
|
||||
result.map(|_| ())
|
||||
}
|
||||
|
||||
@@ -27,10 +27,14 @@ use {
|
||||
GenesisConfig, Module, StorageOf, Trait,
|
||||
};
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -170,7 +174,7 @@ fn contract_transfer() {
|
||||
Balances::set_free_balance(&1, 11);
|
||||
Balances::increase_total_stake_by(11);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 3, 100_000, Vec::new()));
|
||||
assert_ok!(Contract::call(Origin::signed(0), 1, 3, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Balances::free_balance(&0),
|
||||
@@ -205,7 +209,7 @@ fn contract_transfer_oog() {
|
||||
Balances::set_free_balance(&1, 11);
|
||||
Balances::increase_total_stake_by(11);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 3, 135 + 135 + 7, Vec::new()));
|
||||
assert_ok!(Contract::call(Origin::signed(0), 1, 3, 135 + 135 + 7, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Balances::free_balance(&0),
|
||||
@@ -237,7 +241,7 @@ fn contract_transfer_max_depth() {
|
||||
Balances::set_free_balance(&CONTRACT_SHOULD_TRANSFER_TO, 11);
|
||||
Balances::increase_total_stake_by(11);
|
||||
|
||||
assert_ok!(Contract::call(&0, CONTRACT_SHOULD_TRANSFER_TO, 3, 100_000, Vec::new()));
|
||||
assert_ok!(Contract::call(Origin::signed(0), CONTRACT_SHOULD_TRANSFER_TO, 3, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Balances::free_balance(&0),
|
||||
@@ -349,7 +353,7 @@ fn contract_create() {
|
||||
<CodeOf<Test>>::insert(1, code_create.to_vec());
|
||||
|
||||
// When invoked, the contract at address `1` must create a contract with 'transfer' code.
|
||||
assert_ok!(Contract::call(&0, 1, 11, 100_000, Vec::new()));
|
||||
assert_ok!(Contract::call(Origin::signed(0), 1, 11, 100_000, Vec::new()));
|
||||
|
||||
let derived_address = <Test as Trait>::DetermineContractAddress::contract_address_for(
|
||||
&code_ctor_transfer,
|
||||
@@ -368,7 +372,7 @@ fn contract_create() {
|
||||
assert_eq!(Balances::free_balance(&derived_address), 3);
|
||||
|
||||
// Initiate transfer to the newly created contract.
|
||||
assert_ok!(Contract::call(&0, derived_address, 22, 100_000, Vec::new()));
|
||||
assert_ok!(Contract::call(Origin::signed(0), derived_address, 22, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Balances::free_balance(&0),
|
||||
@@ -400,7 +404,7 @@ fn top_level_create() {
|
||||
Balances::increase_total_stake_by(30);
|
||||
|
||||
assert_ok!(Contract::create(
|
||||
&0,
|
||||
Origin::signed(0),
|
||||
11,
|
||||
100_000,
|
||||
code_ctor_transfer.clone(),
|
||||
@@ -439,7 +443,7 @@ fn refunds_unused_gas() {
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new()));
|
||||
assert_ok!(Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(Balances::free_balance(&0), 100_000_000 - 4 - (2 * 135));
|
||||
});
|
||||
@@ -453,7 +457,7 @@ fn call_with_zero_value() {
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new()));
|
||||
assert_ok!(Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(Balances::free_balance(&0), 100_000_000 - (2 * 135));
|
||||
});
|
||||
@@ -467,7 +471,7 @@ fn create_with_zero_endowment() {
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::create(&0, 0, 100_000, code_nop, Vec::new()));
|
||||
assert_ok!(Contract::create(Origin::signed(0), 0, 100_000, code_nop, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Balances::free_balance(&0),
|
||||
@@ -500,7 +504,7 @@ fn account_removal_removes_storage() {
|
||||
// the balance of account 1 is will be below than exsistential threshold.
|
||||
//
|
||||
// This should lead to the removal of all storage associated with this account.
|
||||
assert_ok!(Balances::transfer(&1, 2.into(), 20));
|
||||
assert_ok!(Balances::transfer(Origin::signed(1), 2.into(), 20));
|
||||
|
||||
// Verify that all entries from account 1 is removed, while
|
||||
// entries from account 2 is in place.
|
||||
@@ -540,7 +544,7 @@ fn top_level_call_refunds_even_if_fails() {
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 0, 100_000, Vec::new()),
|
||||
Contract::call(Origin::signed(0), 1, 0, 100_000, Vec::new()),
|
||||
"vm execute returned error while call"
|
||||
);
|
||||
|
||||
@@ -571,19 +575,19 @@ fn block_gas_limit() {
|
||||
|
||||
// Spend 50_000 units of gas (OOG).
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 0, 50_000, Vec::new()),
|
||||
Contract::call(Origin::signed(0), 1, 0, 50_000, Vec::new()),
|
||||
"vm execute returned error while call"
|
||||
);
|
||||
|
||||
// Ensure we can't spend more gas than available in block gas limit.
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 0, 50_001, Vec::new()),
|
||||
Contract::call(Origin::signed(0), 1, 0, 50_001, Vec::new()),
|
||||
"block gas limit is reached"
|
||||
);
|
||||
|
||||
// However, we can spend another 50_000
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 0, 50_000, Vec::new()),
|
||||
Contract::call(Origin::signed(0), 1, 0, 50_000, Vec::new()),
|
||||
"vm execute returned error while call"
|
||||
);
|
||||
},
|
||||
@@ -656,7 +660,7 @@ fn input_data() {
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 0, 50_000, vec![0, 1, 2, 3]));
|
||||
assert_ok!(Contract::call(Origin::signed(0), 1, 0, 50_000, vec![0, 1, 2, 3]));
|
||||
|
||||
// all asserts are made within contract code itself.
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,27 +18,22 @@
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use primitives::traits::{OnFinalise, RefInto, Hash};
|
||||
use primitives::traits::{OnFinalise, Hash};
|
||||
use runtime_io::print;
|
||||
use substrate_runtime_support::dispatch::Result;
|
||||
use substrate_runtime_support::{StorageValue, StorageMap, IsSubType};
|
||||
use {system, democracy};
|
||||
use super::{Trait, Module as Council};
|
||||
use system::{ensure_signed, ensure_root};
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn propose(origin, proposal: Box<T::Proposal>) -> Result;
|
||||
fn vote(origin, proposal: T::Hash, approve: bool) -> Result;
|
||||
fn veto(origin, proposal_hash: T::Hash) -> Result;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn propose(aux, proposal: Box<T::Proposal>) -> Result;
|
||||
fn vote(aux, proposal: T::Hash, approve: bool) -> Result;
|
||||
fn veto(aux, proposal_hash: T::Hash) -> Result;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_cooloff_period(blocks: T::BlockNumber) -> Result;
|
||||
fn set_voting_period(blocks: T::BlockNumber) -> Result;
|
||||
fn set_cooloff_period(origin, blocks: T::BlockNumber) -> Result;
|
||||
fn set_voting_period(origin, blocks: T::BlockNumber) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,9 +73,11 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
// Dispatch
|
||||
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>) -> Result {
|
||||
fn propose(origin: T::Origin, proposal: Box<T::Proposal>) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
let expiry = <system::Module<T>>::block_number() + Self::voting_period();
|
||||
ensure!(Self::will_still_be_councillor_at(aux.ref_into(), expiry), "proposer would not be on council");
|
||||
ensure!(Self::will_still_be_councillor_at(&who, expiry), "proposer would not be on council");
|
||||
|
||||
let proposal_hash = T::Hashing::hash_of(&proposal);
|
||||
|
||||
@@ -93,31 +90,35 @@ impl<T: Trait> Module<T> {
|
||||
Self::set_proposals(&proposals);
|
||||
|
||||
<ProposalOf<T>>::insert(proposal_hash, *proposal);
|
||||
<ProposalVoters<T>>::insert(proposal_hash, vec![aux.ref_into().clone()]);
|
||||
<CouncilVoteOf<T>>::insert((proposal_hash, aux.ref_into().clone()), true);
|
||||
<ProposalVoters<T>>::insert(proposal_hash, vec![who.clone()]);
|
||||
<CouncilVoteOf<T>>::insert((proposal_hash, who), true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn vote(aux: &T::PublicAux, proposal: T::Hash, approve: bool) -> Result {
|
||||
if Self::vote_of((proposal, aux.ref_into().clone())).is_none() {
|
||||
fn vote(origin: T::Origin, proposal: T::Hash, approve: bool) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
if Self::vote_of((proposal, who.clone())).is_none() {
|
||||
let mut voters = Self::proposal_voters(&proposal);
|
||||
voters.push(aux.ref_into().clone());
|
||||
voters.push(who.clone());
|
||||
<ProposalVoters<T>>::insert(proposal, voters);
|
||||
}
|
||||
<CouncilVoteOf<T>>::insert((proposal, aux.ref_into().clone()), approve);
|
||||
<CouncilVoteOf<T>>::insert((proposal, who), approve);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn veto(aux: &T::PublicAux, proposal_hash: T::Hash) -> Result {
|
||||
ensure!(Self::is_councillor(aux.ref_into()), "only councillors may veto council proposals");
|
||||
fn veto(origin: T::Origin, proposal_hash: T::Hash) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
ensure!(Self::is_councillor(&who), "only councillors may veto council proposals");
|
||||
ensure!(<ProposalVoters<T>>::exists(&proposal_hash), "proposal must exist to be vetoed");
|
||||
|
||||
let mut existing_vetoers = Self::veto_of(&proposal_hash)
|
||||
.map(|pair| pair.1)
|
||||
.unwrap_or_else(Vec::new);
|
||||
let insert_position = existing_vetoers.binary_search(aux.ref_into())
|
||||
let insert_position = existing_vetoers.binary_search(&who)
|
||||
.err().ok_or("a councillor may not veto a proposal twice")?;
|
||||
existing_vetoers.insert(insert_position, aux.ref_into().clone());
|
||||
existing_vetoers.insert(insert_position, who);
|
||||
Self::set_veto_of(&proposal_hash, <system::Module<T>>::block_number() + Self::cooloff_period(), existing_vetoers);
|
||||
|
||||
Self::set_proposals(&Self::proposals().into_iter().filter(|&(_, h)| h != proposal_hash).collect::<Vec<_>>());
|
||||
@@ -129,12 +130,14 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_cooloff_period(blocks: T::BlockNumber) -> Result {
|
||||
fn set_cooloff_period(origin: T::Origin, blocks: T::BlockNumber) -> Result {
|
||||
ensure_root(origin)?;
|
||||
<CooloffPeriod<T>>::put(blocks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_voting_period(blocks: T::BlockNumber) -> Result {
|
||||
fn set_voting_period(origin: T::Origin, blocks: T::BlockNumber) -> Result {
|
||||
ensure_root(origin)?;
|
||||
<VotingPeriod<T>>::put(blocks);
|
||||
Ok(())
|
||||
}
|
||||
@@ -182,7 +185,7 @@ impl<T: Trait> Module<T> {
|
||||
fn end_block(now: T::BlockNumber) -> Result {
|
||||
while let Some((proposal, proposal_hash)) = Self::take_proposal_if_expiring_at(now) {
|
||||
let tally = Self::take_tally(&proposal_hash);
|
||||
if let Some(&democracy::PrivCall::cancel_referendum(ref_index)) = IsSubType::<democracy::Module<T>>::is_sub_type(&proposal) {
|
||||
if let Some(&democracy::Call::cancel_referendum(ref_index)) = IsSubType::<democracy::Module<T>>::is_aux_sub_type(&proposal) {
|
||||
if let (_, 0, 0) = tally {
|
||||
<democracy::Module<T>>::internal_cancel_referendum(ref_index);
|
||||
}
|
||||
@@ -217,6 +220,7 @@ impl<T: Trait> OnFinalise<T::BlockNumber> for Council<T> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ::tests::*;
|
||||
use ::tests::Call;
|
||||
use substrate_runtime_support::Hashable;
|
||||
use democracy::VoteThreshold;
|
||||
|
||||
@@ -242,12 +246,12 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
fn set_balance_proposal(value: u64) -> Proposal {
|
||||
Proposal::Balances(balances::PrivCall::set_balance(balances::address::Address::Id(42), value, 0))
|
||||
fn set_balance_proposal(value: u64) -> Call {
|
||||
Call::Balances(balances::Call::set_balance(balances::address::Address::Id(42), value, 0))
|
||||
}
|
||||
|
||||
fn cancel_referendum_proposal(id: u32) -> Proposal {
|
||||
Proposal::Democracy(democracy::PrivCall::cancel_referendum(id))
|
||||
fn cancel_referendum_proposal(id: u32) -> Call {
|
||||
Call::Democracy(democracy::Call::cancel_referendum(id))
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -260,9 +264,9 @@ mod tests {
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(cancellation)));
|
||||
assert_ok!(CouncilVoting::vote(&2, hash, true));
|
||||
assert_ok!(CouncilVoting::vote(&3, hash, true));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation)));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, true));
|
||||
assert_eq!(CouncilVoting::proposals(), vec![(2, hash)]);
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
|
||||
@@ -282,9 +286,9 @@ mod tests {
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(cancellation)));
|
||||
assert_ok!(CouncilVoting::vote(&2, hash, true));
|
||||
assert_ok!(CouncilVoting::vote(&3, hash, false));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation)));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, false));
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
|
||||
System::set_block_number(2);
|
||||
@@ -302,8 +306,8 @@ mod tests {
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(cancellation)));
|
||||
assert_ok!(CouncilVoting::vote(&2, hash, true));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(cancellation)));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, true));
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
|
||||
System::set_block_number(2);
|
||||
@@ -318,8 +322,8 @@ mod tests {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(Origin::signed(2), hash));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums().len(), 0);
|
||||
});
|
||||
@@ -331,12 +335,12 @@ mod tests {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(Origin::signed(2), hash));
|
||||
|
||||
System::set_block_number(3);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_noop!(CouncilVoting::veto(&2, hash), "a councillor may not veto a proposal twice");
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_noop!(CouncilVoting::veto(Origin::signed(2), hash), "a councillor may not veto a proposal twice");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -346,11 +350,11 @@ mod tests {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(Origin::signed(2), hash));
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_noop!(CouncilVoting::propose(&1, Box::new(proposal.clone())), "proposal is vetoed");
|
||||
assert_noop!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())), "proposal is vetoed");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -360,13 +364,13 @@ mod tests {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(Origin::signed(2), hash));
|
||||
|
||||
System::set_block_number(3);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::vote(&2, hash, false));
|
||||
assert_ok!(CouncilVoting::vote(&3, hash, true));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(2), hash, false));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(3), hash, true));
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
|
||||
System::set_block_number(4);
|
||||
@@ -382,12 +386,12 @@ mod tests {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&2, hash));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(Origin::signed(2), hash));
|
||||
|
||||
System::set_block_number(3);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(&3, hash));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::veto(Origin::signed(3), hash));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums().len(), 0);
|
||||
});
|
||||
@@ -399,7 +403,7 @@ mod tests {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 1);
|
||||
assert_eq!(CouncilVoting::proposal_voters(&hash), vec![1]);
|
||||
assert_eq!(CouncilVoting::vote_of((hash, 1)), Some(true));
|
||||
@@ -412,7 +416,7 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (1, 0, 2));
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
|
||||
@@ -428,9 +432,9 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::vote(&2, proposal.blake2_256().into(), true));
|
||||
assert_ok!(CouncilVoting::vote(&3, proposal.blake2_256().into(), true));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(2), proposal.blake2_256().into(), true));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(3), proposal.blake2_256().into(), true));
|
||||
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (3, 0, 0));
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
|
||||
@@ -446,9 +450,9 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::vote(&2, proposal.blake2_256().into(), true));
|
||||
assert_ok!(CouncilVoting::vote(&3, proposal.blake2_256().into(), false));
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(2), proposal.blake2_256().into(), true));
|
||||
assert_ok!(CouncilVoting::vote(Origin::signed(3), proposal.blake2_256().into(), false));
|
||||
assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (2, 1, 0));
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
|
||||
@@ -464,7 +468,7 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_noop!(CouncilVoting::propose(&4, Box::new(proposal)), "proposer would not be on council");
|
||||
assert_noop!(CouncilVoting::propose(Origin::signed(4), Box::new(proposal)), "proposer would not be on council");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ std = [
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-support/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-consensus/std",
|
||||
"substrate-runtime-balances/std",
|
||||
"substrate-runtime-system/std",
|
||||
]
|
||||
|
||||
@@ -43,9 +43,10 @@ extern crate substrate_runtime_system as system;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::result;
|
||||
use primitives::traits::{Zero, OnFinalise, RefInto, As, MaybeSerializeDebug};
|
||||
use primitives::traits::{Zero, OnFinalise, As, MaybeSerializeDebug};
|
||||
use substrate_runtime_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType};
|
||||
use substrate_runtime_support::dispatch::Result;
|
||||
use system::{ensure_signed, ensure_root};
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::collections::HashMap;
|
||||
@@ -59,23 +60,17 @@ pub type PropIndex = u32;
|
||||
pub type ReferendumIndex = u32;
|
||||
|
||||
pub trait Trait: balances::Trait + Sized {
|
||||
type Proposal: Parameter + Dispatchable + IsSubType<Module<Self>> + MaybeSerializeDebug;
|
||||
type Proposal: Parameter + Dispatchable<Origin=Self::Origin> + IsSubType<Module<Self>> + MaybeSerializeDebug;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn propose(origin, proposal: Box<T::Proposal>, value: T::Balance) -> Result;
|
||||
fn second(origin, proposal: PropIndex) -> Result;
|
||||
fn vote(origin, ref_index: ReferendumIndex, approve_proposal: bool) -> Result;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn propose(aux, proposal: Box<T::Proposal>, value: T::Balance) -> Result;
|
||||
fn second(aux, proposal: PropIndex) -> Result;
|
||||
fn vote(aux, ref_index: ReferendumIndex, approve_proposal: bool) -> Result;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) -> Result;
|
||||
fn cancel_referendum(ref_index: ReferendumIndex) -> Result;
|
||||
fn start_referendum(origin, proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) -> Result;
|
||||
fn cancel_referendum(origin, ref_index: ReferendumIndex) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,49 +151,53 @@ impl<T: Trait> Module<T> {
|
||||
// dispatching.
|
||||
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn propose(aux: &T::PublicAux, proposal: Box<T::Proposal>, value: T::Balance) -> Result {
|
||||
fn propose(origin: T::Origin, proposal: Box<T::Proposal>, value: T::Balance) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
ensure!(value >= Self::minimum_deposit(), "value too low");
|
||||
<balances::Module<T>>::reserve(aux.ref_into(), value)
|
||||
<balances::Module<T>>::reserve(&who, value)
|
||||
.map_err(|_| "proposer's balance too low")?;
|
||||
|
||||
let index = Self::public_prop_count();
|
||||
<PublicPropCount<T>>::put(index + 1);
|
||||
<DepositOf<T>>::insert(index, (value, vec![aux.ref_into().clone()]));
|
||||
<DepositOf<T>>::insert(index, (value, vec![who.clone()]));
|
||||
|
||||
let mut props = Self::public_props();
|
||||
props.push((index, (*proposal).clone(), aux.ref_into().clone()));
|
||||
props.push((index, (*proposal).clone(), who));
|
||||
<PublicProps<T>>::put(props);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Propose a sensitive action to be taken.
|
||||
fn second(aux: &T::PublicAux, proposal: PropIndex) -> Result {
|
||||
fn second(origin: T::Origin, proposal: PropIndex) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
let mut deposit = Self::deposit_of(proposal)
|
||||
.ok_or("can only second an existing proposal")?;
|
||||
<balances::Module<T>>::reserve(aux.ref_into(), deposit.0)
|
||||
<balances::Module<T>>::reserve(&who, deposit.0)
|
||||
.map_err(|_| "seconder's balance too low")?;
|
||||
deposit.1.push(aux.ref_into().clone());
|
||||
deposit.1.push(who);
|
||||
<DepositOf<T>>::insert(proposal, deposit);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal;
|
||||
/// false would be a vote to keep the status quo..
|
||||
fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) -> Result {
|
||||
fn vote(origin: T::Origin, ref_index: ReferendumIndex, approve_proposal: bool) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum.");
|
||||
ensure!(!<balances::Module<T>>::total_balance(aux.ref_into()).is_zero(),
|
||||
ensure!(!<balances::Module<T>>::total_balance(&who).is_zero(),
|
||||
"transactor must have balance to signal approval.");
|
||||
if !<VoteOf<T>>::exists(&(ref_index, aux.ref_into().clone())) {
|
||||
if !<VoteOf<T>>::exists(&(ref_index, who.clone())) {
|
||||
let mut voters = Self::voters_for(ref_index);
|
||||
voters.push(aux.ref_into().clone());
|
||||
voters.push(who.clone());
|
||||
<VotersFor<T>>::insert(ref_index, voters);
|
||||
}
|
||||
<VoteOf<T>>::insert(&(ref_index, aux.ref_into().clone()), approve_proposal);
|
||||
<VoteOf<T>>::insert(&(ref_index, who), approve_proposal);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start a referendum.
|
||||
fn start_referendum(proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) -> Result {
|
||||
fn start_referendum(origin: T::Origin, proposal: Box<T::Proposal>, vote_threshold: VoteThreshold) -> Result {
|
||||
ensure_root(origin)?;
|
||||
Self::inject_referendum(
|
||||
<system::Module<T>>::block_number() + Self::voting_period(),
|
||||
*proposal,
|
||||
@@ -207,7 +206,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Remove a referendum.
|
||||
fn cancel_referendum(ref_index: ReferendumIndex) -> Result {
|
||||
fn cancel_referendum(origin: T::Origin, ref_index: ReferendumIndex) -> Result {
|
||||
ensure_root(origin)?;
|
||||
Self::clear_referendum(ref_index);
|
||||
Ok(())
|
||||
}
|
||||
@@ -280,7 +280,7 @@ impl<T: Trait> Module<T> {
|
||||
let total_stake = <balances::Module<T>>::total_stake();
|
||||
Self::clear_referendum(index);
|
||||
if vote_threshold.approved(approve, against, total_stake) {
|
||||
proposal.dispatch()?;
|
||||
proposal.dispatch(system::RawOrigin::Root.into())?;
|
||||
}
|
||||
<NextTally<T>>::put(index + 1);
|
||||
}
|
||||
@@ -354,9 +354,12 @@ mod tests {
|
||||
use primitives::traits::{BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
impl_outer_dispatch! {
|
||||
#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Proposal {
|
||||
pub enum Call where origin: Origin {
|
||||
Balances,
|
||||
Democracy,
|
||||
}
|
||||
@@ -366,7 +369,7 @@ mod tests {
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Test;
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -384,7 +387,7 @@ mod tests {
|
||||
type Event = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Proposal = Proposal;
|
||||
type Proposal = Call;
|
||||
}
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
@@ -422,12 +425,12 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
fn set_balance_proposal(value: u64) -> Proposal {
|
||||
Proposal::Balances(balances::PrivCall::set_balance(balances::address::Address::Id(42), value, 0))
|
||||
fn set_balance_proposal(value: u64) -> Call {
|
||||
Call::Balances(balances::Call::set_balance(balances::address::Address::Id(42), value, 0))
|
||||
}
|
||||
|
||||
fn propose_set_balance(who: u64, value: u64, locked: u64) -> super::Result {
|
||||
Democracy::propose(&who, Box::new(set_balance_proposal(value)), locked)
|
||||
Democracy::propose(Origin::signed(who), Box::new(set_balance_proposal(value)), locked)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -452,7 +455,7 @@ mod tests {
|
||||
|
||||
System::set_block_number(2);
|
||||
let r = 0;
|
||||
assert_ok!(Democracy::vote(&1, r, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r, true));
|
||||
|
||||
assert_eq!(Democracy::referendum_count(), 1);
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
@@ -470,10 +473,10 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(propose_set_balance(1, 2, 5));
|
||||
assert_ok!(Democracy::second(&2, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(Origin::signed(2), 0));
|
||||
assert_ok!(Democracy::second(Origin::signed(5), 0));
|
||||
assert_ok!(Democracy::second(Origin::signed(5), 0));
|
||||
assert_ok!(Democracy::second(Origin::signed(5), 0));
|
||||
assert_eq!(Balances::free_balance(&1), 5);
|
||||
assert_eq!(Balances::free_balance(&2), 15);
|
||||
assert_eq!(Balances::free_balance(&5), 35);
|
||||
@@ -485,10 +488,10 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(propose_set_balance(1, 2, 5));
|
||||
assert_ok!(Democracy::second(&2, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(&5, 0));
|
||||
assert_ok!(Democracy::second(Origin::signed(2), 0));
|
||||
assert_ok!(Democracy::second(Origin::signed(5), 0));
|
||||
assert_ok!(Democracy::second(Origin::signed(5), 0));
|
||||
assert_ok!(Democracy::second(Origin::signed(5), 0));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
assert_eq!(Balances::free_balance(&1), 10);
|
||||
assert_eq!(Balances::free_balance(&2), 20);
|
||||
@@ -517,7 +520,7 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(propose_set_balance(2, 2, 11));
|
||||
assert_noop!(Democracy::second(&1, 0), "seconder\'s balance too low");
|
||||
assert_noop!(Democracy::second(Origin::signed(1), 0), "seconder\'s balance too low");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -531,17 +534,17 @@ mod tests {
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Democracy::vote(&1, 0, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 0, true));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
assert_eq!(Balances::free_balance(&42), 4);
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_ok!(Democracy::vote(&1, 1, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 1, true));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
assert_eq!(Balances::free_balance(&42), 3);
|
||||
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Democracy::vote(&1, 2, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 2, true));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
});
|
||||
}
|
||||
@@ -551,7 +554,7 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&1, r, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r, true));
|
||||
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
||||
@@ -568,8 +571,8 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&1, r, true));
|
||||
assert_ok!(Democracy::cancel_referendum(r));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r, true));
|
||||
assert_ok!(Democracy::cancel_referendum(Origin::ROOT, r));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
@@ -582,7 +585,7 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&1, r, false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r, false));
|
||||
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
assert_eq!(Democracy::vote_of((r, 1)), Some(false));
|
||||
@@ -599,12 +602,12 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&1, r, true));
|
||||
assert_ok!(Democracy::vote(&2, r, false));
|
||||
assert_ok!(Democracy::vote(&3, r, false));
|
||||
assert_ok!(Democracy::vote(&4, r, true));
|
||||
assert_ok!(Democracy::vote(&5, r, false));
|
||||
assert_ok!(Democracy::vote(&6, r, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(2), r, false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(3), r, false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(4), r, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r, false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r, true));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (110, 100));
|
||||
|
||||
@@ -619,8 +622,8 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&5, r, false));
|
||||
assert_ok!(Democracy::vote(&6, r, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r, false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r, true));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (60, 50));
|
||||
|
||||
@@ -638,9 +641,9 @@ mod tests {
|
||||
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap();
|
||||
assert_ok!(Democracy::vote(&4, r, true));
|
||||
assert_ok!(Democracy::vote(&5, r, false));
|
||||
assert_ok!(Democracy::vote(&6, r, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(4), r, true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r, false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r, true));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (100, 50));
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ extern crate substrate_runtime_balances as balances;
|
||||
|
||||
use runtime_primitives::traits::OnFinalise;
|
||||
use runtime_support::{StorageValue, dispatch::Result};
|
||||
use system::{ensure_signed, ensure_root};
|
||||
|
||||
/// Our module's configuration trait. All our types and consts go in here. If the
|
||||
/// module is dependent on specific other modules, then their configuration traits
|
||||
@@ -73,45 +74,44 @@ pub trait Trait: balances::Trait {
|
||||
|
||||
// The module declaration. This states the entry points that we handle. The
|
||||
// macro takes care of the marshalling of arguments and dispatch.
|
||||
//
|
||||
// Anyone can have these functions execute by signing and submitting
|
||||
// an extrinsic. Ensure that calls into each of these execute in a time, memory and
|
||||
// using storage space proportional to any costs paid for by the caller or otherwise the
|
||||
// difficulty of forcing the call to happen.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Information about where this dispatch initiated from is provided as the first argument
|
||||
// "origin". As such functions must always look like:
|
||||
//
|
||||
// `fn foo(origin, bar: Bar, baz: Baz) -> Result = 0;`
|
||||
//
|
||||
// The `Result` is required as part of the syntax (and expands to the conventional dispatch
|
||||
// result of `Result<(), &'static str>`).
|
||||
//
|
||||
// When you come to `impl` them later in the module, you must specify the full type for `origin`:
|
||||
//
|
||||
// `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
|
||||
// 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`.
|
||||
decl_module! {
|
||||
// Simple declaration of the `Module` type. Lets the macro know what its working on.
|
||||
pub struct Module<T: Trait>;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
/// This is your public interface. Be extremely careful.
|
||||
/// This is just a simple example of how to interact with the module from the external
|
||||
/// world.
|
||||
fn accumulate_dummy(origin, increase_by: T::Balance) -> Result;
|
||||
|
||||
// The unpriviledged entry points. Any account can call into these by signing and submitting
|
||||
// an extrinsic. Ensure that calls into each of these execute in a time, memory and
|
||||
// using storage space proportional to any costs paid for by the caller.
|
||||
//
|
||||
// The account that is calling this (i.e. the one that signed the extrinsic) is provided
|
||||
// via the `aux` argument, always first in each function call. As such functions must
|
||||
// always look like:
|
||||
//
|
||||
// `fn foo(aux, bar: Bar, baz: Baz) -> Result = 0;`
|
||||
//
|
||||
// The `Result` is required as part of the syntax (and expands to the conventional dispatch
|
||||
// result of `Result<(), &'static str>`). The index after `=` must be unique within this
|
||||
// enum (the `PrivCall` enum is allowed to reuse indexes).
|
||||
//
|
||||
// When you come to `impl` them later in the module, you must specify the full type for `aux`:
|
||||
//
|
||||
// `fn foo(aux: T::PublicAux, bar: Bar, baz: Baz) { ... }`
|
||||
//
|
||||
// This is your public interface. Be extremely careful.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
// This is just a simple example of how to interact with the module from the external
|
||||
// world.
|
||||
fn accumulate_dummy(aux, increase_by: T::Balance) -> Result;
|
||||
}
|
||||
|
||||
// The priviledged entry points. These are provided to allow any governance modules in
|
||||
// the runtime to be able to execute common functions. Unlike for `Call` there is no
|
||||
// auxilliary data to encode the sender (since there is no sender). Though still important
|
||||
// to ensure that these execute in reasonable time and space, they can do what would
|
||||
// otherwise be costly or unsafe operations.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
// A priviledged call; in this case it resets our dummy value to something new.
|
||||
fn set_dummy(new_dummy: T::Balance) -> Result;
|
||||
/// A priviledged call; in this case it resets our dummy value to something new.
|
||||
fn set_dummy(origin, new_dummy: T::Balance) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ impl<T: Trait> Module<T> {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
// Implement Calls/PrivCalls and add public immutables and private mutables.
|
||||
// Implement Calls and add public immutables and private mutables.
|
||||
|
||||
// Implement dispatched function `accumulate_dummy`. This just increases the value
|
||||
// of `Dummy` by `increase_by`.
|
||||
@@ -206,7 +206,7 @@ impl<T: Trait> Module<T> {
|
||||
// The first is relatively easy to audit for - just ensure all panickers are removed from
|
||||
// logic that executes in production (which you do anyway, right?!). To ensure the second
|
||||
// is followed, you should do all tests for validity at the top of your function. This
|
||||
// is stuff like checking the sender (`aux`) or that state is such that the operation
|
||||
// is stuff like checking the sender (`origin`) or that state is such that the operation
|
||||
// makes sense.
|
||||
//
|
||||
// Once you've determined that it's all good, then enact the operation and change storage.
|
||||
@@ -227,7 +227,10 @@ impl<T: Trait> Module<T> {
|
||||
// no progress.
|
||||
//
|
||||
// If you don't respect these rules, it is likely that your chain will be attackable.
|
||||
fn accumulate_dummy(_aux: &T::PublicAux, increase_by: T::Balance) -> Result {
|
||||
fn accumulate_dummy(origin: T::Origin, increase_by: T::Balance) -> Result {
|
||||
// This is a public call, so we ensure that the origin is some signed account.
|
||||
let _sender = ensure_signed(origin)?;
|
||||
|
||||
// Read the value of dummy from storage.
|
||||
let dummy = Self::dummy();
|
||||
// Will also work using the `::get` on the storage item type iself:
|
||||
@@ -248,13 +251,16 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Implementation of a priviledged call. This doesn't have an `aux` parameter because
|
||||
// Implementation of a priviledged call. This doesn't have an `origin` parameter because
|
||||
// it's not (directly) from an extrinsic, but rather the system as a whole has decided
|
||||
// to execute it. Different runtimes have different reasons for allow priviledged
|
||||
// calls to be executed - we don't need to care why. Because it's priviledged, we can
|
||||
// assume it's a one-off operation and substantial processing/storage/memory can be used
|
||||
// without worrying about gameability or attack scenarios.
|
||||
fn set_dummy(new_value: T::Balance) -> Result {
|
||||
fn set_dummy(origin: T::Origin, new_value: T::Balance) -> Result {
|
||||
// This is a privileged call, so we ensure that the origin is "Root".
|
||||
ensure_root(origin)?;
|
||||
|
||||
// Put the new value into storage.
|
||||
<Dummy<T>>::put(new_value);
|
||||
|
||||
@@ -323,13 +329,17 @@ mod tests {
|
||||
// or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried.
|
||||
use runtime_primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
// For testing the module, we construct most of a mock runtime. This means
|
||||
// first constructing a configuration type (`Test`) which `impl`s each of the
|
||||
// configuration traits of modules we want to use.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -370,7 +380,7 @@ mod tests {
|
||||
assert_eq!(Example::dummy(), Some(42));
|
||||
|
||||
// Check that accumulate works when we have Some value in Dummy already.
|
||||
assert_ok!(Example::accumulate_dummy(&0, 27));
|
||||
assert_ok!(Example::accumulate_dummy(Origin::signed(1), 27));
|
||||
assert_eq!(Example::dummy(), Some(69));
|
||||
|
||||
// Check that finalising the block removes Dummy from storage.
|
||||
@@ -378,7 +388,7 @@ mod tests {
|
||||
assert_eq!(Example::dummy(), None);
|
||||
|
||||
// Check that accumulate works when we Dummy has None in it.
|
||||
assert_ok!(Example::accumulate_dummy(&0, 42));
|
||||
assert_ok!(Example::accumulate_dummy(Origin::signed(1), 42));
|
||||
assert_eq!(Example::dummy(), Some(42));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ hex-literal = "0.1.0"
|
||||
serde = { version = "1.0", default_features = false }
|
||||
serde_derive = { version = "1.0", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-codec-derive = { path = "../../codec/derive", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
|
||||
@@ -18,10 +17,7 @@ substrate-runtime-system = { path = "../system", default_features = false }
|
||||
[dev-dependencies]
|
||||
substrate-primitives = { path = "../../primitives" }
|
||||
substrate-runtime-balances = { path = "../balances" }
|
||||
substrate-runtime-session = { path = "../session" }
|
||||
substrate-runtime-staking = { path = "../staking" }
|
||||
substrate-runtime-consensus = { path = "../consensus" }
|
||||
substrate-runtime-timestamp = { path = "../timestamp" }
|
||||
substrate-codec-derive = { path = "../../codec/derive" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@@ -31,7 +27,6 @@ std = [
|
||||
"serde/std",
|
||||
"serde_derive",
|
||||
"substrate-codec/std",
|
||||
"substrate-codec-derive/std",
|
||||
"substrate-runtime-primitives/std",
|
||||
"substrate-runtime-io/std",
|
||||
"substrate-runtime-system/std",
|
||||
|
||||
@@ -37,9 +37,6 @@ extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_primitives as primitives;
|
||||
extern crate substrate_runtime_system as system;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
@@ -47,23 +44,14 @@ extern crate hex_literal;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_primitives;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_consensus as consensus;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_session as session;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_balances as balances;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::marker::PhantomData;
|
||||
use rstd::result;
|
||||
use primitives::traits::{self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalise,
|
||||
MakePayment, Hash, AuxLookup};
|
||||
MakePayment, Hash};
|
||||
use codec::{Codec, Encode};
|
||||
use system::extrinsics_root;
|
||||
use primitives::{ApplyOutcome, ApplyError};
|
||||
@@ -94,7 +82,7 @@ impl<
|
||||
Address,
|
||||
System: system::Trait,
|
||||
Block: traits::Block<Header=System::Header, Hash=System::Hash>,
|
||||
Lookup: AuxLookup<Source=Address, Target=System::AccountId>,
|
||||
Lookup: traits::Lookup<Source=Address, Target=System::AccountId>,
|
||||
Payment: MakePayment<System::AccountId>,
|
||||
Finalisation: OnFinalise<System::BlockNumber>,
|
||||
> Executive<System, Block, Lookup, Payment, Finalisation> where
|
||||
@@ -186,20 +174,20 @@ impl<
|
||||
// Verify the signature is good.
|
||||
let xt = uxt.check_with(Lookup::lookup).map_err(internal::ApplyError::BadSignature)?;
|
||||
|
||||
if xt.sender() != &Default::default() {
|
||||
if let Some(sender) = xt.sender() {
|
||||
// check index
|
||||
let expected_index = <system::Module<System>>::account_nonce(xt.sender());
|
||||
let expected_index = <system::Module<System>>::account_nonce(sender);
|
||||
if xt.index() != &expected_index { return Err(
|
||||
if xt.index() < &expected_index { internal::ApplyError::Stale } else { internal::ApplyError::Future }
|
||||
) }
|
||||
|
||||
// pay any fees.
|
||||
Payment::make_payment(xt.sender(), encoded_len).map_err(|_| internal::ApplyError::CantPay)?;
|
||||
Payment::make_payment(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?;
|
||||
|
||||
// AUDIT: Under no circumstances may this function panic from here onwards.
|
||||
|
||||
// increment nonce in storage
|
||||
<system::Module<System>>::inc_account_nonce(xt.sender());
|
||||
<system::Module<System>>::inc_account_nonce(sender);
|
||||
}
|
||||
|
||||
// decode parameters and dispatch
|
||||
@@ -231,12 +219,12 @@ mod tests {
|
||||
use runtime_io::with_externalities;
|
||||
use substrate_primitives::{H256, KeccakHasher};
|
||||
use primitives::BuildStorage;
|
||||
use primitives::traits::{Identity, Header as HeaderT, BlakeTwo256, AuxLookup};
|
||||
use primitives::traits::{Header as HeaderT, BlakeTwo256, Lookup};
|
||||
use primitives::testing::{Digest, Header, Block};
|
||||
use system;
|
||||
|
||||
struct NullLookup;
|
||||
impl AuxLookup for NullLookup {
|
||||
impl Lookup for NullLookup {
|
||||
type Source = u64;
|
||||
type Target = u64;
|
||||
fn lookup(s: Self::Source) -> Result<Self::Target, &'static str> {
|
||||
@@ -244,30 +232,22 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Runtime {
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_event!{
|
||||
pub enum MetaEvent for Test {
|
||||
balances, session, staking
|
||||
pub enum MetaEvent for Runtime {
|
||||
balances
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Test;
|
||||
impl consensus::Trait for Test {
|
||||
const NOTE_OFFLINE_POSITION: u32 = 1;
|
||||
type Log = u64;
|
||||
type SessionKey = u64;
|
||||
type OnOfflineValidator = staking::Module<Test>;
|
||||
}
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = staking::Module<Test>;
|
||||
type EnsureAccountLiquid = staking::Module<Test>;
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
pub struct Runtime;
|
||||
impl system::Trait for Runtime {
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = substrate_primitives::H256;
|
||||
@@ -277,27 +257,21 @@ mod tests {
|
||||
type Header = Header;
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
type OnSessionChange = staking::Module<Test>;
|
||||
impl balances::Trait for Runtime {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = ();
|
||||
type EnsureAccountLiquid = ();
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl staking::Trait for Test {
|
||||
type OnRewardMinted = ();
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||
type Moment = u64;
|
||||
}
|
||||
|
||||
type TestXt = primitives::testing::TestXt<Call<Test>>;
|
||||
type Executive = super::Executive<Test, Block<TestXt>, NullLookup, balances::Module<Test>, (session::Module<Test>, staking::Module<Test>)>;
|
||||
type TestXt = primitives::testing::TestXt<Call<Runtime>>;
|
||||
type Executive = super::Executive<Runtime, Block<TestXt>, NullLookup, balances::Module<Runtime>, ()>;
|
||||
|
||||
#[test]
|
||||
fn staking_balance_transfer_dispatch_works() {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
t.extend(balances::GenesisConfig::<Test> {
|
||||
fn balance_transfer_dispatch_works() {
|
||||
let mut t = system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
|
||||
t.extend(balances::GenesisConfig::<Runtime> {
|
||||
balances: vec![(1, 111)],
|
||||
transaction_base_fee: 10,
|
||||
transaction_byte_fee: 0,
|
||||
@@ -306,34 +280,19 @@ mod tests {
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(staking::GenesisConfig::<Test> {
|
||||
sessions_per_era: 0,
|
||||
current_era: 0,
|
||||
intentions: vec![],
|
||||
validator_count: 0,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
early_era_slash: 0,
|
||||
session_reward: 0,
|
||||
offline_slash_grace: 0,
|
||||
}.build_storage().unwrap());
|
||||
let xt = primitives::testing::TestXt((1, 0, Call::transfer(2.into(), 69)));
|
||||
let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2.into(), 69));
|
||||
let mut t = runtime_io::TestExternalities::from(t);
|
||||
with_externalities(&mut t, || {
|
||||
Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default()));
|
||||
Executive::apply_extrinsic(xt).unwrap();
|
||||
assert_eq!(<balances::Module<Test>>::total_balance(&1), 32);
|
||||
assert_eq!(<balances::Module<Test>>::total_balance(&2), 69);
|
||||
assert_eq!(<balances::Module<Runtime>>::total_balance(&1), 32);
|
||||
assert_eq!(<balances::Module<Runtime>>::total_balance(&2), 69);
|
||||
});
|
||||
}
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
t.extend(balances::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.extend(consensus::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.extend(session::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.extend(staking::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
t.extend(timestamp::GenesisConfig::<Test>::default().build_storage().unwrap());
|
||||
let mut t = system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
|
||||
t.extend(balances::GenesisConfig::<Runtime>::default().build_storage().unwrap());
|
||||
t.into()
|
||||
}
|
||||
|
||||
@@ -347,7 +306,7 @@ mod tests {
|
||||
// Blake
|
||||
// state_root: hex!("02532989c613369596025dfcfc821339fc9861987003924913a5a1382f87034a").into(),
|
||||
// Keccak
|
||||
state_root: hex!("ffe27b4c3a8b421fa10592be61fb28eca7ebbe04cbfa99cdda9f703f35522569").into(),
|
||||
state_root: hex!("14a253cb1c5f38beeec8bee962a941b2ba0773b7593564fbe62b9c3a46784df5").into(),
|
||||
extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
@@ -381,7 +340,7 @@ mod tests {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("ffe27b4c3a8b421fa10592be61fb28eca7ebbe04cbfa99cdda9f703f35522569").into(),
|
||||
state_root: hex!("14a253cb1c5f38beeec8bee962a941b2ba0773b7593564fbe62b9c3a46784df5").into(),
|
||||
extrinsics_root: [0u8; 32].into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
@@ -393,11 +352,11 @@ mod tests {
|
||||
#[test]
|
||||
fn bad_extrinsic_not_inserted() {
|
||||
let mut t = new_test_ext();
|
||||
let xt = primitives::testing::TestXt((1, 42, Call::transfer(33.into(), 69)));
|
||||
let xt = primitives::testing::TestXt(Some(1), 42, Call::transfer(33.into(), 69));
|
||||
with_externalities(&mut t, || {
|
||||
Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default()));
|
||||
assert!(Executive::apply_extrinsic(xt).is_err());
|
||||
assert_eq!(<system::Module<Test>>::extrinsic_index(), Some(0));
|
||||
assert_eq!(<system::Module<Runtime>>::extrinsic_index(), Some(0));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,514 +0,0 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Generic implementations of Extrinsic/Header/Block.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Decode, Encode, Codec, Input, Output};
|
||||
use runtime_support::AuxDispatchable;
|
||||
use traits::{self, Member, SimpleArithmetic, SimpleBitOps, MaybeDisplay, Block as BlockT,
|
||||
Header as HeaderT, Hash as HashT, DigestItem as DigestItemT};
|
||||
use rstd::ops;
|
||||
use bft::Justification;
|
||||
|
||||
/// Definition of something that the external world might want to say.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct Extrinsic<Address, Index, Call> {
|
||||
/// Who signed it (note this is not a signature).
|
||||
pub signed: Address,
|
||||
/// The number of extrinsics have come before from the same signer.
|
||||
pub index: Index,
|
||||
/// The function that should be called.
|
||||
pub function: Call,
|
||||
}
|
||||
|
||||
/// A extrinsic right from the external world. Unchecked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct UncheckedExtrinsic<Address, Index, Call, Signature> {
|
||||
/// The actual extrinsic information.
|
||||
pub extrinsic: Extrinsic<Address, Index, Call>,
|
||||
/// The signature.
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl<Address, Index, Call, Signature> UncheckedExtrinsic<Address, Index, Call, Signature> {
|
||||
/// New instance.
|
||||
pub fn new(extrinsic: Extrinsic<Address, Index, Call>, signature: Signature) -> Self {
|
||||
UncheckedExtrinsic {
|
||||
extrinsic,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, AccountId, Index, Call, Signature> UncheckedExtrinsic<Address, Index, Call, ::MaybeUnsigned<Signature>> where
|
||||
Signature: traits::Verify<Signer=AccountId> + Default + Eq,
|
||||
AccountId: Default + Eq,
|
||||
{
|
||||
/// `true` if this extrinsic is signed.
|
||||
pub fn is_signed(&self) -> bool {
|
||||
self.signature.is_signed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, AccountId, Index, Call, Signature, ThisLookup> traits::Checkable<ThisLookup>
|
||||
for UncheckedExtrinsic<Address, Index, Call, ::MaybeUnsigned<Signature>>
|
||||
where
|
||||
Address: Member + Default + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
Signature: traits::Verify<Signer=AccountId> + Eq + Default,
|
||||
AccountId: Member + Default + MaybeDisplay,
|
||||
::MaybeUnsigned<Signature>: Member,
|
||||
Extrinsic<AccountId, Index, Call>: Codec,
|
||||
ThisLookup: FnOnce(Address) -> Result<AccountId, &'static str>,
|
||||
{
|
||||
type Checked = CheckedExtrinsic<AccountId, Index, Call>;
|
||||
|
||||
fn check_with(self, lookup: ThisLookup) -> Result<Self::Checked, &'static str> {
|
||||
if !self.is_signed() {
|
||||
Ok(CheckedExtrinsic(Extrinsic {
|
||||
signed: Default::default(),
|
||||
index: self.extrinsic.index,
|
||||
function: self.extrinsic.function,
|
||||
}))
|
||||
} else {
|
||||
let extrinsic: Extrinsic<AccountId, Index, Call>
|
||||
= Extrinsic {
|
||||
signed: lookup(self.extrinsic.signed)?,
|
||||
index: self.extrinsic.index,
|
||||
function: self.extrinsic.function,
|
||||
};
|
||||
if ::verify_encoded_lazy(&self.signature, &extrinsic, &extrinsic.signed) {
|
||||
Ok(CheckedExtrinsic(extrinsic))
|
||||
} else {
|
||||
Err("bad signature in extrinsic")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Index, Call, Signature> Decode
|
||||
for UncheckedExtrinsic<Address, Index, Call, Signature>
|
||||
where
|
||||
Signature: Decode,
|
||||
Extrinsic<Address, Index, Call>: Decode,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
|
||||
// will be a prefix of u32, which has the total number of bytes following (we don't need
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: u32 = Decode::decode(input)?;
|
||||
|
||||
Some(UncheckedExtrinsic::new(
|
||||
Decode::decode(input)?,
|
||||
Decode::decode(input)?
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Index, Call, Signature> Encode
|
||||
for UncheckedExtrinsic<Address, Index, Call, Signature>
|
||||
where
|
||||
Signature: Encode,
|
||||
Extrinsic<Address, Index, Call>: Encode,
|
||||
{
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
// need to prefix with the total length as u32 to ensure it's binary comptible with
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
self.extrinsic.encode_to(&mut v);
|
||||
self.signature.encode_to(&mut v);
|
||||
|
||||
let length = (v.len() - 4) as u32;
|
||||
length.using_encoded(|s| v[0..4].copy_from_slice(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: use derive when possible.
|
||||
#[cfg(feature = "std")]
|
||||
impl<Address, Index, Call, Signature> fmt::Debug for UncheckedExtrinsic<Address, Index, Call, Signature> where
|
||||
Address: fmt::Debug,
|
||||
Index: fmt::Debug,
|
||||
Call: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UncheckedExtrinsic({:?})", self.extrinsic)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-safe indicator that a extrinsic has been checked.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct CheckedExtrinsic<AccountId, Index, Call>
|
||||
(Extrinsic<AccountId, Index, Call>);
|
||||
|
||||
impl<AccountId, Index, Call> ops::Deref
|
||||
for CheckedExtrinsic<AccountId, Index, Call>
|
||||
where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member,
|
||||
{
|
||||
type Target = Extrinsic<AccountId, Index, Call>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call> traits::Applyable
|
||||
for CheckedExtrinsic<AccountId, Index, Call>
|
||||
where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member + AuxDispatchable<Aux = AccountId>,
|
||||
{
|
||||
type Index = Index;
|
||||
type AccountId = AccountId;
|
||||
|
||||
fn index(&self) -> &Self::Index {
|
||||
&self.0.index
|
||||
}
|
||||
|
||||
fn sender(&self) -> &Self::AccountId {
|
||||
&self.0.signed
|
||||
}
|
||||
|
||||
fn apply(self) -> Result<(), &'static str> {
|
||||
let xt = self.0;
|
||||
xt.function.dispatch(&xt.signed)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Digest<Item> {
|
||||
pub logs: Vec<Item>,
|
||||
}
|
||||
|
||||
impl<Item> Default for Digest<Item> {
|
||||
fn default() -> Self {
|
||||
Digest { logs: Vec::new(), }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Item> traits::Digest for Digest<Item> where
|
||||
Item: DigestItemT + Codec
|
||||
{
|
||||
type Item = Item;
|
||||
|
||||
fn logs(&self) -> &[Self::Item] {
|
||||
&self.logs
|
||||
}
|
||||
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
self.logs.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Abstraction over a block header for a substrate chain.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Header<Number, Hash: HashT, DigestItem> {
|
||||
/// The parent hash.
|
||||
pub parent_hash: <Hash as HashT>::Output,
|
||||
/// The block number.
|
||||
pub number: Number,
|
||||
/// The state trie merkle root
|
||||
pub state_root: <Hash as HashT>::Output,
|
||||
/// The merkle root of the extrinsics.
|
||||
pub extrinsics_root: <Hash as HashT>::Output,
|
||||
/// A chain-specific digest of data useful for light clients or referencing auxiliary data.
|
||||
pub digest: Digest<DigestItem>,
|
||||
}
|
||||
|
||||
// Hack to work around the fact that deriving deserialize doesn't work nicely with
|
||||
// the `hashing` trait used as a parameter.
|
||||
// dummy struct that uses the hash type directly.
|
||||
// https://github.com/serde-rs/serde/issues/1296
|
||||
#[cfg(feature = "std")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Deserialize)]
|
||||
struct DeserializeHeader<N, H, D> {
|
||||
parent_hash: H,
|
||||
number: N,
|
||||
state_root: H,
|
||||
extrinsics_root: H,
|
||||
digest: Digest<D>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<N, D, Hash: HashT> From<DeserializeHeader<N, Hash::Output, D>> for Header<N, Hash, D> {
|
||||
fn from(other: DeserializeHeader<N, Hash::Output, D>) -> Self {
|
||||
Header {
|
||||
parent_hash: other.parent_hash,
|
||||
number: other.number,
|
||||
state_root: other.state_root,
|
||||
extrinsics_root: other.extrinsics_root,
|
||||
digest: other.digest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, Number: 'a, Hash: 'a + HashT, DigestItem: 'a> Deserialize<'a> for Header<Number, Hash, DigestItem> where
|
||||
Number: Deserialize<'a>,
|
||||
Hash::Output: Deserialize<'a>,
|
||||
DigestItem: Deserialize<'a>,
|
||||
{
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
DeserializeHeader::<Number, Hash::Output, DigestItem>::deserialize(de).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [ToDr] Issue with bounds
|
||||
impl<Number, Hash, DigestItem> Decode for Header<Number, Hash, DigestItem> where
|
||||
Number: Decode,
|
||||
Hash: HashT,
|
||||
Hash::Output: Decode,
|
||||
DigestItem: Decode,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Header {
|
||||
parent_hash: Decode::decode(input)?,
|
||||
number: Decode::decode(input)?,
|
||||
state_root: Decode::decode(input)?,
|
||||
extrinsics_root: Decode::decode(input)?,
|
||||
digest: Decode::decode(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem> Encode for Header<Number, Hash, DigestItem> where
|
||||
Number: Encode,
|
||||
Hash: HashT,
|
||||
Hash::Output: Encode,
|
||||
DigestItem: Encode,
|
||||
{
|
||||
fn encode_to<T: Output>(&self, dest: &mut T) {
|
||||
dest.push(&self.parent_hash);
|
||||
dest.push(&self.number);
|
||||
dest.push(&self.state_root);
|
||||
dest.push(&self.extrinsics_root);
|
||||
dest.push(&self.digest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem> traits::Header for Header<Number, Hash, DigestItem> where
|
||||
Number: Member + ::rstd::hash::Hash + Copy + Codec + MaybeDisplay + SimpleArithmetic + Codec,
|
||||
Hash: HashT,
|
||||
DigestItem: DigestItemT + Codec,
|
||||
Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec,
|
||||
{
|
||||
type Number = Number;
|
||||
type Hash = <Hash as HashT>::Output;
|
||||
type Hashing = Hash;
|
||||
type Digest = Digest<DigestItem>;
|
||||
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn set_number(&mut self, num: Self::Number) { self.number = num }
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root }
|
||||
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root }
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash }
|
||||
|
||||
fn digest(&self) -> &Self::Digest { &self.digest }
|
||||
fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest }
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self {
|
||||
Header {
|
||||
number, extrinsics_root: extrinsics_root, state_root, parent_hash, digest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem> Header<Number, Hash, DigestItem> where
|
||||
Number: Member + ::rstd::hash::Hash + Copy + Codec + MaybeDisplay + SimpleArithmetic + Codec,
|
||||
Hash: HashT,
|
||||
DigestItem: DigestItemT + Codec,
|
||||
Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec,
|
||||
{
|
||||
/// Convenience helper for computing the hash of the header without having
|
||||
/// to import the trait.
|
||||
pub fn hash(&self) -> Hash::Output {
|
||||
Hash::hash_of(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Something to identify a block.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub enum BlockId<Block: BlockT> {
|
||||
/// Identify by block header hash.
|
||||
Hash(<<Block as BlockT>::Header as HeaderT>::Hash),
|
||||
/// Identify by block number.
|
||||
Number(<<Block as BlockT>::Header as HeaderT>::Number),
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockId<Block> {
|
||||
/// Create a block ID from a hash.
|
||||
pub fn hash(hash: Block::Hash) -> Self {
|
||||
BlockId::Hash(hash)
|
||||
}
|
||||
|
||||
/// Create a block ID from a number.
|
||||
pub fn number(number: <Block::Header as HeaderT>::Number) -> Self {
|
||||
BlockId::Number(number)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Copy for BlockId<Block> {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Block: BlockT> fmt::Display for BlockId<Block> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Block<Header, Extrinsic> {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// The accompanying extrinsics.
|
||||
pub extrinsics: Vec<Extrinsic>,
|
||||
}
|
||||
|
||||
impl<Header, Extrinsic> traits::Block for Block<Header, Extrinsic>
|
||||
where
|
||||
Header: HeaderT,
|
||||
Extrinsic: Member + Codec,
|
||||
{
|
||||
type Extrinsic = Extrinsic;
|
||||
type Header = Header;
|
||||
type Hash = <Self::Header as traits::Header>::Hash;
|
||||
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.extrinsics[..]
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.extrinsics)
|
||||
}
|
||||
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
|
||||
Block { header, extrinsics }
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block and justification.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct SignedBlock<Header, Extrinsic, Hash> {
|
||||
/// Full block.
|
||||
pub block: Block<Header, Extrinsic>,
|
||||
/// Block header justification.
|
||||
pub justification: Justification<Hash>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use codec::{Decode, Encode};
|
||||
use substrate_primitives::{H256, H512};
|
||||
use super::{Digest, Header, UncheckedExtrinsic, Extrinsic};
|
||||
|
||||
type Block = super::Block<
|
||||
Header<u64, ::traits::BlakeTwo256, Vec<u8>>,
|
||||
UncheckedExtrinsic<H256, u64, u64, ::Ed25519Signature>,
|
||||
>;
|
||||
|
||||
#[test]
|
||||
fn block_roundtrip_serialization() {
|
||||
let block: Block = Block {
|
||||
header: Header {
|
||||
parent_hash: [0u8; 32].into(),
|
||||
number: 100_000,
|
||||
state_root: [1u8; 32].into(),
|
||||
extrinsics_root: [2u8; 32].into(),
|
||||
digest: Digest { logs: vec![vec![1, 2, 3], vec![4, 5, 6]] },
|
||||
},
|
||||
extrinsics: vec![
|
||||
UncheckedExtrinsic::new(
|
||||
Extrinsic {
|
||||
signed: [255u8; 32].into(),
|
||||
index: 0,
|
||||
function: 100,
|
||||
},
|
||||
H512::from([0u8; 64]).into()
|
||||
),
|
||||
UncheckedExtrinsic::new(
|
||||
Extrinsic {
|
||||
signed: [128u8; 32].into(),
|
||||
index: 100,
|
||||
function: 99,
|
||||
},
|
||||
H512::from([255u8; 64]).into()
|
||||
)
|
||||
]
|
||||
};
|
||||
|
||||
{
|
||||
let encoded = ::serde_json::to_vec(&block).unwrap();
|
||||
let decoded: Block = ::serde_json::from_slice(&encoded).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
}
|
||||
{
|
||||
let encoded = block.encode();
|
||||
let decoded = Block::decode(&mut &encoded[..]).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Generic implementation of a block and associated items.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::Codec;
|
||||
use traits::{self, Member, Block as BlockT, Header as HeaderT};
|
||||
use bft::Justification;
|
||||
|
||||
/// Something to identify a block.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub enum BlockId<Block: BlockT> {
|
||||
/// Identify by block header hash.
|
||||
Hash(<<Block as BlockT>::Header as HeaderT>::Hash),
|
||||
/// Identify by block number.
|
||||
Number(<<Block as BlockT>::Header as HeaderT>::Number),
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockId<Block> {
|
||||
/// Create a block ID from a hash.
|
||||
pub fn hash(hash: Block::Hash) -> Self {
|
||||
BlockId::Hash(hash)
|
||||
}
|
||||
|
||||
/// Create a block ID from a number.
|
||||
pub fn number(number: <Block::Header as HeaderT>::Number) -> Self {
|
||||
BlockId::Number(number)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Copy for BlockId<Block> {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Block: BlockT> fmt::Display for BlockId<Block> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Block<Header, Extrinsic> {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// The accompanying extrinsics.
|
||||
pub extrinsics: Vec<Extrinsic>,
|
||||
}
|
||||
|
||||
impl<Header, Extrinsic> traits::Block for Block<Header, Extrinsic>
|
||||
where
|
||||
Header: HeaderT,
|
||||
Extrinsic: Member + Codec,
|
||||
{
|
||||
type Extrinsic = Extrinsic;
|
||||
type Header = Header;
|
||||
type Hash = <Self::Header as traits::Header>::Hash;
|
||||
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.extrinsics[..]
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.extrinsics)
|
||||
}
|
||||
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
|
||||
Block { header, extrinsics }
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block and justification.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct SignedBlock<Header, Extrinsic, Hash> {
|
||||
/// Full block.
|
||||
pub block: Block<Header, Extrinsic>,
|
||||
/// Block header justification.
|
||||
pub justification: Justification<Hash>,
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Generic implementation of an extrinsic that has passed the verification
|
||||
//! stage.
|
||||
|
||||
use runtime_support::Dispatchable;
|
||||
use traits::{self, Member, SimpleArithmetic, MaybeDisplay};
|
||||
|
||||
/// Definition of something that the external world might want to say; its
|
||||
/// existence implies that it has been checked and is good, particularly with
|
||||
/// regards to the signature.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
pub struct CheckedExtrinsic<AccountId, Index, Call> {
|
||||
/// Who this purports to be from, if anyone (note this is not a signature).
|
||||
pub signed: Option<AccountId>,
|
||||
/// The number of extrinsics have come before from the same signer.
|
||||
pub index: Index,
|
||||
/// The function that should be called.
|
||||
pub function: Call,
|
||||
}
|
||||
|
||||
impl<AccountId, Index, Call> traits::Applyable
|
||||
for CheckedExtrinsic<AccountId, Index, Call>
|
||||
where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Index: Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Member + Dispatchable,
|
||||
<Call as Dispatchable>::Origin: From<Option<AccountId>>
|
||||
{
|
||||
type Index = Index;
|
||||
type AccountId = AccountId;
|
||||
|
||||
fn index(&self) -> &Self::Index {
|
||||
&self.index
|
||||
}
|
||||
|
||||
fn sender(&self) -> Option<&Self::AccountId> {
|
||||
self.signed.as_ref()
|
||||
}
|
||||
|
||||
fn apply(self) -> Result<(), &'static str> {
|
||||
self.function.dispatch(self.signed.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Generic implementation of a header and digest.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Decode, Encode, Codec, Input, Output};
|
||||
use traits::{self, Member, SimpleArithmetic, SimpleBitOps, MaybeDisplay,
|
||||
Hash as HashT, DigestItem as DigestItemT};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct Digest<Item> {
|
||||
pub logs: Vec<Item>,
|
||||
}
|
||||
|
||||
impl<Item> Default for Digest<Item> {
|
||||
fn default() -> Self {
|
||||
Digest { logs: Vec::new(), }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Item> traits::Digest for Digest<Item> where
|
||||
Item: DigestItemT + Codec
|
||||
{
|
||||
type Item = Item;
|
||||
|
||||
fn logs(&self) -> &[Self::Item] {
|
||||
&self.logs
|
||||
}
|
||||
|
||||
fn push(&mut self, item: Self::Item) {
|
||||
self.logs.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a block header for a substrate chain.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Header<Number, Hash: HashT, DigestItem> {
|
||||
/// The parent hash.
|
||||
pub parent_hash: <Hash as HashT>::Output,
|
||||
/// The block number.
|
||||
pub number: Number,
|
||||
/// The state trie merkle root
|
||||
pub state_root: <Hash as HashT>::Output,
|
||||
/// The merkle root of the extrinsics.
|
||||
pub extrinsics_root: <Hash as HashT>::Output,
|
||||
/// A chain-specific digest of data useful for light clients or referencing auxiliary data.
|
||||
pub digest: Digest<DigestItem>,
|
||||
}
|
||||
|
||||
// Hack to work around the fact that deriving deserialize doesn't work nicely with
|
||||
// the `hashing` trait used as a parameter.
|
||||
// dummy struct that uses the hash type directly.
|
||||
// https://github.com/serde-rs/serde/issues/1296
|
||||
#[cfg(feature = "std")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Deserialize)]
|
||||
struct DeserializeHeader<N, H, D> {
|
||||
parent_hash: H,
|
||||
number: N,
|
||||
state_root: H,
|
||||
extrinsics_root: H,
|
||||
digest: Digest<D>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<N, D, Hash: HashT> From<DeserializeHeader<N, Hash::Output, D>> for Header<N, Hash, D> {
|
||||
fn from(other: DeserializeHeader<N, Hash::Output, D>) -> Self {
|
||||
Header {
|
||||
parent_hash: other.parent_hash,
|
||||
number: other.number,
|
||||
state_root: other.state_root,
|
||||
extrinsics_root: other.extrinsics_root,
|
||||
digest: other.digest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, Number: 'a, Hash: 'a + HashT, DigestItem: 'a> Deserialize<'a> for Header<Number, Hash, DigestItem> where
|
||||
Number: Deserialize<'a>,
|
||||
Hash::Output: Deserialize<'a>,
|
||||
DigestItem: Deserialize<'a>,
|
||||
{
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
DeserializeHeader::<Number, Hash::Output, DigestItem>::deserialize(de).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [ToDr] Issue with bounds
|
||||
impl<Number, Hash, DigestItem> Decode for Header<Number, Hash, DigestItem> where
|
||||
Number: Decode,
|
||||
Hash: HashT,
|
||||
Hash::Output: Decode,
|
||||
DigestItem: Decode,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(Header {
|
||||
parent_hash: Decode::decode(input)?,
|
||||
number: Decode::decode(input)?,
|
||||
state_root: Decode::decode(input)?,
|
||||
extrinsics_root: Decode::decode(input)?,
|
||||
digest: Decode::decode(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem> Encode for Header<Number, Hash, DigestItem> where
|
||||
Number: Encode,
|
||||
Hash: HashT,
|
||||
Hash::Output: Encode,
|
||||
DigestItem: Encode,
|
||||
{
|
||||
fn encode_to<T: Output>(&self, dest: &mut T) {
|
||||
dest.push(&self.parent_hash);
|
||||
dest.push(&self.number);
|
||||
dest.push(&self.state_root);
|
||||
dest.push(&self.extrinsics_root);
|
||||
dest.push(&self.digest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem> traits::Header for Header<Number, Hash, DigestItem> where
|
||||
Number: Member + ::rstd::hash::Hash + Copy + Codec + MaybeDisplay + SimpleArithmetic + Codec,
|
||||
Hash: HashT,
|
||||
DigestItem: DigestItemT + Codec,
|
||||
Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec,
|
||||
{
|
||||
type Number = Number;
|
||||
type Hash = <Hash as HashT>::Output;
|
||||
type Hashing = Hash;
|
||||
type Digest = Digest<DigestItem>;
|
||||
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn set_number(&mut self, num: Self::Number) { self.number = num }
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root }
|
||||
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root }
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash }
|
||||
|
||||
fn digest(&self) -> &Self::Digest { &self.digest }
|
||||
fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest }
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Self::Digest
|
||||
) -> Self {
|
||||
Header {
|
||||
number, extrinsics_root: extrinsics_root, state_root, parent_hash, digest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash, DigestItem> Header<Number, Hash, DigestItem> where
|
||||
Number: Member + ::rstd::hash::Hash + Copy + Codec + MaybeDisplay + SimpleArithmetic + Codec,
|
||||
Hash: HashT,
|
||||
DigestItem: DigestItemT + Codec,
|
||||
Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec,
|
||||
{
|
||||
/// Convenience helper for computing the hash of the header without having
|
||||
/// to import the trait.
|
||||
pub fn hash(&self) -> Hash::Output {
|
||||
Hash::hash_of(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Generic implementations of Extrinsic/Header/Block.
|
||||
|
||||
mod unchecked_extrinsic;
|
||||
mod checked_extrinsic;
|
||||
mod header;
|
||||
mod block;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::unchecked_extrinsic::UncheckedExtrinsic;
|
||||
pub use self::checked_extrinsic::CheckedExtrinsic;
|
||||
pub use self::header::{Header, Digest};
|
||||
pub use self::block::{Block, SignedBlock, BlockId};
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Tests for the generic implementations of Extrinsic/Header/Block.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use substrate_primitives::{H256, H512};
|
||||
use super::{Digest, Header, UncheckedExtrinsic};
|
||||
|
||||
type Block = super::Block<
|
||||
Header<u64, ::traits::BlakeTwo256, Vec<u8>>,
|
||||
UncheckedExtrinsic<H256, u64, u64, ::Ed25519Signature>,
|
||||
>;
|
||||
|
||||
#[test]
|
||||
fn block_roundtrip_serialization() {
|
||||
let block: Block = Block {
|
||||
header: Header {
|
||||
parent_hash: [0u8; 32].into(),
|
||||
number: 100_000,
|
||||
state_root: [1u8; 32].into(),
|
||||
extrinsics_root: [2u8; 32].into(),
|
||||
digest: Digest { logs: vec![vec![1, 2, 3], vec![4, 5, 6]] },
|
||||
},
|
||||
extrinsics: vec![
|
||||
UncheckedExtrinsic::new_signed(
|
||||
0,
|
||||
100,
|
||||
[255u8; 32].into(),
|
||||
H512::from([0u8; 64]).into()
|
||||
),
|
||||
UncheckedExtrinsic::new_signed(
|
||||
100,
|
||||
99,
|
||||
[128u8; 32].into(),
|
||||
H512::from([255u8; 64]).into()
|
||||
)
|
||||
]
|
||||
};
|
||||
|
||||
{
|
||||
let encoded = ::serde_json::to_vec(&block).unwrap();
|
||||
let decoded: Block = ::serde_json::from_slice(&encoded).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
}
|
||||
{
|
||||
let encoded = block.encode();
|
||||
let decoded = Block::decode(&mut &encoded[..]).unwrap();
|
||||
|
||||
assert_eq!(block, decoded);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// Copyright 2017 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/>.
|
||||
|
||||
//! Generic implementation of an unchecked (pre-verification) extrinsic.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Decode, Encode, Input};
|
||||
use traits::{self, Member, SimpleArithmetic, MaybeDisplay};
|
||||
use super::CheckedExtrinsic;
|
||||
|
||||
/// A extrinsic right from the external world. This is unchecked and so
|
||||
/// can contain a signature.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct UncheckedExtrinsic<Address, Index, Call, Signature> {
|
||||
/// The signature and address, if this is a signed extrinsic.
|
||||
pub signature: Option<(Address, Signature)>,
|
||||
/// The number of extrinsics have come before from the same signer.
|
||||
pub index: Index,
|
||||
/// The function that should be called.
|
||||
pub function: Call,
|
||||
}
|
||||
|
||||
impl<Address, Index, Call, Signature> UncheckedExtrinsic<Address, Index, Call, Signature> {
|
||||
/// New instance of a signed extrinsic aka "transaction".
|
||||
pub fn new_signed(index: Index, function: Call, signed: Address, signature: Signature) -> Self {
|
||||
UncheckedExtrinsic {
|
||||
signature: Some((signed, signature)),
|
||||
index,
|
||||
function,
|
||||
}
|
||||
}
|
||||
|
||||
/// New instance of an unsigned extrinsic aka "inherent".
|
||||
pub fn new_unsigned(index: Index, function: Call) -> Self {
|
||||
UncheckedExtrinsic {
|
||||
signature: None,
|
||||
index,
|
||||
function,
|
||||
}
|
||||
}
|
||||
|
||||
/// `true` if there is a signature.
|
||||
pub fn is_signed(&self) -> bool {
|
||||
self.signature.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, AccountId, Index, Call, Signature, ThisLookup> traits::Checkable<ThisLookup>
|
||||
for UncheckedExtrinsic<Address, Index, Call, Signature>
|
||||
where
|
||||
Address: Member + MaybeDisplay,
|
||||
Index: Encode + Member + MaybeDisplay + SimpleArithmetic,
|
||||
Call: Encode + Member,
|
||||
Signature: Member + traits::Verify<Signer=AccountId>,
|
||||
AccountId: Member + MaybeDisplay,
|
||||
ThisLookup: FnOnce(Address) -> Result<AccountId, &'static str>,
|
||||
{
|
||||
type Checked = CheckedExtrinsic<AccountId, Index, Call>;
|
||||
|
||||
fn check_with(self, lookup: ThisLookup) -> Result<Self::Checked, &'static str> {
|
||||
Ok(match self.signature {
|
||||
Some((signed, signature)) => {
|
||||
let payload = (self.index, self.function);
|
||||
let signed = lookup(signed)?;
|
||||
if !::verify_encoded_lazy(&signature, &payload, &signed) {
|
||||
return Err("bad signature in extrinsic")
|
||||
}
|
||||
CheckedExtrinsic {
|
||||
signed: Some(signed),
|
||||
index: payload.0,
|
||||
function: payload.1,
|
||||
}
|
||||
}
|
||||
None => CheckedExtrinsic {
|
||||
signed: None,
|
||||
index: self.index,
|
||||
function: self.function,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Index, Call, Signature> Decode
|
||||
for UncheckedExtrinsic<Address, Index, Call, Signature>
|
||||
where
|
||||
Address: Decode,
|
||||
Signature: Decode,
|
||||
Index: Decode,
|
||||
Call: Decode,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
|
||||
// will be a prefix of u32, which has the total number of bytes following (we don't need
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: u32 = Decode::decode(input)?;
|
||||
|
||||
Some(UncheckedExtrinsic {
|
||||
signature: Decode::decode(input)?,
|
||||
index: Decode::decode(input)?,
|
||||
function: Decode::decode(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Index, Call, Signature> Encode
|
||||
for UncheckedExtrinsic<Address, Index, Call, Signature>
|
||||
where
|
||||
Address: Encode,
|
||||
Signature: Encode,
|
||||
Index: Encode,
|
||||
Call: Encode,
|
||||
{
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
// need to prefix with the total length as u32 to ensure it's binary comptible with
|
||||
// Vec<u8>. we'll make room for it here, then overwrite once we know the length.
|
||||
v.extend(&[0u8; 4]);
|
||||
|
||||
self.signature.encode_to(&mut v);
|
||||
self.index.encode_to(&mut v);
|
||||
self.function.encode_to(&mut v);
|
||||
|
||||
let length = (v.len() - 4) as u32;
|
||||
length.using_encoded(|s| v[0..4].copy_from_slice(s));
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: use derive when possible.
|
||||
#[cfg(feature = "std")]
|
||||
impl<Address, Index, Call, Signature> fmt::Debug for UncheckedExtrinsic<Address, Index, Call, Signature> where
|
||||
Address: fmt::Debug,
|
||||
Index: fmt::Debug,
|
||||
Call: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UncheckedExtrinsic({:?}, {:?}, {:?})", self.signature.as_ref().map(|x| &x.0), self.function, self.index)
|
||||
}
|
||||
}
|
||||
@@ -146,44 +146,6 @@ impl codec::Encode for ApplyError {
|
||||
/// Result from attempt to apply an extrinsic.
|
||||
pub type ApplyResult = Result<ApplyOutcome, ApplyError>;
|
||||
|
||||
/// Potentially "unsigned" signature verification.
|
||||
#[derive(Eq, PartialEq, Clone, Default, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub struct MaybeUnsigned<T>(pub T);
|
||||
|
||||
impl<T: Verify> MaybeUnsigned<T> where
|
||||
T: Default + Eq,
|
||||
<T as Verify>::Signer: Default + Eq,
|
||||
{
|
||||
fn is_signed(&self) -> bool {
|
||||
self.0 != T::default()
|
||||
}
|
||||
|
||||
fn is_addressed(&self, signer: &<Self as Verify>::Signer) -> bool {
|
||||
signer != &Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Verify> Verify for MaybeUnsigned<T> where
|
||||
T: Default + Eq,
|
||||
<T as Verify>::Signer: Default + Eq,
|
||||
{
|
||||
type Signer = T::Signer;
|
||||
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool {
|
||||
if !self.is_signed() {
|
||||
!self.is_addressed(signer)
|
||||
} else {
|
||||
self.0.verify(msg, signer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for MaybeUnsigned<T> {
|
||||
fn from(t: T) -> Self {
|
||||
MaybeUnsigned(t)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify a signature on an encoded value in a lazy manner. This can be
|
||||
/// an optimization if the signature scheme has an "unsigned" escape hash.
|
||||
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(sig: &V, item: &T, signer: &V::Signer) -> bool {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::fmt::Debug;
|
||||
use codec::Codec;
|
||||
use runtime_support::AuxDispatchable;
|
||||
use runtime_support::Dispatchable;
|
||||
use traits::{self, Checkable, Applyable, BlakeTwo256};
|
||||
|
||||
pub use substrate_primitives::H256;
|
||||
@@ -116,16 +116,19 @@ impl<Xt: 'static + Codec + Sized + Send + Sync + Serialize + DeserializeOwned +
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)]
|
||||
pub struct TestXt<Call>(pub (u64, u64, Call));
|
||||
pub struct TestXt<Call>(pub Option<u64>, pub u64, pub Call);
|
||||
|
||||
impl<Call: Codec + Sync + Send + Serialize + AuxDispatchable, Context> Checkable<Context> for TestXt<Call> {
|
||||
impl<Call: Codec + Sync + Send + Serialize + Dispatchable, Context> Checkable<Context> for TestXt<Call> {
|
||||
type Checked = Self;
|
||||
fn check_with(self, _: Context) -> Result<Self::Checked, &'static str> { Ok(self) }
|
||||
}
|
||||
impl<Call: AuxDispatchable<Aux = u64> + Codec + Sized + Send + Sync + Serialize + DeserializeOwned + Clone + Eq + Debug> Applyable for TestXt<Call> {
|
||||
impl<Call> Applyable for TestXt<Call> where
|
||||
Call: Sized + Send + Sync + Clone + Eq + Dispatchable + Codec + Debug + Serialize + DeserializeOwned,
|
||||
<Call as Dispatchable>::Origin: From<Option<u64>>
|
||||
{
|
||||
type AccountId = u64;
|
||||
type Index = u64;
|
||||
fn sender(&self) -> &u64 { &(self.0).0 }
|
||||
fn index(&self) -> &u64 { &(self.0).1 }
|
||||
fn apply(self) -> Result<(), &'static str> { (self.0).2.dispatch(&(self.0).0) }
|
||||
fn sender(&self) -> Option<&u64> { self.0.as_ref() }
|
||||
fn index(&self) -> &u64 { &self.1 }
|
||||
fn apply(self) -> Result<(), &'static str> { self.2.dispatch(self.0.into()) }
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ pub trait Verify {
|
||||
}
|
||||
|
||||
/// Means of changing one type into another in a manner dependent on the source type.
|
||||
pub trait AuxLookup {
|
||||
pub trait Lookup {
|
||||
/// Type to lookup from.
|
||||
type Source;
|
||||
/// Type to lookup into.
|
||||
@@ -108,24 +108,6 @@ impl<T> Convert<T, ()> for () {
|
||||
fn convert(_: T) -> () { () }
|
||||
}
|
||||
|
||||
pub trait MaybeEmpty {
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
// AccountId is `u64` in tests
|
||||
impl MaybeEmpty for u64 {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
// AccountId is H256 in production
|
||||
impl MaybeEmpty for substrate_primitives::H256 {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RefInto<T> {
|
||||
fn ref_into(&self) -> &T;
|
||||
}
|
||||
@@ -427,7 +409,7 @@ pub trait Applyable: Sized + Send + Sync {
|
||||
type AccountId: Member + MaybeDisplay;
|
||||
type Index: Member + MaybeDisplay + SimpleArithmetic;
|
||||
fn index(&self) -> &Self::Index;
|
||||
fn sender(&self) -> &Self::AccountId;
|
||||
fn sender(&self) -> Option<&Self::AccountId>;
|
||||
fn apply(self) -> Result<(), &'static str>;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,10 @@ extern crate substrate_runtime_system as system;
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{Zero, One, RefInto, OnFinalise, Convert, As};
|
||||
use primitives::traits::{Zero, One, OnFinalise, Convert, As};
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use runtime_support::dispatch::Result;
|
||||
use system::{ensure_signed, ensure_root};
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::collections::HashMap;
|
||||
@@ -75,17 +76,11 @@ pub trait Trait: timestamp::Trait {
|
||||
pub type Event<T> = RawEvent<<T as system::Trait>::BlockNumber>;
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn set_key(origin, key: T::SessionKey) -> Result;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn set_key(aux, key: T::SessionKey) -> Result;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_length(new: T::BlockNumber) -> Result;
|
||||
fn force_new_session(apply_rewards: bool) -> Result;
|
||||
fn set_length(origin, new: T::BlockNumber) -> Result;
|
||||
fn force_new_session(origin, apply_rewards: bool) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,26 +134,34 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next
|
||||
/// session.
|
||||
fn set_key(aux: &T::PublicAux, key: T::SessionKey) -> Result {
|
||||
fn set_key(origin: T::Origin, key: T::SessionKey) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
// set new value for next session
|
||||
<NextKeyFor<T>>::insert(aux.ref_into(), key);
|
||||
<NextKeyFor<T>>::insert(who, key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set a new era length. Won't kick in until the next era change (at current length).
|
||||
fn set_length(new: T::BlockNumber) -> Result {
|
||||
fn set_length(origin: T::Origin, new: T::BlockNumber) -> Result {
|
||||
ensure_root(origin)?;
|
||||
<NextSessionLength<T>>::put(new);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Forces a new session.
|
||||
pub fn force_new_session(apply_rewards: bool) -> Result {
|
||||
<ForcingNewSession<T>>::put(apply_rewards);
|
||||
Ok(())
|
||||
pub fn force_new_session(origin: T::Origin, apply_rewards: bool) -> Result {
|
||||
ensure_root(origin)?;
|
||||
Self::apply_force_new_session(apply_rewards)
|
||||
}
|
||||
|
||||
// INTERNAL API (available to other runtime modules)
|
||||
|
||||
/// Forces a new session, no origin.
|
||||
pub fn apply_force_new_session(apply_rewards: bool) -> Result {
|
||||
<ForcingNewSession<T>>::put(apply_rewards);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the current set of validators.
|
||||
///
|
||||
/// Called by `staking::next_era()` only. `next_session` should be called after this in order to
|
||||
@@ -291,6 +294,10 @@ mod tests {
|
||||
use primitives::traits::{Identity, BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_origin!{
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
impl consensus::Trait for Test {
|
||||
@@ -300,7 +307,7 @@ mod tests {
|
||||
type OnOfflineValidator = ();
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -353,7 +360,7 @@ mod tests {
|
||||
fn should_work_with_early_exit() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Session::set_length(10));
|
||||
assert_ok!(Session::set_length(Origin::ROOT, 10));
|
||||
assert_eq!(Session::blocks_remaining(), 1);
|
||||
Session::check_rotate_session(1);
|
||||
|
||||
@@ -365,7 +372,7 @@ mod tests {
|
||||
System::set_block_number(7);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
assert_eq!(Session::blocks_remaining(), 5);
|
||||
assert_ok!(Session::force_new_session(false));
|
||||
assert_ok!(Session::force_new_session(Origin::ROOT, false));
|
||||
Session::check_rotate_session(7);
|
||||
|
||||
System::set_block_number(8);
|
||||
@@ -388,14 +395,14 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
// Block 1: Change to length 3; no visible change.
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Session::set_length(3));
|
||||
assert_ok!(Session::set_length(Origin::ROOT, 3));
|
||||
Session::check_rotate_session(1);
|
||||
assert_eq!(Session::length(), 2);
|
||||
assert_eq!(Session::current_index(), 0);
|
||||
|
||||
// Block 2: Length now changed to 3. Index incremented.
|
||||
System::set_block_number(2);
|
||||
assert_ok!(Session::set_length(3));
|
||||
assert_ok!(Session::set_length(Origin::ROOT, 3));
|
||||
Session::check_rotate_session(2);
|
||||
assert_eq!(Session::length(), 3);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
@@ -408,7 +415,7 @@ mod tests {
|
||||
|
||||
// Block 4: Change to length 2; no visible change.
|
||||
System::set_block_number(4);
|
||||
assert_ok!(Session::set_length(2));
|
||||
assert_ok!(Session::set_length(Origin::ROOT, 2));
|
||||
Session::check_rotate_session(4);
|
||||
assert_eq!(Session::length(), 3);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
@@ -448,7 +455,7 @@ mod tests {
|
||||
|
||||
// Block 3: Set new key for validator 2; no visible change.
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Session::set_key(&2, 5));
|
||||
assert_ok!(Session::set_key(Origin::signed(2), 5));
|
||||
assert_eq!(Consensus::authorities(), vec![1, 2, 3]);
|
||||
|
||||
Session::check_rotate_session(3);
|
||||
|
||||
@@ -49,9 +49,10 @@ use rstd::prelude::*;
|
||||
use runtime_support::{Parameter, StorageValue, StorageMap};
|
||||
use runtime_support::dispatch::Result;
|
||||
use session::OnSessionChange;
|
||||
use primitives::traits::{Zero, One, Bounded, RefInto, OnFinalise,
|
||||
As, AuxLookup};
|
||||
use primitives::traits::{Zero, One, Bounded, OnFinalise,
|
||||
As, Lookup};
|
||||
use balances::{address::Address, OnMinted};
|
||||
use system::{ensure_root, ensure_signed};
|
||||
|
||||
mod mock;
|
||||
|
||||
@@ -104,25 +105,19 @@ pub trait Trait: balances::Trait + session::Trait {
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(bound(deserialize = "T::Balance: ::serde::de::DeserializeOwned")))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn stake(aux) -> Result;
|
||||
fn unstake(aux, intentions_index: u32) -> Result;
|
||||
fn nominate(aux, target: Address<T::AccountId, T::AccountIndex>) -> Result;
|
||||
fn unnominate(aux, target_index: u32) -> Result;
|
||||
fn register_preferences(aux, intentions_index: u32, prefs: ValidatorPrefs<T::Balance>) -> Result;
|
||||
}
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn stake(origin) -> Result;
|
||||
fn unstake(origin, intentions_index: u32) -> Result;
|
||||
fn nominate(origin, target: Address<T::AccountId, T::AccountIndex>) -> Result;
|
||||
fn unnominate(origin, target_index: u32) -> Result;
|
||||
fn register_preferences(origin, intentions_index: u32, prefs: ValidatorPrefs<T::Balance>) -> Result;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
fn set_sessions_per_era(new: T::BlockNumber) -> Result;
|
||||
fn set_bonding_duration(new: T::BlockNumber) -> Result;
|
||||
fn set_validator_count(new: u32) -> Result;
|
||||
fn force_new_era(apply_rewards: bool) -> Result;
|
||||
fn set_offline_slash_grace(new: u32) -> Result;
|
||||
fn set_sessions_per_era(origin, new: T::BlockNumber) -> Result;
|
||||
fn set_bonding_duration(origin, new: T::BlockNumber) -> Result;
|
||||
fn set_validator_count(origin, new: u32) -> Result;
|
||||
fn force_new_era(origin, apply_rewards: bool) -> Result;
|
||||
fn set_offline_slash_grace(origin, new: u32) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +187,11 @@ decl_storage! {
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
|
||||
/// Deposit one of this module's events.
|
||||
fn deposit_event(event: Event<T>) {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
// PUBLIC IMMUTABLES
|
||||
|
||||
/// MinimumValidatorCount getter, introduces a default.
|
||||
@@ -232,60 +232,62 @@ impl<T: Trait> Module<T> {
|
||||
/// Declare the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
fn stake(aux: &T::PublicAux) -> Result {
|
||||
let aux = aux.ref_into();
|
||||
ensure!(Self::nominating(aux).is_none(), "Cannot stake if already nominating.");
|
||||
fn stake(origin: T::Origin) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
ensure!(Self::nominating(&who).is_none(), "Cannot stake if already nominating.");
|
||||
let mut intentions = <Intentions<T>>::get();
|
||||
// can't be in the list twice.
|
||||
ensure!(intentions.iter().find(|&t| t == aux).is_none(), "Cannot stake if already staked.");
|
||||
intentions.push(aux.clone());
|
||||
ensure!(intentions.iter().find(|&t| t == &who).is_none(), "Cannot stake if already staked.");
|
||||
|
||||
<Bondage<T>>::insert(&who, T::BlockNumber::max_value());
|
||||
intentions.push(who);
|
||||
<Intentions<T>>::put(intentions);
|
||||
<Bondage<T>>::insert(aux, T::BlockNumber::max_value());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retract the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
fn unstake(aux: &T::PublicAux, intentions_index: u32) -> Result {
|
||||
fn unstake(origin: T::Origin, intentions_index: u32) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
// unstake fails in degenerate case of having too few existing staked parties
|
||||
if Self::intentions().len() <= Self::minimum_validator_count() {
|
||||
return Err("cannot unstake when there are too few staked participants")
|
||||
}
|
||||
Self::apply_unstake(aux.ref_into(), intentions_index as usize)
|
||||
Self::apply_unstake(&who, intentions_index as usize)
|
||||
}
|
||||
|
||||
fn nominate(aux: &T::PublicAux, target: Address<T::AccountId, T::AccountIndex>) -> Result {
|
||||
fn nominate(origin: T::Origin, target: Address<T::AccountId, T::AccountIndex>) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
let target = <balances::Module<T>>::lookup(target)?;
|
||||
let aux = aux.ref_into();
|
||||
|
||||
ensure!(Self::nominating(aux).is_none(), "Cannot nominate if already nominating.");
|
||||
ensure!(Self::intentions().iter().find(|&t| t == aux.ref_into()).is_none(), "Cannot nominate if already staked.");
|
||||
ensure!(Self::nominating(&who).is_none(), "Cannot nominate if already nominating.");
|
||||
ensure!(Self::intentions().iter().find(|&t| t == &who).is_none(), "Cannot nominate if already staked.");
|
||||
|
||||
// update nominators_for
|
||||
let mut t = Self::nominators_for(&target);
|
||||
t.push(aux.clone());
|
||||
t.push(who.clone());
|
||||
<NominatorsFor<T>>::insert(&target, t);
|
||||
|
||||
// update nominating
|
||||
<Nominating<T>>::insert(aux, &target);
|
||||
<Nominating<T>>::insert(&who, &target);
|
||||
|
||||
// Update bondage
|
||||
<Bondage<T>>::insert(aux.ref_into(), T::BlockNumber::max_value());
|
||||
<Bondage<T>>::insert(&who, T::BlockNumber::max_value());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Will panic if called when source isn't currently nominating target.
|
||||
/// Updates Nominating, NominatorsFor and NominationBalance.
|
||||
fn unnominate(aux: &T::PublicAux, target_index: u32) -> Result {
|
||||
let source = aux.ref_into();
|
||||
fn unnominate(origin: T::Origin, target_index: u32) -> Result {
|
||||
let source = ensure_signed(origin)?;
|
||||
let target_index = target_index as usize;
|
||||
|
||||
let target = <Nominating<T>>::get(source).ok_or("Account must be nominating")?;
|
||||
let target = <Nominating<T>>::get(&source).ok_or("Account must be nominating")?;
|
||||
|
||||
let mut t = Self::nominators_for(&target);
|
||||
if t.get(target_index) != Some(source) {
|
||||
if t.get(target_index) != Some(&source) {
|
||||
return Err("Invalid target index")
|
||||
}
|
||||
|
||||
@@ -296,66 +298,72 @@ impl<T: Trait> Module<T> {
|
||||
<NominatorsFor<T>>::insert(&target, t);
|
||||
|
||||
// update nominating
|
||||
<Nominating<T>>::remove(source);
|
||||
<Nominating<T>>::remove(&source);
|
||||
|
||||
// update bondage
|
||||
<Bondage<T>>::insert(aux.ref_into(), <system::Module<T>>::block_number() + Self::bonding_duration());
|
||||
<Bondage<T>>::insert(source, <system::Module<T>>::block_number() + Self::bonding_duration());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the given account's preference for slashing behaviour should they be a validator.
|
||||
///
|
||||
/// An error (no-op) if `Self::intentions()[intentions_index] != aux`.
|
||||
/// An error (no-op) if `Self::intentions()[intentions_index] != origin`.
|
||||
fn register_preferences(
|
||||
aux: &T::PublicAux,
|
||||
origin: T::Origin,
|
||||
intentions_index: u32,
|
||||
prefs: ValidatorPrefs<T::Balance>
|
||||
) -> Result {
|
||||
let aux = aux.ref_into();
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
if Self::intentions().get(intentions_index as usize) != Some(aux) {
|
||||
if Self::intentions().get(intentions_index as usize) != Some(&who) {
|
||||
return Err("Invalid index")
|
||||
}
|
||||
|
||||
<ValidatorPreferences<T>>::insert(aux, prefs);
|
||||
<ValidatorPreferences<T>>::insert(who, prefs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// PRIV DISPATCH
|
||||
|
||||
/// Deposit one of this module's events.
|
||||
fn deposit_event(event: Event<T>) {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
/// Set the number of sessions in an era.
|
||||
fn set_sessions_per_era(new: T::BlockNumber) -> Result {
|
||||
fn set_sessions_per_era(origin: T::Origin, new: T::BlockNumber) -> Result {
|
||||
ensure_root(origin)?;
|
||||
<NextSessionsPerEra<T>>::put(&new);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The length of the bonding duration in eras.
|
||||
fn set_bonding_duration(new: T::BlockNumber) -> Result {
|
||||
fn set_bonding_duration(origin: T::Origin, new: T::BlockNumber) -> Result {
|
||||
ensure_root(origin)?;
|
||||
<BondingDuration<T>>::put(&new);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The length of a staking era in sessions.
|
||||
fn set_validator_count(new: u32) -> Result {
|
||||
fn set_validator_count(origin: T::Origin, new: u32) -> Result {
|
||||
ensure_root(origin)?;
|
||||
<ValidatorCount<T>>::put(&new);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Force there to be a new era. This also forces a new session immediately after.
|
||||
/// `apply_rewards` should be true for validators to get the session reward.
|
||||
fn force_new_era(apply_rewards: bool) -> Result {
|
||||
<ForcingNewEra<T>>::put(());
|
||||
<session::Module<T>>::force_new_session(apply_rewards)
|
||||
fn force_new_era(origin: T::Origin, apply_rewards: bool) -> Result {
|
||||
ensure_root(origin)?;
|
||||
Self::apply_force_new_era(apply_rewards)
|
||||
}
|
||||
|
||||
// Just force_new_era without origin check.
|
||||
fn apply_force_new_era(apply_rewards: bool) -> Result {
|
||||
<ForcingNewEra<T>>::put(());
|
||||
<session::Module<T>>::apply_force_new_session(apply_rewards)
|
||||
}
|
||||
|
||||
|
||||
/// Set the offline slash grace period.
|
||||
fn set_offline_slash_grace(new: u32) -> Result {
|
||||
fn set_offline_slash_grace(origin: T::Origin, new: u32) -> Result {
|
||||
ensure_root(origin)?;
|
||||
<OfflineSlashGrace<T>>::put(&new);
|
||||
Ok(())
|
||||
}
|
||||
@@ -555,7 +563,7 @@ impl<T: Trait> consensus::OnOfflineValidator for Module<T> {
|
||||
apply_unstake can only fail if pos wrong; \
|
||||
Self::intentions() doesn't change; qed");
|
||||
}
|
||||
let _ = Self::force_new_era(false);
|
||||
let _ = Self::apply_force_new_era(false);
|
||||
}
|
||||
RawEvent::OfflineSlash(v, slash)
|
||||
} else {
|
||||
|
||||
@@ -25,6 +25,10 @@ use substrate_primitives::{H256, KeccakHasher};
|
||||
use runtime_io;
|
||||
use {GenesisConfig, Module, Trait, consensus, session, system, timestamp, balances};
|
||||
|
||||
impl_outer_origin!{
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
pub struct Test;
|
||||
@@ -35,7 +39,7 @@ impl consensus::Trait for Test {
|
||||
type OnOfflineValidator = ();
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
use super::*;
|
||||
use consensus::OnOfflineValidator;
|
||||
use runtime_io::with_externalities;
|
||||
use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext};
|
||||
use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext, Origin};
|
||||
|
||||
#[test]
|
||||
fn note_null_offline_should_work() {
|
||||
@@ -75,7 +75,7 @@ fn note_offline_grace_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
Balances::set_free_balance(&10, 70);
|
||||
Balances::set_free_balance(&20, 70);
|
||||
assert_ok!(Staking::set_offline_slash_grace(1));
|
||||
assert_ok!(Staking::set_offline_slash_grace(Origin::ROOT, 1));
|
||||
assert_eq!(Staking::offline_slash_grace(), 1);
|
||||
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
@@ -104,7 +104,7 @@ fn note_offline_force_unstake_session_change_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
Balances::set_free_balance(&10, 70);
|
||||
Balances::set_free_balance(&20, 70);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
assert_ok!(Staking::stake(Origin::signed(1)));
|
||||
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Balances::free_balance(&10), 70);
|
||||
@@ -130,7 +130,7 @@ fn note_offline_auto_unstake_session_change_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||
Balances::set_free_balance(&10, 7000);
|
||||
Balances::set_free_balance(&20, 7000);
|
||||
assert_ok!(Staking::register_preferences(&10, 0, ValidatorPrefs { unstake_threshold: 1, validator_payment: 0 }));
|
||||
assert_ok!(Staking::register_preferences(Origin::signed(10), 0, ValidatorPrefs { unstake_threshold: 1, validator_payment: 0 }));
|
||||
|
||||
assert_eq!(Staking::intentions(), vec![10, 20]);
|
||||
|
||||
@@ -236,14 +236,14 @@ fn staking_should_work() {
|
||||
assert_eq!(Staking::validator_count(), 2);
|
||||
assert_eq!(Session::validators(), vec![10, 20]);
|
||||
|
||||
assert_ok!(Staking::set_bonding_duration(2));
|
||||
assert_ok!(Staking::set_bonding_duration(Origin::ROOT, 2));
|
||||
assert_eq!(Staking::bonding_duration(), 2);
|
||||
|
||||
// Block 1: Add three validators. No obvious change.
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
assert_ok!(Staking::stake(&2));
|
||||
assert_ok!(Staking::stake(&4));
|
||||
assert_ok!(Staking::stake(Origin::signed(1)));
|
||||
assert_ok!(Staking::stake(Origin::signed(2)));
|
||||
assert_ok!(Staking::stake(Origin::signed(4)));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::validators(), vec![10, 20]);
|
||||
@@ -256,8 +256,8 @@ fn staking_should_work() {
|
||||
|
||||
// Block 3: Unstake highest, introduce another staker. No change yet.
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Staking::stake(&3));
|
||||
assert_ok!(Staking::unstake(&4, Staking::intentions().iter().position(|&x| x == 4).unwrap() as u32));
|
||||
assert_ok!(Staking::stake(Origin::signed(3)));
|
||||
assert_ok!(Staking::unstake(Origin::signed(4), Staking::intentions().iter().position(|&x| x == 4).unwrap() as u32));
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
|
||||
@@ -269,7 +269,7 @@ fn staking_should_work() {
|
||||
|
||||
// Block 5: Transfer stake from highest to lowest. No change yet.
|
||||
System::set_block_number(5);
|
||||
assert_ok!(Balances::transfer(&4, 1.into(), 40));
|
||||
assert_ok!(Balances::transfer(Origin::signed(4), 1.into(), 40));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
|
||||
// Block 6: Lowest now validator.
|
||||
@@ -279,7 +279,7 @@ fn staking_should_work() {
|
||||
|
||||
// Block 7: Unstake three. No change yet.
|
||||
System::set_block_number(7);
|
||||
assert_ok!(Staking::unstake(&3, Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32));
|
||||
assert_ok!(Staking::unstake(Origin::signed(3), Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::validators(), vec![1, 3]);
|
||||
|
||||
@@ -299,10 +299,10 @@ fn nominating_and_rewards_should_work() {
|
||||
assert_eq!(Session::validators(), vec![10, 20]);
|
||||
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
assert_ok!(Staking::stake(&2));
|
||||
assert_ok!(Staking::stake(&3));
|
||||
assert_ok!(Staking::nominate(&4, 1.into()));
|
||||
assert_ok!(Staking::stake(Origin::signed(1)));
|
||||
assert_ok!(Staking::stake(Origin::signed(2)));
|
||||
assert_ok!(Staking::stake(Origin::signed(3)));
|
||||
assert_ok!(Staking::nominate(Origin::signed(4), 1.into()));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
assert_eq!(Session::validators(), vec![1, 3]); // 4 + 1, 3
|
||||
@@ -312,7 +312,7 @@ fn nominating_and_rewards_should_work() {
|
||||
assert_eq!(Balances::total_balance(&4), 40);
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_ok!(Staking::unnominate(&4, 0));
|
||||
assert_ok!(Staking::unnominate(Origin::signed(4), 0));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 2);
|
||||
assert_eq!(Session::validators(), vec![3, 2]);
|
||||
@@ -322,9 +322,9 @@ fn nominating_and_rewards_should_work() {
|
||||
assert_eq!(Balances::total_balance(&4), 48);
|
||||
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Staking::stake(&4));
|
||||
assert_ok!(Staking::unstake(&3, Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32));
|
||||
assert_ok!(Staking::nominate(&3, 1.into()));
|
||||
assert_ok!(Staking::stake(Origin::signed(4)));
|
||||
assert_ok!(Staking::unstake(Origin::signed(3), Staking::intentions().iter().position(|&x| x == 3).unwrap() as u32));
|
||||
assert_ok!(Staking::nominate(Origin::signed(3), 1.into()));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::validators(), vec![1, 4]);
|
||||
assert_eq!(Balances::total_balance(&1), 12);
|
||||
@@ -345,9 +345,9 @@ fn nominating_and_rewards_should_work() {
|
||||
fn rewards_with_off_the_table_should_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
assert_ok!(Staking::nominate(&2, 1.into()));
|
||||
assert_ok!(Staking::stake(&3));
|
||||
assert_ok!(Staking::stake(Origin::signed(1)));
|
||||
assert_ok!(Staking::nominate(Origin::signed(2), 1.into()));
|
||||
assert_ok!(Staking::stake(Origin::signed(3)));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::validators(), vec![1, 3]); // 1 + 2, 3
|
||||
assert_eq!(Balances::total_balance(&1), 10);
|
||||
@@ -355,7 +355,7 @@ fn rewards_with_off_the_table_should_work() {
|
||||
assert_eq!(Balances::total_balance(&3), 30);
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_ok!(Staking::register_preferences(&1, Staking::intentions().into_iter().position(|i| i == 1).unwrap() as u32, ValidatorPrefs { unstake_threshold: 3, validator_payment: 4 }));
|
||||
assert_ok!(Staking::register_preferences(Origin::signed(1), Staking::intentions().into_iter().position(|i| i == 1).unwrap() as u32, ValidatorPrefs { unstake_threshold: 3, validator_payment: 4 }));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Balances::total_balance(&1), 16);
|
||||
assert_eq!(Balances::total_balance(&2), 24);
|
||||
@@ -376,10 +376,10 @@ fn nominating_slashes_should_work() {
|
||||
|
||||
Timestamp::set_timestamp(15);
|
||||
System::set_block_number(4);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
assert_ok!(Staking::stake(&3));
|
||||
assert_ok!(Staking::nominate(&2, 3.into()));
|
||||
assert_ok!(Staking::nominate(&4, 1.into()));
|
||||
assert_ok!(Staking::stake(Origin::signed(1)));
|
||||
assert_ok!(Staking::stake(Origin::signed(3)));
|
||||
assert_ok!(Staking::nominate(Origin::signed(2), 3.into()));
|
||||
assert_ok!(Staking::nominate(Origin::signed(4), 1.into()));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
@@ -404,12 +404,12 @@ fn nominating_slashes_should_work() {
|
||||
fn double_staking_should_fail() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || {
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
assert_noop!(Staking::stake(&1), "Cannot stake if already staked.");
|
||||
assert_noop!(Staking::nominate(&1, 1.into()), "Cannot nominate if already staked.");
|
||||
assert_ok!(Staking::nominate(&2, 1.into()));
|
||||
assert_noop!(Staking::stake(&2), "Cannot stake if already nominating.");
|
||||
assert_noop!(Staking::nominate(&2, 1.into()), "Cannot nominate if already nominating.");
|
||||
assert_ok!(Staking::stake(Origin::signed(1)));
|
||||
assert_noop!(Staking::stake(Origin::signed(1)), "Cannot stake if already staked.");
|
||||
assert_noop!(Staking::nominate(Origin::signed(1), 1.into()), "Cannot nominate if already staked.");
|
||||
assert_ok!(Staking::nominate(Origin::signed(2), 1.into()));
|
||||
assert_noop!(Staking::stake(Origin::signed(2)), "Cannot stake if already nominating.");
|
||||
assert_noop!(Staking::nominate(Origin::signed(2), 1.into()), "Cannot nominate if already nominating.");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ fn staking_eras_work() {
|
||||
|
||||
// Block 3: Schedule an era length change; no visible changes.
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Staking::set_sessions_per_era(3));
|
||||
assert_ok!(Staking::set_sessions_per_era(Origin::ROOT, 3));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
assert_eq!(Staking::sessions_per_era(), 2);
|
||||
@@ -485,8 +485,8 @@ fn staking_eras_work() {
|
||||
fn staking_balance_transfer_when_bonded_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || {
|
||||
Balances::set_free_balance(&1, 111);
|
||||
assert_ok!(Staking::stake(&1));
|
||||
assert_noop!(Balances::transfer(&1, 2.into(), 69), "cannot transfer illiquid funds");
|
||||
assert_ok!(Staking::stake(Origin::signed(1)));
|
||||
assert_noop!(Balances::transfer(Origin::signed(1), 2.into(), 69), "cannot transfer illiquid funds");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ extern crate safe_mix;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded,
|
||||
Hash, Member, MaybeDisplay, RefInto, MaybeEmpty};
|
||||
Hash, Member, MaybeDisplay};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use safe_mix::TripletMix;
|
||||
|
||||
@@ -69,9 +69,7 @@ pub fn extrinsics_data_root<H: Hash>(xts: Vec<Vec<u8>>) -> H::Output {
|
||||
}
|
||||
|
||||
pub trait Trait: Eq + Clone {
|
||||
// We require that PublicAux impl MaybeEmpty, since we require that inherents - or unsigned
|
||||
// user-level extrinsics - can exist.
|
||||
type PublicAux: RefInto<Self::AccountId> + MaybeEmpty;
|
||||
type Origin: Into<Option<RawOrigin<Self::AccountId>>> + From<RawOrigin<Self::AccountId>>;
|
||||
type Index: Parameter + Member + Default + MaybeDisplay + SimpleArithmetic + Copy;
|
||||
type BlockNumber: Parameter + Member + MaybeDisplay + SimpleArithmetic + Default + Bounded + Copy + rstd::hash::Hash;
|
||||
type Hash: Parameter + Member + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]>;
|
||||
@@ -89,7 +87,7 @@ pub trait Trait: Eq + Clone {
|
||||
pub type DigestItemOf<T> = <<T as Trait>::Digest as traits::Digest>::Item;
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
/// A phase of a block's execution.
|
||||
@@ -126,6 +124,30 @@ impl From<Event> for () {
|
||||
fn from(_: Event) -> () { () }
|
||||
}
|
||||
|
||||
/// Origin for the system module.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub enum RawOrigin<AccountId> {
|
||||
/// The system itself ordained this dispatch to happen: this is the highest privilege level.
|
||||
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,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Exposed trait-generic origin type.
|
||||
pub type Origin<T> = RawOrigin<<T as Trait>::AccountId>;
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as System {
|
||||
|
||||
@@ -146,6 +168,37 @@ decl_storage! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the origin `o` represents a signed extrinsic (i.e. transaction).
|
||||
/// Returns `Ok` with the account that signed the extrinsic or an `Err` otherwise.
|
||||
pub fn ensure_signed<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<AccountId, &'static str>
|
||||
where OuterOrigin: Into<Option<RawOrigin<AccountId>>>
|
||||
{
|
||||
match o.into() {
|
||||
Some(RawOrigin::Signed(t)) => Ok(t),
|
||||
_ => Err("bad origin: expected to be a signed origin"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the origin `o` represents the root. Returns `Ok` or an `Err` otherwise.
|
||||
pub fn ensure_root<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<(), &'static str>
|
||||
where OuterOrigin: Into<Option<RawOrigin<AccountId>>>
|
||||
{
|
||||
match o.into() {
|
||||
Some(RawOrigin::Root) => Ok(()),
|
||||
_ => Err("bad origin: expected to be a root origin"),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>
|
||||
where OuterOrigin: Into<Option<RawOrigin<AccountId>>>
|
||||
{
|
||||
match o.into() {
|
||||
Some(RawOrigin::Inherent) => Ok(()),
|
||||
_ => Err("bad origin: expected to be an inherent origin"),
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Start the execution of a particular block.
|
||||
pub fn initialise(number: &T::BlockNumber, parent_hash: &T::Hash, txs_root: &T::Hash) {
|
||||
@@ -307,10 +360,14 @@ mod tests {
|
||||
use primitives::traits::BlakeTwo256;
|
||||
use primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_origin!{
|
||||
pub enum Origin for Test where system = super {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
impl Trait for Test {
|
||||
type PublicAux = u64;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -332,6 +389,8 @@ mod tests {
|
||||
|
||||
type System = Module<Test>;
|
||||
|
||||
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
GenesisConfig::<Test>::default().build_storage().unwrap().into()
|
||||
}
|
||||
|
||||
@@ -40,11 +40,10 @@ extern crate substrate_codec as codec;
|
||||
|
||||
use runtime_support::{StorageValue, Parameter};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_primitives::traits::{OnFinalise, MaybeEmpty, SimpleArithmetic, As, Zero};
|
||||
use runtime_primitives::traits::{OnFinalise, SimpleArithmetic, As, Zero};
|
||||
use system::ensure_inherent;
|
||||
|
||||
pub trait Trait: consensus::Trait where
|
||||
<Self as system::Trait>::PublicAux: MaybeEmpty
|
||||
{
|
||||
pub trait Trait: consensus::Trait + system::Trait {
|
||||
// the position of the required timestamp-set extrinsic.
|
||||
const TIMESTAMP_SET_POSITION: u32;
|
||||
|
||||
@@ -52,11 +51,8 @@ pub trait Trait: consensus::Trait where
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
fn set(aux, now: T::Moment) -> Result;
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn set(origin, now: T::Moment) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +73,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Set the current time.
|
||||
fn set(aux: &T::PublicAux, now: T::Moment) -> Result {
|
||||
assert!(aux.is_empty());
|
||||
fn set(origin: T::Origin, now: T::Moment) -> Result {
|
||||
ensure_inherent(origin)?;
|
||||
assert!(!<Self as Store>::DidUpdate::exists(), "Timestamp must be updated only once in the block");
|
||||
assert!(
|
||||
<system::Module<T>>::extrinsic_index() == Some(T::TIMESTAMP_SET_POSITION),
|
||||
@@ -146,10 +142,14 @@ mod tests {
|
||||
use runtime_primitives::traits::{BlakeTwo256};
|
||||
use runtime_primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -178,7 +178,7 @@ mod tests {
|
||||
let mut t = runtime_io::TestExternalities::from(t);
|
||||
with_externalities(&mut t, || {
|
||||
Timestamp::set_timestamp(42);
|
||||
assert_ok!(Timestamp::aux_dispatch(Call::set(69), &0));
|
||||
assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT));
|
||||
assert_eq!(Timestamp::now(), 69);
|
||||
});
|
||||
}
|
||||
@@ -191,8 +191,8 @@ mod tests {
|
||||
let mut t = runtime_io::TestExternalities::from(t);
|
||||
with_externalities(&mut t, || {
|
||||
Timestamp::set_timestamp(42);
|
||||
assert_ok!(Timestamp::aux_dispatch(Call::set(69), &0));
|
||||
let _ = Timestamp::aux_dispatch(Call::set(70), &0);
|
||||
assert_ok!(Timestamp::dispatch(Call::set(69), Origin::INHERENT));
|
||||
let _ = Timestamp::dispatch(Call::set(70), Origin::INHERENT);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ mod tests {
|
||||
let mut t = runtime_io::TestExternalities::from(t);
|
||||
with_externalities(&mut t, || {
|
||||
Timestamp::set_timestamp(42);
|
||||
let _ = Timestamp::aux_dispatch(Call::set(46), &0);
|
||||
let _ = Timestamp::dispatch(Call::set(46), Origin::INHERENT);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,9 @@ extern crate substrate_runtime_balances as balances;
|
||||
use rstd::ops::{Mul, Div};
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_primitives::traits::{As, OnFinalise, Zero, RefInto};
|
||||
use runtime_primitives::traits::{As, OnFinalise, Zero};
|
||||
use balances::OnMinted;
|
||||
use system::{ensure_signed, ensure_root};
|
||||
|
||||
/// Our module's configuration trait. All our types and consts go in here. If the
|
||||
/// module is dependent on specific other modules, then their configuration traits
|
||||
@@ -63,35 +64,24 @@ type ProposalIndex = u32;
|
||||
// macro takes care of the marshalling of arguments and dispatch.
|
||||
decl_module! {
|
||||
// Simple declaration of the `Module` type. Lets the macro know what its working on.
|
||||
pub struct Module<T: Trait>;
|
||||
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Call where aux: T::PublicAux {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
// Put forward a suggestion for spending. A deposit proportional to the value
|
||||
// is reserved and slashed if the proposal is rejected. It is returned once the
|
||||
// proposal is awarded.
|
||||
fn propose_spend(aux, value: T::Balance, beneficiary: T::AccountId) -> Result;
|
||||
}
|
||||
fn propose_spend(origin, value: T::Balance, beneficiary: T::AccountId) -> Result;
|
||||
|
||||
// The priviledged entry points. These are provided to allow any governance modules in
|
||||
// the runtime to be able to execute common functions. Unlike for `Call` there is no
|
||||
// auxilliary data to encode the sender (since there is no sender). Though still important
|
||||
// to ensure that these execute in reasonable time and space, they can do what would
|
||||
// otherwise be costly or unsafe operations.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum PrivCall {
|
||||
// Set the balance of funds available to spend.
|
||||
fn set_pot(new_pot: T::Balance) -> Result;
|
||||
fn set_pot(origin, new_pot: T::Balance) -> Result;
|
||||
|
||||
// (Re-)configure this module.
|
||||
fn configure(proposal_bond: Permill, proposal_bond_minimum: T::Balance, spend_period: T::BlockNumber, burn: Permill) -> Result;
|
||||
fn configure(origin, proposal_bond: Permill, proposal_bond_minimum: T::Balance, spend_period: T::BlockNumber, burn: Permill) -> Result;
|
||||
|
||||
// Reject a proposed spend. The original deposit will be slashed.
|
||||
fn reject_proposal(proposal_id: ProposalIndex) -> Result;
|
||||
fn reject_proposal(origin, roposal_id: ProposalIndex) -> Result;
|
||||
|
||||
// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary
|
||||
// and the original deposit will be returned.
|
||||
fn approve_proposal(proposal_id: ProposalIndex) -> Result;
|
||||
fn approve_proposal(origin, proposal_id: ProposalIndex) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,25 +173,26 @@ impl<T: Trait> Module<T> {
|
||||
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
|
||||
}
|
||||
|
||||
// Implement Calls/PrivCalls and add public immutables and private mutables.
|
||||
// Implement Calls and add public immutables and private mutables.
|
||||
|
||||
fn propose_spend(aux: &T::PublicAux, value: T::Balance, beneficiary: T::AccountId) -> Result {
|
||||
let proposer = aux.ref_into();
|
||||
fn propose_spend(origin: T::Origin, value: T::Balance, beneficiary: T::AccountId) -> Result {
|
||||
let proposer = ensure_signed(origin)?;
|
||||
|
||||
let bond = Self::calculate_bond(value);
|
||||
<balances::Module<T>>::reserve(proposer, bond)
|
||||
<balances::Module<T>>::reserve(&proposer, bond)
|
||||
.map_err(|_| "Proposer's balance too low")?;
|
||||
|
||||
let c = Self::proposal_count();
|
||||
<ProposalCount<T>>::put(c + 1);
|
||||
<Proposals<T>>::insert(c, Proposal { proposer: proposer.clone(), value, beneficiary, bond });
|
||||
<Proposals<T>>::insert(c, Proposal { proposer, value, beneficiary, bond });
|
||||
|
||||
Self::deposit_event(RawEvent::Proposed(c));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reject_proposal(proposal_id: ProposalIndex) -> Result {
|
||||
fn reject_proposal(origin: T::Origin, proposal_id: ProposalIndex) -> Result {
|
||||
ensure_root(origin)?;
|
||||
let proposal = <Proposals<T>>::take(proposal_id).ok_or("No proposal at that index")?;
|
||||
|
||||
let value = proposal.bond;
|
||||
@@ -210,7 +201,8 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn approve_proposal(proposal_id: ProposalIndex) -> Result {
|
||||
fn approve_proposal(origin: T::Origin, proposal_id: ProposalIndex) -> Result {
|
||||
ensure_root(origin)?;
|
||||
ensure!(<Proposals<T>>::exists(proposal_id), "No proposal at that index");
|
||||
|
||||
{
|
||||
@@ -224,7 +216,8 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pot(new_pot: T::Balance) -> Result {
|
||||
fn set_pot(origin: T::Origin, new_pot: T::Balance) -> Result {
|
||||
ensure_root(origin)?;
|
||||
// Put the new value into storage.
|
||||
<Pot<T>>::put(new_pot);
|
||||
|
||||
@@ -233,11 +226,13 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
fn configure(
|
||||
origin: T::Origin,
|
||||
proposal_bond: Permill,
|
||||
proposal_bond_minimum: T::Balance,
|
||||
spend_period: T::BlockNumber,
|
||||
burn: Permill
|
||||
) -> Result {
|
||||
ensure_root(origin)?;
|
||||
<ProposalBond<T>>::put(proposal_bond);
|
||||
<ProposalBondMinimum<T>>::put(proposal_bond_minimum);
|
||||
<SpendPeriod<T>>::put(spend_period);
|
||||
@@ -358,10 +353,14 @@ mod tests {
|
||||
use runtime_primitives::traits::{BlakeTwo256};
|
||||
use runtime_primitives::testing::{Digest, Header};
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
impl system::Trait for Test {
|
||||
type PublicAux = Self::AccountId;
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
@@ -428,7 +427,7 @@ mod tests {
|
||||
#[test]
|
||||
fn spend_proposal_takes_min_deposit() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_ok!(Treasury::propose_spend(&0, 1, 3));
|
||||
assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3));
|
||||
assert_eq!(Balances::free_balance(&0), 99);
|
||||
assert_eq!(Balances::reserved_balance(&0), 1);
|
||||
});
|
||||
@@ -437,7 +436,7 @@ mod tests {
|
||||
#[test]
|
||||
fn spend_proposal_takes_proportional_deposit() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_ok!(Treasury::propose_spend(&0, 100, 3));
|
||||
assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3));
|
||||
assert_eq!(Balances::free_balance(&0), 95);
|
||||
assert_eq!(Balances::reserved_balance(&0), 5);
|
||||
});
|
||||
@@ -446,7 +445,7 @@ mod tests {
|
||||
#[test]
|
||||
fn spend_proposal_fails_when_proposer_poor() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_noop!(Treasury::propose_spend(&2, 100, 3), "Proposer's balance too low");
|
||||
assert_noop!(Treasury::propose_spend(Origin::signed(2), 100, 3), "Proposer's balance too low");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -455,8 +454,8 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Treasury::on_minted(100);
|
||||
|
||||
assert_ok!(Treasury::propose_spend(&0, 100, 3));
|
||||
assert_ok!(Treasury::approve_proposal(0));
|
||||
assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3));
|
||||
assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0));
|
||||
|
||||
<Treasury as OnFinalise<u64>>::on_finalise(1);
|
||||
assert_eq!(Balances::free_balance(&3), 0);
|
||||
@@ -479,8 +478,8 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Treasury::on_minted(100);
|
||||
|
||||
assert_ok!(Treasury::propose_spend(&0, 100, 3));
|
||||
assert_ok!(Treasury::reject_proposal(0));
|
||||
assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3));
|
||||
assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0));
|
||||
|
||||
<Treasury as OnFinalise<u64>>::on_finalise(2);
|
||||
assert_eq!(Balances::free_balance(&3), 0);
|
||||
@@ -493,23 +492,23 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Treasury::on_minted(100);
|
||||
|
||||
assert_ok!(Treasury::propose_spend(&0, 100, 3));
|
||||
assert_ok!(Treasury::reject_proposal(0));
|
||||
assert_noop!(Treasury::reject_proposal(0), "No proposal at that index");
|
||||
assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3));
|
||||
assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0));
|
||||
assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), "No proposal at that index");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reject_non_existant_spend_proposal_fails() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_noop!(Treasury::reject_proposal(0), "No proposal at that index");
|
||||
assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), "No proposal at that index");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept_non_existant_spend_proposal_fails() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_noop!(Treasury::approve_proposal(0), "No proposal at that index");
|
||||
assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), "No proposal at that index");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -518,9 +517,9 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Treasury::on_minted(100);
|
||||
|
||||
assert_ok!(Treasury::propose_spend(&0, 100, 3));
|
||||
assert_ok!(Treasury::reject_proposal(0));
|
||||
assert_noop!(Treasury::approve_proposal(0), "No proposal at that index");
|
||||
assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3));
|
||||
assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0));
|
||||
assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), "No proposal at that index");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -529,8 +528,8 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Treasury::on_minted(100);
|
||||
|
||||
assert_ok!(Treasury::propose_spend(&0, 100, 3));
|
||||
assert_ok!(Treasury::approve_proposal(0));
|
||||
assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3));
|
||||
assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0));
|
||||
|
||||
<Treasury as OnFinalise<u64>>::on_finalise(2);
|
||||
assert_eq!(Balances::free_balance(&3), 100);
|
||||
@@ -543,8 +542,8 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
Treasury::on_minted(100);
|
||||
|
||||
assert_ok!(Treasury::propose_spend(&0, 150, 3));
|
||||
assert_ok!(Treasury::approve_proposal(0));
|
||||
assert_ok!(Treasury::propose_spend(Origin::signed(0), 150, 3));
|
||||
assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0));
|
||||
|
||||
<Treasury as OnFinalise<u64>>::on_finalise(2);
|
||||
assert_eq!(Treasury::pot(), 100);
|
||||
|
||||
@@ -29,16 +29,11 @@ extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_codec_derive;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
|
||||
use rstd::prelude::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
@@ -133,18 +128,3 @@ impl RuntimeVersion {
|
||||
self.spec_name == other.spec_name
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Trait {
|
||||
const VERSION: RuntimeVersion;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Get runtime version.
|
||||
pub fn version() -> RuntimeVersion {
|
||||
T::VERSION.clone()
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user