use std::{collections::BTreeSet, fmt::Debug};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{
components::consensus::{
highway_core::{
endorsement::SignedEndorsement,
evidence::Evidence,
state::{self, Panorama},
validators::ValidatorIndex,
},
traits::{Context, ValidatorSecret},
},
types::Timestamp,
NodeRng,
};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) enum Dependency<C: Context> {
Unit(C::Hash),
Evidence(ValidatorIndex),
Endorsement(C::Hash),
}
impl<C: Context> Dependency<C> {
pub(crate) fn is_unit(&self) -> bool {
matches!(self, Dependency::Unit(_))
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) enum Vertex<C: Context> {
Unit(SignedWireUnit<C>),
Evidence(Evidence<C>),
Endorsements(Endorsements<C>),
}
impl<C: Context> Vertex<C> {
pub(crate) fn value(&self) -> Option<&C::ConsensusValue> {
match self {
Vertex::Unit(swunit) => swunit.wire_unit().value.as_ref(),
Vertex::Evidence(_) | Vertex::Endorsements(_) => None,
}
}
pub(crate) fn unit_hash(&self) -> Option<C::Hash> {
match self {
Vertex::Unit(swunit) => Some(swunit.hash()),
Vertex::Evidence(_) | Vertex::Endorsements(_) => None,
}
}
pub(crate) fn is_evidence(&self) -> bool {
match self {
Vertex::Unit(_) | Vertex::Endorsements(_) => false,
Vertex::Evidence(_) => true,
}
}
pub(crate) fn timestamp(&self) -> Option<Timestamp> {
match self {
Vertex::Unit(signed_wire_unit) => Some(signed_wire_unit.wire_unit().timestamp),
Vertex::Evidence(_) => None,
Vertex::Endorsements(_) => None,
}
}
pub(crate) fn signed_wire_unit(&self) -> Option<&SignedWireUnit<C>> {
match self {
Vertex::Unit(signed_wire_unit) => Some(signed_wire_unit),
_ => None,
}
}
pub(crate) fn id(&self) -> Dependency<C> {
match self {
Vertex::Unit(signed_wire_unit) => Dependency::Unit(signed_wire_unit.hash()),
Vertex::Evidence(evidence) => Dependency::Evidence(evidence.perpetrator()),
Vertex::Endorsements(endorsement) => Dependency::Endorsement(endorsement.unit),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct SignedWireUnit<C: Context> {
pub(crate) hashed_wire_unit: HashedWireUnit<C>,
pub(crate) signature: C::Signature,
}
impl<C: Context> SignedWireUnit<C> {
pub(crate) fn new(
hashed_wire_unit: HashedWireUnit<C>,
secret_key: &C::ValidatorSecret,
rng: &mut NodeRng,
) -> Self {
let signature = secret_key.sign(&hashed_wire_unit.hash, rng);
SignedWireUnit {
hashed_wire_unit,
signature,
}
}
pub(crate) fn wire_unit(&self) -> &WireUnit<C> {
self.hashed_wire_unit.wire_unit()
}
pub(crate) fn hash(&self) -> C::Hash {
self.hashed_wire_unit.hash()
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) struct HashedWireUnit<C: Context> {
hash: C::Hash,
wire_unit: WireUnit<C>,
}
impl<C: Context> HashedWireUnit<C> {
pub(crate) fn new(wire_unit: WireUnit<C>) -> Self {
let hash = wire_unit.compute_hash();
Self::new_with_hash(wire_unit, hash)
}
pub(crate) fn into_inner(self) -> WireUnit<C> {
self.wire_unit
}
pub(crate) fn wire_unit(&self) -> &WireUnit<C> {
&self.wire_unit
}
pub(crate) fn hash(&self) -> C::Hash {
self.hash
}
pub(crate) fn new_with_hash(wire_unit: WireUnit<C>, hash: C::Hash) -> Self {
HashedWireUnit { hash, wire_unit }
}
}
impl<C: Context> Serialize for HashedWireUnit<C> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.wire_unit.serialize(serializer)
}
}
impl<'de, C: Context> Deserialize<'de> for HashedWireUnit<C> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(HashedWireUnit::new(<_>::deserialize(deserializer)?))
}
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct WireUnit<C: Context> {
pub(crate) panorama: Panorama<C>,
pub(crate) creator: ValidatorIndex,
pub(crate) instance_id: C::InstanceId,
pub(crate) value: Option<C::ConsensusValue>,
pub(crate) seq_number: u64,
pub(crate) timestamp: Timestamp,
pub(crate) round_exp: u8,
pub(crate) endorsed: BTreeSet<C::Hash>,
}
impl<C: Context> Debug for WireUnit<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct Ellipsis;
impl Debug for Ellipsis {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "..")
}
}
f.debug_struct("WireUnit")
.field("value", &self.value.as_ref().map(|_| Ellipsis))
.field("creator.0", &self.creator.0)
.field("instance_id", &self.instance_id)
.field("seq_number", &self.seq_number)
.field("timestamp", &self.timestamp.millis())
.field("panorama", self.panorama.as_ref())
.field("round_exp", &self.round_exp)
.field("endorsed", &self.endorsed)
.field("round_id()", &self.round_id())
.finish()
}
}
impl<C: Context> WireUnit<C> {
pub(crate) fn into_hashed(self) -> HashedWireUnit<C> {
HashedWireUnit::new(self)
}
pub(crate) fn round_id(&self) -> Timestamp {
state::round_id(self.timestamp, self.round_exp)
}
pub(crate) fn previous(&self) -> Option<&C::Hash> {
self.panorama[self.creator].correct()
}
fn compute_hash(&self) -> C::Hash {
<C as Context>::hash(&bincode::serialize(self).expect("serialize WireUnit"))
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct Endorsements<C: Context> {
pub(crate) unit: C::Hash,
pub(crate) endorsers: Vec<(ValidatorIndex, C::Signature)>,
}
impl<C: Context> Endorsements<C> {
pub fn new<I: IntoIterator<Item = SignedEndorsement<C>>>(endorsements: I) -> Self {
let mut iter = endorsements.into_iter().peekable();
let unit = *iter.peek().expect("non-empty iter").unit();
let endorsers = iter
.map(|e| {
assert_eq!(e.unit(), &unit, "endorsements for different units.");
(e.validator_idx(), *e.signature())
})
.collect();
Endorsements { unit, endorsers }
}
pub fn unit(&self) -> &C::Hash {
&self.unit
}
pub fn validator_ids(&self) -> impl Iterator<Item = ValidatorIndex> + '_ {
self.endorsers.iter().map(|(v, _)| *v)
}
}