use common::remove_random_replicas_from_hashmap;
use rand::thread_rng;
use rstest::rstest;
mod common;
use common::force_timeout;
use common::setup_set;
use common::try_client_request;
use shared_ids::ClientId;
use shared_ids::ReplicaId;
use std::collections::HashMap;
use minbft::timeout::TimeoutType;
use crate::common::DummyPayload;
#[rstest]
fn single_request_to_ten(#[values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] n: u64) {
let mut rng = thread_rng();
for t in 0..n / 2 {
let (mut minbfts, _timeout_handlers) = setup_set(n, t, 2);
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for collected_output_responses in collected_output.responses {
assert_eq!(
collected_output_responses.1,
[(ClientId::from_u64(0), DummyPayload(0, true))]
)
}
}
}
#[rstest]
fn single_request_to_hundred(
#[values(1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 29, 39, 49, 59, 69, 79, 89, 99)] n: u64,
) {
let mut rng = thread_rng();
for t in 0..n / 2 {
let (mut minbfts, _timeout_handlers) = setup_set(n, t, 2);
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for collected_output_responses in collected_output.responses {
assert_eq!(
collected_output_responses.1,
[(ClientId::from_u64(0), DummyPayload(0, true))]
)
}
}
}
#[rstest]
fn single_request_to_ten_only_t_plus_one(#[values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] n: u64) {
let mut rng = thread_rng();
for t in 0..n / 2 {
let (mut minbfts, _timeout_handlers) = setup_set(n, t, 2);
remove_random_replicas_from_hashmap(
&mut minbfts,
t as usize + 1,
Some(ReplicaId::from_u64(0)),
&mut rng,
);
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for collected_output_responses in collected_output.responses {
assert_eq!(
collected_output_responses.1,
[(ClientId::from_u64(0), DummyPayload(0, true))]
);
}
for collected_output_errors in collected_output.errors {
assert!(collected_output_errors.1.is_empty());
}
}
}
#[rstest]
fn single_request_to_hundred_only_t_plus_one(
#[values(1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 29, 39, 49, 59, 69, 79, 89, 99)] n: u64,
) {
let mut rng = thread_rng();
for t in 0..n / 2 {
let (mut minbfts, _timeout_handlers) = setup_set(n, t, 2);
remove_random_replicas_from_hashmap(
&mut minbfts,
t as usize + 1,
Some(ReplicaId::from_u64(0)),
&mut rng,
);
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for collected_output_responses in collected_output.responses {
assert_eq!(
collected_output_responses.1,
[(ClientId::from_u64(0), DummyPayload(0, true))]
)
}
for collected_output_errors in collected_output.errors {
assert!(collected_output_errors.1.is_empty());
}
}
}
#[rstest]
fn single_request_to_ten_only_t(#[values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] n: u64) {
let mut rng = thread_rng();
for t in 0..n / 2 {
let (mut minbfts, _timeout_handlers) = setup_set(n, t, 2);
remove_random_replicas_from_hashmap(
&mut minbfts,
t as usize,
Some(ReplicaId::from_u64(0)),
&mut rng,
);
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for collected_output_responses in collected_output.responses {
assert_eq!(collected_output_responses.1, [])
}
for collected_output_errors in collected_output.errors {
assert!(collected_output_errors.1.is_empty());
}
}
}
#[rstest]
fn single_request_to_hundred_only_t(
#[values(1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 29, 39, 49, 59, 69, 79, 89, 99)] n: u64,
) {
let mut rng = thread_rng();
for t in 0..n / 2 {
let (mut minbfts, _timeout_handlers) = setup_set(n, t, 2);
remove_random_replicas_from_hashmap(
&mut minbfts,
t as usize,
Some(ReplicaId::from_u64(0)),
&mut rng,
);
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for collected_output_responses in collected_output.responses {
assert_eq!(collected_output_responses.1, [])
}
for collected_output_errors in collected_output.errors {
assert!(collected_output_errors.1.is_empty());
}
}
}
#[rstest]
fn view_change_new_view_receives_msgs(
#[values(3, 4, 5, 6, 7, 8, 9, 10)] n: u64,
#[values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] checkpoint_period: u64,
) {
let mut rng = thread_rng();
for t in 1..n / 2 {
let (mut minbfts, mut timeout_handlers) = setup_set(n, t, checkpoint_period);
minbfts.remove(&ReplicaId::from_u64(0));
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for o in collected_output.responses.values() {
assert_eq!(o.len(), 0);
}
let timeouts_to_handle =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(0, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(1, true),
&mut rng,
);
for o in collected_output.responses.values() {
assert_eq!(o.len(), 1);
assert_eq!(
*o.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(1, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
}
}
#[rstest]
fn view_change_two_view_changes(
#[values(6, 7, 8, 9, 10)] n: u64,
#[values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] checkpoint_period: u64,
) {
let mut rng = thread_rng();
for t in 1..n / 2 - 1 {
let (mut minbfts, mut timeout_handlers) = setup_set(n, t, checkpoint_period);
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(0, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(1, true),
&mut rng,
);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(1, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
minbfts.remove(&ReplicaId::from_u64(0));
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(2, true),
&mut rng,
);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 0);
}
let timeouts_to_handle =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
let timeouts_to_handle =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(2, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
minbfts.remove(&ReplicaId::from_u64(1));
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(3, true),
&mut rng,
);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 0);
}
let timeouts_to_handle =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
for (_, responses) in collected_output.responses.iter() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(3, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
}
}
#[rstest]
fn view_change_request_accepted_prev_view(
#[values(4, 5, 6, 7, 8, 9, 10)] n: u64,
#[values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] checkpoint_period: u64,
) {
let mut rng = thread_rng();
for t in 1..n / 2 {
let (mut minbfts, mut timeout_handlers) = setup_set(n, t, checkpoint_period);
for (i, client_request_i) in (0..t).step_by(2).enumerate() {
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(client_request_i, true),
&mut rng,
);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
minbfts.remove(&ReplicaId::from_u64(i.try_into().unwrap()));
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(client_request_i + 1, true),
&mut rng,
);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 0);
}
let timeouts_to_handle =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
let timeouts_to_handle =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(
ClientId::from_u64(0),
DummyPayload(client_request_i + 1, true)
)
);
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
}
for (_, minbft) in minbfts.iter_mut() {
assert!(minbft
.handle_client_message(
ClientId::from_u64(0),
DummyPayload(client_request_i, true)
)
.responses
.is_empty());
}
}
}
}
#[test]
fn send_prepare_twice_to_backup() {
let (mut minbfts, _timeout_handlers) = setup_set(5, 2, 2);
let output = minbfts
.get_mut(&ReplicaId::from_u64(0))
.unwrap()
.handle_client_message(ClientId::from_u64(23), DummyPayload(56, true));
assert_eq!(output.broadcasts.len(), 1);
let broadcasts = Vec::from(output.broadcasts); let mut iter = broadcasts.into_iter();
let prepare = iter.next().unwrap();
let output = minbfts
.get_mut(&ReplicaId::from_u64(1))
.unwrap()
.handle_peer_message(ReplicaId::from_u64(0), prepare.clone());
assert_eq!(output.broadcasts.len(), 1);
let output = minbfts
.get_mut(&ReplicaId::from_u64(1))
.unwrap()
.handle_peer_message(ReplicaId::from_u64(0), prepare);
assert_eq!(output.broadcasts.len(), 0);
}
#[test]
fn two_view_changes_subsequently() {
let mut rng = thread_rng();
let (mut minbfts, mut timeout_handlers) = setup_set(5, 1, 2);
minbfts.remove(&ReplicaId::from_u64(0));
minbfts.remove(&ReplicaId::from_u64(1));
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 0);
}
let timeouts_to_handle = collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
let timeouts_to_handle = collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(0, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
}
#[rstest]
fn multi_view_changes_subsequently(
#[values(5, 6, 7, 8, 9, 11, 21, 31)] n: u64,
#[values(1, 2, 3)] checkpoint_period: u64,
) {
let mut rng = thread_rng();
let mut t = 1;
let mut amount_replicas_removable = n - (2 * t + 1);
while amount_replicas_removable > 1 {
let (mut minbfts, mut timeout_handlers) = setup_set(n, t, checkpoint_period);
let mut amount_replicas_removed = 0;
while amount_replicas_removed < amount_replicas_removable {
minbfts.remove(&ReplicaId::from_u64(amount_replicas_removed));
amount_replicas_removed += 1;
}
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(0, true),
&mut rng,
);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 0);
}
let timeouts_to_handle: HashMap<ReplicaId, Vec<TimeoutType>> =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let mut collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
let mut i = 1;
while i < amount_replicas_removed {
let timeouts_to_handle: HashMap<ReplicaId, Vec<TimeoutType>> =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
i += 1;
}
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(0, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
t += 1;
amount_replicas_removable = n - (2 * t + 1);
}
}
#[rstest]
fn replica_becomes_primary_after_view_change_cycle(
#[values(5, 6, 7, 8, 9, 11, 21, 31)] n: u64,
#[values(1, 2, 3)] checkpoint_period: u64,
) {
let mut rng = thread_rng();
let (mut minbfts, mut timeout_handlers) = setup_set(n, 1, checkpoint_period);
let mut current_req_id = 0;
let mut current_view = 0;
while current_view < n {
let current_primary = minbfts
.remove(&ReplicaId::from_u64(current_view % n))
.unwrap();
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(current_req_id, true),
&mut rng,
);
for o in collected_output.responses.values() {
assert_eq!(o.len(), 0);
}
minbfts.insert(ReplicaId::from_u64(current_view % n), current_primary);
let timeouts_to_handle =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
let timeouts_to_handle =
collected_output.timeouts_to_handle(&mut timeout_handlers, &mut rng);
let collected_output = force_timeout(&mut minbfts, &timeouts_to_handle, &mut rng);
for responses in collected_output.responses.values() {
assert_eq!(responses.len(), 1);
assert_eq!(
*responses.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(current_req_id, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
current_view += 1;
current_req_id += 1;
}
let collected_output = try_client_request(
&mut minbfts,
ClientId::from_u64(0),
DummyPayload(current_req_id, true),
&mut rng,
);
for o in collected_output.responses.values() {
assert_eq!(o.len(), 1);
assert_eq!(
*o.get(0).unwrap(),
(ClientId::from_u64(0), DummyPayload(current_req_id, true))
);
}
for errors in collected_output.errors.values() {
assert!(errors.is_empty());
}
}