use std::{
collections::{BTreeMap, BTreeSet},
sync::Arc,
};
use casper_types::{testing::TestRng, PublicKey, TimeDiff, Timestamp, U512};
use crate::{
components::consensus::{
cl_context::{ClContext, Keypair},
config::Config,
consensus_protocol::{ConsensusProtocol, ProtocolOutcome},
highway_core::{
highway::{SignedWireUnit, Vertex, WireUnit},
highway_testing,
state::{self, tests::ALICE, Observation, Panorama},
State,
},
max_rounds_per_era,
protocols::highway::{
config::Config as HighwayConfig, HighwayMessage, HighwayProtocol, ACTION_ID_VERTEX,
},
tests::utils::{
new_test_chainspec, ALICE_NODE_ID, ALICE_PUBLIC_KEY, ALICE_SECRET_KEY, BOB_PUBLIC_KEY,
},
traits::Context,
utils::{ValidatorIndex, Weight},
SerializedMessage,
},
types::BlockPayload,
};
pub(crate) fn new_test_state<I, T>(weights: I, seed: u64) -> State<ClContext>
where
I: IntoIterator<Item = T>,
T: Into<Weight>,
{
#[allow(clippy::arithmetic_side_effects)] let params = state::Params::new(
seed,
TimeDiff::from_millis(1 << 14),
TimeDiff::from_millis(1 << 19),
TimeDiff::from_millis(1 << 14),
u64::MAX,
0.into(),
Timestamp::MAX,
highway_testing::TEST_ENDORSEMENT_EVIDENCE_LIMIT,
);
let weights = weights.into_iter().map(|w| w.into()).collect::<Vec<_>>();
State::new(weights, params, vec![], vec![])
}
const INSTANCE_ID_DATA: &[u8; 1] = &[123u8; 1];
pub(crate) fn new_test_highway_protocol<I1, I2, T>(
weights: I1,
init_faulty: I2,
) -> Box<dyn ConsensusProtocol<ClContext>>
where
I1: IntoIterator<Item = (PublicKey, T)>,
I2: IntoIterator<Item = PublicKey>,
T: Into<U512>,
{
let weights = weights
.into_iter()
.map(|(pk, w)| (pk, w.into()))
.collect::<Vec<_>>();
let chainspec = new_test_chainspec(weights.clone());
let config = Config {
max_execution_delay: 3,
highway: HighwayConfig {
pending_vertex_timeout: "1min".parse().unwrap(),
log_participation_interval: Some("10sec".parse().unwrap()),
..HighwayConfig::default()
},
..Default::default()
};
let start_timestamp: Timestamp = 0.into();
let (hw_proto, outcomes) = HighwayProtocol::<ClContext>::new_boxed(
ClContext::hash(INSTANCE_ID_DATA),
weights.into_iter().collect(),
&init_faulty.into_iter().collect(),
&None.into_iter().collect(),
&chainspec,
&config,
None,
start_timestamp,
0,
start_timestamp,
None,
);
assert_eq!(3, outcomes.len());
hw_proto
}
pub(crate) const N: Observation<ClContext> = Observation::None;
#[test]
fn send_a_wire_unit_with_too_small_a_round_exp() {
let mut rng = TestRng::new();
let creator: ValidatorIndex = ValidatorIndex(0);
let validators = vec![(ALICE_PUBLIC_KEY.clone(), 100)];
let state: State<ClContext> = new_test_state(validators.iter().map(|(_pk, w)| *w), 0);
let panorama: Panorama<ClContext> = Panorama::from(vec![N]);
let seq_number = panorama.next_seq_num(&state, creator);
let now = Timestamp::zero();
let wunit: WireUnit<ClContext> = WireUnit {
panorama,
creator,
instance_id: ClContext::hash(INSTANCE_ID_DATA),
value: None,
seq_number,
timestamp: now,
round_exp: 0,
endorsed: BTreeSet::new(),
};
let alice_keypair: Keypair = Keypair::from(Arc::clone(&*ALICE_SECRET_KEY));
let highway_message: HighwayMessage<ClContext> = HighwayMessage::NewVertex(Vertex::Unit(
SignedWireUnit::new(wunit.into_hashed(), &alice_keypair),
));
let mut highway_protocol = new_test_highway_protocol(validators, vec![]);
let sender = *ALICE_NODE_ID;
let msg = SerializedMessage::from_message(&highway_message);
let outcomes = highway_protocol.handle_message(&mut rng, sender.to_owned(), msg, now);
assert_eq!(&*outcomes, [ProtocolOutcome::Disconnect(sender)]);
}
#[test]
fn send_a_valid_wire_unit() {
let mut rng = TestRng::new();
let creator: ValidatorIndex = ValidatorIndex(0);
let validators = vec![(ALICE_PUBLIC_KEY.clone(), 100)];
let state: State<ClContext> = new_test_state(validators.iter().map(|(_pk, w)| *w), 0);
let panorama: Panorama<ClContext> = Panorama::from(vec![N]);
let seq_number = panorama.next_seq_num(&state, creator);
let now = Timestamp::zero();
let wunit: WireUnit<ClContext> = WireUnit {
panorama,
creator,
instance_id: ClContext::hash(INSTANCE_ID_DATA),
value: Some(Arc::new(BlockPayload::new(
BTreeMap::new(),
vec![],
Default::default(),
false,
1u8,
))),
seq_number,
timestamp: now,
round_exp: 0,
endorsed: BTreeSet::new(),
};
let alice_keypair: Keypair = Keypair::from(Arc::clone(&*ALICE_SECRET_KEY));
let highway_message: HighwayMessage<ClContext> = HighwayMessage::NewVertex(Vertex::Unit(
SignedWireUnit::new(wunit.into_hashed(), &alice_keypair),
));
let mut highway_protocol = new_test_highway_protocol(validators, vec![]);
let sender = *ALICE_NODE_ID;
let msg = SerializedMessage::from_message(&highway_message);
let mut outcomes = highway_protocol.handle_message(&mut rng, sender, msg, now);
while let Some(outcome) = outcomes.pop() {
match outcome {
ProtocolOutcome::CreatedGossipMessage(_)
| ProtocolOutcome::FinalizedBlock(_)
| ProtocolOutcome::HandledProposedBlock(_) => (),
ProtocolOutcome::QueueAction(ACTION_ID_VERTEX) => {
outcomes.extend(highway_protocol.handle_action(ACTION_ID_VERTEX, now))
}
outcome => panic!("Unexpected outcome: {:?}", outcome),
}
}
}
#[test]
fn detect_doppelganger() {
let mut rng = TestRng::new();
let creator: ValidatorIndex = ALICE;
let validators = vec![
(ALICE_PUBLIC_KEY.clone(), 100),
(BOB_PUBLIC_KEY.clone(), 100),
];
let state: State<ClContext> = new_test_state(validators.iter().map(|(_pk, w)| *w), 0);
let panorama: Panorama<ClContext> = Panorama::from(vec![N, N]);
let seq_number = panorama.next_seq_num(&state, creator);
let instance_id = ClContext::hash(INSTANCE_ID_DATA);
let round_exp = 0;
let now = Timestamp::zero();
let value = Arc::new(BlockPayload::new(
BTreeMap::new(),
vec![],
Default::default(),
false,
1u8,
));
let wunit: WireUnit<ClContext> = WireUnit {
panorama,
creator,
instance_id,
value: Some(value),
seq_number,
timestamp: now,
round_exp,
endorsed: BTreeSet::new(),
};
let alice_keypair: Keypair = Keypair::from(Arc::clone(&*ALICE_SECRET_KEY));
let highway_message: HighwayMessage<ClContext> = HighwayMessage::NewVertex(Vertex::Unit(
SignedWireUnit::new(wunit.into_hashed(), &alice_keypair),
));
let mut highway_protocol = new_test_highway_protocol(validators, vec![]);
let _ = highway_protocol.activate_validator(ALICE_PUBLIC_KEY.clone(), alice_keypair, now, None);
assert!(highway_protocol.is_active());
let sender = *ALICE_NODE_ID;
let msg = SerializedMessage::from_message(&highway_message);
let mut outcomes = highway_protocol.handle_message(&mut rng, sender, msg, now);
while let Some(outcome) = outcomes.pop() {
match outcome {
ProtocolOutcome::DoppelgangerDetected => return,
ProtocolOutcome::QueueAction(ACTION_ID_VERTEX) => {
outcomes.extend(highway_protocol.handle_action(ACTION_ID_VERTEX, now))
}
_ => (),
}
}
panic!("failed to return DoppelgangerDetected effect");
}
#[test]
fn max_rounds_per_era_returns_the_correct_value_for_prod_chainspec_value() {
let max_rounds_per_era = max_rounds_per_era(
20,
TimeDiff::from_seconds(120 * 60),
TimeDiff::from_millis(32768),
);
assert_eq!(219, max_rounds_per_era);
}