overlord/
types.rs

1use std::cmp::{Ord, Ordering, PartialOrd};
2use std::convert::TryFrom;
3
4use bytes::Bytes;
5use derive_more::Display;
6use serde::{Deserialize, Serialize};
7
8use crate::error::ConsensusError;
9use crate::smr::smr_types::{SMRStatus, Step, TriggerType};
10use crate::{Codec, DurationConfig};
11
12/// Address type.
13pub type Address = Bytes;
14/// Hash type.
15pub type Hash = Bytes;
16/// Signature type.
17pub type Signature = Bytes;
18
19/// Vote or QC types. Prevote and precommit QC will promise the rightness and the final consistency
20/// of overlord consensus protocol.
21#[derive(Serialize, Deserialize, Clone, Debug, Display, PartialEq, Eq, Hash)]
22pub enum VoteType {
23    /// Prevote vote or QC.
24    #[display(fmt = "Prevote")]
25    Prevote,
26    /// Precommit Vote or QC.
27    #[display(fmt = "Precommit")]
28    Precommit,
29}
30
31impl From<VoteType> for u8 {
32    fn from(v: VoteType) -> u8 {
33        match v {
34            VoteType::Prevote => 1,
35            VoteType::Precommit => 2,
36        }
37    }
38}
39
40impl From<VoteType> for TriggerType {
41    fn from(v: VoteType) -> TriggerType {
42        match v {
43            VoteType::Prevote => TriggerType::PrevoteQC,
44            VoteType::Precommit => TriggerType::PrecommitQC,
45        }
46    }
47}
48
49impl From<VoteType> for Step {
50    fn from(v: VoteType) -> Step {
51        match v {
52            VoteType::Prevote => Step::Prevote,
53            VoteType::Precommit => Step::Precommit,
54        }
55    }
56}
57
58impl TryFrom<u8> for VoteType {
59    type Error = ConsensusError;
60
61    fn try_from(s: u8) -> Result<Self, Self::Error> {
62        match s {
63            1 => Ok(VoteType::Prevote),
64            2 => Ok(VoteType::Precommit),
65            _ => Err(ConsensusError::Other("".to_string())),
66        }
67    }
68}
69
70/// Overlord messages.
71#[allow(clippy::large_enum_variant)]
72#[derive(Clone, Debug, Display, PartialEq, Eq)]
73pub enum OverlordMsg<T: Codec> {
74    /// Signed proposal message.
75    #[display(fmt = "Signed Proposal")]
76    SignedProposal(SignedProposal<T>),
77    /// Signed vote message.
78    #[display(fmt = "Signed Vote")]
79    SignedVote(SignedVote),
80    /// Aggregated vote message.
81    #[display(fmt = "Aggregated Vote")]
82    AggregatedVote(AggregatedVote),
83    /// Rich status message.
84    #[display(fmt = "Rich Status")]
85    RichStatus(Status),
86    /// Signed choke message
87    #[display(fmt = "Choke Message")]
88    SignedChoke(SignedChoke),
89    /// Stop consensus process.
90    #[display(fmt = "Stop Overlord")]
91    Stop,
92
93    /// This is only for easier testing.
94    #[cfg(test)]
95    Commit(Commit<T>),
96}
97
98impl<T: Codec> OverlordMsg<T> {
99    pub(crate) fn is_rich_status(&self) -> bool {
100        matches!(self, OverlordMsg::RichStatus(_))
101    }
102
103    pub(crate) fn get_height(&self) -> u64 {
104        match self {
105            OverlordMsg::SignedProposal(sp) => sp.proposal.height,
106            OverlordMsg::SignedVote(sv) => sv.get_height(),
107            OverlordMsg::AggregatedVote(av) => av.get_height(),
108            OverlordMsg::RichStatus(s) => s.height,
109            OverlordMsg::SignedChoke(sc) => sc.choke.height,
110            _ => unreachable!(),
111        }
112    }
113}
114
115/// How does state goto the current round.
116#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)]
117pub enum UpdateFrom {
118    /// From a prevote quorum certificate.
119    PrevoteQC(AggregatedVote),
120    /// From a precommit quorum certificate.
121    PrecommitQC(AggregatedVote),
122    /// From a choke quorum certificate.
123    ChokeQC(AggregatedChoke),
124}
125
126/// The reason of overlord view change.
127#[derive(Serialize, Deserialize, Clone, Debug, Display)]
128pub enum ViewChangeReason {
129    ///
130    #[display(fmt = "Do not receive proposal from network")]
131    NoProposalFromNetwork,
132
133    ///
134    #[display(fmt = "Do not receive Prevote QC from network")]
135    NoPrevoteQCFromNetwork,
136
137    ///
138    #[display(fmt = "Do not receive precommit QC from network")]
139    NoPrecommitQCFromNetwork,
140
141    ///
142    #[display(fmt = "Check the block not pass")]
143    CheckBlockNotPass,
144
145    ///
146    #[display(fmt = "Update from a higher round prevote QC from {} to {}", _0, _1)]
147    UpdateFromHigherPrevoteQC(u64, u64),
148
149    ///
150    #[display(fmt = "Update from a higher round precommit QC from {} to {}", _0, _1)]
151    UpdateFromHigherPrecommitQC(u64, u64),
152
153    ///
154    #[display(fmt = "Update from a higher round choke QC from {} to {}", _0, _1)]
155    UpdateFromHigherChokeQC(u64, u64),
156
157    ///
158    #[display(fmt = "{:?} votes count is below threshold", _0)]
159    LeaderReceivedVoteBelowThreshold(VoteType),
160
161    ///
162    #[display(fmt = "other reasons")]
163    Others,
164}
165
166/// A signed proposal.
167#[derive(Clone, Debug, Display, PartialEq, Eq)]
168#[display(fmt = "Signed Proposal {:?}", proposal)]
169pub struct SignedProposal<T: Codec> {
170    /// Signature of the proposal.
171    pub signature: Bytes,
172    /// A proposal.
173    pub proposal: Proposal<T>,
174}
175
176/// A proposal
177#[derive(Clone, Debug, Display, PartialEq, Eq)]
178#[display(fmt = "Proposal height {}, round {}", height, round)]
179pub struct Proposal<T: Codec> {
180    /// Height of the proposal.
181    pub height: u64,
182    /// Round of the proposal.
183    pub round: u64,
184    /// Proposal content.
185    pub content: T,
186    /// Proposal block hash.
187    pub block_hash: Hash,
188    /// Optional field. If the proposal has a PoLC, this contains the lock round and lock votes.
189    pub lock: Option<PoLC>,
190    /// Proposer address.
191    pub proposer: Address,
192}
193
194/// A PoLC.
195#[derive(Clone, Debug, PartialEq, Eq)]
196pub struct PoLC {
197    /// Lock round of the proposal.
198    pub lock_round: u64,
199    /// Lock votes of the proposal.
200    pub lock_votes: AggregatedVote,
201}
202
203/// A signed vote.
204#[derive(Clone, Debug, Display, PartialEq, Eq, Hash)]
205#[display(fmt = "Signed vote {:?}", vote)]
206pub struct SignedVote {
207    /// Signature of the vote.
208    pub signature: Bytes,
209    /// A vote.
210    pub vote: Vote,
211    /// Voter address.
212    pub voter: Address,
213}
214
215impl PartialOrd for SignedVote {
216    fn partial_cmp(&self, other: &SignedVote) -> Option<Ordering> {
217        Some(self.voter.cmp(&other.voter))
218    }
219}
220
221impl Ord for SignedVote {
222    fn cmp(&self, other: &SignedVote) -> Ordering {
223        self.voter.cmp(&other.voter)
224    }
225}
226
227impl SignedVote {
228    /// Get the height of the signed vote.
229    pub fn get_height(&self) -> u64 {
230        self.vote.height
231    }
232
233    /// Get the round of the signed vote.
234    pub fn get_round(&self) -> u64 {
235        self.vote.round
236    }
237
238    /// Get the hash of the signed vote.
239    pub fn get_hash(&self) -> Hash {
240        self.vote.block_hash.clone()
241    }
242
243    /// If the signed vote is a prevote vote.
244    pub fn is_prevote(&self) -> bool {
245        self.vote.vote_type == VoteType::Prevote
246    }
247}
248
249/// An aggregate signature.
250#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)]
251pub struct AggregatedSignature {
252    /// Aggregated signature.
253    #[serde(with = "super::serde_hex")]
254    pub signature: Signature,
255    /// Voter address bit map.
256    #[serde(with = "super::serde_hex")]
257    pub address_bitmap: Bytes,
258}
259
260/// An aggregated vote.
261#[derive(Serialize, Deserialize, Clone, Debug, Display, PartialEq, Eq, Hash)]
262#[rustfmt::skip]
263#[display(fmt = "{:?} aggregated vote height {}, round {}", vote_type, height, round)]
264pub struct AggregatedVote {
265    /// Aggregated signature of the vote.
266    pub signature: AggregatedSignature,
267    /// Type of the vote.
268    pub vote_type: VoteType,
269    /// Height of the vote.
270    pub height: u64,
271    /// Round of the vote.
272    pub round: u64,
273    /// Proposal hash of the vote.
274    #[serde(with = "super::serde_hex")]
275    pub block_hash: Hash,
276    /// The leader that aggregate the signed votes.
277    #[serde(with = "super::serde_hex")]
278    pub leader: Address,
279}
280
281impl AggregatedVote {
282    /// Get the height of the aggregate vote.
283    pub fn get_height(&self) -> u64 {
284        self.height
285    }
286
287    /// Get the round of the aggregate vote.
288    pub fn get_round(&self) -> u64 {
289        self.round
290    }
291
292    /// If the aggregate vote is a prevote quorum certificate.
293    pub fn is_prevote_qc(&self) -> bool {
294        self.vote_type == VoteType::Prevote
295    }
296
297    ///
298    pub fn to_vote(&self) -> Vote {
299        Vote {
300            height: self.height,
301            round: self.round,
302            vote_type: self.vote_type.clone(),
303            block_hash: self.block_hash.clone(),
304        }
305    }
306}
307
308/// A vote.
309#[derive(Clone, Debug, Display, PartialEq, Eq, Hash)]
310#[display(fmt = "{:?} vote height {}, round {}", vote_type, height, round)]
311pub struct Vote {
312    /// Height of the vote.
313    pub height: u64,
314    /// Round of the vote.
315    pub round: u64,
316    /// Type of the vote.
317    pub vote_type: VoteType,
318    /// Block hash of the vote.
319    pub block_hash: Hash,
320}
321
322/// A commit.
323#[derive(Clone, Debug, Display, PartialEq, Eq)]
324#[display(fmt = "Commit height {}", height)]
325pub struct Commit<T: Codec> {
326    /// Height of the commit.
327    pub height: u64,
328    /// Commit content.
329    pub content: T,
330    /// The consensus proof.
331    pub proof: Proof,
332}
333
334/// A Proof.
335#[derive(Clone, Debug, PartialEq, Eq)]
336pub struct Proof {
337    /// Height of the proof.
338    pub height: u64,
339    /// Round of the proof.
340    pub round: u64,
341    /// Block hash of the proof.
342    pub block_hash: Hash,
343    /// Aggregated signature of the proof.
344    pub signature: AggregatedSignature,
345}
346
347/// A rich status.
348#[derive(Clone, Debug, Display, PartialEq, Eq)]
349#[display(fmt = "Rich status height {}", height)]
350pub struct Status {
351    /// New height.
352    pub height: u64,
353    /// New block interval.
354    pub interval: Option<u64>,
355    /// New timeout configuration.
356    pub timer_config: Option<DurationConfig>,
357    /// New authority list.
358    pub authority_list: Vec<Node>,
359}
360
361impl From<Status> for SMRStatus {
362    fn from(s: Status) -> SMRStatus {
363        SMRStatus {
364            height: s.height,
365            new_interval: s.interval,
366            new_config: s.timer_config,
367        }
368    }
369}
370
371impl Status {
372    pub(crate) fn is_consensus_node(&self, address: &Address) -> bool {
373        self.authority_list
374            .iter()
375            .any(|node| node.address == address)
376    }
377}
378
379/// A node info.
380#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
381pub struct Node {
382    /// Node address.
383    #[serde(with = "super::serde_hex")]
384    pub address: Address,
385    /// The propose weight of the node. The field is only effective in `features =
386    /// "random_leader"`.
387    pub propose_weight: u32,
388    /// The vote weight of the node.
389    pub vote_weight: u32,
390}
391
392impl PartialOrd for Node {
393    fn partial_cmp(&self, other: &Node) -> Option<Ordering> {
394        Some(self.address.cmp(&other.address))
395    }
396}
397
398impl Ord for Node {
399    fn cmp(&self, other: &Node) -> Ordering {
400        self.address.cmp(&other.address)
401    }
402}
403
404impl Node {
405    /// Create a new node with defaule propose weight `1` and vote weight `1`.
406    pub fn new(addr: Address) -> Self {
407        Node {
408            address: addr,
409            propose_weight: 1u32,
410            vote_weight: 1u32,
411        }
412    }
413
414    /// Set a new propose weight of the node. Propose weight is only effective in `features =
415    /// "random_leader"`.
416    pub fn set_propose_weight(&mut self, propose_weight: u32) {
417        self.propose_weight = propose_weight;
418    }
419
420    /// Set a new vote weight of the node.
421    pub fn set_vote_weight(&mut self, vote_weight: u32) {
422        self.vote_weight = vote_weight;
423    }
424}
425
426/// A verify response.
427#[derive(Clone, Debug, PartialEq, Eq)]
428pub(crate) struct VerifyResp {
429    /// The height of the verified block.
430    pub(crate) height: u64,
431    /// The round of the verified block.
432    pub(crate) round: u64,
433    /// Verified proposal hash.
434    pub(crate) block_hash: Hash,
435    /// The block is pass or not.
436    pub(crate) is_pass: bool,
437}
438
439/// An aggregated choke.
440#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)]
441pub struct AggregatedChoke {
442    /// The height of the aggregated choke.
443    pub height: u64,
444    /// The round of the aggregated choke.
445    pub round: u64,
446    /// The aggregated signature of the aggregated choke.
447    #[serde(with = "super::serde_hex")]
448    pub signature: Signature,
449    /// The voters of the aggregated choke.
450    #[serde(with = "super::serde_multi_hex")]
451    pub voters: Vec<Address>,
452}
453
454#[allow(clippy::len_without_is_empty)]
455impl AggregatedChoke {
456    pub(crate) fn len(&self) -> usize {
457        self.voters.len()
458    }
459
460    pub(crate) fn to_hash(&self) -> HashChoke {
461        HashChoke {
462            height: self.height,
463            round: self.round,
464        }
465    }
466}
467
468/// A signed choke.
469#[derive(Clone, Debug, Hash, PartialEq, Eq)]
470pub struct SignedChoke {
471    /// The signature of the choke.
472    pub signature: Signature,
473    /// The choke message.
474    pub choke: Choke,
475    /// The choke address.
476    pub address: Address,
477}
478
479/// A choke.
480#[derive(Clone, Debug, Hash, PartialEq, Eq)]
481pub struct Choke {
482    /// The height of the choke.
483    pub height: u64,
484    /// The round of the choke.
485    pub round: u64,
486    /// How does state goto the current round.
487    pub from: UpdateFrom,
488}
489
490impl Choke {
491    pub(crate) fn to_hash(&self) -> HashChoke {
492        HashChoke {
493            height: self.height,
494            round: self.round,
495        }
496    }
497}
498
499#[derive(Clone, Debug)]
500pub(crate) struct HashChoke {
501    pub(crate) height: u64,
502    pub(crate) round: u64,
503}
504
505#[cfg(test)]
506mod test {
507    use super::*;
508    use rand::random;
509
510    fn gen_address() -> Address {
511        Address::from((0..32).map(|_| random::<u8>()).collect::<Vec<_>>())
512    }
513
514    fn mock_node() -> Node {
515        Node::new(gen_address())
516    }
517
518    fn mock_status() -> Status {
519        Status {
520            height: random::<u64>(),
521            interval: None,
522            timer_config: None,
523            authority_list: vec![mock_node(), mock_node()],
524        }
525    }
526
527    #[test]
528    fn test_consensus_power() {
529        let status = mock_status();
530        let consensus_node = status.authority_list[0].address.clone();
531        let sync_node = gen_address();
532
533        assert!(status.is_consensus_node(&consensus_node));
534        assert!(!status.is_consensus_node(&sync_node));
535    }
536}