hotstuff_rs/pacemaker/
messages.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
    Copyright © 2023, ParallelChain Lab
    Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
*/

//! Messages sent between replicas as part of the Pacemaker subprotocol.

use std::mem;

use borsh::{BorshDeserialize, BorshSerialize};

use crate::{
    hotstuff::types::PhaseCertificate,
    networking::{messages::ProgressMessage, receiving::Cacheable},
    types::{
        crypto_primitives::Keypair,
        data_types::*,
        signed_messages::{SignedMessage, Vote},
    },
};

use super::types::TimeoutCertificate;

/// Enum wrapper around any kind of message sent between replicas as part of the Pacemaker subprotocol.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub enum PacemakerMessage {
    /// See [`TimeoutVote`].
    TimeoutVote(TimeoutVote),

    /// See [`AdvanceView`].
    AdvanceView(AdvanceView),
}

impl PacemakerMessage {
    /// Create a [`PacemakerMessage::TimeoutVote`] that indicates that the validator identified by `keypair`
    /// which is currently replicating the `chain_id` blockchain would like to leave the specified
    /// Epoch-Change `view`.
    pub(crate) fn timeout_vote(
        keypair: &Keypair,
        chain_id: ChainID,
        view: ViewNumber,
        highest_tc: Option<TimeoutCertificate>,
    ) -> PacemakerMessage {
        let message = &(chain_id, view).try_to_vec().unwrap();
        let signature = keypair.sign(message);

        PacemakerMessage::TimeoutVote(TimeoutVote {
            chain_id,
            view,
            signature,
            highest_tc,
        })
    }

    /// Create a [`PacemakerMessage::AdvanceView`] which that to get other replicas to leave
    /// [`progress_certificate.view()`](ProgressCertificate::view) and enter
    /// `progress_certificate.view() + 1`.
    pub fn advance_view(progress_certificate: ProgressCertificate) -> PacemakerMessage {
        PacemakerMessage::AdvanceView(AdvanceView {
            progress_certificate,
        })
    }

    /// Get the `ChainID` in the inner message.
    pub fn chain_id(&self) -> ChainID {
        match self {
            PacemakerMessage::TimeoutVote(TimeoutVote { chain_id, .. }) => *chain_id,
            PacemakerMessage::AdvanceView(AdvanceView {
                progress_certificate,
            }) => progress_certificate.chain_id(),
        }
    }

    /// Get the `ViewNumber` in the inner message.
    pub fn view(&self) -> ViewNumber {
        match self {
            PacemakerMessage::TimeoutVote(TimeoutVote { view, .. }) => *view,
            PacemakerMessage::AdvanceView(AdvanceView {
                progress_certificate,
            }) => progress_certificate.view(),
        }
    }

    /// Get the size (in bytes) of the in-memory representation of the inner message.
    pub fn size(&self) -> u64 {
        match self {
            PacemakerMessage::TimeoutVote(_) => mem::size_of::<TimeoutVote>() as u64,
            PacemakerMessage::AdvanceView(_) => mem::size_of::<AdvanceView>() as u64,
        }
    }
}

impl Cacheable for PacemakerMessage {
    fn size(&self) -> u64 {
        self.size()
    }

    fn view(&self) -> ViewNumber {
        self.view()
    }
}

impl Into<ProgressMessage> for PacemakerMessage {
    fn into(self) -> ProgressMessage {
        ProgressMessage::PacemakerMessage(self)
    }
}

/// Vote in favor of leaving a specified Epoch-Change `view` and moving on to the next view.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub struct TimeoutVote {
    /// `ChainID` of the block tree that the replica's validator set extends.
    pub chain_id: ChainID,

    /// The view that this timeout vote seeks to terminate.
    pub view: ViewNumber,

    /// Signature over `chain_id` and `view` made using the sending replica's keypair.
    pub signature: SignatureBytes,

    /// The current highest timeout certificate of the sending replica, i.e., the the one with the highest
    /// [`view`](TimeoutCertificate::view).
    pub highest_tc: Option<TimeoutCertificate>,
}

impl SignedMessage for TimeoutVote {
    fn message_bytes(&self) -> Vec<u8> {
        (self.chain_id, self.view).try_to_vec().unwrap()
    }

    fn signature_bytes(&self) -> SignatureBytes {
        self.signature
    }
}

impl Vote for TimeoutVote {
    fn chain_id(&self) -> ChainID {
        self.chain_id
    }

    fn view(&self) -> ViewNumber {
        self.view
    }
}

/// Message containing cryptographic proof that a replica can safely advance to higher view.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub struct AdvanceView {
    pub progress_certificate: ProgressCertificate,
}

/// Enum wrapper around every kind of [`Certificate`](crate::types::signed_messages::Certificate) that
/// can cause a replica to move to another view.
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub enum ProgressCertificate {
    TimeoutCertificate(TimeoutCertificate),
    PhaseCertificate(PhaseCertificate),
}

impl ProgressCertificate {
    pub fn chain_id(&self) -> ChainID {
        match self {
            ProgressCertificate::TimeoutCertificate(TimeoutCertificate { chain_id, .. }) => {
                *chain_id
            }
            ProgressCertificate::PhaseCertificate(PhaseCertificate { chain_id, .. }) => *chain_id,
        }
    }

    pub fn view(&self) -> ViewNumber {
        match self {
            ProgressCertificate::TimeoutCertificate(TimeoutCertificate { view, .. }) => *view,
            ProgressCertificate::PhaseCertificate(PhaseCertificate { view, .. }) => *view,
        }
    }
}

impl From<PhaseCertificate> for ProgressCertificate {
    fn from(value: PhaseCertificate) -> Self {
        ProgressCertificate::PhaseCertificate(value)
    }
}

impl From<TimeoutCertificate> for ProgressCertificate {
    fn from(value: TimeoutCertificate) -> Self {
        ProgressCertificate::TimeoutCertificate(value)
    }
}