presenting a winner.

This commit is contained in:
Gav
2018-03-01 11:14:23 +01:00
parent 5d6f253459
commit 83f0dc9923
2 changed files with 90 additions and 10 deletions
+63 -10
View File
@@ -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)]
+27
View File
@@ -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())