use std::{collections::BTreeMap, fmt::Debug};
use datasize::DataSize;
use serde::{Deserialize, Serialize};
use either::Either;
use crate::{
components::consensus::{
protocols::zug::{Proposal, RoundId},
traits::{ConsensusNetworkMessage, Context, ValidatorSecret},
utils::ValidatorIndex,
},
utils::ds,
};
#[allow(clippy::arithmetic_side_effects)]
mod relaxed {
use datasize::DataSize;
use serde::{Deserialize, Serialize};
use strum::EnumDiscriminants;
use crate::components::consensus::{
protocols::zug::{proposal::Proposal, RoundId},
traits::{ConsensusNetworkMessage, Context},
};
use super::{SignedMessage, SyncResponse};
#[derive(
Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, DataSize, EnumDiscriminants,
)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
#[strum_discriminants(derive(strum::EnumIter))]
pub(crate) enum Content<C>
where
C: Context,
{
Echo(C::Hash),
Vote(bool),
}
#[derive(
DataSize, Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, EnumDiscriminants,
)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
#[strum_discriminants(derive(strum::EnumIter))]
pub(crate) enum Message<C>
where
C: Context,
{
SyncResponse(SyncResponse<C>),
Proposal {
round_id: RoundId,
instance_id: C::InstanceId,
proposal: Proposal<C>,
echo: SignedMessage<C>,
},
Signed(SignedMessage<C>),
Evidence(SignedMessage<C>, Content<C>, C::Signature),
}
impl<C: Context> ConsensusNetworkMessage for Message<C> {}
}
pub(crate) use relaxed::{Content, ContentDiscriminants, Message, MessageDiscriminants};
use super::registered_sync::RandomId;
impl<C: Context> Content<C> {
pub(crate) fn contradicts(&self, other: &Content<C>) -> bool {
match (self, other) {
(Content::Vote(vote0), Content::Vote(vote1)) => vote0 != vote1,
(Content::Echo(hash0), Content::Echo(hash1)) => hash0 != hash1,
_ => false,
}
}
}
impl<C: Context> Copy for Content<C> {}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, DataSize)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct SignedMessage<C>
where
C: Context,
{
pub(super) round_id: RoundId,
pub(super) instance_id: C::InstanceId,
pub(super) content: Content<C>,
pub(super) validator_idx: ValidatorIndex,
pub(super) signature: C::Signature,
}
impl<C: Context> SignedMessage<C> {
pub(crate) fn sign_new(
round_id: RoundId,
instance_id: C::InstanceId,
content: Content<C>,
validator_idx: ValidatorIndex,
secret: &C::ValidatorSecret,
) -> SignedMessage<C> {
let hash = Self::hash_fields(round_id, &instance_id, &content, validator_idx);
SignedMessage {
round_id,
instance_id,
content,
validator_idx,
signature: secret.sign(&hash),
}
}
pub(crate) fn with(&self, content: Content<C>, signature: C::Signature) -> SignedMessage<C> {
SignedMessage {
content,
signature,
..*self
}
}
pub(crate) fn verify_signature(&self, validator_id: &C::ValidatorId) -> bool {
let hash = Self::hash_fields(
self.round_id,
&self.instance_id,
&self.content,
self.validator_idx,
);
C::verify_signature(&hash, validator_id, &self.signature)
}
fn hash_fields(
round_id: RoundId,
instance_id: &C::InstanceId,
content: &Content<C>,
validator_idx: ValidatorIndex,
) -> C::Hash {
let serialized_fields =
bincode::serialize(&(round_id, instance_id, content, validator_idx))
.expect("failed to serialize fields");
<C as Context>::hash(&serialized_fields)
}
}
#[derive(DataSize, Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct SyncRequest<C>
where
C: Context,
{
pub(crate) round_id: RoundId,
pub(crate) proposal_hash: Option<C::Hash>,
pub(crate) has_proposal: bool,
pub(crate) first_validator_idx: ValidatorIndex,
pub(crate) echoes: u128,
pub(crate) true_votes: u128,
pub(crate) false_votes: u128,
pub(crate) active: u128,
pub(crate) faulty: u128,
pub(crate) instance_id: C::InstanceId,
pub(crate) sync_id: RandomId,
}
impl<C: Context> ConsensusNetworkMessage for SyncRequest<C> {}
impl<C: Context> SyncRequest<C> {
pub(super) fn new_empty_round(
round_id: RoundId,
first_validator_idx: ValidatorIndex,
faulty: u128,
active: u128,
instance_id: C::InstanceId,
sync_id: RandomId,
) -> Self {
SyncRequest {
round_id,
proposal_hash: None,
has_proposal: false,
first_validator_idx,
echoes: 0,
true_votes: 0,
false_votes: 0,
active,
faulty,
instance_id,
sync_id,
}
}
}
#[derive(DataSize, Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
#[serde(bound(
serialize = "C::Hash: Serialize",
deserialize = "C::Hash: Deserialize<'de>",
))]
pub(crate) struct SyncResponse<C>
where
C: Context,
{
pub(crate) round_id: RoundId,
#[data_size(with = ds::maybe_either)]
pub(crate) proposal_or_hash: Option<Either<Proposal<C>, C::Hash>>,
pub(crate) echo_sigs: BTreeMap<ValidatorIndex, C::Signature>,
pub(crate) true_vote_sigs: BTreeMap<ValidatorIndex, C::Signature>,
pub(crate) false_vote_sigs: BTreeMap<ValidatorIndex, C::Signature>,
pub(crate) signed_messages: Vec<SignedMessage<C>>,
pub(crate) evidence: Vec<(SignedMessage<C>, Content<C>, C::Signature)>,
pub(crate) instance_id: C::InstanceId,
pub(crate) sync_id: RandomId,
}
impl<C: Context> Message<C> {
pub(super) fn instance_id(&self) -> &C::InstanceId {
match self {
Message::SyncResponse(SyncResponse { instance_id, .. })
| Message::Signed(SignedMessage { instance_id, .. })
| Message::Proposal { instance_id, .. }
| Message::Evidence(SignedMessage { instance_id, .. }, ..) => instance_id,
}
}
}