use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use crate::{
components::consensus::{
highway_core::{
endorsement::SignedEndorsement,
evidence::Evidence,
state::{self, Panorama},
validators::ValidatorIndex,
},
traits::{Context, ValidatorSecret},
},
types::{CryptoRngCore, Timestamp},
};
#[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> {
Vote(C::Hash),
Evidence(ValidatorIndex),
Endorsement(C::Hash),
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) enum Vertex<C: Context> {
Vote(SignedWireVote<C>),
Evidence(Evidence<C>),
Endorsements(Endorsements<C>),
}
impl<C: Context> Vertex<C> {
pub(crate) fn value(&self) -> Option<&C::ConsensusValue> {
match self {
Vertex::Vote(swvote) => swvote.wire_vote.value.as_ref(),
Vertex::Evidence(_) | Vertex::Endorsements(_) => None,
}
}
pub(crate) fn vote_hash(&self) -> Option<C::Hash> {
match self {
Vertex::Vote(swvote) => Some(swvote.hash()),
Vertex::Evidence(_) | Vertex::Endorsements(_) => None,
}
}
pub(crate) fn is_evidence(&self) -> bool {
match self {
Vertex::Vote(_) | Vertex::Endorsements(_) => false,
Vertex::Evidence(_) => true,
}
}
pub(crate) fn timestamp(&self) -> Option<Timestamp> {
match self {
Vertex::Vote(signed_wire_vote) => Some(signed_wire_vote.wire_vote.timestamp),
Vertex::Evidence(_) => None,
Vertex::Endorsements(_) => None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct SignedWireVote<C: Context> {
pub(crate) wire_vote: WireVote<C>,
pub(crate) signature: C::Signature,
}
impl<C: Context> SignedWireVote<C> {
pub(crate) fn new(
wire_vote: WireVote<C>,
secret_key: &C::ValidatorSecret,
rng: &mut dyn CryptoRngCore,
) -> Self {
let signature = secret_key.sign(&wire_vote.hash(), rng);
SignedWireVote {
wire_vote,
signature,
}
}
pub(crate) fn hash(&self) -> C::Hash {
self.wire_vote.hash()
}
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct WireVote<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: Vec<C::Hash>,
}
impl<C: Context> Debug for WireVote<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("WireVote")
.field("hash()", &self.hash())
.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> WireVote<C> {
pub(crate) fn hash(&self) -> C::Hash {
<C as Context>::hash(&bincode::serialize(self).expect("serialize WireVote"))
}
pub(crate) fn round_id(&self) -> Timestamp {
state::round_id(self.timestamp, self.round_exp)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct Endorsements<C: Context> {
pub(crate) vote: 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 vote = *iter.peek().expect("non-empty iter").vote();
let endorsers = iter
.map(|e| {
assert_eq!(e.vote(), &vote, "endorsements for different votes.");
(e.validator_idx(), *e.signature())
})
.collect();
Endorsements { vote, endorsers }
}
pub fn vote(&self) -> &C::Hash {
&self.vote
}
pub fn validator_ids(&self) -> impl Iterator<Item = &ValidatorIndex> {
self.endorsers.iter().map(|(v, _)| v)
}
}