casper_node/components/consensus/highway_core/
highway.rs

1//! The implementation of the Highway consensus protocol.
2
3mod 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
34/// If a lot of rounds were skipped between two blocks, log at most this many.
35const MAX_SKIPPED_PROPOSAL_LOGS: u64 = 10;
36
37/// An error due to an invalid vertex.
38#[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/// An error due to an invalid ping.
51#[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/// A vertex that has passed initial validation.
62///
63/// The vertex could not be determined to be invalid based on its contents alone. The remaining
64/// checks will be applied once all of its dependencies have been added to `Highway`. (See
65/// `ValidVertex`.)
66#[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/// A vertex that has been validated: `Highway` has all its dependencies and can add it to its
105/// protocol state.
106///
107/// Note that this must only be added to the `Highway` instance that created it. Can cause a panic
108/// or inconsistent state otherwise.
109#[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
134/// A result indicating whether and how a requested dependency is satisfied.
135pub(crate) enum GetDepOutcome<C: Context> {
136    /// We don't have this dependency.
137    None,
138    /// This vertex satisfies the dependency.
139    Vertex(ValidVertex<C>),
140    /// The dependency must be satisfied by providing evidence against this faulty validator, but
141    /// this `Highway` instance does not have direct evidence.
142    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/// An instance of the Highway protocol, containing its local state.
158///
159/// Both observers and active validators must instantiate this, pass in all incoming vertices from
160/// peers, and use a [FinalityDetector](../finality_detector/struct.FinalityDetector.html) to
161/// determine the outcome of the consensus process.
162#[derive(Debug, DataSize)]
163pub(crate) struct Highway<C>
164where
165    C: Context,
166{
167    /// The protocol instance ID. This needs to be unique, to prevent replay attacks.
168    instance_id: C::InstanceId,
169    /// The validator IDs and weight map.
170    validators: Validators<C::ValidatorId>,
171    /// The abstract protocol state.
172    state: State<C>,
173    /// The state of an active validator, who is participating and creating new vertices.
174    active_validator: Option<ActiveValidator<C>>,
175    /// The path to the protocol state file.
176    write_wal: Option<WriteWal<HighwayWalEntry<C>>>,
177}
178
179impl<C: Context> Highway<C> {
180    /// Creates a new `Highway` instance. All participants must agree on the protocol parameters.
181    ///
182    /// Arguments:
183    ///
184    /// * `instance_id`: A unique identifier for every execution of the protocol (e.g. for every
185    ///   era) to prevent replay attacks.
186    /// * `validators`: The set of validators and their weights.
187    /// * `params`: The Highway protocol parameters.
188    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            // we can safely ignore the effects - they were properly processed when persisting the
249            // vertex
250            self.add_valid_vertex(entry.vertex, entry.timestamp);
251        }
252    }
253
254    /// Turns this instance from a passive observer into an active validator that proposes new
255    /// blocks and creates and signs new vertices.
256    ///
257    /// Panics if `id` is not the ID of a validator with a weight in this Highway instance.
258    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    /// Turns this instance into a passive observer, that does not create any new vertices.
292    pub(crate) fn deactivate_validator(&mut self) {
293        self.active_validator = None;
294    }
295
296    /// Switches the active validator to a new round length.
297    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    /// Does initial validation. Returns an error if the vertex is invalid.
304    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    /// Returns the next missing dependency, or `None` if all dependencies of `pvv` are satisfied.
315    ///
316    /// If this returns `None`, `validate_vertex` can be called.
317    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    /// Does full validation. Returns an error if the vertex is invalid.
341    ///
342    /// All dependencies must be added to the state before this validation step.
343    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    /// Add a validated vertex to the protocol state.
354    ///
355    /// The validation must have been performed by _this_ `Highway` instance.
356    /// More precisely: The instance on which `add_valid_vertex` is called must contain everything
357    /// (and possibly more) that the instance on which `validate_vertex` was called contained.
358    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    /// Returns whether the vertex is already part of this protocol state.
385    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    /// Returns whether the validator is known to be faulty and we have evidence.
399    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    /// Marks the given validator as faulty, if it exists.
406    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    /// Returns whether we have a vertex that satisfies the dependency.
413    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, // We don't store signatures; nothing depends on pings.
419        }
420    }
421
422    /// Returns a vertex that satisfies the dependency, if available.
423    ///
424    /// If we send a vertex to a peer who is missing a dependency, they will ask us for it. In that
425    /// case, `get_dependency` will never return `None`, unless the peer is faulty.
426    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, // We don't store ping signatures.
447        }
448    }
449
450    /// Returns a vertex by a validator with the requested sequence number.
451    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        // Here we just use the timer's timestamp, and assume it's ~ Timestamp::now()
485        //
486        // This is because proposal units, i.e. new blocks, are
487        // supposed to have the exact timestamp that matches the
488        // beginning of the round (which we use as the "round ID").
489        //
490        // But at least any discrepancy here can only come from event
491        // handling delays in our own node, and not from timestamps
492        // set by other nodes.
493
494        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        // We just use the block context's timestamp, which is
512        // hopefully not much older than `Timestamp::now()`
513        //
514        // We do this because essentially what happens is this:
515        //
516        // 1. We realize it's our turn to propose a block in
517        // millisecond 64, so we set a timer.
518        //
519        // 2. The timer for timestamp 64 fires, and we request deploys
520        // for the new block from the block proposer (with 64 in the
521        // block context).
522        //
523        // 3. The block proposer responds and we finally end up here,
524        // and can propose the new block. But we still have to use
525        // timestamp 64.
526
527        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    /// Returns an iterator over all validators against which we have direct evidence.
543    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    /// Sets the pause status: While paused we don't create any new units, just pings.
555    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    /// Drops all state other than evidence.
562    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    /// Takes action on a new evidence.
577    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        // Add newly created endorsements to the local state. These can only be our own ones, so we
585        // don't need to look for conflicts and call State::add_endorsements directly.
586        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        // Gossip `Evidence` only if we just learned about faults by the validator.
594        effects.extend(vec![Effect::NewVertex(ValidVertex(Vertex::Evidence(
595            evidence,
596        )))]);
597        effects
598    }
599
600    /// Applies `f` if this is an active validator, otherwise returns `None`.
601    ///
602    /// Newly created vertices are added to the state. If an equivocation of this validator is
603    /// detected, it gets deactivated.
604    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    /// Handles all `NewVertex` effects and adds the vertices to the protocol state.
613    ///
614    /// This needs to be applied to all effects created by `ActiveValidator`, so that new vertices
615    /// are not interpreted as coming from a doppelgänger.
616    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    /// Performs initial validation and returns an error if `vertex` is invalid. (See
636    /// `PreValidatedVertex` and `validate_vertex`.)
637    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    /// Validates `vertex` and returns an error if it is invalid.
678    /// This requires all dependencies to be present.
679    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    /// Adds evidence to the protocol state.
687    /// Gossip the evidence if it's the first equivocation from the creator.
688    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    /// Adds a valid unit to the protocol state.
697    ///
698    /// Validity must be checked before calling this! Adding an invalid unit will result in a panic
699    /// or an inconsistent state.
700    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    /// Adds endorsements to the state. If there are conflicting endorsements, `NewVertex` effects
723    /// are returned containing evidence to prove them faulty.
724    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    /// Adds a ping to the state.
737    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    /// Checks whether the unit was created by a doppelganger.
743    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    /// Returns whether this instance of protocol is an active validator.
750    pub(crate) fn is_active(&self) -> bool {
751        self.active_validator.is_some()
752    }
753
754    /// Returns the instance ID of this Highway instance.
755    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    /// Logs a message if this is a block and any previous blocks were skipped.
766    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; // Not a block by an honest validator. (Don't let faulty validators spam logs.)
776        }
777
778        // Iterate over all rounds since the parent — or since the start time, if there is none.
779        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        // Bob's unit depends on Alice's unit, an endorsement of Alice's unit, and evidence against
910        // Carol.
911        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        // Two units with different values and the same sequence number. Carol equivocated!
998        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        // It's only an equivocation if the two units are different.
1022        assert_eq!(
1023            Err(VertexError::Evidence(EvidenceError::EquivocationSameUnit)),
1024            validate(&wunit0, &CAROL_SEC, &wunit0, &CAROL_SEC)
1025        );
1026
1027        // Both units have Carol as their creator; Bob's signature would be invalid.
1028        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        // If the first unit was actually Bob's and the second Carol's, nobody equivocated.
1038        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        // If the units have different sequence numbers they might belong to the same fork.
1048        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        // If the units are from a different network or era we don't accept the evidence.
1058        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        // Ping by validator that is not bonded, with an index that is outside of boundaries of the
1079        // state.
1080        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        // Verify that sending a Ping from a non-existing validator does not panic.
1087        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}