use std::{collections::BTreeMap, thread, time::Duration};
use num_rational::Ratio;
use casper_types::{
testing::TestRng, ChainNameDigest, FinalitySignatureV2, TestBlockBuilder, Transaction,
};
use crate::components::consensus::tests::utils::{ALICE_PUBLIC_KEY, ALICE_SECRET_KEY};
use super::*;
#[test]
fn handle_acceptance_promotes_and_disqualifies_peers() {
let mut rng = TestRng::new();
let block = TestBlockBuilder::new().build(&mut rng);
let mut builder = BlockBuilder::new(
*block.hash(),
false,
1,
TimeDiff::from_seconds(1),
LegacyRequiredFinality::Strict,
ProtocolVersion::V1_0_0,
);
let honest_peer = NodeId::random(&mut rng);
let dishonest_peer = NodeId::random(&mut rng);
assert!(builder
.handle_acceptance(None, Ok(Some(Acceptance::NeededIt)), true)
.is_ok());
assert!(builder.peer_list().qualified_peers(&mut rng).is_empty());
assert!(builder.peer_list().dishonest_peers().is_empty());
assert!(builder
.handle_acceptance(None, Ok(Some(Acceptance::HadIt)), true)
.is_ok());
assert!(builder.peer_list().qualified_peers(&mut rng).is_empty());
assert!(builder.peer_list().dishonest_peers().is_empty());
assert!(builder.handle_acceptance(None, Ok(None), true).is_ok());
assert!(builder.peer_list().qualified_peers(&mut rng).is_empty());
assert!(builder.peer_list().dishonest_peers().is_empty());
assert!(builder
.handle_acceptance(Some(honest_peer), Ok(None), true)
.is_ok());
assert!(builder.peer_list().qualified_peers(&mut rng).is_empty());
assert!(builder.peer_list().dishonest_peers().is_empty());
assert!(builder
.handle_acceptance(Some(honest_peer), Ok(Some(Acceptance::HadIt)), true)
.is_ok());
assert!(builder.peer_list().qualified_peers(&mut rng).is_empty());
assert!(builder.peer_list().dishonest_peers().is_empty());
assert!(builder
.handle_acceptance(Some(honest_peer), Ok(Some(Acceptance::NeededIt)), true)
.is_ok());
assert!(builder
.peer_list()
.qualified_peers(&mut rng)
.contains(&honest_peer));
assert!(builder.peer_list().dishonest_peers().is_empty());
assert!(builder
.handle_acceptance(
None,
Err(BlockAcquisitionError::InvalidStateTransition),
true
)
.is_err());
assert!(builder
.peer_list()
.qualified_peers(&mut rng)
.contains(&honest_peer));
assert!(builder.peer_list().dishonest_peers().is_empty());
assert!(builder
.handle_acceptance(
Some(dishonest_peer),
Err(BlockAcquisitionError::InvalidStateTransition),
true
)
.is_err());
assert!(builder
.peer_list()
.qualified_peers(&mut rng)
.contains(&honest_peer));
assert!(builder
.peer_list()
.dishonest_peers()
.contains(&dishonest_peer));
}
#[test]
fn handle_acceptance_unlatches_builder() {
let mut rng = TestRng::new();
let block = TestBlockBuilder::new().build(&mut rng);
let mut builder = BlockBuilder::new(
block.header().block_hash(),
false,
1,
TimeDiff::from_seconds(1),
LegacyRequiredFinality::Strict,
ProtocolVersion::V1_0_0,
);
builder.latch_by(2);
assert!(builder
.handle_acceptance(None, Ok(Some(Acceptance::NeededIt)), true)
.is_ok());
assert_eq!(builder.latch.count(), 0);
builder.latch_by(2);
assert!(builder
.handle_acceptance(None, Ok(Some(Acceptance::NeededIt)), false)
.is_ok());
assert_eq!(builder.latch.count(), 0);
builder.latch_by(2);
assert!(builder
.handle_acceptance(None, Ok(Some(Acceptance::HadIt)), true)
.is_ok());
assert_eq!(builder.latch.count(), 2);
assert!(builder
.handle_acceptance(None, Ok(Some(Acceptance::HadIt)), false)
.is_ok());
assert_eq!(builder.latch.count(), 2);
assert!(builder
.handle_acceptance(
None,
Err(BlockAcquisitionError::InvalidStateTransition),
true
)
.is_err());
assert_eq!(builder.latch.count(), 1);
assert!(builder
.handle_acceptance(
None,
Err(BlockAcquisitionError::InvalidStateTransition),
false
)
.is_err());
assert_eq!(builder.latch.count(), 1);
builder.latch_by(1);
assert!(builder.handle_acceptance(None, Ok(None), false).is_ok());
assert_eq!(builder.latch.count(), 2);
assert!(builder.handle_acceptance(None, Ok(None), true).is_ok());
assert_eq!(builder.latch.count(), 1);
}
#[test]
fn register_era_validator_weights() {
let mut rng = TestRng::new();
let block = TestBlockBuilder::new().build(&mut rng);
let mut builder = BlockBuilder::new(
*block.hash(),
false,
1,
TimeDiff::from_seconds(1),
LegacyRequiredFinality::Strict,
ProtocolVersion::V1_0_0,
);
let latest_timestamp = builder.last_progress;
let mut validator_matrix = ValidatorMatrix::new_with_validator(ALICE_SECRET_KEY.clone());
thread::sleep(Duration::from_millis(5));
builder.register_era_validator_weights(&validator_matrix);
assert!(builder.validator_weights.is_none());
assert_eq!(latest_timestamp, builder.last_progress);
builder.era_id = Some(EraId::from(1000));
thread::sleep(Duration::from_millis(5));
builder.register_era_validator_weights(&validator_matrix);
assert!(builder.validator_weights.is_none());
assert_eq!(latest_timestamp, builder.last_progress);
builder.era_id = Some(block.era_id());
let weights = EraValidatorWeights::new(
block.era_id(),
BTreeMap::from([(ALICE_PUBLIC_KEY.clone(), 100.into())]),
Ratio::new(1, 3),
);
validator_matrix.register_era_validator_weights(weights.clone());
thread::sleep(Duration::from_millis(5));
builder.register_era_validator_weights(&validator_matrix);
assert_eq!(builder.validator_weights.unwrap(), weights);
assert_ne!(latest_timestamp, builder.last_progress);
}
#[test]
fn register_executable_block() {
let mut rng = TestRng::new();
let chain_name_hash = ChainNameDigest::random(&mut rng);
let block = TestBlockBuilder::new().build(&mut rng);
let mut builder = BlockBuilder::new(
*block.hash(),
false,
1,
TimeDiff::from_seconds(1),
LegacyRequiredFinality::Strict,
ProtocolVersion::V1_0_0,
);
let mut latest_timestamp = builder.last_progress;
let weights = EraValidatorWeights::new(
block.era_id(),
BTreeMap::from([(ALICE_PUBLIC_KEY.clone(), 100.into())]),
Ratio::new(1, 3),
);
let mut signature_acquisition = SignatureAcquisition::new(
vec![ALICE_PUBLIC_KEY.clone()],
LegacyRequiredFinality::Strict,
);
let sig = FinalitySignatureV2::create(
*block.hash(),
block.height(),
block.era_id(),
chain_name_hash,
&ALICE_SECRET_KEY,
);
assert_eq!(
signature_acquisition.apply_signature(sig.into(), &weights),
Acceptance::NeededIt
);
let expected_txns = vec![Transaction::random(&mut rng)];
let executable_block =
ExecutableBlock::from_block_and_transactions(block.clone(), expected_txns.clone());
builder.acquisition_state = BlockAcquisitionState::HaveStrictFinalitySignatures(
Box::new(block.clone().into()),
signature_acquisition.clone(),
);
thread::sleep(Duration::from_millis(5));
builder.register_made_executable_block(executable_block.clone());
match &builder.acquisition_state {
BlockAcquisitionState::HaveExecutableBlock(actual_block, executable_block, enqueued) => {
assert_eq!(actual_block.hash(), block.hash());
assert_eq!(expected_txns, *executable_block.transactions);
assert!(!enqueued);
}
_ => panic!("Unexpected outcome in registering finalized block"),
}
assert!(!builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
latest_timestamp = builder.last_progress;
builder.should_fetch_execution_state = true;
builder.acquisition_state = BlockAcquisitionState::HaveStrictFinalitySignatures(
Box::new(block.into()),
signature_acquisition.clone(),
);
thread::sleep(Duration::from_millis(5));
builder.register_made_executable_block(executable_block);
assert!(builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
}
#[test]
fn register_block_execution() {
let mut rng = TestRng::new();
let chain_name_hash = ChainNameDigest::random(&mut rng);
let block = TestBlockBuilder::new().build(&mut rng);
let mut builder = BlockBuilder::new(
*block.hash(),
false,
1,
TimeDiff::from_seconds(1),
LegacyRequiredFinality::Strict,
ProtocolVersion::V1_0_0,
);
let mut latest_timestamp = builder.last_progress;
let weights = EraValidatorWeights::new(
block.era_id(),
BTreeMap::from([(ALICE_PUBLIC_KEY.clone(), 100.into())]),
Ratio::new(1, 3),
);
let mut signature_acquisition = SignatureAcquisition::new(
vec![ALICE_PUBLIC_KEY.clone()],
LegacyRequiredFinality::Strict,
);
let sig = FinalitySignatureV2::create(
*block.hash(),
block.height(),
block.era_id(),
chain_name_hash,
&ALICE_SECRET_KEY,
);
assert_eq!(
signature_acquisition.apply_signature(sig.into(), &weights),
Acceptance::NeededIt
);
let executable_block = Box::new(ExecutableBlock::from_block_and_transactions(
block.clone(),
vec![Transaction::random(&mut rng)],
));
builder.acquisition_state =
BlockAcquisitionState::HaveExecutableBlock(Box::new(block.into()), executable_block, false);
assert_eq!(builder.execution_progress, ExecutionProgress::Idle);
thread::sleep(Duration::from_millis(5));
builder.register_block_execution_enqueued();
assert_eq!(builder.execution_progress, ExecutionProgress::Started);
assert!(matches!(
builder.acquisition_state,
BlockAcquisitionState::HaveExecutableBlock(_, _, true)
));
assert!(!builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
latest_timestamp = builder.last_progress;
thread::sleep(Duration::from_millis(5));
builder.register_block_execution_enqueued();
assert_eq!(builder.execution_progress, ExecutionProgress::Started);
assert!(matches!(
builder.acquisition_state,
BlockAcquisitionState::HaveExecutableBlock(_, _, true)
));
assert!(!builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
latest_timestamp = builder.last_progress;
builder.should_fetch_execution_state = true;
thread::sleep(Duration::from_millis(5));
builder.register_block_execution_enqueued();
assert!(builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
}
#[test]
fn register_block_executed() {
let mut rng = TestRng::new();
let chain_name_hash = ChainNameDigest::random(&mut rng);
let block = TestBlockBuilder::new().build(&mut rng);
let mut builder = BlockBuilder::new(
*block.hash(),
false,
1,
TimeDiff::from_seconds(1),
LegacyRequiredFinality::Strict,
ProtocolVersion::V1_0_0,
);
let mut latest_timestamp = builder.last_progress;
let weights = EraValidatorWeights::new(
block.era_id(),
BTreeMap::from([(ALICE_PUBLIC_KEY.clone(), 100.into())]),
Ratio::new(1, 3),
);
let mut signature_acquisition = SignatureAcquisition::new(
vec![ALICE_PUBLIC_KEY.clone()],
LegacyRequiredFinality::Strict,
);
let sig = FinalitySignatureV2::create(
*block.hash(),
block.height(),
block.era_id(),
chain_name_hash,
&ALICE_SECRET_KEY,
);
assert_eq!(
signature_acquisition.apply_signature(sig.into(), &weights),
Acceptance::NeededIt
);
builder.acquisition_state = BlockAcquisitionState::HaveStrictFinalitySignatures(
Box::new(block.into()),
signature_acquisition,
);
builder.execution_progress = ExecutionProgress::Started;
thread::sleep(Duration::from_millis(5));
builder.register_block_executed();
assert_eq!(builder.execution_progress, ExecutionProgress::Done);
assert!(!builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
latest_timestamp = builder.last_progress;
thread::sleep(Duration::from_millis(5));
builder.register_block_executed();
assert_eq!(builder.execution_progress, ExecutionProgress::Done);
assert!(!builder.is_failed());
assert_eq!(latest_timestamp, builder.last_progress);
builder.should_fetch_execution_state = true;
thread::sleep(Duration::from_millis(5));
builder.register_block_executed();
assert!(builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
}
#[test]
fn register_block_marked_complete() {
let mut rng = TestRng::new();
let chain_name_hash = ChainNameDigest::random(&mut rng);
let block = TestBlockBuilder::new().build(&mut rng);
let mut builder = BlockBuilder::new(
*block.hash(),
false,
1,
TimeDiff::from_seconds(1),
LegacyRequiredFinality::Strict,
ProtocolVersion::V1_0_0,
);
builder.should_fetch_execution_state = true;
let mut latest_timestamp = builder.last_progress;
let weights = EraValidatorWeights::new(
block.era_id(),
BTreeMap::from([(ALICE_PUBLIC_KEY.clone(), 100.into())]),
Ratio::new(1, 3),
);
let mut signature_acquisition = SignatureAcquisition::new(
vec![ALICE_PUBLIC_KEY.clone()],
LegacyRequiredFinality::Strict,
);
let sig = FinalitySignatureV2::create(
*block.hash(),
block.height(),
block.era_id(),
chain_name_hash,
&ALICE_SECRET_KEY,
);
assert_eq!(
signature_acquisition.apply_signature(sig.into(), &weights),
Acceptance::NeededIt
);
builder.acquisition_state = BlockAcquisitionState::HaveStrictFinalitySignatures(
Box::new(block.clone().into()),
signature_acquisition.clone(),
);
thread::sleep(Duration::from_millis(5));
builder.register_marked_complete();
assert!(matches!(
builder.acquisition_state,
BlockAcquisitionState::Complete(..)
));
assert!(!builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
latest_timestamp = builder.last_progress;
builder.should_fetch_execution_state = false;
builder.acquisition_state = BlockAcquisitionState::HaveStrictFinalitySignatures(
Box::new(block.into()),
signature_acquisition.clone(),
);
thread::sleep(Duration::from_millis(5));
builder.register_marked_complete();
assert!(builder.is_failed());
assert_ne!(latest_timestamp, builder.last_progress);
}