use std::cmp::{Ord, Ordering, PartialOrd};
use bytes::Bytes;
use derive_more::Display;
use serde::{Deserialize, Serialize};
use crate::smr::smr_types::{SMRStatus, Step, TriggerType};
use crate::{Codec, DurationConfig};
pub type Address = Bytes;
pub type Hash = Bytes;
pub type Signature = Bytes;
#[derive(Clone, Debug, Display, PartialEq, Eq)]
pub enum Role {
#[display(fmt = "Leader")]
Leader,
#[display(fmt = "Replica")]
Replica,
}
impl Into<u8> for Role {
fn into(self) -> u8 {
match self {
Role::Leader => 0,
Role::Replica => 1,
}
}
}
impl From<u8> for Role {
fn from(s: u8) -> Self {
match s {
0 => Role::Leader,
1 => Role::Replica,
_ => panic!("Invalid role!"),
}
}
}
#[derive(Clone, Debug, Display, PartialEq, Eq, Hash)]
pub enum VoteType {
#[display(fmt = "Prevote")]
Prevote,
#[display(fmt = "Precommit")]
Precommit,
}
impl Into<u8> for VoteType {
fn into(self) -> u8 {
match self {
VoteType::Prevote => 1,
VoteType::Precommit => 2,
}
}
}
impl Into<TriggerType> for VoteType {
fn into(self) -> TriggerType {
match self {
VoteType::Prevote => TriggerType::PrevoteQC,
VoteType::Precommit => TriggerType::PrecommitQC,
}
}
}
impl Into<Step> for VoteType {
fn into(self) -> Step {
match self {
VoteType::Prevote => Step::Prevote,
VoteType::Precommit => Step::Precommit,
}
}
}
impl From<u8> for VoteType {
fn from(s: u8) -> Self {
match s {
1 => VoteType::Prevote,
2 => VoteType::Precommit,
_ => panic!("Invalid vote type!"),
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Display, PartialEq, Eq)]
pub enum OverlordMsg<T: Codec> {
#[display(fmt = "Signed Proposal")]
SignedProposal(SignedProposal<T>),
#[display(fmt = "Signed Vote")]
SignedVote(SignedVote),
#[display(fmt = "Aggregated Vote")]
AggregatedVote(AggregatedVote),
#[display(fmt = "Rich Status")]
RichStatus(Status),
#[display(fmt = "Choke Message")]
SignedChoke(SignedChoke),
#[cfg(test)]
Commit(Commit<T>),
}
impl<T: Codec> OverlordMsg<T> {
pub(crate) fn is_rich_status(&self) -> bool {
match self {
OverlordMsg::RichStatus(_) => true,
_ => false,
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum UpdateFrom {
PrevoteQC(AggregatedVote),
PrecommitQC(AggregatedVote),
ChokeQC(AggregatedChoke),
}
#[derive(Clone, Debug, Display, PartialEq, Eq)]
#[display(fmt = "Signed Proposal {:?}", proposal)]
pub struct SignedProposal<T: Codec> {
pub signature: Bytes,
pub proposal: Proposal<T>,
}
#[derive(Clone, Debug, Display, PartialEq, Eq)]
#[display(fmt = "Proposal height {}, round {}", height, round)]
pub struct Proposal<T: Codec> {
pub height: u64,
pub round: u64,
pub content: T,
pub block_hash: Hash,
pub lock: Option<PoLC>,
pub proposer: Address,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PoLC {
pub lock_round: u64,
pub lock_votes: AggregatedVote,
}
#[derive(Clone, Debug, Display, PartialEq, Eq, Hash)]
#[display(fmt = "Signed vote {:?}", vote)]
pub struct SignedVote {
pub signature: Bytes,
pub vote: Vote,
pub voter: Address,
}
impl PartialOrd for SignedVote {
fn partial_cmp(&self, other: &SignedVote) -> Option<Ordering> {
Some(self.voter.cmp(&other.voter))
}
}
impl Ord for SignedVote {
fn cmp(&self, other: &SignedVote) -> Ordering {
self.voter.cmp(&other.voter)
}
}
impl SignedVote {
pub fn get_height(&self) -> u64 {
self.vote.height
}
pub fn get_round(&self) -> u64 {
self.vote.round
}
pub fn get_hash(&self) -> Hash {
self.vote.block_hash.clone()
}
pub fn is_prevote(&self) -> bool {
self.vote.vote_type == VoteType::Prevote
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)]
pub struct AggregatedSignature {
pub signature: Signature,
pub address_bitmap: Bytes,
}
#[derive(Clone, Debug, Display, PartialEq, Eq, Hash)]
#[rustfmt::skip]
#[display(fmt = "{:?} aggregated vote height {}, round {}", vote_type, height, round)]
pub struct AggregatedVote {
pub signature: AggregatedSignature,
pub vote_type: VoteType,
pub height: u64,
pub round: u64,
pub block_hash: Hash,
pub leader: Address,
}
impl AggregatedVote {
pub fn get_height(&self) -> u64 {
self.height
}
pub fn get_round(&self) -> u64 {
self.round
}
pub fn is_prevote_qc(&self) -> bool {
self.vote_type == VoteType::Prevote
}
pub fn to_vote(&self) -> Vote {
Vote {
height: self.height,
round: self.round,
vote_type: self.vote_type.clone(),
block_hash: self.block_hash.clone(),
}
}
}
#[derive(Clone, Debug, Display, PartialEq, Eq, Hash)]
#[display(fmt = "{:?} vote height {}, round {}", vote_type, height, round)]
pub struct Vote {
pub height: u64,
pub round: u64,
pub vote_type: VoteType,
pub block_hash: Hash,
}
#[derive(Clone, Debug, Display, PartialEq, Eq)]
#[display(fmt = "Commit height {}", height)]
pub struct Commit<T: Codec> {
pub height: u64,
pub content: T,
pub proof: Proof,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Proof {
pub height: u64,
pub round: u64,
pub block_hash: Hash,
pub signature: AggregatedSignature,
}
#[derive(Clone, Debug, Display, PartialEq, Eq)]
#[display(fmt = "Rich status height {}", height)]
pub struct Status {
pub height: u64,
pub interval: Option<u64>,
pub timer_config: Option<DurationConfig>,
pub authority_list: Vec<Node>,
}
impl Into<SMRStatus> for Status {
fn into(self) -> SMRStatus {
SMRStatus {
height: self.height,
new_interval: self.interval,
new_config: self.timer_config,
}
}
}
impl Status {
pub(crate) fn is_consensus_node(&self, address: &Address) -> bool {
self.authority_list
.iter()
.any(|node| node.address == address)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Node {
pub address: Address,
pub propose_weight: u32,
pub vote_weight: u32,
}
impl PartialOrd for Node {
fn partial_cmp(&self, other: &Node) -> Option<Ordering> {
Some(self.address.cmp(&other.address))
}
}
impl Ord for Node {
fn cmp(&self, other: &Node) -> Ordering {
self.address.cmp(&other.address)
}
}
impl Node {
pub fn new(addr: Address) -> Self {
Node {
address: addr,
propose_weight: 1u32,
vote_weight: 1u32,
}
}
pub fn set_propose_weight(&mut self, propose_weight: u32) {
self.propose_weight = propose_weight;
}
pub fn set_vote_weight(&mut self, vote_weight: u32) {
self.vote_weight = vote_weight;
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct VerifyResp {
pub(crate) height: u64,
pub(crate) round: u64,
pub(crate) block_hash: Hash,
pub(crate) is_pass: bool,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct AggregatedChoke {
pub height: u64,
pub round: u64,
pub signature: Signature,
pub voters: Vec<Address>,
}
#[allow(clippy::len_without_is_empty)]
impl AggregatedChoke {
pub(crate) fn len(&self) -> usize {
self.voters.len()
}
pub(crate) fn to_hash(&self) -> HashChoke {
HashChoke {
height: self.height,
round: self.round,
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct SignedChoke {
pub signature: Signature,
pub choke: Choke,
pub address: Address,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Choke {
pub height: u64,
pub round: u64,
pub from: UpdateFrom,
}
impl Choke {
pub(crate) fn to_hash(&self) -> HashChoke {
HashChoke {
height: self.height,
round: self.round,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct HashChoke {
pub(crate) height: u64,
pub(crate) round: u64,
}
#[cfg(test)]
mod test {
use super::*;
use rand::random;
fn gen_address() -> Address {
Address::from((0..32).map(|_| random::<u8>()).collect::<Vec<_>>())
}
fn mock_node() -> Node {
Node::new(gen_address())
}
fn mock_status() -> Status {
Status {
height: random::<u64>(),
interval: None,
timer_config: None,
authority_list: vec![mock_node(), mock_node()],
}
}
#[test]
fn test_consensus_power() {
let status = mock_status();
let consensus_node = status.authority_list[0].address.clone();
let sync_node = gen_address();
assert!(status.is_consensus_node(&consensus_node));
assert!(!status.is_consensus_node(&sync_node));
}
}