Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Add rules and unfounding to society. (#4671)
Browse files Browse the repository at this point in the history
* Add rules and unfounding to society.

* Docs and event

* Extra bit of docs.

* Cunningly reduce complexity

* Remove candidates when unfounding.

* Remove suspended candidates when unfounding, too.
  • Loading branch information
gavofyork committed Jan 18, 2020
1 parent 7da697b commit f06aa9a
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 15 deletions.
2 changes: 1 addition & 1 deletion frame/democracy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
/// # <weight>
/// - O(1).
Expand Down
64 changes: 53 additions & 11 deletions frame/society/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
//!
Expand All @@ -254,7 +255,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
}
};
Expand Down Expand Up @@ -404,6 +405,10 @@ decl_storage! {
pub Founder get(founder) build(|config: &GenesisConfig<T, I>| config.members.first().cloned()):
Option<T::AccountId>;

/// 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<T::Hash>;

/// The current set of candidates; bidders that are attempting to become members.
pub Candidates get(candidates): Vec<Bid<T::AccountId, BalanceOf<T, I>>>;

Expand Down Expand Up @@ -805,6 +810,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.
///
/// # <weight>
/// - Two storage mutates to set `Head` and `Founder`. O(1)
Expand All @@ -814,7 +820,7 @@ decl_module! {
/// Total Complexity: O(1)
/// # </weight>
#[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<u8>) {
T::FounderSetOrigin::ensure_origin(origin)?;
ensure!(!<Head<T, I>>::exists(), Error::<T, I>::AlreadyFounded);
ensure!(max_members > 1, Error::<T, I>::MaxMembers);
Expand All @@ -823,8 +829,38 @@ decl_module! {
Self::add_member(&founder)?;
<Head<T, I>>::put(&founder);
<Founder<T, I>>::put(&founder);
Rules::<T, I>::put(T::Hashing::hash(&rules));
Self::deposit_event(RawEvent::Founded(founder));
}

/// Anull the founding of the society.
///
/// 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.
///
/// # <weight>
/// - Two storage reads O(1).
/// - Four storage removals O(1).
/// - One event.
///
/// Total Complexity: O(1)
/// # </weight>
#[weight = SimpleDispatchInfo::FixedNormal(20_000)]
fn unfound(origin) {
let founder = ensure_signed(origin)?;
ensure!(Founder::<T, I>::get() == Some(founder.clone()), Error::<T, I>::NotFounder);
ensure!(Head::<T, I>::get() == Some(founder.clone()), Error::<T, I>::NotHead);

Members::<T, I>::kill();
Head::<T, I>::kill();
Founder::<T, I>::kill();
Rules::<T, I>::kill();
Candidates::<T, I>::kill();
SuspendedCandidates::<T, I>::remove_all();
Self::deposit_event(RawEvent::Unfounded(founder));
}

/// 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
Expand Down Expand Up @@ -1047,6 +1083,10 @@ decl_error! {
NotCandidate,
/// Too many members in the society.
MaxMembers,
/// The caller is not the founder.
NotFounder,
/// The caller is not the head.
NotHead,
}
}

Expand Down Expand Up @@ -1087,6 +1127,8 @@ decl_event! {
DefenderVote(AccountId, bool),
/// A new max member count has been set
NewMaxMembers(u32),
/// Society is unfounded.
Unfounded(AccountId),
}
}

Expand Down Expand Up @@ -1224,16 +1266,16 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
ensure!(Self::head() != Some(m.clone()), Error::<T, I>::Head);
ensure!(Self::founder() != Some(m.clone()), Error::<T, I>::Founder);

<Members<T, I>>::mutate(|members|
match members.binary_search(&m) {
Err(_) => Err(Error::<T, I>::NotMember)?,
Ok(i) => {
members.remove(i);
T::MembershipChanged::change_members_sorted(&[], &[m.clone()], members);
Ok(())
}
let mut members = <Members<T, I>>::get();
match members.binary_search(&m) {
Err(_) => Err(Error::<T, I>::NotMember)?,
Ok(i) => {
members.remove(i);
T::MembershipChanged::change_members_sorted(&[], &[m.clone()], &members[..]);
<Members<T, I>>::put(members);
Ok(())
}
)
}
}

/// End the current period and begin a new one.
Expand Down
35 changes: 32 additions & 3 deletions frame/society/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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
Expand All @@ -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::<Test, _>::AlreadyFounded);
assert_noop!(
Society::found(Origin::signed(1), 20, 100, vec![]),
Error::<Test, _>::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::<Test, _>::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::<Test, _>::NotHead);
});
}

Expand Down

0 comments on commit f06aa9a

Please sign in to comment.