use std::iter;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::validators::ValidatorIndex;
use crate::components::consensus::{
highway_core::{
endorsement::SignedEndorsement, highway::SignedWireUnit, state::State,
validators::Validators,
},
traits::Context,
};
#[derive(Debug, Error, PartialEq)]
pub(crate) enum EvidenceError {
#[error("The sequence numbers in the equivocating units are different.")]
EquivocationDifferentSeqNumbers,
#[error("The creators in the equivocating units are different.")]
EquivocationDifferentCreators,
#[error("The units were created for a different instance ID.")]
EquivocationInstanceId,
#[error("The two units are equal.")]
EquivocationSameUnit,
#[error("The endorsements don't match the unit hashes.")]
EndorsementWrongHash,
#[error("The creators of the conflicting endorsements are different.")]
EndorsementDifferentCreators,
#[error("The swimlane is not a contiguous sequence of units.")]
EndorsementInvalidSwimlane,
#[error("Includes more units than allowed.")]
EndorsementTooManyUnits,
#[error("The perpetrator is not a validator.")]
UnknownPerpetrator,
#[error("The signature is invalid.")]
Signature,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) enum Evidence<C: Context> {
Equivocation(SignedWireUnit<C>, SignedWireUnit<C>),
Endorsements {
endorsement1: SignedEndorsement<C>,
unit1: SignedWireUnit<C>,
endorsement2: SignedEndorsement<C>,
unit2: SignedWireUnit<C>,
swimlane2: Vec<SignedWireUnit<C>>,
},
}
impl<C: Context> Evidence<C> {
pub(crate) fn perpetrator(&self) -> ValidatorIndex {
match self {
Evidence::Equivocation(unit1, _) => unit1.wire_unit().creator,
Evidence::Endorsements { endorsement1, .. } => endorsement1.validator_idx(),
}
}
pub(crate) fn validate(
&self,
validators: &Validators<C::ValidatorId>,
instance_id: &C::InstanceId,
state: &State<C>,
) -> Result<(), EvidenceError> {
match self {
Evidence::Equivocation(unit1, unit2) => {
Self::validate_equivocation(unit1, unit2, instance_id, validators)
}
Evidence::Endorsements {
endorsement1,
unit1,
endorsement2,
unit2,
swimlane2,
} => {
if swimlane2.len() as u64 > state.params().endorsement_evidence_limit() {
return Err(EvidenceError::EndorsementTooManyUnits);
}
let v_id = validators
.id(endorsement1.validator_idx())
.ok_or(EvidenceError::UnknownPerpetrator)?;
if *endorsement1.unit() != unit1.hash() || *endorsement2.unit() != unit2.hash() {
return Err(EvidenceError::EndorsementWrongHash);
}
if endorsement1.validator_idx() != endorsement2.validator_idx() {
return Err(EvidenceError::EndorsementDifferentCreators);
}
for (unit, pred) in iter::once(unit2).chain(swimlane2).tuple_windows() {
if unit.wire_unit().previous() != Some(&pred.hash()) {
return Err(EvidenceError::EndorsementInvalidSwimlane);
}
}
Self::validate_equivocation(
unit1,
swimlane2.last().unwrap_or(unit2),
instance_id,
validators,
)?;
if !C::verify_signature(&endorsement1.hash(), v_id, &endorsement1.signature())
|| !C::verify_signature(&endorsement2.hash(), v_id, &endorsement2.signature())
{
return Err(EvidenceError::Signature);
}
Ok(())
}
}
}
fn validate_equivocation(
unit1: &SignedWireUnit<C>,
unit2: &SignedWireUnit<C>,
instance_id: &C::InstanceId,
validators: &Validators<C::ValidatorId>,
) -> Result<(), EvidenceError> {
let wunit1 = unit1.wire_unit();
let wunit2 = unit2.wire_unit();
let v_id = validators
.id(wunit1.creator)
.ok_or(EvidenceError::UnknownPerpetrator)?;
if wunit1.creator != wunit2.creator {
return Err(EvidenceError::EquivocationDifferentCreators);
}
if wunit1.seq_number != wunit2.seq_number {
return Err(EvidenceError::EquivocationDifferentSeqNumbers);
}
if wunit1.instance_id != *instance_id || wunit2.instance_id != *instance_id {
return Err(EvidenceError::EquivocationInstanceId);
}
if unit1 == unit2 {
return Err(EvidenceError::EquivocationSameUnit);
}
if !C::verify_signature(&unit1.hash(), v_id, &unit1.signature)
|| !C::verify_signature(&unit2.hash(), v_id, &unit2.signature)
{
return Err(EvidenceError::Signature);
}
Ok(())
}
}