use std::{
any::Any,
collections::BTreeMap,
fmt::{self, Debug, Display, Formatter},
path::PathBuf,
};
use anyhow::Error;
use datasize::DataSize;
use serde::{Deserialize, Serialize};
use casper_hashing::Digest;
use casper_types::bytesrepr::ToBytes;
use crate::{
components::consensus::{traits::Context, ActionId, TimerId},
types::{TimeDiff, Timestamp},
NodeRng,
};
#[derive(Clone, DataSize, Eq, PartialEq, Debug, Ord, PartialOrd, Hash)]
pub struct BlockContext<C>
where
C: Context,
{
timestamp: Timestamp,
ancestor_values: Vec<C::ConsensusValue>,
}
impl<C: Context> BlockContext<C> {
pub(crate) fn new(timestamp: Timestamp, ancestor_values: Vec<C::ConsensusValue>) -> Self {
BlockContext {
timestamp,
ancestor_values,
}
}
pub(crate) fn timestamp(&self) -> Timestamp {
self.timestamp
}
pub(crate) fn height(&self) -> u64 {
self.ancestor_values.len() as u64
}
pub(crate) fn ancestor_values(&self) -> &[C::ConsensusValue] {
&self.ancestor_values
}
}
#[derive(Clone, DataSize, Eq, PartialEq, Debug, Ord, PartialOrd, Hash)]
pub struct ProposedBlock<C>
where
C: Context,
{
value: C::ConsensusValue,
context: BlockContext<C>,
}
impl<C: Context> ProposedBlock<C> {
pub(crate) fn new(value: C::ConsensusValue, context: BlockContext<C>) -> Self {
ProposedBlock { value, context }
}
pub(crate) fn value(&self) -> &C::ConsensusValue {
&self.value
}
pub(crate) fn context(&self) -> &BlockContext<C> {
&self.context
}
pub(crate) fn destructure(self) -> (C::ConsensusValue, BlockContext<C>) {
(self.value, self.context)
}
}
impl<C: Context> Display for ProposedBlock<C>
where
C::ConsensusValue: Display,
{
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
write!(
formatter,
"proposed block at {}: {}",
self.context.timestamp(),
self.value
)
}
}
#[derive(Clone, DataSize, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(bound(
serialize = "VID: Ord + Serialize",
deserialize = "VID: Ord + Deserialize<'de>",
))]
pub struct EraReport<VID> {
pub(crate) equivocators: Vec<VID>,
pub(crate) rewards: BTreeMap<VID, u64>,
pub(crate) inactive_validators: Vec<VID>,
}
impl<VID> Default for EraReport<VID>
where
VID: Ord,
{
fn default() -> Self {
EraReport {
equivocators: vec![],
rewards: BTreeMap::new(),
inactive_validators: vec![],
}
}
}
impl<VID> EraReport<VID> {
pub fn hash(&self) -> Digest
where
VID: ToBytes,
{
fn hash_slice_of_validators<VID>(slice_of_validators: &[VID]) -> Digest
where
VID: ToBytes,
{
let hashes = slice_of_validators
.iter()
.map(|validator| {
Digest::hash(validator.to_bytes().expect("Could not serialize validator"))
})
.collect();
Digest::hash_vec_merkle_tree(hashes)
}
let EraReport {
equivocators,
inactive_validators,
rewards,
} = self;
let hashed_equivocators = hash_slice_of_validators(equivocators);
let hashed_inactive_validators = hash_slice_of_validators(inactive_validators);
let hashed_rewards = Digest::hash_btree_map(rewards).expect("Could not hash rewards");
Digest::hash_slice_rfold(&[
hashed_equivocators,
hashed_rewards,
hashed_inactive_validators,
])
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct TerminalBlockData<C: Context> {
pub(crate) rewards: BTreeMap<C::ValidatorId, u64>,
pub(crate) inactive_validators: Vec<C::ValidatorId>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct FinalizedBlock<C: Context> {
pub(crate) value: C::ConsensusValue,
pub(crate) timestamp: Timestamp,
pub(crate) relative_height: u64,
pub(crate) equivocators: Vec<C::ValidatorId>,
pub(crate) terminal_block_data: Option<TerminalBlockData<C>>,
pub(crate) proposer: C::ValidatorId,
}
pub(crate) type ProtocolOutcomes<I, C> = Vec<ProtocolOutcome<I, C>>;
#[derive(Debug)]
pub(crate) enum ProtocolOutcome<I, C: Context> {
CreatedGossipMessage(Vec<u8>),
CreatedTargetedMessage(Vec<u8>, I),
CreatedMessageToRandomPeer(Vec<u8>),
InvalidIncomingMessage(Vec<u8>, I, Error),
ScheduleTimer(Timestamp, TimerId),
QueueAction(ActionId),
CreateNewBlock(BlockContext<C>),
FinalizedBlock(FinalizedBlock<C>),
ValidateConsensusValue {
sender: I,
proposed_block: ProposedBlock<C>,
},
NewEvidence(C::ValidatorId),
SendEvidence(I, C::ValidatorId),
WeAreFaulty,
DoppelgangerDetected,
FttExceeded,
StandstillAlert,
Disconnect(I),
}
pub(crate) trait ConsensusProtocol<I, C: Context>: Send {
fn as_any(&self) -> &dyn Any;
fn handle_message(
&mut self,
rng: &mut NodeRng,
sender: I,
msg: Vec<u8>,
now: Timestamp,
) -> ProtocolOutcomes<I, C>;
fn handle_is_current(&self, now: Timestamp) -> ProtocolOutcomes<I, C>;
fn handle_timer(&mut self, timestamp: Timestamp, timer_id: TimerId) -> ProtocolOutcomes<I, C>;
fn handle_action(&mut self, action_id: ActionId, now: Timestamp) -> ProtocolOutcomes<I, C>;
fn propose(
&mut self,
proposed_block: ProposedBlock<C>,
now: Timestamp,
) -> ProtocolOutcomes<I, C>;
fn resolve_validity(
&mut self,
proposed_block: ProposedBlock<C>,
valid: bool,
now: Timestamp,
) -> ProtocolOutcomes<I, C>;
fn activate_validator(
&mut self,
our_id: C::ValidatorId,
secret: C::ValidatorSecret,
timestamp: Timestamp,
unit_hash_file: Option<PathBuf>,
) -> ProtocolOutcomes<I, C>;
fn deactivate_validator(&mut self);
fn set_evidence_only(&mut self);
fn has_evidence(&self, vid: &C::ValidatorId) -> bool;
fn mark_faulty(&mut self, vid: &C::ValidatorId);
fn request_evidence(&self, sender: I, vid: &C::ValidatorId) -> ProtocolOutcomes<I, C>;
fn set_paused(&mut self, paused: bool);
fn validators_with_evidence(&self) -> Vec<&C::ValidatorId>;
fn has_received_messages(&self) -> bool;
fn is_active(&self) -> bool;
fn instance_id(&self) -> &C::InstanceId;
fn next_round_length(&self) -> Option<TimeDiff>;
}