fix: prevent approving new views not bigger than the last timeout_qc (#165)
This commit is contained in:
parent
2c23bda702
commit
f631d9b737
|
@ -162,11 +162,12 @@ impl<O: Overlay> Carnot<O> {
|
||||||
let new_view = timeout_qc.view + 1;
|
let new_view = timeout_qc.view + 1;
|
||||||
assert!(
|
assert!(
|
||||||
new_view
|
new_view
|
||||||
>= self
|
> self
|
||||||
.last_view_timeout_qc
|
.last_view_timeout_qc
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|qc| qc.view)
|
.map(|qc| qc.view)
|
||||||
.unwrap_or(0)
|
.unwrap_or(0),
|
||||||
|
"can't vote for a new view not bigger than the last timeout_qc"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
new_views.len(),
|
new_views.len(),
|
||||||
|
@ -604,4 +605,150 @@ mod test {
|
||||||
// trying to approve the block again that was already voted.
|
// trying to approve the block again that was already voted.
|
||||||
let _ = engine.approve_block(block);
|
let _ = engine.approve_block(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Ensure that local_timeout() votes on the current view.
|
||||||
|
fn local_timeout() {
|
||||||
|
let mut engine = init_from_genesis();
|
||||||
|
let block = next_block(&engine.genesis_block());
|
||||||
|
engine = engine.receive_block(block.clone()).unwrap(); // received but not approved yet
|
||||||
|
|
||||||
|
let (engine, send) = engine.local_timeout();
|
||||||
|
assert_eq!(engine.highest_voted_view, 1); // updated from 0 (genesis) to 1 (current_view)
|
||||||
|
assert_eq!(
|
||||||
|
send,
|
||||||
|
Some(Send {
|
||||||
|
to: vec![[0; 32]].into_iter().collect(), // root_committee
|
||||||
|
payload: Payload::Timeout(Timeout {
|
||||||
|
view: 1,
|
||||||
|
sender: [0; 32],
|
||||||
|
high_qc: StandardQc {
|
||||||
|
view: 0, // genesis
|
||||||
|
id: [0; 32],
|
||||||
|
},
|
||||||
|
timeout_qc: None
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Ensure that receive_timeout_qc updates current_view, last_view_timeout_qc and local_high_qc.
|
||||||
|
fn receive_timeout_qc_after_local_timeout() {
|
||||||
|
let mut engine = init_from_genesis();
|
||||||
|
let block = next_block(&engine.genesis_block());
|
||||||
|
engine = engine.receive_block(block.clone()).unwrap(); // received but not approved yet
|
||||||
|
|
||||||
|
let (mut engine, _) = engine.local_timeout();
|
||||||
|
|
||||||
|
assert_eq!(engine.current_view(), 1);
|
||||||
|
let timeout_qc = TimeoutQc {
|
||||||
|
view: 1,
|
||||||
|
high_qc: StandardQc {
|
||||||
|
view: 0, // genesis
|
||||||
|
id: [0; 32],
|
||||||
|
},
|
||||||
|
sender: [0; 32],
|
||||||
|
};
|
||||||
|
engine = engine.receive_timeout_qc(timeout_qc.clone());
|
||||||
|
assert_eq!(engine.local_high_qc, timeout_qc.high_qc);
|
||||||
|
assert_eq!(engine.last_view_timeout_qc, Some(timeout_qc.clone()));
|
||||||
|
assert_eq!(engine.current_view(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Ensure that receive_timeout_qc works even before local_timeout occurs.
|
||||||
|
fn receive_timeout_qc_before_local_timeout() {
|
||||||
|
let mut engine = init_from_genesis();
|
||||||
|
let block = next_block(&engine.genesis_block());
|
||||||
|
engine = engine.receive_block(block.clone()).unwrap(); // received but not approved yet
|
||||||
|
|
||||||
|
// before local_timeout occurs
|
||||||
|
|
||||||
|
assert_eq!(engine.current_view(), 1);
|
||||||
|
let timeout_qc = TimeoutQc {
|
||||||
|
view: 1,
|
||||||
|
high_qc: StandardQc {
|
||||||
|
view: 0, // genesis
|
||||||
|
id: [0; 32],
|
||||||
|
},
|
||||||
|
sender: [0; 32],
|
||||||
|
};
|
||||||
|
engine = engine.receive_timeout_qc(timeout_qc.clone());
|
||||||
|
assert_eq!(engine.local_high_qc, timeout_qc.high_qc);
|
||||||
|
assert_eq!(engine.last_view_timeout_qc, Some(timeout_qc.clone()));
|
||||||
|
assert_eq!(engine.current_view(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Ensure that approve_new_view votes on the new view correctly.
|
||||||
|
fn approve_new_view() {
|
||||||
|
let mut engine = init_from_genesis();
|
||||||
|
let block = next_block(&engine.genesis_block());
|
||||||
|
engine = engine.receive_block(block.clone()).unwrap(); // received but not approved yet
|
||||||
|
|
||||||
|
assert_eq!(engine.current_view(), 1); // still waiting for a QC(view=1)
|
||||||
|
let timeout_qc = TimeoutQc {
|
||||||
|
view: 1,
|
||||||
|
high_qc: StandardQc {
|
||||||
|
view: 0, // genesis
|
||||||
|
id: [0; 32],
|
||||||
|
},
|
||||||
|
sender: [0; 32],
|
||||||
|
};
|
||||||
|
engine = engine.receive_timeout_qc(timeout_qc.clone());
|
||||||
|
assert_eq!(engine.local_high_qc, timeout_qc.high_qc);
|
||||||
|
assert_eq!(engine.last_view_timeout_qc, Some(timeout_qc.clone()));
|
||||||
|
assert_eq!(engine.current_view(), 2);
|
||||||
|
assert_eq!(engine.highest_voted_view, -1); // didn't vote on anything yet
|
||||||
|
|
||||||
|
let (engine, send) = engine.approve_new_view(timeout_qc.clone(), HashSet::new());
|
||||||
|
assert_eq!(engine.high_qc(), timeout_qc.high_qc);
|
||||||
|
assert_eq!(engine.current_view(), 2); // not changed
|
||||||
|
assert_eq!(engine.highest_voted_view, 2);
|
||||||
|
assert_eq!(
|
||||||
|
send.clone().payload,
|
||||||
|
Payload::NewView(NewView {
|
||||||
|
view: 2,
|
||||||
|
sender: [0; 32],
|
||||||
|
timeout_qc: timeout_qc.clone(),
|
||||||
|
high_qc: timeout_qc.clone().high_qc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "can't vote for a new view not bigger than the last timeout_qc")]
|
||||||
|
fn approve_new_view_not_bigger_than_timeout_qc() {
|
||||||
|
let mut engine = init_from_genesis();
|
||||||
|
let block = next_block(&engine.genesis_block());
|
||||||
|
engine = engine.receive_block(block.clone()).unwrap(); // received but not approved yet
|
||||||
|
|
||||||
|
assert_eq!(engine.current_view(), 1);
|
||||||
|
let timeout_qc1 = TimeoutQc {
|
||||||
|
view: 1,
|
||||||
|
high_qc: StandardQc {
|
||||||
|
view: 0, // genesis
|
||||||
|
id: [0; 32],
|
||||||
|
},
|
||||||
|
sender: [0; 32],
|
||||||
|
};
|
||||||
|
engine = engine.receive_timeout_qc(timeout_qc1.clone());
|
||||||
|
assert_eq!(engine.last_view_timeout_qc, Some(timeout_qc1.clone()));
|
||||||
|
|
||||||
|
// receiving a timeout_qc2 before approving new_view(timeout_qc1)
|
||||||
|
let timeout_qc2 = TimeoutQc {
|
||||||
|
view: 2,
|
||||||
|
high_qc: StandardQc {
|
||||||
|
view: 0, // genesis
|
||||||
|
id: [0; 32],
|
||||||
|
},
|
||||||
|
sender: [0; 32],
|
||||||
|
};
|
||||||
|
engine = engine.receive_timeout_qc(timeout_qc2.clone());
|
||||||
|
assert_eq!(engine.last_view_timeout_qc, Some(timeout_qc2.clone()));
|
||||||
|
|
||||||
|
// we expect new_view(timeout_qc2), but...
|
||||||
|
let _ = engine.approve_new_view(timeout_qc1.clone(), HashSet::new());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rebuild(&mut self, _timeout_qc: crate::TimeoutQc) {
|
fn rebuild(&mut self, _timeout_qc: crate::TimeoutQc) {
|
||||||
todo!()
|
// do nothing for now
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_member_of_child_committee(&self, _parent: NodeId, _child: NodeId) -> bool {
|
fn is_member_of_child_committee(&self, _parent: NodeId, _child: NodeId) -> bool {
|
||||||
|
|
Loading…
Reference in New Issue