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

Add rules and unfounding to society. #4671

Merged
merged 6 commits into from
Jan 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
/// member.
///
/// # <weight>
/// - Two storage reads O(1).
/// - Four storage removals O(1).
/// - One event.
///
/// Total Complexity: O(1)
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
/// # </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();
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
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);
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
});
}

Expand Down