mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 22:21:07 +00:00
Council motions for approving treasury proposals (#694)
* Treasury in runtime, generic approve/reject * Add logic for council origin * Add tests. * Configurable number of members in EnsureMembers * Fix grumbles * Fix spelling * Comment
This commit is contained in:
Generated
+3
@@ -446,6 +446,7 @@ dependencies = [
|
||||
"substrate-runtime-staking 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-treasury 0.1.0",
|
||||
"substrate-state-machine 0.1.0",
|
||||
"triehash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -492,6 +493,7 @@ dependencies = [
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
"substrate-runtime-treasury 0.1.0",
|
||||
"substrate-runtime-version 0.1.0",
|
||||
]
|
||||
|
||||
@@ -2708,6 +2710,7 @@ dependencies = [
|
||||
"serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"substrate-codec 0.1.0",
|
||||
"substrate-codec-derive 0.1.0",
|
||||
"substrate-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
|
||||
@@ -51,7 +51,7 @@ use std::sync::Arc;
|
||||
use demo_primitives::{AccountId, Hash};
|
||||
use demo_runtime::{Block, BlockId, GenesisConfig,
|
||||
BalancesConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, SessionConfig,
|
||||
StakingConfig, TimestampConfig};
|
||||
StakingConfig, TimestampConfig, TreasuryConfig, Permill};
|
||||
use futures::{Future, Sink, Stream};
|
||||
use tokio::runtime::Runtime;
|
||||
use demo_executor::NativeExecutor;
|
||||
@@ -211,6 +211,12 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
||||
timestamp: Some(TimestampConfig {
|
||||
period: 5, // 5 second block time.
|
||||
}),
|
||||
treasury: Some(TreasuryConfig {
|
||||
proposal_bond: Permill::from_percent(5),
|
||||
proposal_bond_minimum: 1_000_000,
|
||||
spend_period: 12 * 60 * 24,
|
||||
burn: Permill::from_percent(50),
|
||||
}),
|
||||
};
|
||||
|
||||
let client = Arc::new(client::new_in_mem::<NativeExecutor<demo_executor::Executor>, Block, _>(executor, genesis_config)?);
|
||||
|
||||
@@ -25,3 +25,4 @@ substrate-runtime-session = { path = "../../substrate/runtime/session" }
|
||||
substrate-runtime-staking = { path = "../../substrate/runtime/staking" }
|
||||
substrate-runtime-system = { path = "../../substrate/runtime/system" }
|
||||
substrate-runtime-consensus = { path = "../../substrate/runtime/consensus" }
|
||||
substrate-runtime-treasury = { path = "../../substrate/runtime/treasury" }
|
||||
|
||||
@@ -35,6 +35,7 @@ extern crate triehash;
|
||||
#[cfg(test)] extern crate substrate_runtime_staking as staking;
|
||||
#[cfg(test)] extern crate substrate_runtime_system as system;
|
||||
#[cfg(test)] extern crate substrate_runtime_consensus as consensus;
|
||||
#[cfg(test)] extern crate substrate_runtime_treasury as treasury;
|
||||
#[cfg(test)] #[macro_use] extern crate hex_literal;
|
||||
|
||||
pub use substrate_executor::NativeExecutor;
|
||||
@@ -53,7 +54,7 @@ mod tests {
|
||||
use demo_primitives::{Hash, BlockNumber, AccountId};
|
||||
use runtime_primitives::traits::Header as HeaderT;
|
||||
use runtime_primitives::{ApplyOutcome, ApplyError, ApplyResult};
|
||||
use {balances, staking, session, system, consensus};
|
||||
use {balances, staking, session, system, consensus, treasury};
|
||||
use system::{EventRecord, Phase};
|
||||
use demo_runtime::{Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances,
|
||||
BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System, Event};
|
||||
@@ -234,6 +235,7 @@ mod tests {
|
||||
democracy: Some(Default::default()),
|
||||
council: Some(Default::default()),
|
||||
timestamp: Some(Default::default()),
|
||||
treasury: Some(Default::default()),
|
||||
}.build_storage().unwrap().into()
|
||||
}
|
||||
|
||||
@@ -260,7 +262,7 @@ mod tests {
|
||||
construct_block(
|
||||
1,
|
||||
[69u8; 32].into(),
|
||||
hex!("ddfc4d60889b25215f4fe6ead4e38b7522fa20809a793476eae3ad5ab2d9c399").into(),
|
||||
hex!("1e930ccf2a39029931fcb9173637ab99a7de9d0364e7bf57cfbcb3eb4619e0d4").into(),
|
||||
vec![CheckedExtrinsic {
|
||||
signed: Some(alice()),
|
||||
index: 0,
|
||||
@@ -273,7 +275,7 @@ mod tests {
|
||||
construct_block(
|
||||
2,
|
||||
block1().1,
|
||||
hex!("2b4464c7e0d51325505663ae2ebd2246fcefc5cb998e9c29d8030c559cbbf27f").into(),
|
||||
hex!("80e45869c9a9f513695b94baf479913ddf0bc9653f1be63baa383be8553a9e13").into(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
signed: Some(bob()),
|
||||
@@ -293,7 +295,7 @@ mod tests {
|
||||
construct_block(
|
||||
1,
|
||||
[69u8; 32].into(),
|
||||
hex!("e45221804da3a3609454d4e09debe6364cc6af63c2ff067d802d1af62fea32ae").into(),
|
||||
hex!("58bf7cd5a908de7296bfc0524d89086384df3e8010ab83c8599be036445d6c79").into(),
|
||||
vec![CheckedExtrinsic {
|
||||
signed: Some(alice()),
|
||||
index: 0,
|
||||
@@ -328,6 +330,18 @@ mod tests {
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: Event::system(system::Event::ExtrinsicSuccess)
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Finalization,
|
||||
event: Event::treasury(treasury::RawEvent::Spending(0))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Finalization,
|
||||
event: Event::treasury(treasury::RawEvent::Burnt(0))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Finalization,
|
||||
event: Event::treasury(treasury::RawEvent::Rollover(0))
|
||||
}
|
||||
]);
|
||||
});
|
||||
@@ -368,6 +382,18 @@ mod tests {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: Event::system(system::Event::ExtrinsicSuccess)
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Finalization,
|
||||
event: Event::treasury(treasury::RawEvent::Spending(0))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Finalization,
|
||||
event: Event::treasury(treasury::RawEvent::Burnt(0))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Finalization,
|
||||
event: Event::treasury(treasury::RawEvent::Rollover(0))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Finalization,
|
||||
event: Event::session(session::RawEvent::NewSession(1))
|
||||
|
||||
@@ -27,6 +27,7 @@ substrate-runtime-session = { path = "../../substrate/runtime/session" }
|
||||
substrate-runtime-staking = { path = "../../substrate/runtime/staking" }
|
||||
substrate-runtime-system = { path = "../../substrate/runtime/system" }
|
||||
substrate-runtime-timestamp = { path = "../../substrate/runtime/timestamp" }
|
||||
substrate-runtime-treasury = { path = "../../substrate/runtime/treasury" }
|
||||
substrate-runtime-version = { path = "../../substrate/runtime/version" }
|
||||
demo-primitives = { path = "../primitives" }
|
||||
|
||||
@@ -48,6 +49,7 @@ std = [
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-system/std",
|
||||
"substrate-runtime-timestamp/std",
|
||||
"substrate-runtime-treasury/std",
|
||||
"substrate-runtime-version/std",
|
||||
"demo-primitives/std",
|
||||
"serde_derive",
|
||||
|
||||
@@ -35,6 +35,7 @@ extern crate serde_derive;
|
||||
extern crate serde;
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_primitives;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_codec_derive;
|
||||
@@ -49,6 +50,7 @@ extern crate substrate_runtime_session as session;
|
||||
extern crate substrate_runtime_staking as staking;
|
||||
extern crate substrate_runtime_system as system;
|
||||
extern crate substrate_runtime_timestamp as timestamp;
|
||||
extern crate substrate_runtime_treasury as treasury;
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_version as version;
|
||||
extern crate demo_primitives;
|
||||
@@ -57,9 +59,11 @@ use demo_primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index
|
||||
use runtime_primitives::generic;
|
||||
use runtime_primitives::traits::{Convert, BlakeTwo256, DigestItem};
|
||||
use version::RuntimeVersion;
|
||||
use council::motions as council_motions;
|
||||
use substrate_primitives::u32_trait::{_2, _4};
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub use runtime_primitives::BuildStorage;
|
||||
pub use runtime_primitives::{BuildStorage, Permill};
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
@@ -161,9 +165,27 @@ pub type Council = council::Module<Runtime>;
|
||||
/// Council voting module for this concrete runtime.
|
||||
pub type CouncilVoting = council::voting::Module<Runtime>;
|
||||
|
||||
impl council::motions::Trait for Runtime {
|
||||
type Origin = Origin;
|
||||
type Proposal = Call;
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
/// Council motions module for this concrete runtime.
|
||||
pub type CouncilMotions = council_motions::Module<Runtime>;
|
||||
|
||||
impl treasury::Trait for Runtime {
|
||||
type ApproveOrigin = council_motions::EnsureMembers<_4>;
|
||||
type RejectOrigin = council_motions::EnsureMembers<_2>;
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
/// Treasury module for this concrete runtime.
|
||||
pub type Treasury = treasury::Module<Runtime>;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum Event for Runtime {
|
||||
balances, session, staking, democracy
|
||||
balances, session, staking, democracy, treasury, council_motions
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +197,7 @@ impl_outer_log! {
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Runtime {
|
||||
council_motions
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +211,8 @@ impl_outer_dispatch! {
|
||||
Democracy,
|
||||
Council,
|
||||
CouncilVoting,
|
||||
CouncilMotions,
|
||||
Treasury,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +226,7 @@ impl_outer_config! {
|
||||
DemocracyConfig => democracy,
|
||||
CouncilConfig => council,
|
||||
TimestampConfig => timestamp,
|
||||
TreasuryConfig => treasury,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +254,7 @@ pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Index, Call,
|
||||
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)>;
|
||||
((((((), Treasury), Council), Democracy), Staking), Session)>;
|
||||
|
||||
pub mod api {
|
||||
impl_stubs!(
|
||||
|
||||
Generated
+20
@@ -111,6 +111,7 @@ dependencies = [
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
"substrate-runtime-timestamp 0.1.0",
|
||||
"substrate-runtime-treasury 0.1.0",
|
||||
"substrate-runtime-version 0.1.0",
|
||||
]
|
||||
|
||||
@@ -648,6 +649,7 @@ 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-keyring 0.1.0",
|
||||
"substrate-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
@@ -842,6 +844,24 @@ dependencies = [
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-treasury"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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-primitives 0.1.0",
|
||||
"substrate-runtime-balances 0.1.0",
|
||||
"substrate-runtime-io 0.1.0",
|
||||
"substrate-runtime-primitives 0.1.0",
|
||||
"substrate-runtime-std 0.1.0",
|
||||
"substrate-runtime-support 0.1.0",
|
||||
"substrate-runtime-system 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-runtime-version"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -25,6 +25,7 @@ substrate-runtime-session = { path = "../../../substrate/runtime/session", defau
|
||||
substrate-runtime-staking = { path = "../../../substrate/runtime/staking", default-features = false }
|
||||
substrate-runtime-system = { path = "../../../substrate/runtime/system", default-features = false }
|
||||
substrate-runtime-timestamp = { path = "../../../substrate/runtime/timestamp", default-features = false }
|
||||
substrate-runtime-treasury = { path = "../../../substrate/runtime/treasury", default-features = false }
|
||||
substrate-runtime-version = { path = "../../../substrate/runtime/version", default-features = false }
|
||||
demo-primitives = { path = "../../primitives", default-features = false }
|
||||
|
||||
@@ -47,6 +48,7 @@ std = [
|
||||
"substrate-runtime-staking/std",
|
||||
"substrate-runtime-system/std",
|
||||
"substrate-runtime-timestamp/std",
|
||||
"substrate-runtime-treasury/std",
|
||||
"substrate-runtime-version/std",
|
||||
"demo-primitives/std",
|
||||
]
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -92,6 +92,8 @@ pub use hashing::{blake2_256, twox_128, twox_256};
|
||||
#[cfg(feature = "std")]
|
||||
pub mod hexdisplay;
|
||||
|
||||
pub mod u32_trait;
|
||||
|
||||
pub mod hash;
|
||||
mod hasher;
|
||||
pub mod sandbox;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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/>.
|
||||
|
||||
//! An u32 trait with "values" as impl'd types.
|
||||
|
||||
/// A u32 value, wrapped in a trait because we don't yet have const generics.
|
||||
pub trait Value {
|
||||
/// The actual value represented by the impl'ing type.
|
||||
const VALUE: u32;
|
||||
}
|
||||
/// Type representing the value 0 for the `Value` trait.
|
||||
pub struct _0; impl Value for _0 { const VALUE: u32 = 0; }
|
||||
/// Type representing the value 1 for the `Value` trait.
|
||||
pub struct _1; impl Value for _1 { const VALUE: u32 = 1; }
|
||||
/// Type representing the value 2 for the `Value` trait.
|
||||
pub struct _2; impl Value for _2 { const VALUE: u32 = 2; }
|
||||
/// Type representing the value 3 for the `Value` trait.
|
||||
pub struct _3; impl Value for _3 { const VALUE: u32 = 3; }
|
||||
/// Type representing the value 4 for the `Value` trait.
|
||||
pub struct _4; impl Value for _4 { const VALUE: u32 = 4; }
|
||||
/// Type representing the value 5 for the `Value` trait.
|
||||
pub struct _5; impl Value for _5 { const VALUE: u32 = 5; }
|
||||
/// Type representing the value 6 for the `Value` trait.
|
||||
pub struct _6; impl Value for _6 { const VALUE: u32 = 6; }
|
||||
/// Type representing the value 7 for the `Value` trait.
|
||||
pub struct _7; impl Value for _7 { const VALUE: u32 = 7; }
|
||||
/// Type representing the value 8 for the `Value` trait.
|
||||
pub struct _8; impl Value for _8 { const VALUE: u32 = 8; }
|
||||
/// Type representing the value 9 for the `Value` trait.
|
||||
pub struct _9; impl Value for _9 { const VALUE: u32 = 9; }
|
||||
/// Type representing the value 10 for the `Value` trait.
|
||||
pub struct _10; impl Value for _10 { const VALUE: u32 = 10; }
|
||||
/// Type representing the value 11 for the `Value` trait.
|
||||
pub struct _11; impl Value for _11 { const VALUE: u32 = 11; }
|
||||
/// Type representing the value 12 for the `Value` trait.
|
||||
pub struct _12; impl Value for _12 { const VALUE: u32 = 12; }
|
||||
/// Type representing the value 13 for the `Value` trait.
|
||||
pub struct _13; impl Value for _13 { const VALUE: u32 = 13; }
|
||||
/// Type representing the value 14 for the `Value` trait.
|
||||
pub struct _14; impl Value for _14 { const VALUE: u32 = 14; }
|
||||
/// Type representing the value 15 for the `Value` trait.
|
||||
pub struct _15; impl Value for _15 { const VALUE: u32 = 15; }
|
||||
/// Type representing the value 16 for the `Value` trait.
|
||||
pub struct _16; impl Value for _16 { const VALUE: u32 = 16; }
|
||||
/// Type representing the value 24 for the `Value` trait.
|
||||
pub struct _24; impl Value for _24 { const VALUE: u32 = 24; }
|
||||
/// Type representing the value 32 for the `Value` trait.
|
||||
pub struct _32; impl Value for _32 { const VALUE: u32 = 32; }
|
||||
/// Type representing the value 40 for the `Value` trait.
|
||||
pub struct _40; impl Value for _40 { const VALUE: u32 = 40; }
|
||||
/// Type representing the value 48 for the `Value` trait.
|
||||
pub struct _48; impl Value for _48 { const VALUE: u32 = 48; }
|
||||
/// Type representing the value 56 for the `Value` trait.
|
||||
pub struct _56; impl Value for _56 { const VALUE: u32 = 56; }
|
||||
/// Type representing the value 64 for the `Value` trait.
|
||||
pub struct _64; impl Value for _64 { const VALUE: u32 = 64; }
|
||||
/// Type representing the value 80 for the `Value` trait.
|
||||
pub struct _80; impl Value for _80 { const VALUE: u32 = 80; }
|
||||
/// Type representing the value 96 for the `Value` trait.
|
||||
pub struct _96; impl Value for _96 { const VALUE: u32 = 96; }
|
||||
/// Type representing the value 112 for the `Value` trait.
|
||||
pub struct _112; impl Value for _112 { const VALUE: u32 = 112; }
|
||||
/// Type representing the value 128 for the `Value` trait.
|
||||
pub struct _128; impl Value for _128 { const VALUE: u32 = 128; }
|
||||
/// Type representing the value 160 for the `Value` trait.
|
||||
pub struct _160; impl Value for _160 { const VALUE: u32 = 160; }
|
||||
/// Type representing the value 192 for the `Value` trait.
|
||||
pub struct _192; impl Value for _192 { const VALUE: u32 = 192; }
|
||||
/// Type representing the value 224 for the `Value` trait.
|
||||
pub struct _224; impl Value for _224 { const VALUE: u32 = 224; }
|
||||
/// Type representing the value 256 for the `Value` trait.
|
||||
pub struct _256; impl Value for _256 { const VALUE: u32 = 256; }
|
||||
/// Type representing the value 384 for the `Value` trait.
|
||||
pub struct _384; impl Value for _384 { const VALUE: u32 = 384; }
|
||||
/// Type representing the value 512 for the `Value` trait.
|
||||
pub struct _512; impl Value for _512 { const VALUE: u32 = 512; }
|
||||
@@ -37,6 +37,7 @@ macro_rules! __impl_outer_event_json_metadata {
|
||||
$( $module:ident )*
|
||||
) => {
|
||||
impl $runtime {
|
||||
#[allow(dead_code)]
|
||||
pub fn outer_event_json_metadata() -> &'static str {
|
||||
concat!(r#"{ "name": ""#, stringify!($event_name), r#"", "items": { "#,
|
||||
r#""system": "system::Event""#,
|
||||
|
||||
@@ -116,7 +116,7 @@ macro_rules! impl_outer_origin {
|
||||
pub enum $name {
|
||||
system($system::Origin<$trait>),
|
||||
$(
|
||||
$module($module::Origin<$trait>),
|
||||
$module($module::Origin),
|
||||
)*
|
||||
#[allow(dead_code)]
|
||||
Void($crate::Void)
|
||||
@@ -149,13 +149,13 @@ macro_rules! impl_outer_origin {
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl From<$module::Origin<$trait>> for $name {
|
||||
fn from(x: $module::Origin<$trait>) -> Self {
|
||||
impl From<$module::Origin> for $name {
|
||||
fn from(x: $module::Origin) -> Self {
|
||||
$name::$module(x)
|
||||
}
|
||||
}
|
||||
impl<T: Trait> Into<Option<$module::Origin<T>>> for $name<T> {
|
||||
fn into(self) -> Option<$module::Origin<T>> {
|
||||
impl Into<Option<$module::Origin>> for $name {
|
||||
fn into(self) -> Option<$module::Origin> {
|
||||
if let $name::$module(l) = self {
|
||||
Some(l)
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@ serde_derive = { version = "1.0", optional = true }
|
||||
safe-mix = { version = "1.0", default_features = false}
|
||||
substrate-keyring = { path = "../../keyring", optional = true }
|
||||
substrate-codec = { path = "../../codec", default_features = false }
|
||||
substrate-codec-derive = { path = "../../codec/derive", default_features = false }
|
||||
substrate-primitives = { path = "../../primitives", default_features = false }
|
||||
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
|
||||
@@ -29,6 +30,7 @@ std = [
|
||||
"safe-mix/std",
|
||||
"substrate-keyring",
|
||||
"substrate-codec/std",
|
||||
"substrate-codec-derive/std",
|
||||
"substrate-primitives/std",
|
||||
"substrate-runtime-std/std",
|
||||
"substrate-runtime-io/std",
|
||||
|
||||
@@ -25,8 +25,13 @@ extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
|
||||
extern crate integer_sqrt;
|
||||
extern crate substrate_codec as codec;
|
||||
#[macro_use] extern crate substrate_codec_derive;
|
||||
extern crate substrate_primitives;
|
||||
#[cfg(feature = "std")] extern crate substrate_keyring as keyring;
|
||||
#[macro_use] extern crate substrate_runtime_std as rstd;
|
||||
@@ -47,6 +52,7 @@ use balances::address::Address;
|
||||
use system::{ensure_signed, ensure_root};
|
||||
|
||||
pub mod voting;
|
||||
pub mod motions;
|
||||
|
||||
// no polynomial attacks:
|
||||
//
|
||||
@@ -630,9 +636,18 @@ mod tests {
|
||||
use primitives::traits::{BlakeTwo256};
|
||||
use primitives::testing::{Digest, Header};
|
||||
use substrate_primitives::KeccakHasher;
|
||||
use motions as council_motions;
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
pub enum Origin for Test {
|
||||
council_motions
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum Event for Test {
|
||||
balances, democracy, council_motions
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_dispatch! {
|
||||
@@ -654,20 +669,26 @@ mod tests {
|
||||
type Digest = Digest;
|
||||
type AccountId = u64;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
type Event = Event;
|
||||
}
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type AccountIndex = u64;
|
||||
type OnFreeBalanceZero = ();
|
||||
type EnsureAccountLiquid = ();
|
||||
type Event = ();
|
||||
type Event = Event;
|
||||
}
|
||||
impl democracy::Trait for Test {
|
||||
type Proposal = Call;
|
||||
type Event = ();
|
||||
type Event = Event;
|
||||
}
|
||||
impl Trait for Test {
|
||||
}
|
||||
impl council_motions::Trait for Test {
|
||||
type Origin = Origin;
|
||||
type Proposal = Call;
|
||||
type Event = Event;
|
||||
}
|
||||
impl Trait for Test {}
|
||||
|
||||
pub fn new_test_ext(with_council: bool) -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
// 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/>.
|
||||
|
||||
//! Council voting system.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::result;
|
||||
use substrate_primitives::u32_trait::Value as U32;
|
||||
use primitives::traits::{Hash, EnsureOrigin, MaybeSerializeDebug};
|
||||
use substrate_runtime_support::dispatch::{Result, Dispatchable, Parameter};
|
||||
use substrate_runtime_support::{StorageValue, StorageMap};
|
||||
use super::{Trait as CouncilTrait, Module as Council};
|
||||
use system::{self, ensure_signed};
|
||||
|
||||
/// Simple index type for proposal counting.
|
||||
pub type ProposalIndex = u32;
|
||||
|
||||
pub trait Trait: CouncilTrait + MaybeSerializeDebug {
|
||||
/// The outer origin type.
|
||||
type Origin: From<Origin>;
|
||||
|
||||
/// The outer call dispatch type.
|
||||
type Proposal: Parameter + Dispatchable<Origin=<Self as Trait>::Origin> + MaybeSerializeDebug;
|
||||
|
||||
/// The outer event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
}
|
||||
|
||||
/// Origin for the council module.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub enum Origin {
|
||||
/// It has been condoned by a given number of council members.
|
||||
Members(u32),
|
||||
}
|
||||
|
||||
/// Outwardly visible event.
|
||||
pub type Event<T> = RawEvent<<T as system::Trait>::Hash, <T as system::Trait>::AccountId>;
|
||||
|
||||
/// Event for this module.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Clone)]
|
||||
pub enum RawEvent<Hash, AccountId> {
|
||||
/// A motion (given hash) has been proposed (by given account) with a threshold (given u32).
|
||||
Proposed(AccountId, ProposalIndex, Hash, u32),
|
||||
/// A motion (given hash) has been voted on by given account, leaving
|
||||
/// a tally (yes votes and no votes given as u32s respectively).
|
||||
Voted(AccountId, Hash, bool, u32, u32),
|
||||
/// A motion was approved by the required threshold.
|
||||
Approved(Hash),
|
||||
/// A motion was not approved by the required threshold.
|
||||
Disapproved(Hash),
|
||||
/// A motion was executed; `bool` is true if returned without error.
|
||||
Executed(Hash, bool),
|
||||
}
|
||||
|
||||
impl<H, A> From<RawEvent<H, A>> for () {
|
||||
fn from(_: RawEvent<H, A>) -> () { () }
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
#[cfg_attr(feature = "std", serde(bound(deserialize = "<T as Trait>::Proposal: ::serde::de::DeserializeOwned")))]
|
||||
pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {
|
||||
fn propose(origin, threshold: u32, proposal: Box<<T as Trait>::Proposal>) -> Result;
|
||||
fn vote(origin, proposal: T::Hash, index: ProposalIndex, approve: bool) -> Result;
|
||||
}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as CouncilMotions {
|
||||
/// The (hashes of) the active proposals.
|
||||
pub Proposals get(proposals): default Vec<T::Hash>;
|
||||
/// Actual proposal for a given hash, if it's current.
|
||||
pub ProposalOf get(proposal_of): map [ T::Hash => <T as Trait>::Proposal ];
|
||||
/// Votes for a given proposal: (required_yes_votes, yes_voters, no_voters).
|
||||
pub Voting get(voting): map [ T::Hash => (ProposalIndex, u32, Vec<T::AccountId>, Vec<T::AccountId>) ];
|
||||
/// Proposals so far.
|
||||
pub ProposalCount get(proposal_count): default u32;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
pub fn is_councillor(who: &T::AccountId) -> bool {
|
||||
<Council<T>>::active_council().iter()
|
||||
.any(|&(ref a, _)| a == who)
|
||||
}
|
||||
|
||||
// Dispatch
|
||||
fn propose(origin: <T as system::Trait>::Origin, threshold: u32, proposal: Box<<T as Trait>::Proposal>) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
ensure!(Self::is_councillor(&who), "proposer not on council");
|
||||
|
||||
let proposal_hash = T::Hashing::hash_of(&proposal);
|
||||
|
||||
ensure!(!<ProposalOf<T>>::exists(proposal_hash), "duplicate proposals not allowed");
|
||||
|
||||
if threshold < 2 {
|
||||
let ok = proposal.dispatch(Origin::Members(1).into()).is_ok();
|
||||
Self::deposit_event(RawEvent::Executed(proposal_hash, ok));
|
||||
} else {
|
||||
let index = Self::proposal_count();
|
||||
<ProposalCount<T>>::mutate(|i| *i += 1);
|
||||
<Proposals<T>>::mutate(|proposals| proposals.push(proposal_hash));
|
||||
<ProposalOf<T>>::insert(proposal_hash, *proposal);
|
||||
<Voting<T>>::insert(proposal_hash, (index, threshold, vec![who.clone()], vec![]));
|
||||
|
||||
Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn vote(origin: <T as system::Trait>::Origin, proposal: T::Hash, index: ProposalIndex, approve: bool) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
ensure!(Self::is_councillor(&who), "voter not on council");
|
||||
|
||||
let mut voting = Self::voting(&proposal).ok_or("proposal must exist")?;
|
||||
ensure!(voting.0 == index, "mismatched index");
|
||||
|
||||
let position_yes = voting.2.iter().position(|a| a == &who);
|
||||
let position_no = voting.3.iter().position(|a| a == &who);
|
||||
|
||||
if approve {
|
||||
if position_yes.is_none() {
|
||||
voting.2.push(who.clone());
|
||||
} else {
|
||||
return Err("duplicate vote ignored")
|
||||
}
|
||||
if let Some(pos) = position_no {
|
||||
voting.3.swap_remove(pos);
|
||||
}
|
||||
} else {
|
||||
if position_no.is_none() {
|
||||
voting.3.push(who.clone());
|
||||
} else {
|
||||
return Err("duplicate vote ignored")
|
||||
}
|
||||
if let Some(pos) = position_yes {
|
||||
voting.2.swap_remove(pos);
|
||||
}
|
||||
}
|
||||
|
||||
let yes_votes = voting.2.len() as u32;
|
||||
let no_votes = voting.3.len() as u32;
|
||||
Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes));
|
||||
|
||||
let threshold = voting.1;
|
||||
let potential_votes = <Council<T>>::active_council().len() as u32;
|
||||
let approved = yes_votes >= threshold;
|
||||
let disapproved = potential_votes - no_votes < threshold;
|
||||
if approved || disapproved {
|
||||
if approved {
|
||||
Self::deposit_event(RawEvent::Approved(proposal));
|
||||
|
||||
// execute motion, assuming it exists.
|
||||
if let Some(p) = <ProposalOf<T>>::take(&proposal) {
|
||||
let ok = p.dispatch(Origin::Members(threshold).into()).is_ok();
|
||||
Self::deposit_event(RawEvent::Executed(proposal, ok));
|
||||
}
|
||||
} else {
|
||||
// disapproved
|
||||
Self::deposit_event(RawEvent::Disapproved(proposal));
|
||||
}
|
||||
|
||||
// remove vote
|
||||
<Voting<T>>::remove(&proposal);
|
||||
<Proposals<T>>::mutate(|proposals| proposals.retain(|h| h != &proposal));
|
||||
} else {
|
||||
// update voting
|
||||
<Voting<T>>::insert(&proposal, voting);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the origin `o` represents at least `n` council members. Returns
|
||||
/// `Ok` or an `Err` otherwise.
|
||||
pub fn ensure_council_members<OuterOrigin>(o: OuterOrigin, n: u32) -> result::Result<u32, &'static str>
|
||||
where OuterOrigin: Into<Option<Origin>>
|
||||
{
|
||||
match o.into() {
|
||||
Some(Origin::Members(x)) if x >= n => Ok(n),
|
||||
_ => Err("bad origin: expected to be a threshold number of council members"),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnsureMembers<N: U32>(::rstd::marker::PhantomData<N>);
|
||||
impl<O, N: U32> EnsureOrigin<O> for EnsureMembers<N>
|
||||
where O: Into<Option<Origin>>
|
||||
{
|
||||
type Success = u32;
|
||||
fn ensure_origin(o: O) -> result::Result<Self::Success, &'static str> {
|
||||
ensure_council_members(o, N::VALUE)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ::tests::*;
|
||||
use ::tests::{Call, Origin, Event as OuterEvent};
|
||||
use substrate_runtime_support::Hashable;
|
||||
use system::{EventRecord, Phase};
|
||||
|
||||
type CouncilMotions = super::Module<Test>;
|
||||
|
||||
#[test]
|
||||
fn motions_basic_environment_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
assert_eq!(CouncilMotions::proposals(), Vec::<H256>::new());
|
||||
});
|
||||
}
|
||||
|
||||
fn set_balance_proposal(value: u64) -> Call {
|
||||
Call::Balances(balances::Call::set_balance(balances::address::Address::Id(42), value, 0))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn motions_propose_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone())));
|
||||
assert_eq!(CouncilMotions::proposals(), vec![hash]);
|
||||
assert_eq!(CouncilMotions::proposal_of(&hash), Some(proposal));
|
||||
assert_eq!(CouncilMotions::voting(&hash), Some((0, 3, vec![1], Vec::<u64>::new())));
|
||||
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Proposed(1, 0, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 3))
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn motions_ignoring_non_council_proposals_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_noop!(CouncilMotions::propose(Origin::signed(42), 3, Box::new(proposal.clone())), "proposer not on council");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn motions_ignoring_non_council_votes_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash: H256 = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone())));
|
||||
assert_noop!(CouncilMotions::vote(Origin::signed(42), hash.clone(), 0, true), "voter not on council");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn motions_ignoring_bad_index_council_vote_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(3);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash: H256 = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone())));
|
||||
assert_noop!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 1, true), "mismatched index");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn motions_revoting_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash: H256 = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilMotions::propose(Origin::signed(1), 2, Box::new(proposal.clone())));
|
||||
assert_eq!(CouncilMotions::voting(&hash), Some((0, 2, vec![1], Vec::<u64>::new())));
|
||||
assert_noop!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, true), "duplicate vote ignored");
|
||||
assert_ok!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, false));
|
||||
assert_eq!(CouncilMotions::voting(&hash), Some((0, 2, Vec::<u64>::new(), vec![1])));
|
||||
assert_noop!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, false), "duplicate vote ignored");
|
||||
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Proposed(1, 0, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 2))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Voted(1, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false, 0, 1))
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn motions_disapproval_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash: H256 = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 0, false));
|
||||
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Proposed(1, 0, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 3))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Voted(2, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false, 1, 1))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Disapproved(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into()))
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn motions_approval_works() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
let hash: H256 = proposal.blake2_256().into();
|
||||
assert_ok!(CouncilMotions::propose(Origin::signed(1), 2, Box::new(proposal.clone())));
|
||||
assert_ok!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 0, true));
|
||||
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Proposed(1, 0, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), 2))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Voted(2, hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), true, 2, 0))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Approved(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into()))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: OuterEvent::council_motions(RawEvent::Executed(hex!["a900ca23832b1f42a5d4af5d0ece88da63fbb4049cc00bac3f741eabb5a79c45"].into(), false))
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -98,6 +98,8 @@ impl<T: Trait> Module<T> {
|
||||
fn vote(origin: T::Origin, proposal: T::Hash, approve: bool) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
ensure!(Self::is_councillor(&who), "only councillors may vote on council proposals");
|
||||
|
||||
if Self::vote_of((proposal, who.clone())).is_none() {
|
||||
let mut voters = Self::proposal_voters(&proposal);
|
||||
voters.push(who.clone());
|
||||
@@ -220,7 +222,7 @@ impl<T: Trait> OnFinalise<T::BlockNumber> for Council<T> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ::tests::*;
|
||||
use ::tests::Call;
|
||||
use ::tests::{Call, Origin};
|
||||
use substrate_runtime_support::Hashable;
|
||||
use democracy::VoteThreshold;
|
||||
|
||||
@@ -471,4 +473,14 @@ mod tests {
|
||||
assert_noop!(CouncilVoting::propose(Origin::signed(4), Box::new(proposal)), "proposer would not be on council");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vote_by_public_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(true), || {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(CouncilVoting::propose(Origin::signed(1), Box::new(proposal.clone())));
|
||||
assert_noop!(CouncilVoting::vote(Origin::signed(4), proposal.blake2_256().into(), true), "only councillors may vote on council proposals");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,40 @@ impl BuildStorage for StorageMap {
|
||||
}
|
||||
}
|
||||
|
||||
/// Permill is parts-per-million (i.e. after multiplying by this, divide by 1000000).
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Permill(u32);
|
||||
|
||||
// TODO: impl Mul<Permill> for N where N: As<usize>
|
||||
impl Permill {
|
||||
pub fn times<N: traits::As<usize> + ::rstd::ops::Mul<N, Output=N> + ::rstd::ops::Div<N, Output=N>>(self, b: N) -> N {
|
||||
// TODO: handle overflows
|
||||
b * <N as traits::As<usize>>::sa(self.0 as usize) / <N as traits::As<usize>>::sa(1000000)
|
||||
}
|
||||
|
||||
pub fn from_millionths(x: u32) -> Permill { Permill(x) }
|
||||
|
||||
pub fn from_percent(x: u32) -> Permill { Permill(x * 10_000) }
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn from_fraction(x: f64) -> Permill { Permill((x * 1_000_000.0) as u32) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<f64> for Permill {
|
||||
fn from(x: f64) -> Permill {
|
||||
Permill::from_fraction(x)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<f32> for Permill {
|
||||
fn from(x: f32) -> Permill {
|
||||
Permill::from_fraction(x as f64)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ed25519 signature verify.
|
||||
#[derive(Eq, PartialEq, Clone, Default, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
|
||||
@@ -47,6 +47,12 @@ pub trait Verify {
|
||||
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool;
|
||||
}
|
||||
|
||||
/// Some sort of check on the origin is performed by this object.
|
||||
pub trait EnsureOrigin<OuterOrigin> {
|
||||
type Success;
|
||||
fn ensure_origin(o: OuterOrigin) -> Result<Self::Success, &'static str>;
|
||||
}
|
||||
|
||||
/// Means of changing one type into another in a manner dependent on the source type.
|
||||
pub trait Lookup {
|
||||
/// Type to lookup from.
|
||||
|
||||
@@ -45,7 +45,7 @@ extern crate safe_mix;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded,
|
||||
Hash, Member, MaybeDisplay};
|
||||
Hash, Member, MaybeDisplay, EnsureOrigin};
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use safe_mix::TripletMix;
|
||||
|
||||
@@ -168,6 +168,14 @@ decl_storage! {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnsureRoot<AccountId>(::rstd::marker::PhantomData<AccountId>);
|
||||
impl<O: Into<Option<RawOrigin<AccountId>>>, AccountId> EnsureOrigin<O> for EnsureRoot<AccountId> {
|
||||
type Success = ();
|
||||
fn ensure_origin(o: O) -> Result<Self::Success, &'static str> {
|
||||
ensure_root(o)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(any(feature = "std", test), macro_use)]
|
||||
#[cfg_attr(feature = "std", macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
|
||||
#[macro_use]
|
||||
extern crate substrate_runtime_support as runtime_support;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
#[cfg(feature = "std")]
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -41,10 +41,10 @@ extern crate substrate_runtime_primitives as runtime_primitives;
|
||||
extern crate substrate_runtime_system as system;
|
||||
extern crate substrate_runtime_balances as balances;
|
||||
|
||||
use rstd::ops::{Mul, Div};
|
||||
use rstd::prelude::*;
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_primitives::traits::{As, OnFinalise, Zero};
|
||||
use runtime_primitives::{Permill, traits::{OnFinalise, Zero, EnsureOrigin}};
|
||||
use balances::OnMinted;
|
||||
use system::{ensure_signed, ensure_root};
|
||||
|
||||
@@ -54,6 +54,12 @@ use system::{ensure_signed, ensure_root};
|
||||
///
|
||||
/// `system::Trait` should always be included in our implied traits.
|
||||
pub trait Trait: balances::Trait {
|
||||
/// Origin from which approvals must come.
|
||||
type ApproveOrigin: EnsureOrigin<Self::Origin>;
|
||||
|
||||
/// Origin from which rejections must come.
|
||||
type RejectOrigin: EnsureOrigin<Self::Origin>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
}
|
||||
@@ -95,19 +101,6 @@ pub struct Proposal<AccountId, Balance> {
|
||||
bond: Balance,
|
||||
}
|
||||
|
||||
/// Permill is parts-per-million (i.e. after multiplying by this, divide by `PERMILL`).
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Permill(u32);
|
||||
|
||||
// TODO: impl Mul<Permill> for N where N: As<usize>
|
||||
impl Permill {
|
||||
fn times<N: As<usize> + Mul<N, Output=N> + Div<N, Output=N>>(self, b: N) -> N {
|
||||
// TODO: handle overflows
|
||||
b * <N as As<usize>>::sa(self.0 as usize) / <N as As<usize>>::sa(1000000)
|
||||
}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Treasury {
|
||||
// Config...
|
||||
@@ -192,7 +185,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
fn reject_proposal(origin: T::Origin, proposal_id: ProposalIndex) -> Result {
|
||||
ensure_root(origin)?;
|
||||
T::RejectOrigin::ensure_origin(origin)?;
|
||||
|
||||
let proposal = <Proposals<T>>::take(proposal_id).ok_or("No proposal at that index")?;
|
||||
|
||||
let value = proposal.bond;
|
||||
@@ -202,7 +196,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
fn approve_proposal(origin: T::Origin, proposal_id: ProposalIndex) -> Result {
|
||||
ensure_root(origin)?;
|
||||
T::ApproveOrigin::ensure_origin(origin)?;
|
||||
|
||||
ensure!(<Proposals<T>>::exists(proposal_id), "No proposal at that index");
|
||||
|
||||
<Approvals<T>>::mutate(|v| v.push(proposal_id));
|
||||
@@ -298,7 +293,7 @@ impl<T: Trait> OnFinalise<T::BlockNumber> for Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
#[cfg(feature = "std")]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -311,19 +306,19 @@ pub struct GenesisConfig<T: Trait> {
|
||||
pub burn: Permill,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
proposal_bond: Default::default(),
|
||||
proposal_bond_minimum: Default::default(),
|
||||
spend_period: Default::default(),
|
||||
spend_period: runtime_primitives::traits::One::one(),
|
||||
burn: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Trait> runtime_primitives::BuildStorage for GenesisConfig<T>
|
||||
{
|
||||
fn build_storage(self) -> ::std::result::Result<runtime_primitives::StorageMap, String> {
|
||||
@@ -372,6 +367,8 @@ mod tests {
|
||||
type Event = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type ApproveOrigin = system::EnsureRoot<u64>;
|
||||
type RejectOrigin = system::EnsureRoot<u64>;
|
||||
type Event = ();
|
||||
}
|
||||
type Balances = balances::Module<Test>;
|
||||
@@ -389,10 +386,10 @@ mod tests {
|
||||
reclaim_rebate: 0,
|
||||
}.build_storage().unwrap());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
proposal_bond: Permill(50_000), // 5%
|
||||
proposal_bond: Permill::from_percent(5),
|
||||
proposal_bond_minimum: 1,
|
||||
spend_period: 2,
|
||||
burn: Permill(500_000), // 50%
|
||||
burn: Permill::from_percent(50),
|
||||
}.build_storage().unwrap());
|
||||
t.into()
|
||||
}
|
||||
@@ -400,10 +397,10 @@ mod tests {
|
||||
#[test]
|
||||
fn genesis_config_works() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_eq!(Treasury::proposal_bond(), Permill(50_000));
|
||||
assert_eq!(Treasury::proposal_bond(), Permill::from_percent(5));
|
||||
assert_eq!(Treasury::proposal_bond_minimum(), 1);
|
||||
assert_eq!(Treasury::spend_period(), 2);
|
||||
assert_eq!(Treasury::burn(), Permill(500_000));
|
||||
assert_eq!(Treasury::burn(), Permill::from_percent(50));
|
||||
assert_eq!(Treasury::pot(), 0);
|
||||
assert_eq!(Treasury::proposal_count(), 0);
|
||||
});
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user