From a591f5664fe002a4cf63595125fc6712316125a5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 18 Jan 2020 14:17:33 +0100 Subject: [PATCH 1/6] Add rules and unfounding to society. --- frame/democracy/src/lib.rs | 2 +- frame/society/src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++-- frame/society/src/tests.rs | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index 354e93cc292ee..d7790be8f7065 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -497,7 +497,7 @@ decl_module! { } /// Vote in a referendum on behalf of a stash. If `vote.is_aye()`, the vote is to enact - /// the proposal; otherwise it is a vote to keep the status quo. + /// the proposal; otherwise it is a vote to keep the status quo. /// /// # /// - O(1). diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 348607d196c86..61767b33980c3 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -254,7 +254,7 @@ use sp_std::prelude::*; use codec::{Encode, Decode}; use sp_runtime::{Percent, ModuleId, RuntimeDebug, traits::{ - StaticLookup, AccountIdConversion, Saturating, Zero, IntegerSquareRoot, + StaticLookup, AccountIdConversion, Saturating, Zero, IntegerSquareRoot, Hash, TrailingZeroInput, CheckedSub, EnsureOrigin } }; @@ -404,6 +404,10 @@ decl_storage! { pub Founder get(founder) build(|config: &GenesisConfig| config.members.first().cloned()): Option; + /// A hash of the rules of this society concerning membership. Can only be set once and + /// only by the founder. + pub Rules get(rules): Option; + /// The current set of candidates; bidders that are attempting to become members. pub Candidates get(candidates): Vec>>; @@ -805,6 +809,7 @@ decl_module! { /// Parameters: /// - `founder` - The first member and head of the newly founded society. /// - `max_members` - The initial max number of members for the society. + /// - `rules` - The rules of this society concerning membership. /// /// # /// - Two storage mutates to set `Head` and `Founder`. O(1) @@ -814,7 +819,7 @@ decl_module! { /// Total Complexity: O(1) /// # #[weight = SimpleDispatchInfo::FixedNormal(10_000)] - fn found(origin, founder: T::AccountId, max_members: u32) { + fn found(origin, founder: T::AccountId, max_members: u32, rules: Vec) { T::FounderSetOrigin::ensure_origin(origin)?; ensure!(!>::exists(), Error::::AlreadyFounded); ensure!(max_members > 1, Error::::MaxMembers); @@ -823,8 +828,35 @@ decl_module! { Self::add_member(&founder)?; >::put(&founder); >::put(&founder); + Rules::::put(T::Hashing::hash(&rules)); Self::deposit_event(RawEvent::Founded(founder)); } + + /// Anull the founding of the society. + /// + /// This may be done when there is only one member. + /// + /// The dispatch origin for this call must be Signed, and the signing account must be the + /// `Founder`. + /// + /// # + /// - Two storage mutates to set `Head` and `Founder`. O(1) + /// - One storage write to add the first member to society. O(1) + /// - One event. + /// + /// Total Complexity: O(1) + /// # + #[weight = SimpleDispatchInfo::FixedNormal(10_000)] + fn unfound(origin) { + let who = ensure_signed(origin)?; + ensure!(&>::get()[..] == &[who][..], Error::::NotFounder); + + Members::::kill(); + Head::::kill(); + Founder::::kill(); + Rules::::kill(); + } + /// Allow suspension judgement origin to make judgement on a suspended member. /// /// If a suspended member is forgiven, we simply add them back as a member, not affecting @@ -1047,6 +1079,8 @@ decl_error! { NotCandidate, /// Too many members in the society. MaxMembers, + /// The society is not a single member who is the caller. + NotFounder, } } diff --git a/frame/society/src/tests.rs b/frame/society/src/tests.rs index 580edc36431b8..0bb81d78a1aad 100644 --- a/frame/society/src/tests.rs +++ b/frame/society/src/tests.rs @@ -21,6 +21,7 @@ use mock::*; use frame_support::{assert_ok, assert_noop}; use sp_runtime::traits::BadOrigin; +use sp_core::blake2_256; #[test] fn founding_works() { @@ -31,9 +32,9 @@ fn founding_works() { assert_eq!(Society::pot(), 0); // Account 1 is set as the founder origin // Account 5 cannot start a society - assert_noop!(Society::found(Origin::signed(5), 20, 100), BadOrigin); + assert_noop!(Society::found(Origin::signed(5), 20, 100, vec![]), BadOrigin); // Account 1 can start a society, where 10 is the founding member - assert_ok!(Society::found(Origin::signed(1), 10, 100)); + assert_ok!(Society::found(Origin::signed(1), 10, 100, b"be cool".to_vec())); // Society members only include 10 assert_eq!(Society::members(), vec![10]); // 10 is the head of the society @@ -42,11 +43,39 @@ fn founding_works() { assert_eq!(Society::founder(), Some(10)); // 100 members max assert_eq!(Society::max_members(), 100); + // rules are correct + assert_eq!(Society::rules(), Some(blake2_256(b"be cool").into())); // Pot grows after first rotation period run_to_block(4); assert_eq!(Society::pot(), 1000); // Cannot start another society - assert_noop!(Society::found(Origin::signed(1), 20, 100), Error::::AlreadyFounded); + assert_noop!( + Society::found(Origin::signed(1), 20, 100, vec![]), + Error::::AlreadyFounded + ); + }); +} + +#[test] +fn unfounding_works() { + EnvBuilder::new().with_max_members(0).with_members(vec![]).execute(|| { + // Account 1 sets the founder... + assert_ok!(Society::found(Origin::signed(1), 10, 100, vec![])); + // Account 2 cannot unfound it as it's not the founder. + assert_noop!(Society::unfound(Origin::signed(2)), Error::::NotFounder); + // Account 10 can, though. + assert_ok!(Society::unfound(Origin::signed(10))); + + // 1 sets the founder to 20 this time + assert_ok!(Society::found(Origin::signed(1), 20, 100, vec![])); + // Bring in a new member... + assert_ok!(Society::bid(Origin::signed(10), 0)); + run_to_block(4); + assert_ok!(Society::vote(Origin::signed(20), 10, true)); + run_to_block(8); + + // Unfounding won't work now, even though it's from 20. + assert_noop!(Society::unfound(Origin::signed(20)), Error::::NotFounder); }); } From fa1f7393926ccb991f2275e0d48528468d672e63 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 18 Jan 2020 14:26:21 +0100 Subject: [PATCH 2/6] Docs and event --- frame/society/src/lib.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 61767b33980c3..24da34b0a740e 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -228,6 +228,7 @@ //! * `defender_vote` - A member can vote to approve or reject a defender's continued membership //! to the society. //! * `payout` - A member can claim their first matured payment. +//! * `unfound` - Allow the founder to unfound the society when they are the only member. //! //! #### For Super Users //! @@ -840,21 +841,22 @@ decl_module! { /// `Founder`. /// /// # - /// - Two storage mutates to set `Head` and `Founder`. O(1) - /// - One storage write to add the first member to society. O(1) + /// - Four storage removals O(1). /// - One event. /// /// Total Complexity: O(1) /// # - #[weight = SimpleDispatchInfo::FixedNormal(10_000)] + #[weight = SimpleDispatchInfo::FixedNormal(20_000)] fn unfound(origin) { - let who = ensure_signed(origin)?; - ensure!(&>::get()[..] == &[who][..], Error::::NotFounder); + let founder = ensure_signed(origin)?; + let members = >::get(); + ensure!(members.len() == 1 && &members[0] == &founder, Error::::NotFounder); Members::::kill(); Head::::kill(); Founder::::kill(); Rules::::kill(); + Self::deposit_event(RawEvent::Unfounded(founder)); } /// Allow suspension judgement origin to make judgement on a suspended member. @@ -1121,6 +1123,8 @@ decl_event! { DefenderVote(AccountId, bool), /// A new max member count has been set NewMaxMembers(u32), + /// Society is unfounded. + Unfounded(AccountId), } } From 2671f44b29d389130670b7e175d7414066dd5345 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 18 Jan 2020 14:27:39 +0100 Subject: [PATCH 3/6] Extra bit of docs. --- frame/society/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 24da34b0a740e..eb923320963ad 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -841,6 +841,7 @@ decl_module! { /// `Founder`. /// /// # + /// - One storage read, O(M). /// - Four storage removals O(1). /// - One event. /// From ecb2a0e25aed98acd93c45dba215f7fa0444ecf1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 18 Jan 2020 15:27:16 +0100 Subject: [PATCH 4/6] Cunningly reduce complexity --- frame/society/src/lib.rs | 17 +++++++++-------- frame/society/src/tests.rs | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index eb923320963ad..ac63dd7605774 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -835,13 +835,12 @@ decl_module! { /// Anull the founding of the society. /// - /// This may be done when there is only one member. - /// - /// The dispatch origin for this call must be Signed, and the signing account must be the - /// `Founder`. + /// The dispatch origin for this call must be Signed, and the signing account must be both + /// the `Founder` and the `Head`. This implies that it may only be done when there is one + /// member. /// /// # - /// - One storage read, O(M). + /// - Two storage reads O(1). /// - Four storage removals O(1). /// - One event. /// @@ -850,8 +849,8 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(20_000)] fn unfound(origin) { let founder = ensure_signed(origin)?; - let members = >::get(); - ensure!(members.len() == 1 && &members[0] == &founder, Error::::NotFounder); + ensure!(Founder::::get() == Some(founder.clone()), Error::::NotFounder); + ensure!(Head::::get() == Some(founder.clone()), Error::::NotHead); Members::::kill(); Head::::kill(); @@ -1082,8 +1081,10 @@ decl_error! { NotCandidate, /// Too many members in the society. MaxMembers, - /// The society is not a single member who is the caller. + /// The caller is not the founder. NotFounder, + /// The caller is not the head. + NotHead, } } diff --git a/frame/society/src/tests.rs b/frame/society/src/tests.rs index 0bb81d78a1aad..3e5afc47f500a 100644 --- a/frame/society/src/tests.rs +++ b/frame/society/src/tests.rs @@ -75,7 +75,7 @@ fn unfounding_works() { run_to_block(8); // Unfounding won't work now, even though it's from 20. - assert_noop!(Society::unfound(Origin::signed(20)), Error::::NotFounder); + assert_noop!(Society::unfound(Origin::signed(20)), Error::::NotHead); }); } From 4dfd4e2aec831e5d42d16eba7639ae83529a95e4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 18 Jan 2020 17:08:55 +0100 Subject: [PATCH 5/6] Remove candidates when unfounding. --- frame/society/src/lib.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index ac63dd7605774..91521fe667fc2 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -856,6 +856,7 @@ decl_module! { Head::::kill(); Founder::::kill(); Rules::::kill(); + Candidates::::kill(); Self::deposit_event(RawEvent::Unfounded(founder)); } @@ -1264,16 +1265,16 @@ impl, I: Instance> Module { ensure!(Self::head() != Some(m.clone()), Error::::Head); ensure!(Self::founder() != Some(m.clone()), Error::::Founder); - >::mutate(|members| - match members.binary_search(&m) { - Err(_) => Err(Error::::NotMember)?, - Ok(i) => { - members.remove(i); - T::MembershipChanged::change_members_sorted(&[], &[m.clone()], members); - Ok(()) - } + let mut members = >::get(); + match members.binary_search(&m) { + Err(_) => Err(Error::::NotMember)?, + Ok(i) => { + members.remove(i); + T::MembershipChanged::change_members_sorted(&[], &[m.clone()], &members[..]); + >::put(members); + Ok(()) } - ) + } } /// End the current period and begin a new one. From 57b2586ea937caea96f8a1eabdd75c40377896ef Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 18 Jan 2020 18:05:13 +0100 Subject: [PATCH 6/6] Remove suspended candidates when unfounding, too. --- frame/society/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 91521fe667fc2..f4e5904ea42d7 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -857,6 +857,7 @@ decl_module! { Founder::::kill(); Rules::::kill(); Candidates::::kill(); + SuspendedCandidates::::remove_all(); Self::deposit_event(RawEvent::Unfounded(founder)); }