use std::collections::BTreeMap;
use crate::cert::{CertError, NodeId, QuorumCert, StakeSource, Vote, VoteVerifier, SIGNATURE_LEN};
use crate::finality::{
canonical_vote_message, signer_floor, Finality, Id, Position, EMPTY, QC_FINALITY,
QUORUM_CERT_VERSION, VOTE_MESSAGE_LEN, VOTE_TAG,
};
use crate::pop::NODE_LEN;
use crate::zap;
pub const VOTE: u8 = 62;
pub const VOTE_PAYLOAD_LEN: usize = 4 + VOTE_MESSAGE_LEN + 4 + NODE_LEN + 4 + SIGNATURE_LEN;
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum VoteError {
Wire,
Position,
NotAccept,
Signature,
Cert(CertError),
}
impl From<CertError> for VoteError {
fn from(e: CertError) -> Self {
VoteError::Cert(e)
}
}
impl std::fmt::Display for VoteError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VoteError::Wire => f.write_str("not a vote frame"),
VoteError::Position => f.write_str("vote is over another position"),
VoteError::NotAccept => f.write_str("a reject is not a finality vote"),
VoteError::Signature => f.write_str("signer does not resolve, or signature is bad"),
VoteError::Cert(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for VoteError {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SignedVote {
pub position: Position,
pub accept: bool,
pub node: NodeId,
pub signature: Vec<u8>,
}
impl SignedVote {
pub fn message(&self) -> Vec<u8> {
canonical_vote_message(&self.position, self.accept)
}
pub fn verify(&self, verifier: &dyn VoteVerifier, epoch_height: u64) -> bool {
verifier.verify_vote(&self.node, &self.message(), &self.signature, epoch_height)
}
pub fn record(&self) -> Vote {
Vote {
node_id: self.node,
accept: self.accept,
signature: self.signature.clone(),
}
}
pub fn encode(&self) -> Vec<u8> {
let mut w = zap::Writer::with_capacity(VOTE_PAYLOAD_LEN);
w.bytes(&self.message())
.bytes(&self.node)
.bytes(&self.signature);
w.take()
}
pub fn decode(payload: &[u8]) -> Result<Self, VoteError> {
let mut r = zap::Reader::new(payload);
let message = r.bytes().ok_or(VoteError::Wire)?;
let node = r.bytes().ok_or(VoteError::Wire)?;
let signature = r.bytes().ok_or(VoteError::Wire)?;
if r.remaining() != 0 {
return Err(VoteError::Wire);
}
if node.len() != NODE_LEN || signature.len() != SIGNATURE_LEN {
return Err(VoteError::Wire);
}
let (position, accept) = read_message(message)?;
Ok(SignedVote {
position,
accept,
node: node.try_into().map_err(|_| VoteError::Wire)?,
signature: signature.to_vec(),
})
}
}
pub fn read_message(message: &[u8]) -> Result<(Position, bool), VoteError> {
if message.len() != VOTE_MESSAGE_LEN {
return Err(VoteError::Wire);
}
let tag = VOTE_TAG.len(); if &message[..tag] != VOTE_TAG {
return Err(VoteError::Wire);
}
let version = u16::from_be_bytes([message[tag], message[tag + 1]]);
if version != QUORUM_CERT_VERSION {
return Err(CertError::Version {
got: version,
want: QUORUM_CERT_VERSION,
}
.into());
}
let role = message[tag + 2];
if role != QC_FINALITY {
return Err(CertError::Type {
got: role,
want: QC_FINALITY,
}
.into());
}
let id = |at: usize| -> Id { message[at..at + 32].try_into().expect("32 bytes") };
let chain_id = id(21);
let height = u64::from_be_bytes(message[53..61].try_into().expect("8 bytes"));
let round = u32::from_be_bytes(message[61..65].try_into().expect("4 bytes"));
let canonical_id = id(65);
let parent_canonical_id = id(97);
let execution_state_root = id(129);
let payload_root = id(161);
let validator_set_root = id(193);
let accept = match message[225] {
0x00 => false,
0x01 => true,
_ => return Err(VoteError::Wire),
};
Ok((
Position {
chain_id,
height,
round,
block_id: EMPTY,
parent_id: EMPTY,
canonical_id,
parent_canonical_id,
execution_state_root,
payload_root,
validator_set_root,
},
accept,
))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Slot {
pub chain: Id,
pub height: u64,
pub round: u32,
}
impl Slot {
pub fn of(position: &Position) -> Slot {
Slot {
chain: position.chain_id,
height: position.height,
round: position.round,
}
}
pub fn read(message: &[u8]) -> Result<Slot, VoteError> {
read_message(message).map(|(position, _)| Slot::of(&position))
}
}
pub trait VoteTransport {
fn broadcast(&self, vote: &SignedVote);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Tally {
position: Position,
tier: Finality,
epoch_height: u64,
message: Vec<u8>,
votes: BTreeMap<NodeId, Vec<u8>>,
}
impl Tally {
pub fn new(position: Position, tier: Finality, epoch_height: u64) -> Result<Self, VoteError> {
if tier != Finality::Nova && tier != Finality::Quasar {
return Err(CertError::UnknownTier(tier).into());
}
let message = canonical_vote_message(&position, true);
Ok(Tally {
position,
tier,
epoch_height,
message,
votes: BTreeMap::new(),
})
}
pub fn message(&self) -> &[u8] {
&self.message
}
pub fn position(&self) -> &Position {
&self.position
}
pub fn tier(&self) -> Finality {
self.tier
}
pub fn epoch_height(&self) -> u64 {
self.epoch_height
}
pub fn len(&self) -> usize {
self.votes.len()
}
pub fn is_empty(&self) -> bool {
self.votes.is_empty()
}
pub fn add(
&mut self,
vote: &SignedVote,
verifier: &dyn VoteVerifier,
) -> Result<bool, VoteError> {
if !vote.accept {
return Err(VoteError::NotAccept);
}
if vote.message() != self.message {
return Err(VoteError::Position);
}
if self.votes.contains_key(&vote.node) {
return Ok(false);
}
if !verifier.verify_vote(
&vote.node,
&self.message,
&vote.signature,
self.epoch_height,
) {
return Err(VoteError::Signature);
}
self.votes.insert(vote.node, vote.signature.clone());
Ok(true)
}
pub fn cert(
&self,
verifier: &dyn VoteVerifier,
stake: &dyn StakeSource,
) -> Result<QuorumCert, VoteError> {
let votes: Vec<Vote> = self
.votes
.iter()
.map(|(node, signature)| Vote {
node_id: *node,
accept: true,
signature: signature.clone(),
})
.collect();
let n = stake.signer_count(self.epoch_height);
let derived = signer_floor(self.tier, n);
let threshold = u32::try_from(derived).map_err(|_| CertError::SignerFloor {
have: self.votes.len() as i64,
need: derived,
n,
})?;
let cert = QuorumCert::assemble(self.tier, self.position.clone(), threshold, &votes)?;
cert.verify_weighted(verifier, stake, self.epoch_height)?;
Ok(cert)
}
}