From 1893c65f647aa78ac31f824ff4f76ef48f79dd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 23 Oct 2018 10:09:42 +0200 Subject: [PATCH 1/4] General `decl_module` improvements --- srml/support/src/dispatch.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index 8bdfcc88b9356..5dc396c058318 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -75,7 +75,7 @@ macro_rules! decl_module { decl_module!(@normalize $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> - for enum $call_type where origin: $origin_type where system = system + for enum $call_type where origin: $origin_type, system = system {} [] $($t)* @@ -84,14 +84,14 @@ macro_rules! decl_module { ( $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> - for enum $call_type:ident where origin: $origin_type:ty where system = $system:ident { + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident { $($t:tt)* } ) => { decl_module!(@normalize $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> - for enum $call_type where origin: $origin_type where system = $system + for enum $call_type where origin: $origin_type, system = $system {} [] $($t)* @@ -101,7 +101,7 @@ macro_rules! decl_module { (@normalize $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> - for enum $call_type:ident where origin: $origin_type:ty where system = $system:ident + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident { $( $on_finalise:tt )* } [ $($t:tt)* ] $(#[doc = $doc_attr:tt])* @@ -111,7 +111,7 @@ macro_rules! decl_module { decl_module!(@normalize $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> - for enum $call_type where origin: $origin_type where system = $system + for enum $call_type where origin: $origin_type, system = $system { fn on_finalise( $( $param_name : $param ),* ) { $( $impl )* } } [ $($t)* ] $($rest)* @@ -120,7 +120,7 @@ macro_rules! decl_module { (@normalize $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> - for enum $call_type:ident where origin: $origin_type:ty where system = $system:ident + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident { $( $on_finalise:tt )* } [ $($t:tt)* ] $(#[doc = $doc_attr:tt])* @@ -130,7 +130,7 @@ macro_rules! decl_module { decl_module!(@normalize $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> - for enum $call_type where origin: $origin_type where system = $system + for enum $call_type where origin: $origin_type, system = $system { $( $on_finalise )* } [ $($t)* $(#[doc = $doc_attr])* fn $fn_name(origin $( , $param_name : $param )* ) -> $result; ] $($rest)* @@ -139,7 +139,7 @@ macro_rules! decl_module { (@normalize $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> - for enum $call_type:ident where origin: $origin_type:ty where system = $system:ident + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident { $( $on_finalise:tt )* } [ $($t:tt)* ] $(#[doc = $doc_attr:tt])* @@ -149,7 +149,7 @@ macro_rules! decl_module { decl_module!(@normalize $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> - for enum $call_type where origin: $origin_type where system = $system + for enum $call_type where origin: $origin_type, system = $system { $( $on_finalise )* } [ $($t)* $(#[doc = $doc_attr])* fn $fn_name(root $( , $param_name : $param )* ) -> $result; ] $($rest)* @@ -158,14 +158,14 @@ macro_rules! decl_module { (@normalize $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> - for enum $call_type:ident where origin: $origin_type:ty where system = $system:ident + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident { $( $on_finalise:tt )* } [ $($t:tt)* ] ) => { decl_module!(@imp $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> - for enum $call_type where origin: $origin_type where system = $system { + for enum $call_type where origin: $origin_type, system = $system { $($t)* } { $( $on_finalise )* } @@ -221,7 +221,7 @@ macro_rules! decl_module { (@imp $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> - for enum $call_type:ident where origin: $origin_type:ty where system = $system:ident { + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident { $( $(#[doc = $doc_attr:tt])* fn $fn_name:ident($from:ident $( , $param_name:ident : $param:ty)*) -> $result:ty; From 711868a6ca3d095a119bf401e7ddeb2a66bbb187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 23 Oct 2018 11:37:53 +0200 Subject: [PATCH 2/4] Make `deposit_event` implementable by `decl_module!` --- srml/assets/src/lib.rs | 7 +-- srml/balances/src/lib.rs | 7 +-- srml/contract/src/lib.rs | 6 +-- srml/council/src/motions.rs | 7 +-- srml/council/src/seats.rs | 7 +-- srml/council/src/voting.rs | 7 +-- srml/democracy/src/lib.rs | 7 +-- srml/example/src/lib.rs | 9 ++-- srml/session/src/lib.rs | 7 +-- srml/staking/src/lib.rs | 7 +-- srml/support/src/dispatch.rs | 92 ++++++++++++++++++++++++++++++++++++ srml/system/src/lib.rs | 20 ++++---- srml/treasury/src/lib.rs | 6 +-- 13 files changed, 115 insertions(+), 74 deletions(-) diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index d92bf44495f09..791907166a8db 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -69,6 +69,7 @@ type AssetId = u32; decl_module! { // Simple declaration of the `Module` type. Lets the macro know what its working on. pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; /// Issue a new class of fungible assets. There are, and will only ever be, `total` /// such assets and they'll all belong to the `origin` initially. It will have an /// identifier `AssetId` instance: this will be specified in the `Issued` event. @@ -107,12 +108,6 @@ decl_storage! { // The main implementation block for the module. impl Module { - /// Deposit one of this module's events. - // TODO: move into `decl_module` macro. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - // Public immutables /// Get the asset `id` balance of `who`. diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index f06990626e04a..53563f5d6636d 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -125,6 +125,7 @@ pub trait Trait: system::Trait { decl_module! { pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; fn transfer(origin, dest: RawAddress, value: ::Type) -> Result; fn set_balance(who: RawAddress, free: ::Type, reserved: ::Type) -> Result; } @@ -232,12 +233,6 @@ pub enum UpdateBalanceOutcome { } impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - // PUBLIC IMMUTABLES /// The combined balance of `who`. diff --git a/srml/contract/src/lib.rs b/srml/contract/src/lib.rs index d57a65bc1ff36..2175ee94b1d14 100644 --- a/srml/contract/src/lib.rs +++ b/srml/contract/src/lib.rs @@ -151,6 +151,7 @@ where decl_module! { /// Contracts module. pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; // TODO: Change AccountId to staking::Address fn call( origin, @@ -221,11 +222,6 @@ impl double_map::StorageDoubleMap for StorageOf { } impl Module { - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - /// Make a call to a specified account, optionally transferring some balance. fn call( origin: ::Origin, diff --git a/srml/council/src/motions.rs b/srml/council/src/motions.rs index a260eec60edf0..d57af36faed88 100644 --- a/srml/council/src/motions.rs +++ b/srml/council/src/motions.rs @@ -68,6 +68,7 @@ decl_event!( decl_module! { #[cfg_attr(feature = "std", serde(bound(deserialize = "::Proposal: ::serde::de::DeserializeOwned")))] pub struct Module for enum Call where origin: ::Origin { + fn deposit_event() = default; fn propose(origin, threshold: Compact, proposal: Box<::Proposal>) -> Result; fn vote(origin, proposal: T::Hash, index: Compact, approve: bool) -> Result; } @@ -91,12 +92,6 @@ decl_storage! { } impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - pub fn is_councillor(who: &T::AccountId) -> bool { >::active_council().iter() .any(|&(ref a, _)| a == who) diff --git a/srml/council/src/seats.rs b/srml/council/src/seats.rs index 24d06ecf6a286..62e57974bb919 100644 --- a/srml/council/src/seats.rs +++ b/srml/council/src/seats.rs @@ -87,6 +87,7 @@ pub trait Trait: democracy::Trait { decl_module! { pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; fn set_approvals(origin, votes: Vec, index: Compact) -> Result; fn reap_inactive_voter(origin, reporter_index: Compact, who: Address, who_index: Compact, assumed_vote_index: Compact) -> Result; fn retract_voter(origin, index: Compact) -> Result; @@ -176,12 +177,6 @@ decl_event!( ); impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - // exposed immutables. /// True if we're currently in a presentation period. diff --git a/srml/council/src/voting.rs b/srml/council/src/voting.rs index 86c2c40c2a82a..42363ac41defb 100644 --- a/srml/council/src/voting.rs +++ b/srml/council/src/voting.rs @@ -33,6 +33,7 @@ pub trait Trait: CouncilTrait { decl_module! { pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; fn propose(origin, proposal: Box) -> Result; fn vote(origin, proposal: T::Hash, approve: bool) -> Result; fn veto(origin, proposal_hash: T::Hash) -> Result; @@ -73,12 +74,6 @@ decl_event!( ); impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - pub fn is_vetoed>(proposal: B) -> bool { Self::veto_of(proposal.borrow()) .map(|(expiry, _): (T::BlockNumber, Vec)| >::block_number() < expiry) diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index 0d54dbf4413f9..f2abaf9581ddb 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -62,6 +62,7 @@ pub trait Trait: balances::Trait + Sized { decl_module! { pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; fn propose(origin, proposal: Box, value: ::Type) -> Result; fn second(origin, proposal: Compact) -> Result; fn vote(origin, ref_index: Compact, approve_proposal: bool) -> Result; @@ -121,12 +122,6 @@ decl_event!( ); impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - // exposed immutables. /// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index 5f81e4336a899..26db089a49c71 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -103,6 +103,9 @@ pub trait Trait: balances::Trait { decl_module! { // Simple declaration of the `Module` type. Lets the macro know what its working on. pub struct Module for enum Call where origin: T::Origin { + /// Deposit one of this module's events by using the default implementation. + /// It is also possible to provide a custom implementation. + fn deposit_event() = default; /// 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. @@ -174,12 +177,6 @@ decl_storage! { // functions that do not write to storage and operation functions that do. // - Private functions. These are your usual private utilities unavailable to other modules. impl Module { - /// Deposit one of this module's events. - // TODO: move into `decl_module` macro. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - // Implement Calls and add public immutables and private mutables. // Implement dispatched function `accumulate_dummy`. This just increases the value diff --git a/srml/session/src/lib.rs b/srml/session/src/lib.rs index 44d8922723b96..f125528a43512 100644 --- a/srml/session/src/lib.rs +++ b/srml/session/src/lib.rs @@ -67,6 +67,7 @@ pub trait Trait: timestamp::Trait { decl_module! { pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; fn set_key(origin, key: T::SessionKey) -> Result; fn set_length(new: ::Type) -> Result; @@ -111,12 +112,6 @@ decl_storage! { } impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - /// The number of validators currently. pub fn validator_count() -> u32 { >::get().len() as u32 // TODO: can probably optimised diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 370c17ddb564f..27080e2335b7d 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -105,6 +105,7 @@ pub trait Trait: balances::Trait + session::Trait { decl_module! { #[cfg_attr(feature = "std", serde(bound(deserialize = "T::Balance: ::serde::de::DeserializeOwned")))] pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; fn stake(origin) -> Result; fn unstake(origin, intentions_index: Compact) -> Result; fn nominate(origin, target: Address) -> Result; @@ -189,12 +190,6 @@ decl_storage! { } impl Module { - - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - // PUBLIC IMMUTABLES /// The length of a staking era in blocks. diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index 5dc396c058318..a19bb67cb3fc1 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -77,6 +77,7 @@ macro_rules! decl_module { pub struct $mod_type<$trait_instance: $trait_name> for enum $call_type where origin: $origin_type, system = system {} + {} [] $($t)* ); @@ -93,6 +94,7 @@ macro_rules! decl_module { pub struct $mod_type<$trait_instance: $trait_name> for enum $call_type where origin: $origin_type, system = $system {} + {} [] $($t)* ); @@ -102,9 +104,52 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + {} { $( $on_finalise:tt )* } [ $($t:tt)* ] $(#[doc = $doc_attr:tt])* + $vis:vis fn deposit_event() = default; + $($rest:tt)* + ) => { + decl_module!(@normalize + $(#[$attr])* + pub struct $mod_type<$trait_instance: $trait_name> + for enum $call_type where origin: $origin_type, system = $system + { $vis fn deposit_event() = default; } + { $( $on_finalise )* } + [ $($t)* ] + $($rest)* + ); + }; + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + {} + { $( $on_finalise:tt )* } + [ $($t:tt)* ] + $(#[doc = $doc_attr:tt])* + $vis:vis fn deposit_event($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } + $($rest:tt)* + ) => { + decl_module!(@normalize + $(#[$attr])* + pub struct $mod_type<$trait_instance: $trait_name> + for enum $call_type where origin: $origin_type, system = $system + { $vis fn deposit_event($( $param_name: $param ),* ) { $( $impl )* } } + { $( $on_finalise )* } + [ $($t)* ] + $($rest)* + ); + }; + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $deposit_event:tt )* } + {} + [ $($t:tt)* ] + $(#[doc = $doc_attr:tt])* fn on_finalise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } $($rest:tt)* ) => { @@ -112,6 +157,7 @@ macro_rules! decl_module { $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> for enum $call_type where origin: $origin_type, system = $system + { $( $deposit_event )* } { fn on_finalise( $( $param_name : $param ),* ) { $( $impl )* } } [ $($t)* ] $($rest)* @@ -121,6 +167,7 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $deposit_event:tt )* } { $( $on_finalise:tt )* } [ $($t:tt)* ] $(#[doc = $doc_attr:tt])* @@ -131,6 +178,7 @@ macro_rules! decl_module { $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> for enum $call_type where origin: $origin_type, system = $system + { $( $deposit_event )* } { $( $on_finalise )* } [ $($t)* $(#[doc = $doc_attr])* fn $fn_name(origin $( , $param_name : $param )* ) -> $result; ] $($rest)* @@ -140,6 +188,7 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $deposit_event:tt )* } { $( $on_finalise:tt )* } [ $($t:tt)* ] $(#[doc = $doc_attr:tt])* @@ -150,6 +199,7 @@ macro_rules! decl_module { $(#[$attr])* pub struct $mod_type<$trait_instance: $trait_name> for enum $call_type where origin: $origin_type, system = $system + { $( $deposit_event )* } { $( $on_finalise )* } [ $($t)* $(#[doc = $doc_attr])* fn $fn_name(root $( , $param_name : $param )* ) -> $result; ] $($rest)* @@ -159,6 +209,7 @@ macro_rules! decl_module { $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $deposit_event:tt )* } { $( $on_finalise:tt )* } [ $($t:tt)* ] ) => { @@ -168,6 +219,7 @@ macro_rules! decl_module { for enum $call_type where origin: $origin_type, system = $system { $($t)* } + { $( $deposit_event )* } { $( $on_finalise )* } ); }; @@ -188,6 +240,38 @@ macro_rules! decl_module { } }; + // no `deposit_event` function wanted + (@impl_deposit_event + $module:ident<$trait_instance:ident: $trait_name:ident>; + $system:ident; + ) => {}; + + (@impl_deposit_event + $module:ident<$trait_instance:ident: $trait_name:ident>; + $system:ident; + $vis:vis fn deposit_event() = default; + ) => { + impl<$trait_instance: $trait_name> $module<$trait_instance> { + $vis fn deposit_event(event: Event<$trait_instance>) { + <$system::Module<$trait_instance>>::deposit_event( + <$trait_instance as $trait_name>::Event::from(event).into() + ); + } + } + }; + + (@impl_deposit_event + $module:ident<$trait_instance:ident: $trait_name:ident>; + $system:ident; + $vis:vis fn deposit_event($param:ident : $param_ty:ty) { $( $impl:tt )* } + ) => { + impl<$trait_instance: $trait_name> $module<$trait_instance> { + $vis fn deposit_event($param: $param_ty) { + $( $impl )* + } + } + }; + (@impl_on_finalise $module:ident<$trait_instance:ident: $trait_name:ident>; fn on_finalise() { $( $impl:tt )* } @@ -227,6 +311,7 @@ macro_rules! decl_module { fn $fn_name:ident($from:ident $( , $param_name:ident : $param:ty)*) -> $result:ty; )* } + { $( $deposit_event:tt )* } { $( $on_finalise:tt )* } ) => { // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. @@ -250,6 +335,13 @@ macro_rules! decl_module { $( $on_finalise )* } + decl_module! { + @impl_deposit_event + $mod_type<$trait_instance: $trait_name>; + $system; + $( $deposit_event )* + } + #[cfg(feature = "std")] $(#[$attr])* #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index ad8957c3491d9..c34f11bf8dbd7 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -86,7 +86,16 @@ pub trait Trait: Eq + Clone { pub type DigestItemOf = <::Digest as traits::Digest>::Item; decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin { + /// Deposits an event onto this block's event record. + pub fn deposit_event(event: T::Event) { + let extrinsic_index = Self::extrinsic_index(); + let phase = extrinsic_index.map_or(Phase::Finalization, |c| Phase::ApplyExtrinsic(c)); + let mut events = Self::events(); + events.push(EventRecord { phase, event }); + >::put(events); + } + } } /// A phase of a block's execution. @@ -301,15 +310,6 @@ impl Module { >::put(l); } - /// Deposits an event onto this block's event record. - pub fn deposit_event(event: T::Event) { - let extrinsic_index = Self::extrinsic_index(); - let phase = extrinsic_index.map_or(Phase::Finalization, |c| Phase::ApplyExtrinsic(c)); - let mut events = Self::events(); - events.push(EventRecord { phase, event }); - >::put(events); - } - /// Calculate the current block's random seed. fn calculate_random() -> T::Hash { assert!(Self::block_number() > Zero::zero(), "Block number may never be zero"); diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index 4c7c9c9107463..ed2b7d6dfb924 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -73,6 +73,7 @@ type ProposalIndex = u32; decl_module! { // Simple declaration of the `Module` type. Lets the macro know what its working on. pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; // 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. @@ -160,11 +161,6 @@ decl_event!( ); impl Module { - /// Deposit one of this module's events. - fn deposit_event(event: Event) { - >::deposit_event(::Event::from(event).into()); - } - // Implement Calls and add public immutables and private mutables. fn propose_spend(origin: T::Origin, value: ::Type, beneficiary: Address) -> Result { From 575ab2cd5e7c099f7d8dde7c2e8fccb8daea1dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Tue, 23 Oct 2018 16:14:14 +0200 Subject: [PATCH 3/4] Make `decl_module!` implement calls directly --- srml/assets/src/lib.rs | 75 +++--- srml/balances/src/lib.rs | 112 ++++----- srml/consensus/src/lib.rs | 95 ++++---- srml/contract/src/lib.rs | 187 +++++++------- srml/council/src/motions.rs | 182 +++++++------- srml/council/src/seats.rs | 456 +++++++++++++++++------------------ srml/council/src/voting.rs | 152 ++++++------ srml/democracy/src/lib.rs | 137 +++++------ srml/example/src/lib.rs | 162 ++++++------- srml/session/src/lib.rs | 44 ++-- srml/staking/src/lib.rs | 291 +++++++++++----------- srml/support/src/dispatch.rs | 113 +++++---- srml/support/src/metadata.rs | 8 +- srml/timestamp/src/lib.rs | 55 +++-- srml/treasury/src/lib.rs | 146 ++++++----- 15 files changed, 1078 insertions(+), 1137 deletions(-) diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index 791907166a8db..bde551e8863ee 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -73,13 +73,43 @@ decl_module! { /// Issue a new class of fungible assets. There are, and will only ever be, `total` /// such assets and they'll all belong to the `origin` initially. It will have an /// identifier `AssetId` instance: this will be specified in the `Issued` event. - fn issue(origin, total: T::Balance) -> Result; + fn issue(origin, total: T::Balance) -> Result { + let origin = ensure_signed(origin)?; + + let id = Self::next_asset_id(); + >::mutate(|id| *id += 1); + + >::insert((id, origin.clone()), total); + + Self::deposit_event(RawEvent::Issued(id, origin, total)); + Ok(()) + } /// Move some assets from one holder to another. - fn transfer(origin, id: AssetId, target: T::AccountId, total: T::Balance) -> Result; + fn transfer(origin, id: AssetId, target: T::AccountId, amount: T::Balance) -> Result { + let origin = ensure_signed(origin)?; + let origin_account = (id, origin.clone()); + let origin_balance = >::get(&origin_account); + ensure!(origin_balance >= amount, "origin account balance must be greater than amount"); + + Self::deposit_event(RawEvent::Transfered(id, origin, target.clone(), amount)); + >::insert(origin_account, origin_balance - amount); + >::mutate((id, target), |balance| *balance += amount); + + Ok(()) + } /// Destroy any assets of `id` owned by `origin`. - fn destroy(origin, id: AssetId) -> Result; + fn destroy(origin, id: AssetId) -> Result { + let origin = ensure_signed(origin)?; + + let balance = >::take((id, origin.clone())); + ensure!(!balance.is_zero(), "origin balance should be non-zero"); + + Self::deposit_event(RawEvent::Destroyed(id, origin, balance)); + + Ok(()) + } } } @@ -114,45 +144,6 @@ impl Module { pub fn balance(id: AssetId, who: T::AccountId) -> T::Balance { >::get((id, who)) } - - // Implement Calls and add public immutables and private mutables. - - fn issue(origin: T::Origin, total: T::Balance) -> Result { - let origin = ensure_signed(origin)?; - - let id = Self::next_asset_id(); - >::mutate(|id| *id += 1); - - - >::insert((id, origin.clone()), total); - - Self::deposit_event(RawEvent::Issued(id, origin, total)); - Ok(()) - } - - fn transfer(origin: T::Origin, id: AssetId, target: T::AccountId, amount: T::Balance) -> Result { - let origin = ensure_signed(origin)?; - let origin_account = (id, origin.clone()); - let origin_balance = >::get(&origin_account); - ensure!(origin_balance >= amount, "origin account balance must be greater than amount"); - - Self::deposit_event(RawEvent::Transfered(id, origin, target.clone(), amount)); - >::insert(origin_account, origin_balance - amount); - >::mutate((id, target), |balance| *balance += amount); - - Ok(()) - } - - fn destroy(origin: T::Origin, id: AssetId) -> Result { - let origin = ensure_signed(origin)?; - - let balance = >::take((id, origin.clone())); - ensure!(!balance.is_zero(), "origin balance should be non-zero"); - - Self::deposit_event(RawEvent::Destroyed(id, origin, balance)); - - Ok(()) - } } #[cfg(test)] diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 53563f5d6636d..83eb4081327d4 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -126,8 +126,63 @@ pub trait Trait: system::Trait { decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - fn transfer(origin, dest: RawAddress, value: ::Type) -> Result; - fn set_balance(who: RawAddress, free: ::Type, reserved: ::Type) -> Result; + + /// Transfer some liquid free balance to another staker. + pub fn transfer( + origin, + dest: RawAddress, + value: ::Type + ) -> Result { + let transactor = ensure_signed(origin)?; + + let dest = Self::lookup(dest)?; + let value = value.into(); + let from_balance = Self::free_balance(&transactor); + let to_balance = Self::free_balance(&dest); + let would_create = to_balance.is_zero(); + let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; + let liability = match value.checked_add(&fee) { + Some(l) => l, + None => return Err("got overflow after adding a fee to value"), + }; + + let new_from_balance = match from_balance.checked_sub(&liability) { + Some(b) => b, + None => return Err("balance too low to send value"), + }; + if would_create && value < Self::existential_deposit() { + return Err("value too low to create account"); + } + T::EnsureAccountLiquid::ensure_account_liquid(&transactor)?; + + // NOTE: total stake being stored in the same type means that this could never overflow + // but better to be safe than sorry. + let new_to_balance = match to_balance.checked_add(&value) { + Some(b) => b, + None => return Err("destination balance too high to receive value"), + }; + + 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); + Self::deposit_event(RawEvent::Transfer(transactor, dest, value, fee)); + } + + Ok(()) + } + + /// Set the balances of a given account. + fn set_balance( + who: RawAddress, + free: ::Type, + reserved: ::Type + ) -> Result { + let who = Self::lookup(who)?; + Self::set_free_balance(&who, free.into()); + Self::set_reserved_balance(&who, reserved.into()); + Ok(()) + } } } @@ -280,58 +335,7 @@ impl Module { } } - // PUBLIC DISPATCH - - /// Transfer some liquid free balance to another staker. - pub fn transfer(origin: T::Origin, dest: Address, value: ::Type) -> Result { - let transactor = ensure_signed(origin)?; - - let dest = Self::lookup(dest)?; - let value = value.into(); - let from_balance = Self::free_balance(&transactor); - let to_balance = Self::free_balance(&dest); - let would_create = to_balance.is_zero(); - let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; - let liability = match value.checked_add(&fee) { - Some(l) => l, - None => return Err("got overflow after adding a fee to value"), - }; - - let new_from_balance = match from_balance.checked_sub(&liability) { - Some(b) => b, - None => return Err("balance too low to send value"), - }; - if would_create && value < Self::existential_deposit() { - return Err("value too low to create account"); - } - T::EnsureAccountLiquid::ensure_account_liquid(&transactor)?; - - // NOTE: total stake being stored in the same type means that this could never overflow - // but better to be safe than sorry. - let new_to_balance = match to_balance.checked_add(&value) { - Some(b) => b, - None => return Err("destination balance too high to receive value"), - }; - - 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); - Self::deposit_event(RawEvent::Transfer(transactor, dest, value, fee)); - } - - Ok(()) - } - - /// Set the balances of a given account. - fn set_balance(who: Address, free: ::Type, reserved: ::Type) -> Result { - let who = Self::lookup(who)?; - Self::set_free_balance(&who, free.into()); - Self::set_reserved_balance(&who, reserved.into()); - Ok(()) - } - - // PUBLIC MUTABLES (DANGEROUS) + //PUBLIC MUTABLES (DANGEROUS) /// Set the free balance of an account to some new value. /// diff --git a/srml/consensus/src/lib.rs b/srml/consensus/src/lib.rs index ba72df18f57d1..6975fe792ca2a 100644 --- a/srml/consensus/src/lib.rs +++ b/srml/consensus/src/lib.rs @@ -143,11 +143,51 @@ decl_storage! { decl_module! { pub struct Module for enum Call where origin: T::Origin { - fn report_misbehavior(origin, report: Vec) -> Result; - fn note_offline(origin, offline_val_indices: Vec) -> Result; - fn remark(origin, remark: Vec) -> Result; - fn set_code(new: Vec) -> Result; - fn set_storage(items: Vec) -> Result; + /// Report some misbehaviour. + fn report_misbehavior(origin, _report: Vec) -> Result { + ensure_signed(origin)?; + // TODO. + Ok(()) + } + + /// 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(origin, offline_val_indices: Vec) -> Result { + ensure_inherent(origin)?; + assert!( + >::extrinsic_index() == Some(T::NOTE_OFFLINE_POSITION), + "note_offline extrinsic must be at position {} in the block", + T::NOTE_OFFLINE_POSITION + ); + + for validator_index in offline_val_indices.into_iter() { + T::OnOfflineValidator::on_offline_validator(validator_index as usize); + } + + Ok(()) + } + + /// Make some on-chain remark. + fn remark(origin, _remark: Vec) -> Result { + ensure_signed(origin)?; + Ok(()) + } + + /// Set the new code. + fn set_code(new: Vec) -> Result { + storage::unhashed::put_raw(well_known_keys::CODE, &new); + Ok(()) + } + + /// Set some items of storage. + fn set_storage(items: Vec) -> Result { + for i in &items { + storage::unhashed::put_raw(&i.0, &i.1); + } + Ok(()) + } + fn on_finalise() { if let Some(original_authorities) = >::take() { let current_authorities = AuthorityStorageVec::::items(); @@ -165,51 +205,6 @@ impl Module { AuthorityStorageVec::::items() } - /// Set the new code. - fn set_code(new: Vec) -> Result { - storage::unhashed::put_raw(well_known_keys::CODE, &new); - Ok(()) - } - - /// Set some items of storage. - fn set_storage(items: Vec) -> Result { - for i in &items { - storage::unhashed::put_raw(&i.0, &i.1); - } - Ok(()) - } - - /// Report some misbehaviour. - fn report_misbehavior(origin: T::Origin, _report: Vec) -> Result { - ensure_signed(origin)?; - // TODO. - Ok(()) - } - - /// 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(origin: T::Origin, offline_val_indices: Vec) -> Result { - ensure_inherent(origin)?; - assert!( - >::extrinsic_index() == Some(T::NOTE_OFFLINE_POSITION), - "note_offline extrinsic must be at position {} in the block", - T::NOTE_OFFLINE_POSITION - ); - - for validator_index in offline_val_indices.into_iter() { - T::OnOfflineValidator::on_offline_validator(validator_index as usize); - } - - Ok(()) - } - - /// Make some on-chain remark. - fn remark(origin: T::Origin, _remark: Vec) -> Result { - ensure_signed(origin)?; - Ok(()) - } - /// Set the current set of authorities' session keys. /// /// Called by `next_session` only. diff --git a/srml/contract/src/lib.rs b/srml/contract/src/lib.rs index 2175ee94b1d14..30e82f4923cb5 100644 --- a/srml/contract/src/lib.rs +++ b/srml/contract/src/lib.rs @@ -153,21 +153,102 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; // TODO: Change AccountId to staking::Address + /// Make a call to a specified account, optionally transferring some balance. fn call( origin, dest: T::AccountId, value: ::Type, gas_limit: ::Type, data: Vec - ) -> Result; + ) -> Result { + let origin = ensure_signed(origin)?; + let value = value.into(); + let gas_limit = gas_limit.into(); + + // 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::(&origin, gas_limit)?; + + let mut ctx = ExecutionContext { + self_account: origin.clone(), + depth: 0, + overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), + events: Vec::new(), + }; + + let mut output_data = Vec::new(); + let result = ctx.call(origin.clone(), dest, value, &mut gas_meter, &data, &mut output_data); + + if let Ok(_) = result { + // Commit all changes that made it thus far into the persistant storage. + account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); + + // Then deposit all events produced. + ctx.events.into_iter().for_each(Self::deposit_event); + } + + // Refund cost of the unused gas. + // + // 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::(&origin, gas_meter); + + result.map(|_| ()) + } + /// Create a new contract, optionally transfering some balance to the created account. + /// + /// Creation is executed as follows: + /// + /// - the destination address is computed based on the sender and hash of the code. + /// - account is created at the computed address. + /// - the `ctor_code` is executed in the context of the newly created account. Buffer returned + /// 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( origin, - value: ::Type, + endowment: ::Type, gas_limit: ::Type, - init_code: Vec, + ctor_code: Vec, data: Vec - ) -> Result; + ) -> Result { + let origin = ensure_signed(origin)?; + let endowment = endowment.into(); + let gas_limit = gas_limit.into(); + + // 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::(&origin, gas_limit)?; + + let mut ctx = ExecutionContext { + self_account: origin.clone(), + depth: 0, + overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), + events: Vec::new(), + }; + 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. + account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); + + // Then deposit all events produced. + ctx.events.into_iter().for_each(Self::deposit_event); + } + + // Refund cost of the unused gas. + // + // 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::(&origin, gas_meter); + + result.map(|_| ()) + } + fn on_finalise() { >::kill(); } @@ -221,104 +302,6 @@ impl double_map::StorageDoubleMap for StorageOf { type Value = Vec; } -impl Module { - /// Make a call to a specified account, optionally transferring some balance. - fn call( - origin: ::Origin, - dest: T::AccountId, - value: ::Type, - gas_limit: ::Type, - data: Vec, - ) -> Result { - let origin = ensure_signed(origin)?; - let value = value.into(); - let gas_limit = gas_limit.into(); - - // 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::(&origin, gas_limit)?; - - let mut ctx = ExecutionContext { - self_account: origin.clone(), - depth: 0, - overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), - events: Vec::new(), - }; - - let mut output_data = Vec::new(); - let result = ctx.call(origin.clone(), dest, value, &mut gas_meter, &data, &mut output_data); - - if let Ok(_) = result { - // Commit all changes that made it thus far into the persistant storage. - account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); - - // Then deposit all events produced. - ctx.events.into_iter().for_each(Self::deposit_event); - } - - // Refund cost of the unused gas. - // - // 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::(&origin, gas_meter); - - result.map(|_| ()) - } - - /// Create a new contract, optionally transfering some balance to the created account. - /// - /// Creation is executed as follows: - /// - /// - the destination address is computed based on the sender and hash of the code. - /// - account is created at the computed address. - /// - the `ctor_code` is executed in the context of the newly created account. Buffer returned - /// 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( - origin: ::Origin, - endowment: ::Type, - gas_limit: ::Type, - ctor_code: Vec, - data: Vec, - ) -> Result { - let origin = ensure_signed(origin)?; - let endowment = endowment.into(); - let gas_limit = gas_limit.into(); - - // 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::(&origin, gas_limit)?; - - let mut ctx = ExecutionContext { - self_account: origin.clone(), - depth: 0, - overlay: OverlayAccountDb::::new(&account_db::DirectAccountDb), - events: Vec::new(), - }; - 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. - account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); - - // Then deposit all events produced. - ctx.events.into_iter().for_each(Self::deposit_event); - } - - // Refund cost of the unused gas. - // - // 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::(&origin, gas_meter); - - result.map(|_| ()) - } -} - impl balances::OnFreeBalanceZero for Module { fn on_free_balance_zero(who: &T::AccountId) { >::remove(who); diff --git a/srml/council/src/motions.rs b/srml/council/src/motions.rs index d57af36faed88..a2c5115633f19 100644 --- a/srml/council/src/motions.rs +++ b/srml/council/src/motions.rs @@ -69,8 +69,95 @@ decl_module! { #[cfg_attr(feature = "std", serde(bound(deserialize = "::Proposal: ::serde::de::DeserializeOwned")))] pub struct Module for enum Call where origin: ::Origin { fn deposit_event() = default; - fn propose(origin, threshold: Compact, proposal: Box<::Proposal>) -> Result; - fn vote(origin, proposal: T::Hash, index: Compact, approve: bool) -> Result; + fn propose(origin, threshold: Compact, proposal: Box<::Proposal>) -> Result { + let who = ensure_signed(origin)?; + let threshold = threshold.into(); + + ensure!(Self::is_councillor(&who), "proposer not on council"); + + let proposal_hash = T::Hashing::hash_of(&proposal); + + ensure!(!>::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(); + >::mutate(|i| *i += 1); + >::mutate(|proposals| proposals.push(proposal_hash)); + >::insert(proposal_hash, *proposal); + >::insert(proposal_hash, (index, threshold, vec![who.clone()], vec![])); + + Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold)); + } + Ok(()) + } + + fn vote(origin, proposal: T::Hash, index: Compact, approve: bool) -> Result { + let who = ensure_signed(origin)?; + let index = index.into(); + + 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 = >::active_council().len() as u32; + let approved = yes_votes >= threshold; + let disapproved = potential_votes.saturating_sub(no_votes) < threshold; + if approved || disapproved { + if approved { + Self::deposit_event(RawEvent::Approved(proposal)); + + // execute motion, assuming it exists. + if let Some(p) = >::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 + >::remove(&proposal); + >::mutate(|proposals| proposals.retain(|h| h != &proposal)); + } else { + // update voting + >::insert(&proposal, voting); + } + + Ok(()) + } } } @@ -96,97 +183,6 @@ impl Module { >::active_council().iter() .any(|&(ref a, _)| a == who) } - - // Dispatch - fn propose(origin: ::Origin, threshold: Compact, proposal: Box<::Proposal>) -> Result { - let who = ensure_signed(origin)?; - let threshold = threshold.into(); - - ensure!(Self::is_councillor(&who), "proposer not on council"); - - let proposal_hash = T::Hashing::hash_of(&proposal); - - ensure!(!>::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(); - >::mutate(|i| *i += 1); - >::mutate(|proposals| proposals.push(proposal_hash)); - >::insert(proposal_hash, *proposal); - >::insert(proposal_hash, (index, threshold, vec![who.clone()], vec![])); - - Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold)); - } - Ok(()) - } - - fn vote(origin: ::Origin, proposal: T::Hash, index: Compact, approve: bool) -> Result { - let who = ensure_signed(origin)?; - let index = index.into(); - - 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 = >::active_council().len() as u32; - let approved = yes_votes >= threshold; - let disapproved = potential_votes.saturating_sub(no_votes) < threshold; - if approved || disapproved { - if approved { - Self::deposit_event(RawEvent::Approved(proposal)); - - // execute motion, assuming it exists. - if let Some(p) = >::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 - >::remove(&proposal); - >::mutate(|proposals| proposals.retain(|h| h != &proposal)); - } else { - // update voting - >::insert(&proposal, voting); - } - - Ok(()) - } } /// Ensure that the origin `o` represents at least `n` council members. Returns diff --git a/srml/council/src/seats.rs b/srml/council/src/seats.rs index 62e57974bb919..957b69bdfe0a8 100644 --- a/srml/council/src/seats.rs +++ b/srml/council/src/seats.rs @@ -88,16 +88,227 @@ pub trait Trait: democracy::Trait { decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - fn set_approvals(origin, votes: Vec, index: Compact) -> Result; - fn reap_inactive_voter(origin, reporter_index: Compact, who: Address, who_index: Compact, assumed_vote_index: Compact) -> Result; - fn retract_voter(origin, index: Compact) -> Result; - fn submit_candidacy(origin, slot: Compact) -> Result; - fn present_winner(origin, candidate: Address, total: ::Type, index: Compact) -> Result; - - fn set_desired_seats(count: Compact) -> Result; - fn remove_member(who: Address) -> Result; - fn set_presentation_duration(count: ::Type) -> Result; - fn set_term_duration(count: ::Type) -> Result; + + /// Set candidate approvals. Approval slots stay valid as long as candidates in those slots + /// are registered. + fn set_approvals(origin, votes: Vec, index: Compact) -> Result { + let who = ensure_signed(origin)?; + let index: VoteIndex = index.into(); + + ensure!(!Self::presentation_active(), "no approval changes during presentation period"); + ensure!(index == Self::vote_index(), "incorrect vote index"); + if !>::exists(&who) { + // not yet a voter - deduct bond. + // NOTE: this must be the last potential bailer, since it changes state. + >::reserve(&who, Self::voting_bond())?; + + >::put({ + let mut v = Self::voters(); + v.push(who.clone()); + v + }); + } + >::insert(&who, index); + >::insert(&who, votes); + Ok(()) + } + + /// Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices + /// must now be either unregistered or registered to a candidate that registered the slot after + /// the voter gave their last approval set. + /// + /// May be called by anyone. Returns the voter deposit to `signed`. + fn reap_inactive_voter( + origin, + reporter_index: Compact, + who: Address, + who_index: Compact, + assumed_vote_index: Compact + ) -> Result { + let reporter = ensure_signed(origin)?; + let assumed_vote_index: VoteIndex = assumed_vote_index.into(); + + let who = >::lookup(who)?; + ensure!(!Self::presentation_active(), "cannot reap during presentation period"); + ensure!(Self::voter_last_active(&reporter).is_some(), "reporter must be a voter"); + let last_active = Self::voter_last_active(&who).ok_or("target for inactivity cleanup must be active")?; + ensure!(assumed_vote_index == Self::vote_index(), "vote index not current"); + ensure!(last_active < assumed_vote_index - Self::inactivity_grace_period(), "cannot reap during grace perid"); + let voters = Self::voters(); + let reporter_index: u32 = reporter_index.into(); + let reporter_index = reporter_index as usize; + let who_index: u32 = who_index.into(); + let who_index = who_index as usize; + ensure!(reporter_index < voters.len() && voters[reporter_index] == reporter, "bad reporter index"); + ensure!(who_index < voters.len() && voters[who_index] == who, "bad target index"); + + // will definitely kill one of signed or who now. + + let valid = !Self::approvals_of(&who).iter() + .zip(Self::candidates().iter()) + .any(|(&appr, addr)| + appr && + *addr != T::AccountId::default() && + Self::candidate_reg_info(addr).map_or(false, |x| x.0 <= last_active)/*defensive only: all items in candidates list are registered*/ + ); + + Self::remove_voter( + if valid { &who } else { &reporter }, + if valid { who_index } else { reporter_index }, + voters + ); + if valid { + // This only fails if `who` doesn't exist, which it clearly must do since its the origin. + // Still, it's no more harmful to propagate any error at this point. + >::repatriate_reserved(&who, &reporter, Self::voting_bond())?; + Self::deposit_event(RawEvent::VoterReaped(who, reporter)); + } else { + >::slash_reserved(&reporter, Self::voting_bond()); + Self::deposit_event(RawEvent::BadReaperSlashed(reporter)); + } + Ok(()) + } + + /// Remove a voter. All votes are cancelled and the voter deposit is returned. + fn retract_voter(origin, index: Compact) -> Result { + let who = ensure_signed(origin)?; + + ensure!(!Self::presentation_active(), "cannot retract when presenting"); + ensure!(>::exists(&who), "cannot retract non-voter"); + let voters = Self::voters(); + let index: u32 = index.into(); + let index = index as usize; + ensure!(index < voters.len(), "retraction index invalid"); + ensure!(voters[index] == who, "retraction index mismatch"); + + Self::remove_voter(&who, index, voters); + >::unreserve(&who, Self::voting_bond()); + Ok(()) + } + + /// Submit oneself for candidacy. + /// + /// Account must have enough transferrable funds in it to pay the bond. + fn submit_candidacy(origin, slot: Compact) -> Result { + let who = ensure_signed(origin)?; + + ensure!(!Self::is_a_candidate(&who), "duplicate candidate submission"); + let slot: u32 = slot.into(); + let slot = slot as usize; + let count = Self::candidate_count() as usize; + let candidates = Self::candidates(); + ensure!( + (slot == count && count == candidates.len()) || + (slot < candidates.len() && candidates[slot] == T::AccountId::default()), + "invalid candidate slot" + ); + // NOTE: This must be last as it has side-effects. + >::reserve(&who, Self::candidacy_bond()) + .map_err(|_| "candidate has not enough funds")?; + + >::insert(&who, (Self::vote_index(), slot as u32)); + let mut candidates = candidates; + if slot == candidates.len() { + candidates.push(who); + } else { + candidates[slot] = who; + } + >::put(candidates); + >::put(count as u32 + 1); + Ok(()) + } + + /// Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates. + /// Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()`` + /// `signed` should have at least + fn present_winner( + origin, + candidate: Address, + total: ::Type, + index: Compact + ) -> Result { + let who = ensure_signed(origin)?; + let total = total.into(); + let index: VoteIndex = index.into(); + + let candidate = >::lookup(candidate)?; + ensure!(index == Self::vote_index(), "index not current"); + let (_, _, expiring) = Self::next_finalise().ok_or("cannot present outside of presentation period")?; + let stakes = Self::snapshoted_stakes(); + let voters = Self::voters(); + let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len() as u64); + ensure!(>::can_slash(&who, bad_presentation_punishment), "presenter must have sufficient slashable funds"); + + let mut leaderboard = Self::leaderboard().ok_or("leaderboard must exist while present phase active")?; + ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard"); + + if let Some(p) = Self::active_council().iter().position(|&(ref c, _)| c == &candidate) { + ensure!(p < expiring.len(), "candidate must not form a duplicated member if elected"); + } + + let (registered_since, candidate_index): (VoteIndex, u32) = + Self::candidate_reg_info(&candidate).ok_or("presented candidate must be current")?; + let actual_total = voters.iter() + .zip(stakes.iter()) + .filter_map(|(voter, stake)| + match Self::voter_last_active(voter) { + Some(b) if b >= registered_since => + Self::approvals_of(voter).get(candidate_index as usize) + .and_then(|approved| if *approved { Some(*stake) } else { None }), + _ => None, + }) + .fold(Zero::zero(), |acc, n| acc + n); + let dupe = leaderboard.iter().find(|&&(_, ref c)| c == &candidate).is_some(); + if total == actual_total && !dupe { + // insert into leaderboard + leaderboard[0] = (total, candidate); + leaderboard.sort_by_key(|&(t, _)| t); + >::put(leaderboard); + Ok(()) + } else { + // we can rest assured it will be Ok since we checked `can_slash` earlier; still + // better safe than sorry. + let _ = >::slash(&who, bad_presentation_punishment); + Err(if dupe { "duplicate presentation" } else { "incorrect total" }) + } + } + + /// Set the desired member count; if lower than the current count, then seats will not be up + /// election when they expire. If more, then a new vote will be started if one is not already + /// in progress. + fn set_desired_seats(count: Compact) -> Result { + let count: u32 = count.into(); + >::put(count); + Ok(()) + } + + /// Remove a particular member. A tally will happen instantly (if not already in a presentation + /// period) to fill the seat if removal means that the desired members are not met. + /// This is effective immediately. + fn remove_member(who: Address) -> Result { + let who = >::lookup(who)?; + let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council() + .into_iter() + .filter(|i| i.0 != who) + .collect(); + >::put(new_council); + Ok(()) + } + + /// Set the presentation duration. If there is currently a vote being presented for, will + /// invoke `finalise_vote`. + fn set_presentation_duration(count: ::Type) -> Result { + >::put(count.into()); + Ok(()) + } + + /// Set the presentation duration. If there is current a vote being presented for, will + /// invoke `finalise_vote`. + fn set_term_duration(count: ::Type) -> Result { + >::put(count.into()); + Ok(()) + } + fn on_finalise(n: T::BlockNumber) { if let Err(e) = Self::end_block(n) { print("Guru meditation"); @@ -225,230 +436,7 @@ impl Module { } } - // dispatch - - /// Set candidate approvals. Approval slots stay valid as long as candidates in those slots - /// are registered. - fn set_approvals(origin: T::Origin, votes: Vec, index: Compact) -> Result { - let who = ensure_signed(origin)?; - let index: VoteIndex = index.into(); - - ensure!(!Self::presentation_active(), "no approval changes during presentation period"); - ensure!(index == Self::vote_index(), "incorrect vote index"); - if !>::exists(&who) { - // not yet a voter - deduct bond. - // NOTE: this must be the last potential bailer, since it changes state. - >::reserve(&who, Self::voting_bond())?; - - >::put({ - let mut v = Self::voters(); - v.push(who.clone()); - v - }); - } - >::insert(&who, index); - >::insert(&who, votes); - Ok(()) - } - - /// Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices - /// must now be either unregistered or registered to a candidate that registered the slot after - /// the voter gave their last approval set. - /// - /// May be called by anyone. Returns the voter deposit to `signed`. - fn reap_inactive_voter( - origin: T::Origin, - reporter_index: Compact, - who: Address, - who_index: Compact, - assumed_vote_index: Compact - ) -> Result { - let reporter = ensure_signed(origin)?; - let assumed_vote_index: VoteIndex = assumed_vote_index.into(); - - let who = >::lookup(who)?; - ensure!(!Self::presentation_active(), "cannot reap during presentation period"); - ensure!(Self::voter_last_active(&reporter).is_some(), "reporter must be a voter"); - let last_active = Self::voter_last_active(&who).ok_or("target for inactivity cleanup must be active")?; - ensure!(assumed_vote_index == Self::vote_index(), "vote index not current"); - ensure!(last_active < assumed_vote_index - Self::inactivity_grace_period(), "cannot reap during grace perid"); - let voters = Self::voters(); - let reporter_index: u32 = reporter_index.into(); - let reporter_index = reporter_index as usize; - let who_index: u32 = who_index.into(); - let who_index = who_index as usize; - ensure!(reporter_index < voters.len() && voters[reporter_index] == reporter, "bad reporter index"); - ensure!(who_index < voters.len() && voters[who_index] == who, "bad target index"); - - // will definitely kill one of signed or who now. - - let valid = !Self::approvals_of(&who).iter() - .zip(Self::candidates().iter()) - .any(|(&appr, addr)| - appr && - *addr != T::AccountId::default() && - Self::candidate_reg_info(addr).map_or(false, |x| x.0 <= last_active)/*defensive only: all items in candidates list are registered*/ - ); - - Self::remove_voter( - if valid { &who } else { &reporter }, - if valid { who_index } else { reporter_index }, - voters - ); - if valid { - // This only fails if `who` doesn't exist, which it clearly must do since its the origin. - // Still, it's no more harmful to propagate any error at this point. - >::repatriate_reserved(&who, &reporter, Self::voting_bond())?; - Self::deposit_event(RawEvent::VoterReaped(who, reporter)); - } else { - >::slash_reserved(&reporter, Self::voting_bond()); - Self::deposit_event(RawEvent::BadReaperSlashed(reporter)); - } - Ok(()) - } - - /// Remove a voter. All votes are cancelled and the voter deposit is returned. - fn retract_voter(origin: T::Origin, index: Compact) -> Result { - let who = ensure_signed(origin)?; - - ensure!(!Self::presentation_active(), "cannot retract when presenting"); - ensure!(>::exists(&who), "cannot retract non-voter"); - let voters = Self::voters(); - let index: u32 = index.into(); - let index = index as usize; - ensure!(index < voters.len(), "retraction index invalid"); - ensure!(voters[index] == who, "retraction index mismatch"); - - Self::remove_voter(&who, index, voters); - >::unreserve(&who, Self::voting_bond()); - Ok(()) - } - - /// Submit oneself for candidacy. - /// - /// Account must have enough transferrable funds in it to pay the bond. - fn submit_candidacy(origin: T::Origin, slot: Compact) -> Result { - let who = ensure_signed(origin)?; - - ensure!(!Self::is_a_candidate(&who), "duplicate candidate submission"); - let slot: u32 = slot.into(); - let slot = slot as usize; - let count = Self::candidate_count() as usize; - let candidates = Self::candidates(); - ensure!( - (slot == count && count == candidates.len()) || - (slot < candidates.len() && candidates[slot] == T::AccountId::default()), - "invalid candidate slot" - ); - // NOTE: This must be last as it has side-effects. - >::reserve(&who, Self::candidacy_bond()) - .map_err(|_| "candidate has not enough funds")?; - - >::insert(&who, (Self::vote_index(), slot as u32)); - let mut candidates = candidates; - if slot == candidates.len() { - candidates.push(who); - } else { - candidates[slot] = who; - } - >::put(candidates); - >::put(count as u32 + 1); - Ok(()) - } - - /// Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates. - /// Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()`` - /// `signed` should have at least - fn present_winner( - origin: T::Origin, - candidate: Address, - total: ::Type, - index: Compact - ) -> Result { - let who = ensure_signed(origin)?; - let total = total.into(); - let index: VoteIndex = index.into(); - - let candidate = >::lookup(candidate)?; - ensure!(index == Self::vote_index(), "index not current"); - let (_, _, expiring) = Self::next_finalise().ok_or("cannot present outside of presentation period")?; - let stakes = Self::snapshoted_stakes(); - let voters = Self::voters(); - let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len() as u64); - ensure!(>::can_slash(&who, bad_presentation_punishment), "presenter must have sufficient slashable funds"); - - let mut leaderboard = Self::leaderboard().ok_or("leaderboard must exist while present phase active")?; - ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard"); - - if let Some(p) = Self::active_council().iter().position(|&(ref c, _)| c == &candidate) { - ensure!(p < expiring.len(), "candidate must not form a duplicated member if elected"); - } - - let (registered_since, candidate_index): (VoteIndex, u32) = - Self::candidate_reg_info(&candidate).ok_or("presented candidate must be current")?; - let actual_total = voters.iter() - .zip(stakes.iter()) - .filter_map(|(voter, stake)| - match Self::voter_last_active(voter) { - Some(b) if b >= registered_since => - Self::approvals_of(voter).get(candidate_index as usize) - .and_then(|approved| if *approved { Some(*stake) } else { None }), - _ => None, - }) - .fold(Zero::zero(), |acc, n| acc + n); - let dupe = leaderboard.iter().find(|&&(_, ref c)| c == &candidate).is_some(); - if total == actual_total && !dupe { - // insert into leaderboard - leaderboard[0] = (total, candidate); - leaderboard.sort_by_key(|&(t, _)| t); - >::put(leaderboard); - Ok(()) - } else { - // we can rest assured it will be Ok since we checked `can_slash` earlier; still - // better safe than sorry. - let _ = >::slash(&who, bad_presentation_punishment); - Err(if dupe { "duplicate presentation" } else { "incorrect total" }) - } - } - - /// Set the desired member count; if lower than the current count, then seats will not be up - /// election when they expire. If more, then a new vote will be started if one is not already - /// in progress. - fn set_desired_seats(count: Compact) -> Result { - let count: u32 = count.into(); - >::put(count); - Ok(()) - } - - /// Remove a particular member. A tally will happen instantly (if not already in a presentation - /// period) to fill the seat if removal means that the desired members are not met. - /// This is effective immediately. - fn remove_member(who: Address) -> Result { - let who = >::lookup(who)?; - let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council() - .into_iter() - .filter(|i| i.0 != who) - .collect(); - >::put(new_council); - Ok(()) - } - - /// Set the presentation duration. If there is currently a vote being presented for, will - /// invoke `finalise_vote`. - fn set_presentation_duration(count: ::Type) -> Result { - >::put(count.into()); - Ok(()) - } - - /// Set the presentation duration. If there is current a vote being presented for, will - /// invoke `finalise_vote`. - fn set_term_duration(count: ::Type) -> Result { - >::put(count.into()); - Ok(()) - } - - // private - + // Private /// Check there's nothing to do this block fn end_block(block_number: T::BlockNumber) -> Result { if (block_number % Self::voting_period()).is_zero() { diff --git a/srml/council/src/voting.rs b/srml/council/src/voting.rs index 42363ac41defb..b598e8ca05257 100644 --- a/srml/council/src/voting.rs +++ b/srml/council/src/voting.rs @@ -34,12 +34,81 @@ pub trait Trait: CouncilTrait { decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - fn propose(origin, proposal: Box) -> Result; - fn vote(origin, proposal: T::Hash, approve: bool) -> Result; - fn veto(origin, proposal_hash: T::Hash) -> Result; - fn set_cooloff_period(blocks: ::Type) -> Result; - fn set_voting_period(blocks: ::Type) -> Result; + fn propose(origin, proposal: Box) -> Result { + let who = ensure_signed(origin)?; + + let expiry = >::block_number() + Self::voting_period(); + ensure!(Self::will_still_be_councillor_at(&who, expiry), "proposer would not be on council"); + + let proposal_hash = T::Hashing::hash_of(&proposal); + + ensure!(!>::exists(proposal_hash), "duplicate proposals not allowed"); + ensure!(!Self::is_vetoed(&proposal_hash), "proposal is vetoed"); + + let mut proposals = Self::proposals(); + proposals.push((expiry, proposal_hash)); + proposals.sort_by_key(|&(expiry, _)| expiry); + Self::set_proposals(&proposals); + + >::insert(proposal_hash, *proposal); + >::insert(proposal_hash, vec![who.clone()]); + >::insert((proposal_hash, who.clone()), true); + + Ok(()) + } + + fn vote(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() { + >::mutate(proposal, |voters| voters.push(who.clone())); + } + >::insert((proposal, who), approve); + Ok(()) + } + + fn veto(origin, proposal_hash: T::Hash) -> Result { + let who = ensure_signed(origin)?; + + ensure!(Self::is_councillor(&who), "only councillors may veto council proposals"); + ensure!(>::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(&who) + .err().ok_or("a councillor may not veto a proposal twice")?; + existing_vetoers.insert(insert_position, who); + Self::set_veto_of( + &proposal_hash, + >::block_number() + Self::cooloff_period(), + existing_vetoers + ); + + Self::set_proposals( + &Self::proposals().into_iter().filter(|&(_, h)| h != proposal_hash + ).collect::>()); + >::remove(proposal_hash); + >::remove(proposal_hash); + for (c, _) in >::active_council() { + >::remove((proposal_hash, c)); + } + Ok(()) + } + + fn set_cooloff_period(blocks: ::Type) -> Result { + >::put(blocks.into()); + Ok(()) + } + + fn set_voting_period(blocks: ::Type) -> Result { + >::put(blocks.into()); + Ok(()) + } + fn on_finalise(n: T::BlockNumber) { if let Err(e) = Self::end_block(n) { print("Guru meditation"); @@ -96,78 +165,7 @@ impl Module { Self::generic_tally(proposal_hash, |w: &T::AccountId, p: &T::Hash| Self::vote_of((*p, w.clone()))) } - // Dispatch - fn propose(origin: T::Origin, proposal: Box) -> Result { - let who = ensure_signed(origin)?; - - let expiry = >::block_number() + Self::voting_period(); - ensure!(Self::will_still_be_councillor_at(&who, expiry), "proposer would not be on council"); - - let proposal_hash = T::Hashing::hash_of(&proposal); - - ensure!(!>::exists(proposal_hash), "duplicate proposals not allowed"); - ensure!(!Self::is_vetoed(&proposal_hash), "proposal is vetoed"); - - let mut proposals = Self::proposals(); - proposals.push((expiry, proposal_hash)); - proposals.sort_by_key(|&(expiry, _)| expiry); - Self::set_proposals(&proposals); - - >::insert(proposal_hash, *proposal); - >::insert(proposal_hash, vec![who.clone()]); - >::insert((proposal_hash, who.clone()), true); - - Ok(()) - } - - 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() { - >::mutate(proposal, |voters| voters.push(who.clone())); - } - >::insert((proposal, who), approve); - Ok(()) - } - - 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!(>::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(&who) - .err().ok_or("a councillor may not veto a proposal twice")?; - existing_vetoers.insert(insert_position, who); - Self::set_veto_of(&proposal_hash, >::block_number() + Self::cooloff_period(), existing_vetoers); - - Self::set_proposals(&Self::proposals().into_iter().filter(|&(_, h)| h != proposal_hash).collect::>()); - >::remove(proposal_hash); - >::remove(proposal_hash); - for (c, _) in >::active_council() { - >::remove((proposal_hash, c)); - } - Ok(()) - } - - fn set_cooloff_period(blocks: ::Type) -> Result { - >::put(blocks.into()); - Ok(()) - } - - fn set_voting_period(blocks: ::Type) -> Result { - >::put(blocks.into()); - Ok(()) - } - - // private - - + // Private fn set_veto_of(proposal: &T::Hash, expiry: T::BlockNumber, vetoers: Vec) { >::insert(proposal, (expiry, vetoers)); } diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index f2abaf9581ddb..9acacb2ea9651 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -63,12 +63,73 @@ pub trait Trait: balances::Trait + Sized { decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - fn propose(origin, proposal: Box, value: ::Type) -> Result; - fn second(origin, proposal: Compact) -> Result; - fn vote(origin, ref_index: Compact, approve_proposal: bool) -> Result; - fn start_referendum(proposal: Box, vote_threshold: VoteThreshold) -> Result; - fn cancel_referendum(ref_index: Compact) -> Result; + /// Propose a sensitive action to be taken. + fn propose( + origin, + proposal: Box, + value: ::Type + ) -> Result { + let who = ensure_signed(origin)?; + let value = value.into(); + + ensure!(value >= Self::minimum_deposit(), "value too low"); + >::reserve(&who, value) + .map_err(|_| "proposer's balance too low")?; + + let index = Self::public_prop_count(); + >::put(index + 1); + >::insert(index, (value, vec![who.clone()])); + + let mut props = Self::public_props(); + props.push((index, (*proposal).clone(), who)); + >::put(props); + Ok(()) + } + + /// Propose a sensitive action to be taken. + fn second(origin, proposal: Compact) -> Result { + let who = ensure_signed(origin)?; + let proposal: PropIndex = proposal.into(); + let mut deposit = Self::deposit_of(proposal) + .ok_or("can only second an existing proposal")?; + >::reserve(&who, deposit.0) + .map_err(|_| "seconder's balance too low")?; + deposit.1.push(who); + >::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(origin, ref_index: Compact, approve_proposal: bool) -> Result { + let who = ensure_signed(origin)?; + let ref_index = ref_index.into(); + ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum."); + ensure!(!>::total_balance(&who).is_zero(), + "transactor must have balance to signal approval."); + if !>::exists(&(ref_index, who.clone())) { + >::mutate(ref_index, |voters| voters.push(who.clone())); + } + >::insert(&(ref_index, who), approve_proposal); + Ok(()) + } + + /// Start a referendum. + fn start_referendum(proposal: Box, vote_threshold: VoteThreshold) -> Result { + Self::inject_referendum( + >::block_number() + Self::voting_period(), + *proposal, + vote_threshold + ).map(|_| ()) + } + + /// Remove a referendum. + fn cancel_referendum(ref_index: Compact) -> Result { + Self::clear_referendum(ref_index.into()); + Ok(()) + } + fn on_finalise(n: T::BlockNumber) { if let Err(e) = Self::end_block(n) { runtime_io::print(e); @@ -162,71 +223,7 @@ impl Module { .fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d)) } - // dispatching. - - /// Propose a sensitive action to be taken. - fn propose(origin: T::Origin, proposal: Box, value: ::Type) -> Result { - let who = ensure_signed(origin)?; - let value = value.into(); - - ensure!(value >= Self::minimum_deposit(), "value too low"); - >::reserve(&who, value) - .map_err(|_| "proposer's balance too low")?; - - let index = Self::public_prop_count(); - >::put(index + 1); - >::insert(index, (value, vec![who.clone()])); - - let mut props = Self::public_props(); - props.push((index, (*proposal).clone(), who)); - >::put(props); - Ok(()) - } - - /// Propose a sensitive action to be taken. - fn second(origin: T::Origin, proposal: Compact) -> Result { - let who = ensure_signed(origin)?; - let proposal: PropIndex = proposal.into(); - let mut deposit = Self::deposit_of(proposal) - .ok_or("can only second an existing proposal")?; - >::reserve(&who, deposit.0) - .map_err(|_| "seconder's balance too low")?; - deposit.1.push(who); - >::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(origin: T::Origin, ref_index: Compact, approve_proposal: bool) -> Result { - let who = ensure_signed(origin)?; - let ref_index = ref_index.into(); - ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum."); - ensure!(!>::total_balance(&who).is_zero(), - "transactor must have balance to signal approval."); - if !>::exists(&(ref_index, who.clone())) { - >::mutate(ref_index, |voters| voters.push(who.clone())); - } - >::insert(&(ref_index, who), approve_proposal); - Ok(()) - } - - /// Start a referendum. - fn start_referendum(proposal: Box, vote_threshold: VoteThreshold) -> Result { - Self::inject_referendum( - >::block_number() + Self::voting_period(), - *proposal, - vote_threshold - ).map(|_| ()) - } - - /// Remove a referendum. - fn cancel_referendum(ref_index: Compact) -> Result { - Self::clear_referendum(ref_index.into()); - Ok(()) - } - - // exposed mutables. + // Exposed mutables. /// Start a referendum. Can be called directly by the council. pub fn internal_start_referendum(proposal: T::Proposal, vote_threshold: VoteThreshold) -> result::Result { diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index 26db089a49c71..d6d7ea31faf5f 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -109,10 +109,84 @@ decl_module! { /// 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; + // This just increases the value of `Dummy` by `increase_by`. + // + // Since this is a dispatched function there are two extremely important things to + // remember: + // + // - MUST NOT PANIC: Under no circumstances (save, perhaps, storage getting into an + // irreparably damaged state) must this function panic. + // - NO SIDE-EFFECTS ON ERROR: This function must either complete totally (and return + // `Ok(())` or it must have no side-effects on storage and return `Err('Some reason')`. + // + // 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 (`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. + // If you can't be certain that the operation will succeed without substantial computation + // then you have a classic blockchain attack scenario. The normal way of managing this is + // to attach a bond to the operation. As the first major alteration of storage, reserve + // some value from the sender's account (`Balances` module has a `reserve` function for + // exactly this scenario). This amount should be enough to cover any costs of the + // substantial execution in case it turns out that you can't proceed with the operation. + // + // If it eventually transpires that the operation is fine and, therefore, that the + // expense of the checks should be borne by the network, then you can refund the reserved + // deposit. If, however, the operation turns out to be invalid and the computation is + // wasted, then you can burn it or repatriate elsewhere. + // + // Security bonds ensure that attackers can't game it by ensuring that anyone interacting + // with the system either progresses it or pays for the trouble of faffing around with + // no progress. + // + // If you don't respect these rules, it is likely that your chain will be attackable. + fn accumulate_dummy(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 itself: + // let dummy = >::get(); + + // Calculate the new value. + // let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); + + // Put the new value into storage. + // >::put(new_dummy); + // Will also work with a reference: + // >::put(&new_dummy); + + // Here's the new one of read and then modify the value. + >::mutate(|dummy| { + let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); + *dummy = Some(new_dummy); + }); + + // Let's deposit an event to let the outside world know this happened. + Self::deposit_event(RawEvent::Dummy(increase_by)); + + // All good. + Ok(()) + } /// A privileged call; in this case it resets our dummy value to something new. - fn set_dummy(new_dummy: T::Balance) -> Result; + // Implementation of a privileged 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 privileged + // calls to be executed - we don't need to care why. Because it's privileged, 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 { + // Put the new value into storage. + >::put(new_value); + + // All good. + Ok(()) + } // The signature could also look like: `fn on_finalise()` fn on_finalise(_n: T::BlockNumber) { @@ -171,79 +245,11 @@ decl_storage! { // The main implementation block for the module. Functions here fall into three broad // categories: -// - Implementations of dispatch functions. The dispatch code generated by the module macro -// expects each of its functions to be implemented. // - Public interface. These are functions that are `pub` and generally fall into inspector // functions that do not write to storage and operation functions that do. // - Private functions. These are your usual private utilities unavailable to other modules. impl Module { - // Implement Calls and add public immutables and private mutables. - - // Implement dispatched function `accumulate_dummy`. This just increases the value - // of `Dummy` by `increase_by`. - // - // Since this is a dispatched function there are two extremely important things to - // remember: - // - // - MUST NOT PANIC: Under no circumstances (save, perhaps, storage getting into an - // irreparably damaged state) must this function panic. - // - NO SIDE-EFFECTS ON ERROR: This function must either complete totally (and return - // `Ok(())` or it must have no side-effects on storage and return `Err('Some reason')`. - // - // 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 (`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. - // If you can't be certain that the operation will succeed without substantial computation - // then you have a classic blockchain attack scenario. The normal way of managing this is - // to attach a bond to the operation. As the first major alteration of storage, reserve - // some value from the sender's account (`Balances` module has a `reserve` function for - // exactly this scenario). This amount should be enough to cover any costs of the - // substantial execution in case it turns out that you can't proceed with the operation. - // - // If it eventually transpires that the operation is fine and, therefore, that the - // expense of the checks should be borne by the network, then you can refund the reserved - // deposit. If, however, the operation turns out to be invalid and the computation is - // wasted, then you can burn it or repatriate elsewhere. - // - // Security bonds ensure that attackers can't game it by ensuring that anyone interacting - // with the system either progresses it or pays for the trouble of faffing around with - // no progress. - // - // If you don't respect these rules, it is likely that your chain will be attackable. - 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 itself: - // let dummy = >::get(); - - // Calculate the new value. - // let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); - - // Put the new value into storage. - // >::put(new_dummy); - // Will also work with a reference: - // >::put(&new_dummy); - - // Here's the new one of read and then modify the value. - >::mutate(|dummy| { - let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); - *dummy = Some(new_dummy); - }); - - // Let's deposit an event to let the outside world know this happened. - Self::deposit_event(RawEvent::Dummy(increase_by)); - - // All good. - Ok(()) - } - + // Add public immutables and private mutables. #[allow(dead_code)] fn accumulate_foo(origin: T::Origin, increase_by: T::Balance) -> Result { let _sender = ensure_signed(origin)?; @@ -258,20 +264,6 @@ impl Module { Ok(()) } - - // Implementation of a privileged 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 privileged - // calls to be executed - we don't need to care why. Because it's privileged, 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 { - // Put the new value into storage. - >::put(new_value); - - // All good. - Ok(()) - } } #[cfg(test)] diff --git a/srml/session/src/lib.rs b/srml/session/src/lib.rs index f125528a43512..cc75c75bc52b6 100644 --- a/srml/session/src/lib.rs +++ b/srml/session/src/lib.rs @@ -68,10 +68,27 @@ pub trait Trait: timestamp::Trait { decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - fn set_key(origin, key: T::SessionKey) -> Result; - fn set_length(new: ::Type) -> Result; - fn force_new_session(apply_rewards: bool) -> Result; + /// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next + /// session. + fn set_key(origin, key: T::SessionKey) -> Result { + let who = ensure_signed(origin)?; + // set new value for next session + >::insert(who, key); + Ok(()) + } + + /// Set a new session length. Won't kick in until the next session change (at current length). + fn set_length(new: ::Type) -> Result { + >::put(new.into()); + Ok(()) + } + + /// Forces a new session. + fn force_new_session(apply_rewards: bool) -> Result { + Self::apply_force_new_session(apply_rewards) + } + fn on_finalise(n: T::BlockNumber) { Self::check_rotate_session(n); } @@ -122,28 +139,7 @@ impl Module { >::get().unwrap_or_else(T::BlockNumber::zero) } - /// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next - /// session. - fn set_key(origin: T::Origin, key: T::SessionKey) -> Result { - let who = ensure_signed(origin)?; - // set new value for next session - >::insert(who, key); - Ok(()) - } - - /// Set a new session length. Won't kick in until the next session change (at current length). - fn set_length(new: ::Type) -> Result { - >::put(new.into()); - Ok(()) - } - - /// Forces a new session. - pub fn force_new_session(apply_rewards: bool) -> Result { - 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 { >::put(apply_rewards); diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 27080e2335b7d..680da340c54c5 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -106,17 +106,139 @@ decl_module! { #[cfg_attr(feature = "std", serde(bound(deserialize = "T::Balance: ::serde::de::DeserializeOwned")))] pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - fn stake(origin) -> Result; - fn unstake(origin, intentions_index: Compact) -> Result; - fn nominate(origin, target: Address) -> Result; - fn unnominate(origin, target_index: Compact) -> Result; - fn register_preferences(origin, intentions_index: Compact, prefs: ValidatorPrefs) -> Result; - - fn set_sessions_per_era(new: ::Type) -> Result; - fn set_bonding_duration(new: ::Type) -> Result; - fn set_validator_count(new: Compact) -> Result; - fn force_new_era(apply_rewards: bool) -> Result; - fn set_offline_slash_grace(new: Compact) -> Result; + + /// Declare the desire to stake for the transactor. + /// + /// Effects will be felt at the beginning of the next era. + fn stake(origin) -> Result { + let who = ensure_signed(origin)?; + ensure!(Self::nominating(&who).is_none(), "Cannot stake if already nominating."); + let mut intentions = >::get(); + // can't be in the list twice. + ensure!(intentions.iter().find(|&t| t == &who).is_none(), "Cannot stake if already staked."); + + >::insert(&who, T::BlockNumber::max_value()); + intentions.push(who); + >::put(intentions); + Ok(()) + } + + /// Retract the desire to stake for the transactor. + /// + /// Effects will be felt at the beginning of the next era. + fn unstake(origin, intentions_index: Compact) -> Result { + let who = ensure_signed(origin)?; + let intentions_index: u32 = intentions_index.into(); + // unstake fails in degenerate case of having too few existing staked parties + if Self::intentions().len() <= Self::minimum_validator_count() as usize { + return Err("cannot unstake when there are too few staked participants") + } + Self::apply_unstake(&who, intentions_index as usize) + } + + fn nominate(origin, target: Address) -> Result { + let who = ensure_signed(origin)?; + let target = >::lookup(target)?; + + 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(who.clone()); + >::insert(&target, t); + + // update nominating + >::insert(&who, &target); + + // Update bondage + >::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(origin, target_index: Compact) -> Result { + let source = ensure_signed(origin)?; + let target_index: u32 = target_index.into(); + let target_index = target_index as usize; + + let target = >::get(&source).ok_or("Account must be nominating")?; + + let mut t = Self::nominators_for(&target); + if t.get(target_index) != Some(&source) { + return Err("Invalid target index") + } + + // Ok - all valid. + + // update nominators_for + t.swap_remove(target_index); + >::insert(&target, t); + + // update nominating + >::remove(&source); + + // update bondage + >::insert( + source, + >::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] != origin`. + fn register_preferences( + origin, + intentions_index: Compact, + prefs: ValidatorPrefs + ) -> Result { + let who = ensure_signed(origin)?; + let intentions_index: u32 = intentions_index.into(); + + if Self::intentions().get(intentions_index as usize) != Some(&who) { + return Err("Invalid index") + } + + >::insert(who, prefs); + + Ok(()) + } + + /// Set the number of sessions in an era. + fn set_sessions_per_era(new: ::Type) -> Result { + >::put(new.into()); + Ok(()) + } + + /// The length of the bonding duration in eras. + fn set_bonding_duration(new: ::Type) -> Result { + >::put(new.into()); + Ok(()) + } + + /// The length of a staking era in sessions. + fn set_validator_count(new: Compact) -> Result { + let new: u32 = new.into(); + >::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 { + Self::apply_force_new_era(apply_rewards) + } + + /// Set the offline slash grace period. + fn set_offline_slash_grace(new: Compact) -> Result { + let new: u32 = new.into(); + >::put(new); + Ok(()) + } } } @@ -190,6 +312,12 @@ decl_storage! { } impl Module { + // Just force_new_era without origin check. + fn apply_force_new_era(apply_rewards: bool) -> Result { + >::put(()); + >::apply_force_new_session(apply_rewards) + } + // PUBLIC IMMUTABLES /// The length of a staking era in blocks. @@ -220,147 +348,6 @@ impl Module { } } - // PUBLIC DISPATCH - - /// Declare the desire to stake for the transactor. - /// - /// Effects will be felt at the beginning of the next era. - 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 = >::get(); - // can't be in the list twice. - ensure!(intentions.iter().find(|&t| t == &who).is_none(), "Cannot stake if already staked."); - - >::insert(&who, T::BlockNumber::max_value()); - intentions.push(who); - >::put(intentions); - Ok(()) - } - - /// Retract the desire to stake for the transactor. - /// - /// Effects will be felt at the beginning of the next era. - fn unstake(origin: T::Origin, intentions_index: Compact) -> Result { - let who = ensure_signed(origin)?; - let intentions_index: u32 = intentions_index.into(); - // unstake fails in degenerate case of having too few existing staked parties - if Self::intentions().len() <= Self::minimum_validator_count() as usize { - return Err("cannot unstake when there are too few staked participants") - } - Self::apply_unstake(&who, intentions_index as usize) - } - - fn nominate(origin: T::Origin, target: Address) -> Result { - let who = ensure_signed(origin)?; - let target = >::lookup(target)?; - - 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(who.clone()); - >::insert(&target, t); - - // update nominating - >::insert(&who, &target); - - // Update bondage - >::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(origin: T::Origin, target_index: Compact) -> Result { - let source = ensure_signed(origin)?; - let target_index: u32 = target_index.into(); - let target_index = target_index as usize; - - let target = >::get(&source).ok_or("Account must be nominating")?; - - let mut t = Self::nominators_for(&target); - if t.get(target_index) != Some(&source) { - return Err("Invalid target index") - } - - // Ok - all valid. - - // update nominators_for - t.swap_remove(target_index); - >::insert(&target, t); - - // update nominating - >::remove(&source); - - // update bondage - >::insert(source, >::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] != origin`. - fn register_preferences( - origin: T::Origin, - intentions_index: Compact, - prefs: ValidatorPrefs - ) -> Result { - let who = ensure_signed(origin)?; - let intentions_index: u32 = intentions_index.into(); - - if Self::intentions().get(intentions_index as usize) != Some(&who) { - return Err("Invalid index") - } - - >::insert(who, prefs); - - Ok(()) - } - - // PRIV DISPATCH - - /// Set the number of sessions in an era. - fn set_sessions_per_era(new: ::Type) -> Result { - >::put(new.into()); - Ok(()) - } - - /// The length of the bonding duration in eras. - fn set_bonding_duration(new: ::Type) -> Result { - >::put(new.into()); - Ok(()) - } - - /// The length of a staking era in sessions. - fn set_validator_count(new: Compact) -> Result { - let new: u32 = new.into(); - >::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 { - Self::apply_force_new_era(apply_rewards) - } - - // Just force_new_era without origin check. - fn apply_force_new_era(apply_rewards: bool) -> Result { - >::put(()); - >::apply_force_new_session(apply_rewards) - } - - - /// Set the offline slash grace period. - fn set_offline_slash_grace(new: Compact) -> Result { - let new: u32 = new.into(); - >::put(new); - Ok(()) - } - // PUBLIC MUTABLES (DANGEROUS) /// Slash a given validator by a specific amount. Removes the slash from their balance by preference, diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index a19bb67cb3fc1..8a131336ae717 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -171,7 +171,7 @@ macro_rules! decl_module { { $( $on_finalise:tt )* } [ $($t:tt)* ] $(#[doc = $doc_attr:tt])* - fn $fn_name:ident(origin $(, $param_name:ident : $param:ty)* ) -> $result:ty ; + $fn_vis:vis fn $fn_name:ident($origin:ident $(, $param_name:ident : $param:ty)* ) -> $result:ty { $( $impl:tt )* } $($rest:tt)* ) => { decl_module!(@normalize @@ -180,7 +180,11 @@ macro_rules! decl_module { for enum $call_type where origin: $origin_type, system = $system { $( $deposit_event )* } { $( $on_finalise )* } - [ $($t)* $(#[doc = $doc_attr])* fn $fn_name(origin $( , $param_name : $param )* ) -> $result; ] + [ + $($t)* + $(#[doc = $doc_attr])* + $fn_vis fn $fn_name($origin $( , $param_name : $param )* ) -> $result { $( $impl )* } + ] $($rest)* ); }; @@ -192,7 +196,7 @@ macro_rules! decl_module { { $( $on_finalise:tt )* } [ $($t:tt)* ] $(#[doc = $doc_attr:tt])* - fn $fn_name:ident($( $param_name:ident : $param:ty),* ) -> $result:ty ; + $fn_vis:vis fn $fn_name:ident($( $param_name:ident : $param:ty),* ) -> $result:ty { $( $impl:tt )* } $($rest:tt)* ) => { decl_module!(@normalize @@ -201,7 +205,11 @@ macro_rules! decl_module { for enum $call_type where origin: $origin_type, system = $system { $( $deposit_event )* } { $( $on_finalise )* } - [ $($t)* $(#[doc = $doc_attr])* fn $fn_name(root $( , $param_name : $param )* ) -> $result; ] + [ + $($t)* + $(#[doc = $doc_attr])* + fn $fn_name(root $( , $param_name : $param )* ) -> $result { $( $impl )* } + ] $($rest)* ); }; @@ -224,12 +232,6 @@ macro_rules! decl_module { ); }; - (@call - origin - $mod_type:ident $trait_instance:ident $fn_name:ident $origin:ident $system:ident [ $( $param_name:ident),* ] - ) => { - <$mod_type<$trait_instance>>::$fn_name( $origin $(, $param_name )* ) - }; (@call root $mod_type:ident $trait_instance:ident $fn_name:ident $origin:ident $system:ident [ $( $param_name:ident),* ] @@ -239,6 +241,12 @@ macro_rules! decl_module { <$mod_type<$trait_instance>>::$fn_name( $( $param_name ),* ) } }; + (@call + $ingore:ident + $mod_type:ident $trait_instance:ident $fn_name:ident $origin:ident $system:ident [ $( $param_name:ident),* ] + ) => { + <$mod_type<$trait_instance>>::$fn_name( $origin $(, $param_name )* ) + }; // no `deposit_event` function wanted (@impl_deposit_event @@ -302,13 +310,40 @@ macro_rules! decl_module { for $module<$trait_instance> {} }; + (@impl_function + $module:ident<$trait_instance:ident: $trait_name:ident>; + $origin_ty:ty; + root; + $vis:vis fn $name:ident ( root $(, $param:ident : $param_ty:ty )* ) -> $result:ty { $( $impl:tt )* } + ) => { + impl<$trait_instance: $trait_name> $module<$trait_instance> { + $vis fn $name($( $param: $param_ty ),* ) -> $result { + $( $impl )* + } + } + }; + (@impl_function + $module:ident<$trait_instance:ident: $trait_name:ident>; + $origin_ty:ty; + $ignore:ident; + $vis:vis fn $name:ident ( $origin:ident $(, $param:ident : $param_ty:ty )* ) -> $result:ty { $( $impl:tt )* } + ) => { + impl<$trait_instance: $trait_name> $module<$trait_instance> { + $vis fn $name($origin: $origin_ty $(, $param: $param_ty )* ) -> $result { + $( $impl )* + } + } + }; + (@imp $(#[$attr:meta])* pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident> for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident { $( $(#[doc = $doc_attr:tt])* - fn $fn_name:ident($from:ident $( , $param_name:ident : $param:ty)*) -> $result:ty; + $fn_vis:vis fn $fn_name:ident( + $from:ident $( , $param_name:ident : $param:ty)* + ) -> $result:ty { $( $impl:tt )* } )* } { $( $deposit_event:tt )* } @@ -342,6 +377,16 @@ macro_rules! decl_module { $( $deposit_event )* } + $( + decl_module! { + @impl_function + $mod_type<$trait_instance: $trait_name>; + $origin_type; + $from; + $fn_vis fn $fn_name ($from $(, $param_name : $param )* ) -> $result { $( $impl )* } + } + )* + #[cfg(feature = "std")] $(#[$attr])* #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -449,7 +494,11 @@ macro_rules! decl_module { match self { $( $call_type::$fn_name( $( $param_name ),* ) => { - decl_module!(@call $from $mod_type $trait_instance $fn_name _origin $system [ $( $param_name ),* ]) + decl_module!( + @call + $from + $mod_type $trait_instance $fn_name _origin $system [ $( $param_name ),* ] + ) }, )* _ => { panic!("__PhantomItem should never be used.") }, @@ -786,11 +835,11 @@ mod tests { decl_module! { pub struct Module for enum Call where origin: T::Origin { /// Hi, this is a comment. - fn aux_0(origin) -> Result; - fn aux_1(origin, data: i32) -> Result; - fn aux_2(origin, data: i32, data2: String) -> Result; - fn aux_3() -> Result; - fn aux_4(data: i32) -> Result; + fn aux_0(_origin) -> Result { unreachable!() } + fn aux_1(_origin, _data: i32) -> Result { unreachable!() } + fn aux_2(_origin, _data: i32, _data2: String) -> Result { unreachable!() } + fn aux_3() -> Result { unreachable!() } + fn aux_4(_data: i32) -> Result { unreachable!() } } } @@ -812,7 +861,7 @@ mod tests { name: DecodeDifferent::Encode("aux_1"), arguments: DecodeDifferent::Encode(&[ FunctionArgumentMetadata { - name: DecodeDifferent::Encode("data"), + name: DecodeDifferent::Encode("_data"), ty: DecodeDifferent::Encode("i32"), } ]), @@ -823,11 +872,11 @@ mod tests { name: DecodeDifferent::Encode("aux_2"), arguments: DecodeDifferent::Encode(&[ FunctionArgumentMetadata { - name: DecodeDifferent::Encode("data"), + name: DecodeDifferent::Encode("_data"), ty: DecodeDifferent::Encode("i32"), }, FunctionArgumentMetadata { - name: DecodeDifferent::Encode("data2"), + name: DecodeDifferent::Encode("_data2"), ty: DecodeDifferent::Encode("String"), } ]), @@ -844,7 +893,7 @@ mod tests { name: DecodeDifferent::Encode("aux_4"), arguments: DecodeDifferent::Encode(&[ FunctionArgumentMetadata { - name: DecodeDifferent::Encode("data"), + name: DecodeDifferent::Encode("_data"), ty: DecodeDifferent::Encode("i32"), } ]), @@ -854,28 +903,6 @@ mod tests { }, }; - impl Module { - fn aux_0(_: T::Origin) -> Result { - unreachable!() - } - - fn aux_1(_: T::Origin, _: i32) -> Result { - unreachable!() - } - - fn aux_2(_: T::Origin, _: i32, _: String) -> Result { - unreachable!() - } - - fn aux_3() -> Result { - unreachable!() - } - - fn aux_4(_: i32) -> Result { - unreachable!() - } - } - struct TraitImpl {} impl Trait for TraitImpl { diff --git a/srml/support/src/metadata.rs b/srml/support/src/metadata.rs index 2a6b05bde2ead..c1c4966442fbb 100644 --- a/srml/support/src/metadata.rs +++ b/srml/support/src/metadata.rs @@ -160,13 +160,7 @@ mod tests { decl_module! { pub struct Module for enum Call where origin: T::Origin { - fn aux_0(origin) -> Result; - } - } - - impl Module { - fn aux_0(_: T::Origin) -> Result { - unreachable!() + fn aux_0(_origin) -> Result { unreachable!() } } } } diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index 6b42a8218a22e..1779807af137b 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -74,7 +74,33 @@ pub trait Trait: consensus::Trait + system::Trait { decl_module! { pub struct Module for enum Call where origin: T::Origin { - fn set(origin, now: ::Type) -> Result; + /// Set the current time. + /// + /// Extrinsic with this call should be placed at the specific position in the each block + /// (specified by the Trait::TIMESTAMP_SET_POSITION) typically at the start of the each block. + /// This call should be invoked exactly once per block. It will panic at the finalization phase, + /// if this call hasn't been invoked by that time. + /// + /// The timestamp should be greater than the previous one by the amount specified by `block_period`. + fn set(origin, now: ::Type) -> Result { + ensure_inherent(origin)?; + let now = now.into(); + + assert!(!::DidUpdate::exists(), "Timestamp must be updated only once in the block"); + assert!( + >::extrinsic_index() == Some(T::TIMESTAMP_SET_POSITION), + "Timestamp extrinsic must be at position {} in the block", + T::TIMESTAMP_SET_POSITION + ); + assert!( + Self::now().is_zero() || now >= Self::now() + Self::block_period(), + "Timestamp must increment by at least between sequential blocks" + ); + ::Now::put(now); + ::DidUpdate::put(true); + Ok(()) + } + fn on_finalise() { assert!(::DidUpdate::take(), "Timestamp must be updated once in the block"); } @@ -103,33 +129,6 @@ impl Module { Self::now() } - /// Set the current time. - /// - /// Extrinsic with this call should be placed at the specific position in the each block - /// (specified by the Trait::TIMESTAMP_SET_POSITION) typically at the start of the each block. - /// This call should be invoked exactly once per block. It will panic at the finalization phase, - /// if this call hasn't been invoked by that time. - /// - /// The timestamp should be greater than the previous one by the amount specified by `block_period`. - fn set(origin: T::Origin, now: ::Type) -> Result { - ensure_inherent(origin)?; - let now = now.into(); - - assert!(!::DidUpdate::exists(), "Timestamp must be updated only once in the block"); - assert!( - >::extrinsic_index() == Some(T::TIMESTAMP_SET_POSITION), - "Timestamp extrinsic must be at position {} in the block", - T::TIMESTAMP_SET_POSITION - ); - assert!( - Self::now().is_zero() || now >= Self::now() + Self::block_period(), - "Timestamp must increment by at least between sequential blocks" - ); - ::Now::put(now); - ::DidUpdate::put(true); - Ok(()) - } - /// Set the timestamp to something in particular. Only used for tests. #[cfg(feature = "std")] pub fn set_timestamp(now: T::Moment) { diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index ed2b7d6dfb924..7d314674bc392 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -74,23 +74,79 @@ decl_module! { // Simple declaration of the `Module` type. Lets the macro know what its working on. pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - // 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(origin, value: ::Type, beneficiary: Address) -> Result; + /// 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( + origin, + value: ::Type, + beneficiary: Address + ) -> Result { + let proposer = ensure_signed(origin)?; + let beneficiary = >::lookup(beneficiary)?; + let value = value.into(); + + let bond = Self::calculate_bond(value); + >::reserve(&proposer, bond) + .map_err(|_| "Proposer's balance too low")?; + + let c = Self::proposal_count(); + >::put(c + 1); + >::insert(c, Proposal { proposer, value, beneficiary, bond }); + + Self::deposit_event(RawEvent::Proposed(c)); + + Ok(()) + } + + /// Set the balance of funds available to spend. + fn set_pot(new_pot: ::Type) -> Result { + // Put the new value into storage. + >::put(new_pot.into()); - // Set the balance of funds available to spend. - fn set_pot(new_pot: ::Type) -> Result; + // All good. + Ok(()) + } + + /// (Re-)configure this module. + fn configure( + proposal_bond: Permill, + proposal_bond_minimum: ::Type, + spend_period: ::Type, + burn: Permill + ) -> Result { + >::put(proposal_bond); + >::put(proposal_bond_minimum.into()); + >::put(spend_period.into()); + >::put(burn); + Ok(()) + } - // (Re-)configure this module. - fn configure(proposal_bond: Permill, proposal_bond_minimum: ::Type, spend_period: ::Type, burn: Permill) -> Result; + /// Reject a proposed spend. The original deposit will be slashed. + fn reject_proposal(origin, proposal_id: Compact) -> Result { + T::RejectOrigin::ensure_origin(origin)?; + let proposal_id: ProposalIndex = proposal_id.into(); + + let proposal = >::take(proposal_id).ok_or("No proposal at that index")?; + + let value = proposal.bond; + let _ = >::slash_reserved(&proposal.proposer, value); + + Ok(()) + } - // Reject a proposed spend. The original deposit will be slashed. - fn reject_proposal(origin, proposal_id: Compact) -> 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(origin, proposal_id: Compact) -> Result { + T::ApproveOrigin::ensure_origin(origin)?; + let proposal_id = proposal_id.into(); - // 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(origin, proposal_id: Compact) -> Result; + ensure!(>::exists(proposal_id), "No proposal at that index"); + + >::mutate(|v| v.push(proposal_id)); + + Ok(()) + } fn on_finalise(n: T::BlockNumber) { // Check to see if we should spend some funds! @@ -161,69 +217,7 @@ decl_event!( ); impl Module { - // Implement Calls and add public immutables and private mutables. - - fn propose_spend(origin: T::Origin, value: ::Type, beneficiary: Address) -> Result { - let proposer = ensure_signed(origin)?; - let beneficiary = >::lookup(beneficiary)?; - let value = value.into(); - - let bond = Self::calculate_bond(value); - >::reserve(&proposer, bond) - .map_err(|_| "Proposer's balance too low")?; - - let c = Self::proposal_count(); - >::put(c + 1); - >::insert(c, Proposal { proposer, value, beneficiary, bond }); - - Self::deposit_event(RawEvent::Proposed(c)); - - Ok(()) - } - - fn reject_proposal(origin: T::Origin, proposal_id: Compact) -> Result { - T::RejectOrigin::ensure_origin(origin)?; - let proposal_id: ProposalIndex = proposal_id.into(); - - let proposal = >::take(proposal_id).ok_or("No proposal at that index")?; - - let value = proposal.bond; - let _ = >::slash_reserved(&proposal.proposer, value); - - Ok(()) - } - - fn approve_proposal(origin: T::Origin, proposal_id: Compact) -> Result { - T::ApproveOrigin::ensure_origin(origin)?; - let proposal_id = proposal_id.into(); - - ensure!(>::exists(proposal_id), "No proposal at that index"); - - >::mutate(|v| v.push(proposal_id)); - - Ok(()) - } - - fn set_pot(new_pot: ::Type) -> Result { - // Put the new value into storage. - >::put(new_pot.into()); - - // All good. - Ok(()) - } - - fn configure( - proposal_bond: Permill, - proposal_bond_minimum: ::Type, - spend_period: ::Type, - burn: Permill - ) -> Result { - >::put(proposal_bond); - >::put(proposal_bond_minimum.into()); - >::put(spend_period.into()); - >::put(burn); - Ok(()) - } + // Add public immutables and private mutables. /// The needed bond for a proposal whose spend is `value`. fn calculate_bond(value: T::Balance) -> T::Balance { From 65b99b08e0c5f39402f52c6a75ec6be8e9692807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 26 Oct 2018 12:06:29 +0200 Subject: [PATCH 4/4] Regenerate the wasm file after master rebase --- .../release/node_runtime.compact.wasm | Bin 607677 -> 612872 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 461eff1a39b998d3475828130dfaee6cacad27bb..3ffbf0d1b1afbc7e6ddf799faccd87cf2e2f81cb 100644 GIT binary patch delta 94159 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%0DSRKv@zm-vC05?TEjqBwbim^&)6(z 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 Um+j1{p9MrWqebQcJtiCae*rfL^Z)<= delta 88871 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�d2VpRx0f}$Gyw~Lkt&4ZKP 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 B26g}d