diff --git a/substrate/demo/runtime/src/runtime/council.rs b/substrate/demo/runtime/src/runtime/council.rs index 366a41794b69a70dcbf508332cd1670eb29c9c67..7f82a6972a50f219a4f1a5693031cc10e92d952c 100644 --- a/substrate/demo/runtime/src/runtime/council.rs +++ b/substrate/demo/runtime/src/runtime/council.rs @@ -82,17 +82,17 @@ 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 APPROVALS_OF: &[u8] = b"cou:apr:"; // Vec<bool> 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"; -const CANDIDATE_COUNT: &[u8] = b"cou:cnc"; +const VOTERS: &[u8] = b"cou:vrs"; // Vec<AccountId> +const CANDIDATES: &[u8] = b"cou:can"; // Vec<AccountId>, has holes +const CANDIDATE_COUNT: &[u8] = b"cou:cnc"; // u32 // temporary state (only relevant during finalisation/presentation) const NEXT_FINALISE: &[u8] = b"cou:nxt"; -const SNAPSHOTED_STAKE_OF: &[u8] = b"cou:sss:"; // AccountId -> Balance -const WINNERS: &[u8] = b"cou:win"; // Vec<(Balance, AccountId)> ORDERED low -> high +const SNAPSHOTED_STAKES: &[u8] = b"cou:sss"; // Vec<Balance> +const LEADERBOARD: &[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 { @@ -200,7 +200,12 @@ pub fn candidate_reg_index(who: &AccountId) -> Option<VoteIndex> { /// The last cleared vote index that this voter was last active at. pub fn voter_last_active(voter: &AccountId) -> Option<VoteIndex> { - storage::get(LAST_ACTIVE_OF) + storage::get(&voter.to_keyed_vec(LAST_ACTIVE_OF)) +} + +/// The last cleared vote index that this voter was last active at. +pub fn approvals_of(voter: &AccountId) -> Vec<bool> { + storage::get_or_default(&voter.to_keyed_vec(APPROVALS_OF)) } pub mod public { @@ -220,6 +225,11 @@ pub mod public { // break. // assert!(staking::unlock_block(signed) == staking::LockStatus::Liquid); staking::internal::set_balance(signed, b - voting_bond()); + storage::put(VOTERS, &{ + let mut v: Vec<AccountId> = storage::get_or_default(VOTERS); + v.push(signed.clone()); + v + }); } storage::put(&signed.to_keyed_vec(APPROVALS_OF), votes); storage::put(&signed.to_keyed_vec(LAST_ACTIVE_OF), &index); @@ -236,9 +246,16 @@ pub mod public { } /// Remove a voter. All votes are cancelled and the voter deposit is returned. - pub fn retract_voter(signed: &AccountId) { + pub fn retract_voter(signed: &AccountId, index: u32) { assert!(!presentation_active()); assert!(storage::exists(&signed.to_keyed_vec(LAST_ACTIVE_OF))); + storage::put(VOTERS, &{ + let mut v: Vec<AccountId> = storage::get_or_default(VOTERS); + let index = index as usize; + assert!(index < v.len() && v[index] == *signed); + v.swap_remove(index); + v + }); storage::kill(&signed.to_keyed_vec(APPROVALS_OF)); storage::kill(&signed.to_keyed_vec(LAST_ACTIVE_OF)); staking::internal::set_balance(signed, staking::balance(signed) + voting_bond()); @@ -271,10 +288,38 @@ 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() /// `signed` should have at least - pub fn present(signed: &AccountId, who: &AccountId, index: VoteIndex) { + pub fn present(signed: &AccountId, candidate: &AccountId, candidate_index: u32, total: Balance, index: VoteIndex) { assert_eq!(index, vote_index()); assert!(presentation_active()); - unimplemented!(); + let b = staking::balance(signed); + let stakes: Vec<Balance> = storage::get_or_default(SNAPSHOTED_STAKES); + let voters: Vec<AccountId> = storage::get_or_default(VOTERS); + let bad_presentation_punishment = present_slash_per_voter() * voters.len() as Balance; + assert!(b >= bad_presentation_punishment); + + let mut leaderboard: Vec<(Balance, AccountId)> = storage::get_or_default(LEADERBOARD); + assert!(total > leaderboard[0].0); + + let registered_since = storage::get(&candidate.to_keyed_vec(REGISTER_INDEX_OF)).expect("presented candidate must be current"); + let candidate_index = candidate_index as usize; + let actual_total = voters.iter() + .zip(stakes.iter()) + .filter_map(|(voter, stake)| + match voter_last_active(voter) { + Some(b) if b >= registered_since => + approvals_of(voter).get(candidate_index) + .and_then(|approved| if *approved { Some(stake) } else { None }), + _ => None, + }) + .sum(); + if total == actual_total { + // insert into leaderboard + leaderboard[0] = (total, candidate.clone()); + leaderboard.sort_by_key(|&(t, _)| t); + storage::put(LEADERBOARD, &leaderboard); + } else { + staking::internal::set_balance(signed, b - bad_presentation_punishment); + } } } @@ -341,6 +386,13 @@ fn start_tally() { 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)); + + let voters: Vec<AccountId> = storage::get_or_default(VOTERS); + let votes: Vec<Balance> = voters.iter().map(staking::balance).collect(); + storage::put(SNAPSHOTED_STAKES, &votes); + + // initialise leaderboard. + storage::put(LEADERBOARD, &vec![(0 as Balance, AccountId::default()); empty_seats]); } } @@ -350,6 +402,7 @@ fn start_tally() { /// Clears all presented candidates, returning the bond of the elected ones. fn finalise_tally() { unimplemented!(); + storage::kill(SNAPSHOTED_STAKES); } #[cfg(test)] diff --git a/substrate/substrate/codec/src/slicable.rs b/substrate/substrate/codec/src/slicable.rs index c133b4636d06c95f219269da1e16f0dd77e72145..74b165ec4ccee6761fb1a690634cdfe515e221dc 100644 --- a/substrate/substrate/codec/src/slicable.rs +++ b/substrate/substrate/codec/src/slicable.rs @@ -112,6 +112,33 @@ impl Slicable for Vec<u8> { } } +// TODO: implement for all primitives. +impl Slicable for Vec<u64> { + fn decode<I: Input>(input: &mut I) -> Option<Self> { + u32::decode(input).and_then(move |len| { + let len = len as usize; + let mut vec = Vec::with_capacity(len); + for i in 0..len { + vec.push(u64::decode(input)?); + } + Some(vec) + }) + } + + fn encode(&self) -> Vec<u8> { + let len = self.len(); + assert!(len <= u32::max_value() as usize, "Attempted to serialize vec with too many elements."); + + // TODO: optimise - no need to create a new vec and copy - can just reserve and encode in place + let mut r: Vec<u8> = Vec::new().and(&(len as u32)); + for i in self.iter() { + r.extend(&i.encode()); + } + r + } +} + +// TODO: use a BitVec-like representation. impl Slicable for Vec<bool> { fn decode<I: Input>(input: &mut I) -> Option<Self> { <Vec<u8>>::decode(input).map(|a| a.into_iter().map(|b| b != 0).collect())