diff --git a/substrate/demo/runtime/src/runtime/council.rs b/substrate/demo/runtime/src/runtime/council.rs index e0ffaa8fc4..2b01f21ab5 100644 --- a/substrate/demo/runtime/src/runtime/council.rs +++ b/substrate/demo/runtime/src/runtime/council.rs @@ -68,24 +68,30 @@ use runtime::staking::Balance; type VoteIndex = u32; -const ACTIVE_COUNCIL: &[u8] = b"cou:act"; - -const APPROVALS_OF: &[u8] = b"cou:apr:"; -const VOTERS: &[u8] = b"cou:vrs"; -const CANDIDATES: &[u8] = b"cou:can"; - -const VOTE_COUNT: &[u8] = b"cou:vco"; - +// parameters const CANDIDACY_BOND: &[u8] = b"cou:cbo"; const VOTING_BOND: &[u8] = b"cou:vbo"; +const PRESENT_SLASH_PER_VOTER: &[u8] = b"cou:pss"; const CARRY_COUNT: &[u8] = b"cou:cco"; const PRESENTATION_DURATION: &[u8] = b"cou:pdu"; const TERM_DURATION: &[u8] = b"cou:trm"; const DESIRED_SEATS: &[u8] = b"cou:sts"; +// permanent state (always relevant, changes only at the finalisation of voting) +const ACTIVE_COUNCIL: &[u8] = b"cou:act"; +const VOTE_COUNT: &[u8] = b"cou:vco"; + +// persistent state (always relevant, changes constantly) +const APPROVALS_OF: &[u8] = b"cou:apr:"; +const REGISTER_INDEX_OF: &[u8] = b"cou:reg:"; // Candidate -> VoteIndex +const LAST_ACTIVE_OF: &[u8] = b"cou:lac:"; // Voter -> VoteIndex +const VOTERS: &[u8] = b"cou:vrs"; +const CANDIDATES: &[u8] = b"cou:can"; + +// temporary state (only relevant during finalisation/presentation) const NEXT_FINALISE: &[u8] = b"cou:nxt"; -const STAKE_SNAPSHOT: &[u8] = b"cou:sss"; -const WINNERS: &[u8] = b"cou:win"; +const SNAPSHOTED_STAKE_OF: &[u8] = b"cou:sss:"; // AccountId -> Balance +const WINNERS: &[u8] = b"cou:win"; // Vec<(Balance, AccountId)> ORDERED low -> high /// How much should be locked up in order to submit one's candidacy. pub fn candidacy_bond() -> Balance { @@ -111,6 +117,12 @@ pub fn term_duration() -> BlockNumber { .expect("all core parameters of council module must be in place") } +/// The punishment, per voter, if you provide an invalid presentation. +pub fn present_slash_per_voter() -> Balance { + storage::get(PRESENT_SLASH_PER_VOTER) + .expect("all core parameters of council module must be in place") +} + /// Number of accounts that should be sitting on the council. pub fn desired_seats() -> u32 { storage::get(DESIRED_SEATS) @@ -149,6 +161,8 @@ pub fn next_tally() -> Option { } else { // next tally begins once enough council members expire to bring members below desired. if desired_seats <= coming { + // the entire amount of desired seats is less than those new members - we'll have + // to wait until they expire. Some(next_possible + term_duration()) } else { Some(c[c.len() - (desired_seats - coming) as usize].1) @@ -162,31 +176,36 @@ pub fn next_finalise() -> Option<(BlockNumber, u32, Vec)> { storage::get(NEXT_FINALISE) } -/// The number of seats that will be elected on the next tally. -pub fn empty_seats() -> u32 { - unimplemented!(); -} - /// The total number of votes that have happened or are in progress. pub fn vote_index() -> VoteIndex { storage::get_or_default(VOTE_COUNT) } -/// The queue of candidate indices that will be cleared. -pub fn candidate_clear_queue(vote: VoteIndex) -> Vec { - unimplemented!(); +/// The vote index that the candidate `who` was registered or `None` if they are not currently +/// registered. +pub fn candidate_reg_index(who: &AccountId) -> Option { + storage::get(&who.to_keyed_vec(REGISTER_INDEX_OF)) } /// The last cleared vote index that this voter was last active at. -pub fn voter_last_active(voter: &AccountId) -> VoteIndex { - unimplemented!(); +pub fn voter_last_active(voter: &AccountId) -> Option { + storage::get(LAST_ACTIVE_OF) } pub mod public { use super::*; - /// Remove a voter. For it not to panic, the combination of candidate_clear_queue from the - /// when it was last active until the penultimate vote should result in no approvals. + /// Set candidate approvals. Approval slots stay valid as long as candidates in those slots + /// are registered. + pub fn set_approvals(who: &AccountId, votes: &Vec, index: VoteIndex) { + assert_eq!(index, vote_index()); + storage::put(&who.to_keyed_vec(APPROVALS_OF), votes); + storage::put(&who.to_keyed_vec(LAST_ACTIVE_OF), &index); + } + + /// 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`. pub fn kill_inactive_voter(signed: &AccountId, who: &AccountId) { @@ -207,7 +226,9 @@ pub mod public { /// Claim that `signed` is one of the top carry_count() + current_vote().1 candidates. /// Only works if the block number >= current_vote().0 and < current_vote().0 + presentation_duration() - pub fn present(signed: &AccountId) { + /// `signed` should have at least + pub fn present(signed: &AccountId, who: &AccountId, index: VoteIndex) { + assert_eq!(index, vote_index()); unimplemented!(); } } @@ -255,27 +276,34 @@ pub mod internal { pub fn end_block() { if let Some(number) = next_tally() { if system::block_number() == number { - close_voting(); + start_tally(); } } if let Some((number, _, _)) = next_finalise() { if system::block_number() == number { - finalise_vote(); + finalise_tally(); } } } } /// Close the voting, snapshot the staking and the number of seats that are actually up for grabs. -fn close_voting() { - unimplemented!(); +fn start_tally() { + let active_council = active_council(); + let desired_seats = desired_seats() as usize; + let number = system::block_number(); + let expiring = active_council.iter().take_while(|i| i.1 == number).cloned().collect::>(); + if active_council.len() - expiring.len() < desired_seats { + let empty_seats = desired_seats - (active_council.len() - expiring.len()); + storage::put(NEXT_FINALISE, &(number + presentation_duration(), empty_seats as u32, expiring)); + } } /// Finalise the vote, removing each of the `removals` and inserting `seats` of the most approved /// candidates in their place. If the total council members is less than the desired membership /// a new vote is started. /// Clears all presented candidates, returning the bond of the elected ones. -fn finalise_vote() { +fn finalise_tally() { unimplemented!(); } diff --git a/substrate/substrate/codec/src/slicable.rs b/substrate/substrate/codec/src/slicable.rs index b2aeb08035..c133b4636d 100644 --- a/substrate/substrate/codec/src/slicable.rs +++ b/substrate/substrate/codec/src/slicable.rs @@ -112,6 +112,16 @@ impl Slicable for Vec { } } +impl Slicable for Vec { + fn decode(input: &mut I) -> Option { + >::decode(input).map(|a| a.into_iter().map(|b| b != 0).collect()) + } + + fn encode(&self) -> Vec { + >::encode(&self.iter().map(|&b| if b {1} else {0}).collect()) + } +} + macro_rules! impl_vec_simple_array { ($($size:expr),*) => { $(