use crate::{
bitfield::{Bit1, Bitfield},
std::ops::AddAssign,
voter_set::{VoterInfo, VoterSet},
weights::VoteWeight,
};
use super::Phase;
#[cfg_attr(any(feature = "std", test), derive(Debug))]
#[cfg_attr(test, derive(Clone))]
pub struct Context<T: Ord + Eq> {
voters: VoterSet<T>,
equivocations: Bitfield,
}
impl<T: Ord + Eq> Context<T> {
pub fn new(voters: VoterSet<T>) -> Self {
Context { voters, equivocations: Bitfield::new() }
}
pub fn voters(&self) -> &VoterSet<T> {
&self.voters
}
pub fn equivocation_weight(&self, p: Phase) -> VoteWeight {
match p {
Phase::Prevote => weight(self.equivocations.iter1s_even(), &self.voters),
Phase::Precommit => weight(self.equivocations.iter1s_odd(), &self.voters),
}
}
pub fn equivocated(&mut self, v: &VoterInfo, p: Phase) {
self.equivocations.set_bit(Vote::new(v, p).bit.position);
}
pub fn weight(&self, n: &VoteNode, p: Phase) -> VoteWeight {
if self.equivocations.is_blank() {
match p {
Phase::Prevote => weight(n.bits.iter1s_even(), &self.voters),
Phase::Precommit => weight(n.bits.iter1s_odd(), &self.voters),
}
} else {
match p {
Phase::Prevote => {
let bits = n.bits.iter1s_merged_even(&self.equivocations);
weight(bits, &self.voters)
},
Phase::Precommit => {
let bits = n.bits.iter1s_merged_odd(&self.equivocations);
weight(bits, &self.voters)
},
}
}
}
}
pub struct Vote {
bit: Bit1,
}
impl Vote {
pub fn new(v: &VoterInfo, p: Phase) -> Vote {
Vote {
bit: Bit1 {
position: match p {
Phase::Prevote => v.position() * 2,
Phase::Precommit => v.position() * 2 + 1,
},
},
}
}
fn voter<'a, Id>(&'a self, vs: &'a VoterSet<Id>) -> Option<(&'a Id, &'a VoterInfo)>
where
Id: Eq + Ord,
{
vs.nth(self.bit.position / 2)
}
}
#[derive(Clone, Debug)]
pub struct VoteNode {
bits: Bitfield,
}
impl Default for VoteNode {
fn default() -> Self {
Self { bits: Bitfield::new() }
}
}
impl AddAssign<&VoteNode> for VoteNode {
fn add_assign(&mut self, rhs: &VoteNode) {
self.bits.merge(&rhs.bits);
}
}
impl AddAssign<&Vote> for VoteNode {
fn add_assign(&mut self, rhs: &Vote) {
self.bits.set_bit(rhs.bit.position);
}
}
fn weight<V, I>(bits: I, voters: &VoterSet<V>) -> VoteWeight
where
V: Eq + Ord,
I: Iterator<Item = Bit1>,
{
let mut total = VoteWeight(0);
for bit in bits {
let vote = Vote { bit };
if let Some((_id, v)) = vote.voter(voters) {
total = total + v.weight()
}
}
total
}
#[cfg(test)]
mod tests {
use super::*;
use crate::std::vec::Vec;
use quickcheck::*;
impl Arbitrary for Phase {
fn arbitrary(g: &mut Gen) -> Self {
*g.choose(&[Phase::Prevote, Phase::Precommit]).unwrap()
}
}
impl Arbitrary for Context<usize> {
fn arbitrary(g: &mut Gen) -> Self {
let mut ctx = Context::new(VoterSet::arbitrary(g));
let n = usize::arbitrary(g) % ctx.voters.len().get();
let equivocators = (0..=n)
.map(|_| ctx.voters.nth_mod(usize::arbitrary(g)).1.clone())
.collect::<Vec<_>>();
for v in equivocators {
ctx.equivocated(&v, Phase::arbitrary(g))
}
ctx
}
}
#[test]
fn vote_voter() {
fn prop(vs: VoterSet<usize>, phase: Phase) {
for (id, v) in vs.iter() {
assert_eq!(Vote::new(v, phase).voter(&vs), Some((id, v)))
}
}
quickcheck(prop as fn(_, _))
}
#[test]
fn weights() {
fn prop(ctx: Context<usize>, phase: Phase, voters: Vec<usize>) {
let e = ctx.equivocation_weight(phase);
let t = ctx.voters.total_weight();
assert!(e <= t);
let mut n = VoteNode::default();
let mut expected = e;
for v in voters {
let (_id, v) = ctx.voters.nth_mod(v);
let vote = Vote::new(v, phase);
if !ctx.equivocations.test_bit(vote.bit.position) &&
!n.bits.test_bit(vote.bit.position)
{
expected = expected + v.weight();
}
n += &vote;
}
let w = ctx.weight(&n, phase);
assert!(w <= t);
assert_eq!(w, expected);
}
quickcheck(prop as fn(_, _, _))
}
}