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

Commit

Permalink
node/approval-voting: extract and test count_no_shows method
Browse files Browse the repository at this point in the history
This commit extracts no_show computation into a pure function so that it can be
extensively unit tested.
  • Loading branch information
Lldenaurois committed Jun 16, 2021
1 parent 685dc4e commit 0cf9c7a
Showing 1 changed file with 180 additions and 21 deletions.
201 changes: 180 additions & 21 deletions node/core/approval-voting/src/approval_checking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! Utilities for checking whether a candidate has been approved under a given block.

use polkadot_node_primitives::approval::DelayTranche;
use polkadot_primitives::v1::ValidatorIndex;
use bitvec::slice::BitSlice;
use bitvec::order::Lsb0 as BitOrderLsb0;

Expand Down Expand Up @@ -263,6 +264,38 @@ impl State {
}
}


/// Computes the number of no_show validators in a set of assignments given the relevant approvals
/// and tick parameters. This method also returns the next tick at which a no_show will occur amongs
/// the set of validators that have not submitted an approval.
fn count_no_shows(
assignments: &[(ValidatorIndex, Tick)],
approvals: &BitSlice<BitOrderLsb0, u8>,
clock_drift: Tick,
no_show_duration: Tick,
drifted_tick_now: Tick,
) -> (usize, Option<u64>) {
let mut next_no_show = None;
let no_shows = assignments.iter()
.map(|(v_index, tick)| (v_index, tick.saturating_sub(clock_drift) + no_show_duration))
.filter(|&(v_index, no_show_at)| {
let has_approved = approvals.get(v_index.0 as usize).map(|b| *b).unwrap_or(false);

let is_no_show = !has_approved && no_show_at <= drifted_tick_now;

if !is_no_show && !has_approved {
next_no_show = super::min_prefer_some(
next_no_show,
Some(no_show_at + clock_drift),
);
}

is_no_show
}).count();

(no_shows, next_no_show)
}

/// Determine the amount of tranches of assignments needed to determine approval of a candidate.
pub fn tranches_to_approve(
approval_entry: &ApprovalEntry,
Expand Down Expand Up @@ -336,26 +369,13 @@ pub fn tranches_to_approve(
//
// While we count the no-shows, we also determine the next possible no-show we might
// see within this tranche.
let mut next_no_show = None;
let no_shows = {
let next_no_show = &mut next_no_show;
assignments.iter()
.map(|(v_index, tick)| (v_index, tick.saturating_sub(clock_drift) + no_show_duration))
.filter(|&(v_index, no_show_at)| {
let has_approved = approvals.get(v_index.0 as usize).map(|b| *b).unwrap_or(false);

let is_no_show = !has_approved && no_show_at <= drifted_tick_now;

if !is_no_show && !has_approved {
*next_no_show = super::min_prefer_some(
*next_no_show,
Some(no_show_at + clock_drift),
);
}

is_no_show
}).count()
};
let (no_shows, next_no_show) = count_no_shows(
assignments,
approvals,
clock_drift,
no_show_duration,
drifted_tick_now,
);

let s = s.advance(n_assignments, no_shows, next_no_show);
let output = s.output(tranche, needed_approvals, n_validators, no_show_duration);
Expand Down Expand Up @@ -384,7 +404,7 @@ pub fn tranches_to_approve(
mod tests {
use super::*;

use polkadot_primitives::v1::{GroupIndex, ValidatorIndex};
use polkadot_primitives::v1::GroupIndex;
use bitvec::bitvec;
use bitvec::order::Lsb0 as BitOrderLsb0;

Expand Down Expand Up @@ -938,6 +958,145 @@ mod tests {
},
);
}

#[test]
fn count_no_show_test() {
use crate::time::Tick;

let n_validators = 4;

#[derive(Debug)]
struct NoShowTest {
assignments: Vec<(ValidatorIndex, Tick)>,
approvals: Vec<usize>,
clock_drift: Tick,
no_show_duration: Tick,
drifted_tick_now: Tick,
exp_no_shows: usize,
exp_next_no_show: Option<u64>,
}

let tests = vec![
// Empty base case, zero no_shows and no next_no_show.
NoShowTest {
assignments: vec![],
approvals: vec![],
clock_drift: 0,
no_show_duration: 0,
drifted_tick_now: 0,
exp_no_shows: 0,
exp_next_no_show: None,
},
// Single no_show validator.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 20)],
approvals: vec![],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 1,
exp_next_no_show: None,
},
// Single validator as next_no_show.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 21)],
approvals: vec![],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 0,
exp_next_no_show: Some(31),
},
// Single validator with last-minute approval.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 20)],
approvals: vec![1],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 0,
exp_next_no_show: None,
},
// Single validator with late approval.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 21)],
approvals: vec![1],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 0,
exp_next_no_show: None,
},
// Two almost late validators, next_no_show ordered last.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 22), (ValidatorIndex(2), 21)],
approvals: vec![],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 0,
exp_next_no_show: Some(31),
},
// Two almost late validators, next_no_show ordered first.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 21), (ValidatorIndex(2), 22)],
approvals: vec![],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 0,
exp_next_no_show: Some(31),
},
// One almost-late validator, one no_show validator, and one
// approving validator.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 21), (ValidatorIndex(2), 20), (ValidatorIndex(3), 20)],
approvals: vec![3],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 1,
exp_next_no_show: Some(31),
},
// Three no_show validators.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 20), (ValidatorIndex(2), 20), (ValidatorIndex(3), 20)],
approvals: vec![],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 3,
exp_next_no_show: None,
},
// Three approving validators.
NoShowTest {
assignments: vec![(ValidatorIndex(1), 20), (ValidatorIndex(2), 20), (ValidatorIndex(3), 20)],
approvals: vec![1, 2, 3],
clock_drift: 10,
no_show_duration: 10,
drifted_tick_now: 20,
exp_no_shows: 0,
exp_next_no_show: None,
},
];

for test in tests {
let mut approvals = bitvec![BitOrderLsb0, u8; 0; n_validators];
for &v_index in &test.approvals {
approvals.set(v_index, true);
}

let (no_shows, next_no_show) = count_no_shows(
&test.assignments,
&approvals,
test.clock_drift,
test.no_show_duration,
test.drifted_tick_now,
);
assert_eq!(no_shows, test.exp_no_shows, "for test: {:?}", test);
assert_eq!(next_no_show, test.exp_next_no_show, "for test {:?}", test);
}
}
}

#[test]
Expand Down

0 comments on commit 0cf9c7a

Please sign in to comment.