test(fuzz): add approve_new_view
& receive_safe_block_with_aggregated_qc
transition (#208)
This commit is contained in:
parent
8fad13b0cc
commit
deeb3eeba0
@ -1,6 +1,6 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use consensus_engine::{Block, LeaderProof, NodeId, Qc, StandardQc, TimeoutQc, View};
|
||||
use consensus_engine::{AggregateQc, Block, LeaderProof, NodeId, Qc, StandardQc, TimeoutQc, View};
|
||||
use proptest::prelude::*;
|
||||
use proptest::strategy::BoxedStrategy;
|
||||
use proptest_state_machine::ReferenceStateMachine;
|
||||
@ -71,6 +71,8 @@ impl ReferenceStateMachine for RefState {
|
||||
state.transition_local_timeout(),
|
||||
state.transition_receive_timeout_qc_for_current_view(),
|
||||
state.transition_receive_timeout_qc_for_old_view(),
|
||||
state.transition_approve_new_view_with_latest_timeout_qc(),
|
||||
state.transition_receive_safe_block_with_aggregated_qc(),
|
||||
]
|
||||
.boxed()
|
||||
}
|
||||
@ -99,6 +101,10 @@ impl ReferenceStateMachine for RefState {
|
||||
Transition::ReceiveTimeoutQcForOldView(timeout_qc) => {
|
||||
timeout_qc.view < state.current_view()
|
||||
}
|
||||
Transition::ApproveNewViewWithLatestTimeoutQc(timeout_qc, _) => {
|
||||
state.latest_timeout_qcs().contains(timeout_qc)
|
||||
&& state.highest_voted_view < RefState::new_view_from(timeout_qc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,6 +144,11 @@ impl ReferenceStateMachine for RefState {
|
||||
Transition::ReceiveTimeoutQcForOldView(_) => {
|
||||
// Nothing to do because we expect the state doesn't change.
|
||||
}
|
||||
Transition::ApproveNewViewWithLatestTimeoutQc(timeout_qc, _) => {
|
||||
let new_view = RefState::new_view_from(timeout_qc);
|
||||
state.chain.entry(new_view).or_insert(ViewEntry::new());
|
||||
state.highest_voted_view = new_view;
|
||||
}
|
||||
}
|
||||
|
||||
state
|
||||
@ -228,23 +239,12 @@ impl RefState {
|
||||
|
||||
// Generate a Transition::ReceiveTimeoutQcForCurrentView
|
||||
fn transition_receive_timeout_qc_for_current_view(&self) -> BoxedStrategy<Transition> {
|
||||
let view = self.current_view();
|
||||
let high_qc = self
|
||||
.chain
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|(_, entry)| entry.high_qc());
|
||||
|
||||
if let Some(high_qc) = high_qc {
|
||||
Just(Transition::ReceiveTimeoutQcForCurrentView(TimeoutQc {
|
||||
view,
|
||||
high_qc,
|
||||
sender: SENDER,
|
||||
}))
|
||||
.boxed()
|
||||
} else {
|
||||
Just(Transition::Nop).boxed()
|
||||
}
|
||||
Just(Transition::ReceiveTimeoutQcForCurrentView(TimeoutQc {
|
||||
view: self.current_view(),
|
||||
high_qc: self.high_qc(),
|
||||
sender: SENDER,
|
||||
}))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
// Generate a Transition::ReceiveTimeoutQcForOldView
|
||||
@ -271,6 +271,44 @@ impl RefState {
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a Transition::ApproveNewViewWithLatestTimeoutQc.
|
||||
fn transition_approve_new_view_with_latest_timeout_qc(&self) -> BoxedStrategy<Transition> {
|
||||
let latest_timeout_qcs: Vec<TimeoutQc> = self
|
||||
.latest_timeout_qcs()
|
||||
.iter()
|
||||
.filter(|timeout_qc| self.highest_voted_view < RefState::new_view_from(timeout_qc))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if latest_timeout_qcs.is_empty() {
|
||||
Just(Transition::Nop).boxed()
|
||||
} else {
|
||||
proptest::sample::select(latest_timeout_qcs)
|
||||
.prop_map(move |timeout_qc| {
|
||||
//TODO: set new_views
|
||||
Transition::ApproveNewViewWithLatestTimeoutQc(timeout_qc, HashSet::new())
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a Transition::ReceiveSafeBlock, but with AggregatedQc.
|
||||
fn transition_receive_safe_block_with_aggregated_qc(&self) -> BoxedStrategy<Transition> {
|
||||
//TODO: more randomness
|
||||
let current_view = self.current_view();
|
||||
|
||||
Just(Transition::ReceiveSafeBlock(Block {
|
||||
id: rand::thread_rng().gen(),
|
||||
view: current_view + 1,
|
||||
parent_qc: Qc::Aggregated(AggregateQc {
|
||||
high_qc: self.high_qc(),
|
||||
view: current_view,
|
||||
}),
|
||||
leader_proof: LEADER_PROOF.clone(),
|
||||
}))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn highest_voted_view(&self) -> View {
|
||||
self.highest_voted_view
|
||||
}
|
||||
@ -289,6 +327,31 @@ impl RefState {
|
||||
timeout_qc.view + 1
|
||||
}
|
||||
|
||||
fn high_qc(&self) -> StandardQc {
|
||||
self.chain
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|(_, entry)| entry.high_qc())
|
||||
.unwrap() // doesn't fail because self.chain always contains at least a genesis block
|
||||
}
|
||||
|
||||
fn latest_timeout_qcs(&self) -> Vec<TimeoutQc> {
|
||||
let latest_timeout_qc_view_entry = self
|
||||
.chain
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|(_, entry)| !entry.timeout_qcs.is_empty());
|
||||
|
||||
match latest_timeout_qc_view_entry {
|
||||
Some((_, entry)) => entry
|
||||
.timeout_qcs
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<TimeoutQc>>(),
|
||||
None => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn consecutive_block(parent: &Block) -> Block {
|
||||
Block {
|
||||
// use rand because we don't want this to be shrinked by proptest
|
||||
@ -304,6 +367,10 @@ impl RefState {
|
||||
}
|
||||
|
||||
impl ViewEntry {
|
||||
fn new() -> ViewEntry {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.blocks.is_empty() && self.timeout_qcs.is_empty()
|
||||
}
|
||||
|
@ -109,6 +109,15 @@ impl StateMachineTest for ConsensusEngineTest {
|
||||
// Check that the engine state didn't change.
|
||||
assert_eq!(engine, prev_engine);
|
||||
|
||||
ConsensusEngineTest { engine }
|
||||
}
|
||||
Transition::ApproveNewViewWithLatestTimeoutQc(timeout_qc, new_views) => {
|
||||
let (engine, _) = state.engine.approve_new_view(timeout_qc.clone(), new_views);
|
||||
assert_eq!(
|
||||
engine.highest_voted_view(),
|
||||
RefState::new_view_from(&timeout_qc)
|
||||
);
|
||||
|
||||
ConsensusEngineTest { engine }
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
use consensus_engine::{Block, TimeoutQc};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use consensus_engine::{Block, NewView, TimeoutQc};
|
||||
|
||||
// State transtitions that will be picked randomly
|
||||
#[derive(Clone, Debug)]
|
||||
@ -11,6 +13,6 @@ pub enum Transition {
|
||||
LocalTimeout,
|
||||
ReceiveTimeoutQcForCurrentView(TimeoutQc),
|
||||
ReceiveTimeoutQcForOldView(TimeoutQc),
|
||||
//TODO: add more invalid transitions that must be rejected by consensus-engine
|
||||
//TODO: add more transitions
|
||||
ApproveNewViewWithLatestTimeoutQc(TimeoutQc, HashSet<NewView>),
|
||||
//TODO: add more corner transitions
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user