use std::collections::HashMap;
use borsh::{BorshDeserialize, BorshSerialize};
use ed25519_dalek::Verifier;
use super::messages::PhaseVote;
use crate::{
block_tree::{
accessors::internal::{BlockTreeError, BlockTreeSingleton},
pluggables::KVStore,
},
types::{
data_types::*,
signed_messages::{Certificate, Collector},
validator_set::*,
},
};
#[derive(Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
pub struct PhaseCertificate {
pub chain_id: ChainID,
pub view: ViewNumber,
pub block: CryptoHash,
pub phase: Phase,
pub signatures: SignatureSet,
}
impl Certificate for PhaseCertificate {
type Vote = PhaseVote;
fn is_correct<K: KVStore>(
&self,
block_tree: &BlockTreeSingleton<K>,
) -> Result<bool, BlockTreeError> {
if self.is_genesis_pc() {
return Ok(true);
};
let block_height = block_tree.block_height(&self.block)?;
let validator_set_state = block_tree.validator_set_state()?;
let result = match (block_height, validator_set_state.update_height()) {
(None, _) | (Some(_), &None) => {
self.is_correctly_signed(validator_set_state.committed_validator_set())
}
(Some(height), &Some(update_height)) => {
if height < update_height {
self.is_correctly_signed(validator_set_state.previous_validator_set())
} else if height > update_height {
self.is_correctly_signed(validator_set_state.committed_validator_set())
} else {
match self.phase {
Phase::Decide => {
match block_tree.validator_set_updates_status(&self.block)? {
ValidatorSetUpdatesStatus::Committed => self.is_correctly_signed(
validator_set_state.committed_validator_set(),
),
ValidatorSetUpdatesStatus::Pending(vs_updates) => {
let mut new_validator_set =
block_tree.committed_validator_set()?;
new_validator_set.apply_updates(&vs_updates);
self.is_correctly_signed(&new_validator_set)
}
ValidatorSetUpdatesStatus::None => false,
}
}
Phase::Prepare | Phase::Precommit | Phase::Commit => {
match block_tree.validator_set_updates_status(&self.block)? {
ValidatorSetUpdatesStatus::Committed => self.is_correctly_signed(
validator_set_state.previous_validator_set(),
),
ValidatorSetUpdatesStatus::Pending(_) => self.is_correctly_signed(
validator_set_state.committed_validator_set(),
),
ValidatorSetUpdatesStatus::None => false,
}
}
_ => false,
}
}
}
};
Ok(result)
}
fn is_correctly_signed(&self, validator_set: &ValidatorSet) -> bool {
if self.signatures.len() != validator_set.len() {
return false;
}
let mut total_power: TotalPower = TotalPower::new(0);
for (signature, (signer, power)) in self
.signatures
.iter()
.zip(validator_set.validators_and_powers())
{
if let Some(signature) = signature {
if let Ok(signature) = Signature::from_slice(&signature.bytes()) {
if signer
.verify(
&(self.chain_id, self.view, self.block, self.phase)
.try_to_vec()
.unwrap(),
&signature,
)
.is_ok()
{
total_power += power;
} else {
return false;
}
} else {
return false;
}
}
}
total_power >= validator_set.quorum()
}
}
impl PhaseCertificate {
pub const fn genesis_pc() -> PhaseCertificate {
PhaseCertificate {
chain_id: ChainID::new(0),
view: ViewNumber::init(),
block: CryptoHash::new([0u8; 32]),
phase: Phase::Generic,
signatures: SignatureSet::genesis(),
}
}
pub fn is_genesis_pc(&self) -> bool {
*self == Self::genesis_pc()
}
pub fn is_block_justify(&self) -> bool {
self.phase.is_generic() || self.phase.is_decide()
}
pub fn is_nudge_justify(&self) -> bool {
self.phase.is_prepare() || self.phase.is_precommit() || self.phase.is_commit()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize, Debug)]
pub enum Phase {
Generic,
Prepare,
Precommit,
Commit,
Decide,
}
impl Phase {
pub fn is_generic(self) -> bool {
self == Phase::Generic
}
pub fn is_prepare(self) -> bool {
self == Phase::Prepare
}
pub fn is_precommit(self) -> bool {
matches!(self, Phase::Precommit)
}
pub fn is_commit(self) -> bool {
matches!(self, Phase::Commit)
}
pub fn is_decide(self) -> bool {
matches!(self, Phase::Decide)
}
}
#[derive(Clone)]
pub(crate) struct PhaseVoteCollector {
chain_id: ChainID,
view: ViewNumber,
validator_set: ValidatorSet,
signature_sets: HashMap<(CryptoHash, Phase), (SignatureSet, TotalPower)>,
}
impl Collector for PhaseVoteCollector {
type Vote = PhaseVote;
type Certificate = PhaseCertificate;
fn new(chain_id: ChainID, view: ViewNumber, validator_set: ValidatorSet) -> Self {
Self {
chain_id,
view,
validator_set,
signature_sets: HashMap::new(),
}
}
fn chain_id(&self) -> ChainID {
self.chain_id
}
fn view(&self) -> ViewNumber {
self.view
}
fn validator_set(&self) -> &ValidatorSet {
&self.validator_set
}
fn collect(
&mut self,
signer: &VerifyingKey,
phase_vote: PhaseVote,
) -> Option<PhaseCertificate> {
if self.chain_id != phase_vote.chain_id || self.view != phase_vote.view {
return None;
}
if let Some(pos) = self.validator_set.position(signer) {
if let std::collections::hash_map::Entry::Vacant(e) = self
.signature_sets
.entry((phase_vote.block, phase_vote.phase))
{
e.insert((
SignatureSet::new(self.validator_set.len()),
TotalPower::new(0),
));
}
let (signature_set, signature_set_power) = self
.signature_sets
.get_mut(&(phase_vote.block, phase_vote.phase))
.unwrap();
if signature_set.get(pos).is_none() {
signature_set.set(pos, Some(phase_vote.signature));
*signature_set_power += *self.validator_set.power(signer).unwrap();
if *signature_set_power >= self.validator_set.quorum() {
let (signatures, _) = self
.signature_sets
.remove(&(phase_vote.block, phase_vote.phase))
.unwrap();
let collected_pc = PhaseCertificate {
chain_id: self.chain_id,
view: self.view,
block: phase_vote.block,
phase: phase_vote.phase,
signatures,
};
return Some(collected_pc);
}
}
}
None
}
}