1mod vertex;
4
5pub(crate) use crate::components::consensus::highway_core::state::Params;
6pub use vertex::{
7 Dependency, Endorsements, HashedWireUnit, Ping, SignedWireUnit, Vertex, WireUnit,
8};
9
10use std::path::PathBuf;
11
12use datasize::DataSize;
13use serde::{Deserialize, Serialize};
14use thiserror::Error;
15use tracing::{debug, error, info, trace, warn};
16
17use casper_types::{TimeDiff, Timestamp};
18
19use crate::components::consensus::{
20 consensus_protocol::BlockContext,
21 highway_core::{
22 active_validator::{ActiveValidator, Effect},
23 endorsement::{Endorsement, EndorsementError},
24 evidence::{Evidence, EvidenceError},
25 state::{Fault, Observation, State, UnitError},
26 },
27 traits::Context,
28 utils::{
29 wal::{ReadWal, WalEntry, WriteWal},
30 Validator, ValidatorIndex, Validators, Weight,
31 },
32};
33
34const MAX_SKIPPED_PROPOSAL_LOGS: u64 = 10;
36
37#[derive(Debug, Error, PartialEq)]
39pub(crate) enum VertexError {
40 #[error("The vertex contains an invalid unit: `{0}`")]
41 Unit(#[from] UnitError),
42 #[error("The vertex contains invalid evidence: `{0}`")]
43 Evidence(#[from] EvidenceError),
44 #[error("The endorsements contains invalid entry: `{0}`")]
45 Endorsement(#[from] EndorsementError),
46 #[error("Invalid ping: `{0}`")]
47 Ping(#[from] PingError),
48}
49
50#[derive(Debug, Error, Eq, PartialEq)]
52pub(crate) enum PingError {
53 #[error("The creator is not a validator.")]
54 Creator,
55 #[error("The signature is invalid.")]
56 Signature,
57 #[error("The ping is for a different consensus protocol instance.")]
58 InstanceId,
59}
60
61#[derive(Clone, DataSize, Debug, Eq, PartialEq, Hash)]
67pub(crate) struct PreValidatedVertex<C>(Vertex<C>)
68where
69 C: Context;
70
71impl<C: Context> PreValidatedVertex<C> {
72 pub(crate) fn inner(&self) -> &Vertex<C> {
73 &self.0
74 }
75
76 pub(crate) fn timestamp(&self) -> Option<Timestamp> {
77 self.0.timestamp()
78 }
79
80 #[cfg(test)]
81 pub(crate) fn into_vertex(self) -> Vertex<C> {
82 self.0
83 }
84}
85
86impl<C: Context> From<ValidVertex<C>> for PreValidatedVertex<C> {
87 fn from(vv: ValidVertex<C>) -> PreValidatedVertex<C> {
88 PreValidatedVertex(vv.0)
89 }
90}
91
92impl<C: Context> From<ValidVertex<C>> for Vertex<C> {
93 fn from(vv: ValidVertex<C>) -> Vertex<C> {
94 vv.0
95 }
96}
97
98impl<C: Context> From<PreValidatedVertex<C>> for Vertex<C> {
99 fn from(pvv: PreValidatedVertex<C>) -> Vertex<C> {
100 pvv.0
101 }
102}
103
104#[derive(Clone, DataSize, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
110#[serde(bound(
111 serialize = "C::Hash: Serialize",
112 deserialize = "C::Hash: Deserialize<'de>",
113))]
114pub(crate) struct ValidVertex<C>(pub(crate) Vertex<C>)
115where
116 C: Context;
117
118impl<C: Context> ValidVertex<C> {
119 pub(crate) fn inner(&self) -> &Vertex<C> {
120 &self.0
121 }
122
123 pub(crate) fn is_proposal(&self) -> bool {
124 self.0.value().is_some()
125 }
126 pub(crate) fn endorsements(&self) -> Option<&Endorsements<C>> {
127 match &self.0 {
128 Vertex::Endorsements(endorsements) => Some(endorsements),
129 Vertex::Evidence(_) | Vertex::Unit(_) | Vertex::Ping(_) => None,
130 }
131 }
132}
133
134pub(crate) enum GetDepOutcome<C: Context> {
136 None,
138 Vertex(ValidVertex<C>),
140 Evidence(C::ValidatorId),
143}
144
145#[derive(Serialize, Deserialize, Debug, PartialEq)]
146#[serde(bound(
147 serialize = "C::Hash: Serialize",
148 deserialize = "C::Hash: Deserialize<'de>",
149))]
150struct HighwayWalEntry<C: Context> {
151 vertex: ValidVertex<C>,
152 timestamp: Timestamp,
153}
154
155impl<C: Context> WalEntry for HighwayWalEntry<C> {}
156
157#[derive(Debug, DataSize)]
163pub(crate) struct Highway<C>
164where
165 C: Context,
166{
167 instance_id: C::InstanceId,
169 validators: Validators<C::ValidatorId>,
171 state: State<C>,
173 active_validator: Option<ActiveValidator<C>>,
175 write_wal: Option<WriteWal<HighwayWalEntry<C>>>,
177}
178
179impl<C: Context> Highway<C> {
180 pub(crate) fn new(
189 instance_id: C::InstanceId,
190 validators: Validators<C::ValidatorId>,
191 params: Params,
192 protocol_state_file: Option<PathBuf>,
193 ) -> Highway<C> {
194 info!(%validators, instance=%instance_id, "creating Highway instance");
195 let weights = validators.iter().map(Validator::weight);
196 let banned = validators.iter_banned_idx();
197 let cannot_propose = validators.iter_cannot_propose_idx();
198 let state = State::new(weights, params, banned, cannot_propose);
199 let (write_wal, entries) = if let Some(protocol_state_file) = protocol_state_file.as_ref() {
200 let entries = Self::read_stored_vertices(protocol_state_file);
201 let write_wal = match WriteWal::<HighwayWalEntry<C>>::new(protocol_state_file) {
202 Ok(wal) => Some(wal),
203 Err(err) => {
204 panic!("couldn't open WriteWal: {}", err);
205 }
206 };
207 (write_wal, entries)
208 } else {
209 (None, vec![])
210 };
211 let mut result = Highway {
212 instance_id,
213 validators,
214 state,
215 active_validator: None,
216 write_wal,
217 };
218 result.restore_state(entries);
219 result
220 }
221
222 fn read_stored_vertices(protocol_state_file: &PathBuf) -> Vec<HighwayWalEntry<C>> {
223 let mut read_wal = match ReadWal::<HighwayWalEntry<C>>::new(protocol_state_file) {
224 Ok(wal) => wal,
225 Err(err) => {
226 panic!("couldn't open ReadWal: {}", err);
227 }
228 };
229 let mut entries = vec![];
230 loop {
231 match read_wal.read_next_entry() {
232 Ok(Some(entry)) => {
233 entries.push(entry);
234 }
235 Ok(None) => {
236 break;
237 }
238 Err(err) => {
239 panic!("error while reading ReadWal: {}", err);
240 }
241 }
242 }
243 entries
244 }
245
246 fn restore_state(&mut self, entries: Vec<HighwayWalEntry<C>>) {
247 for entry in entries {
248 self.add_valid_vertex(entry.vertex, entry.timestamp);
251 }
252 }
253
254 pub(crate) fn activate_validator(
259 &mut self,
260 id: C::ValidatorId,
261 secret: C::ValidatorSecret,
262 current_time: Timestamp,
263 _unit_hash_file: Option<PathBuf>,
264 target_ftt: Weight,
265 ) -> Vec<Effect<C>> {
266 if self.active_validator.is_some() {
267 error!(?id, "activate_validator called twice");
268 return vec![];
269 }
270 let idx = match self.validators.get_index(&id) {
271 Some(idx) => idx,
272 None => {
273 error!(?id, "missing own validator ID");
274 return vec![];
275 }
276 };
277 let start_time = current_time.max(self.state.params().start_timestamp());
278 let (av, effects) = ActiveValidator::new(
279 idx,
280 secret,
281 current_time,
282 start_time,
283 &self.state,
284 target_ftt,
285 self.instance_id,
286 );
287 self.active_validator = Some(av);
288 self.add_new_own_vertices(effects, current_time)
289 }
290
291 pub(crate) fn deactivate_validator(&mut self) {
293 self.active_validator = None;
294 }
295
296 pub(crate) fn set_round_len(&mut self, new_round_len: TimeDiff) {
298 if let Some(ref mut av) = self.active_validator {
299 av.set_round_len(new_round_len);
300 }
301 }
302
303 pub(crate) fn pre_validate_vertex(
305 &self,
306 vertex: Vertex<C>,
307 ) -> Result<PreValidatedVertex<C>, (Vertex<C>, VertexError)> {
308 match self.do_pre_validate_vertex(&vertex) {
309 Err(err) => Err((vertex, err)),
310 Ok(()) => Ok(PreValidatedVertex(vertex)),
311 }
312 }
313
314 pub(super) fn missing_dependency(&self, pvv: &PreValidatedVertex<C>) -> Option<Dependency<C>> {
318 match pvv.inner() {
319 Vertex::Evidence(_) | Vertex::Ping(_) => None,
320 Vertex::Endorsements(endorsements) => {
321 let unit = *endorsements.unit();
322 if !self.state.has_unit(&unit) {
323 Some(Dependency::Unit(unit))
324 } else {
325 None
326 }
327 }
328 Vertex::Unit(unit) => unit
329 .wire_unit()
330 .panorama
331 .missing_dependency(&self.state)
332 .or_else(|| {
333 self.state
334 .needs_endorsements(unit)
335 .map(Dependency::Endorsement)
336 }),
337 }
338 }
339
340 pub(crate) fn validate_vertex(
344 &self,
345 pvv: PreValidatedVertex<C>,
346 ) -> Result<ValidVertex<C>, (PreValidatedVertex<C>, VertexError)> {
347 match self.do_validate_vertex(pvv.inner()) {
348 Err(err) => Err((pvv, err)),
349 Ok(()) => Ok(ValidVertex(pvv.0)),
350 }
351 }
352
353 pub(crate) fn add_valid_vertex(
359 &mut self,
360 ValidVertex(vertex): ValidVertex<C>,
361 now: Timestamp,
362 ) -> Vec<Effect<C>> {
363 if !self.has_vertex(&vertex) {
364 if let Some(ref mut wal) = self.write_wal {
365 let entry = HighwayWalEntry {
366 vertex: ValidVertex(vertex.clone()),
367 timestamp: now,
368 };
369 if let Err(err) = wal.record_entry(&entry) {
370 error!("error recording entry: {}", err);
371 }
372 }
373 match vertex {
374 Vertex::Unit(unit) => self.add_valid_unit(unit, now),
375 Vertex::Evidence(evidence) => self.add_evidence(evidence),
376 Vertex::Endorsements(endorsements) => self.add_endorsements(endorsements),
377 Vertex::Ping(ping) => self.add_ping(ping),
378 }
379 } else {
380 vec![]
381 }
382 }
383
384 pub(crate) fn has_vertex(&self, vertex: &Vertex<C>) -> bool {
386 match vertex {
387 Vertex::Unit(unit) => self.state.has_unit(&unit.hash()),
388 Vertex::Evidence(evidence) => self.state.has_evidence(evidence.perpetrator()),
389 Vertex::Endorsements(endorsements) => {
390 let unit = endorsements.unit();
391 self.state
392 .has_all_endorsements(unit, endorsements.validator_ids())
393 }
394 Vertex::Ping(ping) => self.state.has_ping(ping.creator(), ping.timestamp()),
395 }
396 }
397
398 pub(crate) fn has_evidence(&self, vid: &C::ValidatorId) -> bool {
400 self.validators
401 .get_index(vid)
402 .is_some_and(|vidx| self.state.has_evidence(vidx))
403 }
404
405 pub(crate) fn mark_faulty(&mut self, vid: &C::ValidatorId) {
407 if let Some(vidx) = self.validators.get_index(vid) {
408 self.state.mark_faulty(vidx);
409 }
410 }
411
412 pub(crate) fn has_dependency(&self, dependency: &Dependency<C>) -> bool {
414 match dependency {
415 Dependency::Unit(hash) => self.state.has_unit(hash),
416 Dependency::Evidence(idx) => self.state.is_faulty(*idx),
417 Dependency::Endorsement(hash) => self.state.is_endorsed(hash),
418 Dependency::Ping(_, _) => false, }
420 }
421
422 pub(crate) fn get_dependency(&self, dependency: &Dependency<C>) -> GetDepOutcome<C> {
427 match dependency {
428 Dependency::Unit(hash) => match self.state.wire_unit(hash, self.instance_id) {
429 None => GetDepOutcome::None,
430 Some(unit) => GetDepOutcome::Vertex(ValidVertex(Vertex::Unit(unit))),
431 },
432 Dependency::Evidence(idx) => match self.state.maybe_fault(*idx) {
433 None | Some(Fault::Banned) => GetDepOutcome::None,
434 Some(Fault::Direct(ev)) => {
435 GetDepOutcome::Vertex(ValidVertex(Vertex::Evidence(ev.clone())))
436 }
437 Some(Fault::Indirect) => {
438 let vid = self.validators.id(*idx).expect("missing validator").clone();
439 GetDepOutcome::Evidence(vid)
440 }
441 },
442 Dependency::Endorsement(hash) => match self.state.maybe_endorsements(hash) {
443 None => GetDepOutcome::None,
444 Some(e) => GetDepOutcome::Vertex(ValidVertex(Vertex::Endorsements(e))),
445 },
446 Dependency::Ping(_, _) => GetDepOutcome::None, }
448 }
449
450 pub(crate) fn get_dependency_by_index(
452 &self,
453 vid: ValidatorIndex,
454 unit_seq: u64,
455 ) -> GetDepOutcome<C> {
456 let obs = match self.state.panorama().get(vid) {
457 Some(obs) => obs,
458 None => return GetDepOutcome::None,
459 };
460 match obs {
461 Observation::None => GetDepOutcome::None,
462 Observation::Faulty => match self.state.maybe_fault(vid) {
463 None | Some(Fault::Banned) => GetDepOutcome::None,
464 Some(Fault::Direct(ev)) => {
465 GetDepOutcome::Vertex(ValidVertex(Vertex::Evidence(ev.clone())))
466 }
467 Some(Fault::Indirect) => match self.validators.id(vid) {
468 Some(vid) => GetDepOutcome::Evidence(vid.clone()),
469 None => GetDepOutcome::None,
470 },
471 },
472 Observation::Correct(last_seen) => self
473 .state
474 .find_in_swimlane(last_seen, unit_seq)
475 .and_then(|req_hash| self.state.wire_unit(req_hash, self.instance_id))
476 .map(|swunit| GetDepOutcome::Vertex(ValidVertex(Vertex::Unit(swunit))))
477 .unwrap_or_else(|| GetDepOutcome::None),
478 }
479 }
480
481 pub(crate) fn handle_timer(&mut self, timestamp: Timestamp) -> Vec<Effect<C>> {
482 let instance_id = self.instance_id;
483
484 self.map_active_validator(
495 |av, state| av.handle_timer(timestamp, state, instance_id),
496 timestamp,
497 )
498 .unwrap_or_else(|| {
499 debug!(%timestamp, "Ignoring `handle_timer` event: only an observer node.");
500 vec![]
501 })
502 }
503
504 pub(crate) fn propose(
505 &mut self,
506 value: C::ConsensusValue,
507 block_context: BlockContext<C>,
508 ) -> Vec<Effect<C>> {
509 let instance_id = self.instance_id;
510
511 let timestamp = block_context.timestamp();
528 self.map_active_validator(
529 |av, state| av.propose(value, block_context, state, instance_id),
530 timestamp,
531 )
532 .unwrap_or_else(|| {
533 warn!("ignoring `propose` event: validator has been deactivated");
534 vec![]
535 })
536 }
537
538 pub(crate) fn validators(&self) -> &Validators<C::ValidatorId> {
539 &self.validators
540 }
541
542 pub(crate) fn validators_with_evidence(&self) -> impl Iterator<Item = &C::ValidatorId> {
544 self.validators
545 .enumerate_ids()
546 .filter(move |(idx, _)| self.state.has_evidence(*idx))
547 .map(|(_, v_id)| v_id)
548 }
549
550 pub(crate) fn state(&self) -> &State<C> {
551 &self.state
552 }
553
554 pub(crate) fn set_paused(&mut self, paused: bool) {
556 if let Some(av) = &mut self.active_validator {
557 av.set_paused(paused);
558 }
559 }
560
561 pub(crate) fn retain_evidence_only(&mut self) {
563 self.deactivate_validator();
564 self.state.retain_evidence_only();
565 }
566
567 fn on_new_unit(&mut self, uhash: &C::Hash, timestamp: Timestamp) -> Vec<Effect<C>> {
568 let instance_id = self.instance_id;
569 self.map_active_validator(
570 |av, state| av.on_new_unit(uhash, timestamp, state, instance_id),
571 timestamp,
572 )
573 .unwrap_or_default()
574 }
575
576 fn on_new_evidence(&mut self, evidence: Evidence<C>) -> Vec<Effect<C>> {
578 let state = &self.state;
579 let mut effects = self
580 .active_validator
581 .as_mut()
582 .map(|av| av.on_new_evidence(&evidence, state))
583 .unwrap_or_default();
584 for effect in effects.iter() {
587 if let Effect::NewVertex(vv) = effect {
588 if let Some(e) = vv.endorsements() {
589 self.state.add_endorsements(e.clone());
590 }
591 }
592 }
593 effects.extend(vec![Effect::NewVertex(ValidVertex(Vertex::Evidence(
595 evidence,
596 )))]);
597 effects
598 }
599
600 fn map_active_validator<F>(&mut self, f: F, timestamp: Timestamp) -> Option<Vec<Effect<C>>>
605 where
606 F: FnOnce(&mut ActiveValidator<C>, &State<C>) -> Vec<Effect<C>>,
607 {
608 let effects = f(self.active_validator.as_mut()?, &self.state);
609 Some(self.add_new_own_vertices(effects, timestamp))
610 }
611
612 fn add_new_own_vertices(
617 &mut self,
618 effects: Vec<Effect<C>>,
619 timestamp: Timestamp,
620 ) -> Vec<Effect<C>> {
621 let mut result = Vec::with_capacity(effects.len());
622 for effect in &effects {
623 match effect {
624 Effect::NewVertex(vv) => {
625 result.extend(self.add_valid_vertex(vv.clone(), timestamp))
626 }
627 Effect::WeAreFaulty(_) => self.deactivate_validator(),
628 Effect::ScheduleTimer(_) | Effect::RequestNewBlock(_, _) => (),
629 }
630 }
631 result.extend(effects);
632 result
633 }
634
635 fn do_pre_validate_vertex(&self, vertex: &Vertex<C>) -> Result<(), VertexError> {
638 match vertex {
639 Vertex::Unit(unit) => {
640 let creator = unit.wire_unit().creator;
641 let v_id = self.validators.id(creator).ok_or(UnitError::Creator)?;
642 if unit.wire_unit().instance_id != self.instance_id {
643 return Err(UnitError::InstanceId.into());
644 }
645 if !C::verify_signature(&unit.hash(), v_id, &unit.signature) {
646 return Err(UnitError::Signature.into());
647 }
648 Ok(self.state.pre_validate_unit(unit)?)
649 }
650 Vertex::Evidence(evidence) => {
651 Ok(evidence.validate(&self.validators, &self.instance_id, self.state.params())?)
652 }
653 Vertex::Endorsements(endorsements) => {
654 let unit = *endorsements.unit();
655 if endorsements.endorsers.is_empty() {
656 return Err(EndorsementError::Empty.into());
657 }
658 for (creator, signature) in endorsements.endorsers.iter() {
659 let v_id = self
660 .validators
661 .id(*creator)
662 .ok_or(EndorsementError::Creator)?;
663 if self.state.maybe_fault(*creator) == Some(&Fault::Banned) {
664 return Err(EndorsementError::Banned.into());
665 }
666 let endorsement: Endorsement<C> = Endorsement::new(unit, *creator);
667 if !C::verify_signature(&endorsement.hash(), v_id, signature) {
668 return Err(EndorsementError::Signature.into());
669 }
670 }
671 Ok(())
672 }
673 Vertex::Ping(ping) => ping.validate(&self.validators, &self.instance_id),
674 }
675 }
676
677 fn do_validate_vertex(&self, vertex: &Vertex<C>) -> Result<(), VertexError> {
680 match vertex {
681 Vertex::Unit(unit) => Ok(self.state.validate_unit(unit)?),
682 Vertex::Evidence(_) | Vertex::Endorsements(_) | Vertex::Ping(_) => Ok(()),
683 }
684 }
685
686 fn add_evidence(&mut self, evidence: Evidence<C>) -> Vec<Effect<C>> {
689 if self.state.add_evidence(evidence.clone()) {
690 self.on_new_evidence(evidence)
691 } else {
692 vec![]
693 }
694 }
695
696 fn add_valid_unit(&mut self, swunit: SignedWireUnit<C>, now: Timestamp) -> Vec<Effect<C>> {
701 let unit_hash = swunit.hash();
702 let creator = swunit.wire_unit().creator;
703 let was_honest = !self.state.is_faulty(creator);
704 self.state.add_valid_unit(swunit);
705 self.log_if_missing_proposal(&unit_hash);
706 let mut evidence_effects = self
707 .state
708 .maybe_evidence(creator)
709 .cloned()
710 .map(|ev| {
711 if was_honest {
712 self.on_new_evidence(ev)
713 } else {
714 vec![]
715 }
716 })
717 .unwrap_or_default();
718 evidence_effects.extend(self.on_new_unit(&unit_hash, now));
719 evidence_effects
720 }
721
722 fn add_endorsements(&mut self, endorsements: Endorsements<C>) -> Vec<Effect<C>> {
725 let evidence = self
726 .state
727 .find_conflicting_endorsements(&endorsements, &self.instance_id);
728 self.state.add_endorsements(endorsements);
729 let add_and_create_effect = |ev: Evidence<C>| {
730 self.state.add_evidence(ev.clone());
731 Effect::NewVertex(ValidVertex(Vertex::Evidence(ev)))
732 };
733 evidence.into_iter().map(add_and_create_effect).collect()
734 }
735
736 fn add_ping(&mut self, ping: Ping<C>) -> Vec<Effect<C>> {
738 self.state.add_ping(ping.creator(), ping.timestamp());
739 vec![]
740 }
741
742 pub(crate) fn is_doppelganger_vertex(&self, vertex: &Vertex<C>) -> bool {
744 self.active_validator
745 .as_ref()
746 .is_some_and(|av| av.is_doppelganger_vertex(vertex, &self.state))
747 }
748
749 pub(crate) fn is_active(&self) -> bool {
751 self.active_validator.is_some()
752 }
753
754 pub(crate) fn instance_id(&self) -> &C::InstanceId {
756 &self.instance_id
757 }
758
759 pub(crate) fn next_round_length(&self) -> Option<TimeDiff> {
760 self.active_validator
761 .as_ref()
762 .map(|av| av.next_round_length())
763 }
764
765 fn log_if_missing_proposal(&self, unit_hash: &C::Hash) {
767 let state = &self.state;
768 let unit = state.unit(unit_hash);
769 let r_id = unit.round_id();
770 if unit.timestamp != r_id
771 || unit.block != *unit_hash
772 || state.leader(r_id) != unit.creator
773 || state.is_faulty(unit.creator)
774 {
775 return; }
777
778 let parent_timestamp = if let Some(parent_hash) = state.block(unit_hash).parent() {
780 state.unit(parent_hash).timestamp
781 } else {
782 state.params().start_timestamp()
783 };
784 for skipped_r_id in (1..=MAX_SKIPPED_PROPOSAL_LOGS)
785 .filter_map(|i| r_id.checked_sub(state.params().min_round_length().checked_mul(i)?))
786 .take_while(|skipped_r_id| *skipped_r_id > parent_timestamp)
787 {
788 let leader_index = state.leader(skipped_r_id);
789 let leader_id = match self.validators.id(leader_index) {
790 None => {
791 error!(?leader_index, "missing leader validator ID");
792 return;
793 }
794 Some(leader_id) => leader_id,
795 };
796 if state.is_faulty(leader_index) {
797 trace!(
798 ?leader_index, %leader_id, round_id = %skipped_r_id,
799 "missing proposal: faulty leader was skipped",
800 );
801 } else {
802 let reason = state.panorama()[leader_index]
803 .correct()
804 .and_then(|leader_hash| {
805 state
806 .swimlane(leader_hash)
807 .find(|(_, unit)| unit.timestamp <= skipped_r_id)
808 .filter(|(_, unit)| unit.timestamp == skipped_r_id)
809 })
810 .map_or("the leader missed their turn", |_| {
811 "the leader's proposal got orphaned"
812 });
813 info!(
814 ?leader_index, %leader_id, round_id = %skipped_r_id,
815 "missing proposal: {}", reason,
816 );
817 }
818 }
819 }
820}
821
822#[cfg(test)]
823#[allow(clippy::arithmetic_side_effects)]
824pub(crate) mod tests {
825 use std::{collections::BTreeSet, iter::FromIterator};
826
827 use casper_types::Timestamp;
828
829 use crate::components::consensus::{
830 highway_core::{
831 evidence::{Evidence, EvidenceError},
832 highway::{
833 vertex::Ping, Dependency, Highway, SignedWireUnit, UnitError, Vertex, VertexError,
834 WireUnit,
835 },
836 highway_testing::TEST_INSTANCE_ID,
837 state::{tests::*, Panorama, State},
838 },
839 traits::ValidatorSecret,
840 utils::Validators,
841 };
842
843 pub(crate) fn test_validators() -> Validators<u32> {
844 let vid_weights: Vec<(u32, u64)> =
845 vec![(ALICE_SEC, ALICE), (BOB_SEC, BOB), (CAROL_SEC, CAROL)]
846 .into_iter()
847 .map(|(sk, vid)| {
848 assert_eq!(sk.0, vid.0);
849 (sk.0, WEIGHTS[vid.0 as usize].0)
850 })
851 .collect();
852 Validators::from_iter(vid_weights)
853 }
854
855 #[test]
856 fn invalid_signature_error() {
857 let now: Timestamp = 500.into();
858
859 let state: State<TestContext> = State::new_test(WEIGHTS, 0);
860 let mut highway = Highway {
861 instance_id: TEST_INSTANCE_ID,
862 validators: test_validators(),
863 state,
864 active_validator: None,
865 write_wal: None,
866 };
867 let wunit = WireUnit {
868 panorama: Panorama::new(WEIGHTS.len()),
869 creator: CAROL,
870 instance_id: highway.instance_id,
871 value: Some(0),
872 seq_number: 0,
873 timestamp: Timestamp::zero(),
874 round_exp: 4,
875 endorsed: BTreeSet::new(),
876 };
877 let invalid_signature = 1u64;
878 let invalid_signature_unit = SignedWireUnit {
879 hashed_wire_unit: wunit.clone().into_hashed(),
880 signature: invalid_signature,
881 };
882 let invalid_vertex = Vertex::Unit(invalid_signature_unit);
883 let err = VertexError::Unit(UnitError::Signature);
884 let expected = (invalid_vertex.clone(), err);
885 assert_eq!(Err(expected), highway.pre_validate_vertex(invalid_vertex));
886
887 let hwunit = wunit.into_hashed();
888 let valid_signature = CAROL_SEC.sign(&hwunit.hash());
889 let correct_signature_unit = SignedWireUnit {
890 hashed_wire_unit: hwunit,
891 signature: valid_signature,
892 };
893 let valid_vertex = Vertex::Unit(correct_signature_unit);
894 let pvv = highway.pre_validate_vertex(valid_vertex).unwrap();
895 assert_eq!(None, highway.missing_dependency(&pvv));
896 let vv = highway.validate_vertex(pvv).unwrap();
897 assert!(highway.add_valid_vertex(vv, now).is_empty());
898 }
899
900 #[test]
901 fn missing_dependency() -> Result<(), AddUnitError<TestContext>> {
902 let mut state = State::new_test(WEIGHTS, 0);
903 let now: Timestamp = 500.into();
904
905 add_unit!(state, CAROL, 0xC0; N, N, N)?;
906 add_unit!(state, CAROL, 0xC1; N, N, N)?;
907 let a = add_unit!(state, ALICE, 0xA; N, N, N)?;
908 endorse!(state, a; ALICE, BOB, CAROL);
909 let b = add_unit!(state, BOB, 0xB; a, N, F; a)?;
912
913 let end_a = state.maybe_endorsements(&a).expect("unit a is endorsed");
914 let ev_c = state.maybe_evidence(CAROL).unwrap().clone();
915 let wunit_a = state.wire_unit(&a, TEST_INSTANCE_ID).unwrap();
916 let wunit_b = state.wire_unit(&b, TEST_INSTANCE_ID).unwrap();
917
918 let mut highway = Highway {
919 instance_id: TEST_INSTANCE_ID,
920 validators: test_validators(),
921 state: State::new_test(WEIGHTS, 0),
922 active_validator: None,
923 write_wal: None,
924 };
925
926 let vertex_end_a = Vertex::Endorsements(end_a);
927 let pvv_a = highway.pre_validate_vertex(Vertex::Unit(wunit_a)).unwrap();
928 let pvv_end_a = highway.pre_validate_vertex(vertex_end_a).unwrap();
929 let pvv_ev_c = highway.pre_validate_vertex(Vertex::Evidence(ev_c)).unwrap();
930 let pvv_b = highway.pre_validate_vertex(Vertex::Unit(wunit_b)).unwrap();
931
932 assert_eq!(
933 Some(Dependency::Unit(a)),
934 highway.missing_dependency(&pvv_b)
935 );
936 assert_eq!(
937 Some(Dependency::Unit(a)),
938 highway.missing_dependency(&pvv_end_a)
939 );
940 assert_eq!(None, highway.missing_dependency(&pvv_a));
941 let vv_a = highway.validate_vertex(pvv_a).unwrap();
942 highway.add_valid_vertex(vv_a, now);
943
944 assert_eq!(None, highway.missing_dependency(&pvv_end_a));
945 assert_eq!(
946 Some(Dependency::Evidence(CAROL)),
947 highway.missing_dependency(&pvv_b)
948 );
949 assert_eq!(None, highway.missing_dependency(&pvv_ev_c));
950 let vv_ev_c = highway.validate_vertex(pvv_ev_c).unwrap();
951 highway.add_valid_vertex(vv_ev_c, now);
952
953 assert_eq!(
954 Some(Dependency::Endorsement(a)),
955 highway.missing_dependency(&pvv_b)
956 );
957 assert_eq!(None, highway.missing_dependency(&pvv_end_a));
958 let vv_end_a = highway.validate_vertex(pvv_end_a).unwrap();
959 highway.add_valid_vertex(vv_end_a, now);
960
961 assert_eq!(None, highway.missing_dependency(&pvv_b));
962 let vv_b = highway.validate_vertex(pvv_b).unwrap();
963 highway.add_valid_vertex(vv_b, now);
964
965 Ok(())
966 }
967
968 #[test]
969 fn invalid_evidence() {
970 let state: State<TestContext> = State::new_test(WEIGHTS, 0);
971 let highway = Highway {
972 instance_id: TEST_INSTANCE_ID,
973 validators: test_validators(),
974 state,
975 active_validator: None,
976 write_wal: None,
977 };
978
979 let validate = |wunit0: &WireUnit<TestContext>,
980 signer0: &TestSecret,
981 wunit1: &WireUnit<TestContext>,
982 signer1: &TestSecret| {
983 let hwunit0 = wunit0.clone().into_hashed();
984 let swunit0 = SignedWireUnit::new(hwunit0, signer0);
985 let hwunit1 = wunit1.clone().into_hashed();
986 let swunit1 = SignedWireUnit::new(hwunit1, signer1);
987 let evidence = Evidence::Equivocation(swunit0, swunit1);
988 let vertex = Vertex::Evidence(evidence);
989 highway
990 .pre_validate_vertex(vertex.clone())
991 .map_err(|(v, err)| {
992 assert_eq!(v, vertex);
993 err
994 })
995 };
996
997 let mut wunit0 = WireUnit {
999 panorama: Panorama::new(WEIGHTS.len()),
1000 creator: CAROL,
1001 instance_id: highway.instance_id,
1002 value: Some(0),
1003 seq_number: 0,
1004 timestamp: Timestamp::zero(),
1005 round_exp: 4,
1006 endorsed: BTreeSet::new(),
1007 };
1008 let wunit1 = WireUnit {
1009 panorama: Panorama::new(WEIGHTS.len()),
1010 creator: CAROL,
1011 instance_id: highway.instance_id,
1012 value: Some(1),
1013 seq_number: 0,
1014 timestamp: Timestamp::zero(),
1015 round_exp: 4,
1016 endorsed: BTreeSet::new(),
1017 };
1018
1019 assert!(validate(&wunit0, &CAROL_SEC, &wunit1, &CAROL_SEC,).is_ok());
1020
1021 assert_eq!(
1023 Err(VertexError::Evidence(EvidenceError::EquivocationSameUnit)),
1024 validate(&wunit0, &CAROL_SEC, &wunit0, &CAROL_SEC)
1025 );
1026
1027 assert_eq!(
1029 Err(VertexError::Evidence(EvidenceError::Signature)),
1030 validate(&wunit0, &CAROL_SEC, &wunit1, &BOB_SEC)
1031 );
1032 assert_eq!(
1033 Err(VertexError::Evidence(EvidenceError::Signature)),
1034 validate(&wunit0, &BOB_SEC, &wunit1, &CAROL_SEC)
1035 );
1036
1037 wunit0.creator = BOB;
1039 assert_eq!(
1040 Err(VertexError::Evidence(
1041 EvidenceError::EquivocationDifferentCreators
1042 )),
1043 validate(&wunit0, &BOB_SEC, &wunit1, &CAROL_SEC)
1044 );
1045 wunit0.creator = CAROL;
1046
1047 wunit0.seq_number = 1;
1049 assert_eq!(
1050 Err(VertexError::Evidence(
1051 EvidenceError::EquivocationDifferentSeqNumbers
1052 )),
1053 validate(&wunit0, &CAROL_SEC, &wunit1, &CAROL_SEC)
1054 );
1055 wunit0.seq_number = 0;
1056
1057 wunit0.instance_id = TEST_INSTANCE_ID + 1;
1059 assert_eq!(
1060 Err(VertexError::Evidence(EvidenceError::EquivocationInstanceId)),
1061 validate(&wunit0, &CAROL_SEC, &wunit1, &CAROL_SEC)
1062 );
1063 }
1064
1065 #[test]
1066 fn invalid_ping_ndrs1077_regression() {
1067 let now: Timestamp = 500.into();
1068
1069 let state: State<TestContext> = State::new_test(WEIGHTS, 0);
1070 let highway = Highway {
1071 instance_id: TEST_INSTANCE_ID,
1072 validators: test_validators(),
1073 state,
1074 active_validator: None,
1075 write_wal: None,
1076 };
1077
1078 let ping: Vertex<TestContext> =
1081 Vertex::Ping(Ping::new(DAN, now, TEST_INSTANCE_ID, &DAN_SEC));
1082 assert!(
1083 DAN.0 >= WEIGHTS.len() as u32,
1084 "should use validator that is not bonded"
1085 );
1086 assert!(!highway.has_vertex(&ping));
1088 }
1089
1090 #[test]
1091 fn own_initial_ping_is_not_from_doppelganger() {
1092 let now: Timestamp = 500.into();
1093 let later = 501.into();
1094
1095 let state: State<TestContext> = State::new_test(WEIGHTS, 0);
1096 let target_ftt = state.total_weight() / 3;
1097 let mut highway = Highway {
1098 instance_id: TEST_INSTANCE_ID,
1099 validators: test_validators(),
1100 state,
1101 active_validator: None,
1102 write_wal: None,
1103 };
1104
1105 let _effects =
1106 highway.activate_validator(ALICE.0, ALICE_SEC.clone(), now, None, target_ftt);
1107
1108 let ping = Vertex::Ping(Ping::new(ALICE, now, TEST_INSTANCE_ID, &ALICE_SEC));
1109 assert!(!highway.is_doppelganger_vertex(&ping));
1110 let ping = Vertex::Ping(Ping::new(ALICE, later, TEST_INSTANCE_ID, &ALICE_SEC));
1111 assert!(highway.is_doppelganger_vertex(&ping));
1112 }
1113}