use exonum::{
crypto::Hash,
helpers::{Height, Round, ValidatorId},
merkledb::ObjectHash,
};
use std::time::Duration;
use crate::{
sandbox::{sandbox_tests_helper::*, timestamping_sandbox},
state::PROPOSE_REQUEST_TIMEOUT,
};
#[test]
fn handle_round_timeout_ignore_if_height_and_round_are_not_the_same() {
let sandbox = timestamping_sandbox();
let tx = gen_timestamping_tx();
let propose = ProposeBuilder::new(&sandbox)
.with_tx_hashes(&[tx.object_hash()]) .build();
let block = sandbox.create_block(&[tx.clone()]);
let precommit_1 = sandbox.create_precommit(
ValidatorId(1),
Height(1),
Round(1),
propose.object_hash(),
block.object_hash(),
sandbox.time().into(),
sandbox.secret_key(ValidatorId(1)),
);
let precommit_2 = sandbox.create_precommit(
ValidatorId(2),
Height(1),
Round(1),
propose.object_hash(),
block.object_hash(),
sandbox.time().into(),
sandbox.secret_key(ValidatorId(2)),
);
let precommit_3 = sandbox.create_precommit(
ValidatorId(3),
Height(1),
Round(1),
propose.object_hash(),
block.object_hash(),
sandbox.time().into(),
sandbox.secret_key(ValidatorId(3)),
);
sandbox.recv(&precommit_1);
sandbox.add_time(Duration::from_millis(PROPOSE_REQUEST_TIMEOUT));
sandbox.send(
sandbox.public_key(ValidatorId(1)),
&make_request_propose_from_precommit(&sandbox, precommit_1.as_ref()),
);
sandbox.send(
sandbox.public_key(ValidatorId(1)),
&make_request_prevote_from_precommit(&sandbox, precommit_1.as_ref()),
);
sandbox.recv(&precommit_2);
sandbox.add_time(Duration::from_millis(PROPOSE_REQUEST_TIMEOUT));
sandbox.send(
sandbox.public_key(ValidatorId(2)),
&make_request_propose_from_precommit(&sandbox, precommit_2.as_ref()),
);
sandbox.send(
sandbox.public_key(ValidatorId(2)),
&make_request_prevote_from_precommit(&sandbox, precommit_2.as_ref()),
);
sandbox.recv(&propose);
sandbox.recv(&tx);
sandbox.broadcast(&make_prevote_from_propose(&sandbox, &propose));
sandbox.assert_state(Height(1), Round(1));
sandbox.recv(&precommit_3);
sandbox.assert_state(Height(2), Round(1));
sandbox.check_broadcast_status(Height(2), block.object_hash());
sandbox.add_time(Duration::from_millis(0));
sandbox.add_time(Duration::from_millis(
sandbox.current_round_timeout() - 2 * PROPOSE_REQUEST_TIMEOUT,
));
sandbox.assert_state(Height(2), Round(1));
}
#[test]
fn handle_round_timeout_increment_round_add_new_round_timeout() {
let sandbox = timestamping_sandbox();
sandbox.add_time(Duration::from_millis(sandbox.current_round_timeout() - 1));
sandbox.assert_state(Height(1), Round(1));
sandbox.add_time(Duration::from_millis(1));
sandbox.assert_state(Height(1), Round(2));
sandbox.add_time(Duration::from_millis(sandbox.current_round_timeout() - 1));
sandbox.assert_state(Height(1), Round(2));
sandbox.add_time(Duration::from_millis(1));
sandbox.assert_state(Height(1), Round(3));
sandbox.add_time(Duration::from_millis(0));
}
#[test]
fn test_send_propose_and_prevote_when_we_are_leader() {
let sandbox = timestamping_sandbox();
sandbox.add_time(Duration::from_millis(sandbox.current_round_timeout()));
sandbox.add_time(Duration::from_millis(
sandbox.current_round_timeout() + PROPOSE_TIMEOUT,
));
sandbox.assert_state(Height(1), Round(3));
let propose = ProposeBuilder::new(&sandbox).build();
sandbox.broadcast(&propose);
sandbox.broadcast(&make_prevote_from_propose(&sandbox, &propose));
sandbox.add_time(Duration::from_millis(0));
}
#[test]
fn handle_round_timeout_send_prevote_if_locked_to_propose() {
let sandbox = timestamping_sandbox();
let propose = ProposeBuilder::new(&sandbox).build();
let block = BlockBuilder::new(&sandbox).build();
sandbox.recv(&propose);
sandbox.broadcast(&sandbox.create_prevote(
ValidatorId(0),
Height(1),
Round(1),
propose.object_hash(),
NOT_LOCKED,
sandbox.secret_key(ValidatorId(0)),
));
sandbox.recv(&sandbox.create_prevote(
ValidatorId(1),
Height(1),
Round(1),
propose.object_hash(),
NOT_LOCKED,
sandbox.secret_key(ValidatorId(1)),
));
sandbox.assert_lock(NOT_LOCKED, None);
sandbox.recv(&sandbox.create_prevote(
ValidatorId(2),
Height(1),
Round(1),
propose.object_hash(),
NOT_LOCKED,
sandbox.secret_key(ValidatorId(2)),
));
sandbox.assert_lock(Round(1), Some(propose.object_hash()));
sandbox.broadcast(&sandbox.create_precommit(
ValidatorId(0),
Height(1),
Round(1),
propose.object_hash(),
block.object_hash(),
sandbox.time().into(),
sandbox.secret_key(ValidatorId(0)),
));
sandbox.assert_lock(Round(1), Some(propose.object_hash()));
sandbox.add_time(Duration::from_millis(0));
sandbox.add_time(Duration::from_millis(sandbox.current_round_timeout()));
sandbox.broadcast(&sandbox.create_prevote(
ValidatorId(0),
Height(1),
Round(2),
propose.object_hash(),
Round(1),
sandbox.secret_key(ValidatorId(0)),
));
sandbox.add_time(Duration::from_millis(0));
}
#[test]
#[should_panic(expected = "Sent unexpected message Requests(ProposeRequest")]
fn test_handle_round_timeout_queue_prevote_message_from_next_round() {
let sandbox = timestamping_sandbox();
sandbox.recv(&sandbox.create_prevote(
ValidatorId(2),
Height(1),
Round(2),
Hash::zero(),
NOT_LOCKED,
sandbox.secret_key(ValidatorId(2)),
));
sandbox.add_time(Duration::from_millis(sandbox.current_round_timeout()));
sandbox.add_time(Duration::from_millis(PROPOSE_REQUEST_TIMEOUT));
sandbox.add_time(Duration::from_millis(0));
}
#[test]
fn test_round_timeout_increase() {
let sandbox = timestamping_sandbox();
let sandbox_state = SandboxState::new();
sandbox.add_time(Duration::from_millis(sandbox.first_round_timeout() - 1));
sandbox.assert_state(Height(1), Round(1));
sandbox.add_time(Duration::from_millis(1));
sandbox.assert_state(Height(1), Round(2));
sandbox.add_time(Duration::from_millis(
sandbox.first_round_timeout() + sandbox.round_timeout_increase() - 1,
));
sandbox.assert_state(Height(1), Round(2));
sandbox.add_time(Duration::from_millis(1));
sandbox.assert_state(Height(1), Round(3));
add_round_with_transactions(&sandbox, &sandbox_state, &[]);
sandbox.assert_state(Height(1), Round(4));
sandbox.add_time(Duration::from_millis(
sandbox.first_round_timeout() + 3 * sandbox.round_timeout_increase() - 1,
));
sandbox.assert_state(Height(1), Round(4));
sandbox.add_time(Duration::from_millis(1));
sandbox.assert_state(Height(1), Round(5));
}