Skip to main content

hotshot_types/
message.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7//! Network message types
8//!
9//! This module contains types used to represent the various types of messages that
10//! `HotShot` nodes can send among themselves.
11
12use std::{
13    fmt::{self, Debug},
14    marker::PhantomData,
15    sync::Arc,
16};
17
18use committable::Committable;
19use hotshot_utils::anytrace::*;
20use parking_lot::RwLock;
21use serde::{Deserialize, Serialize, de::DeserializeOwned};
22use vbs::version::Version;
23use versions::{
24    DRB_AND_HEADER_UPGRADE_VERSION, EPOCH_VERSION, NEW_PROTOCOL_VERSION, TIMEOUT_EPOCH_VERSION,
25    Upgrade,
26};
27
28/// The version we should expect for external messages
29pub const EXTERNAL_MESSAGE_VERSION: Version = Version { major: 0, minor: 0 };
30
31use crate::{
32    data::{
33        DaProposal, DaProposal2, EpochNumber, Leaf2, QuorumProposal, QuorumProposal2,
34        QuorumProposal2Legacy, QuorumProposalWrapper, UpgradeProposal, VidDisperseShare0,
35        VidDisperseShare1, VidDisperseShare2, ViewNumber,
36    },
37    epoch_membership::{EpochMembership, EpochMembershipCoordinator},
38    request_response::ProposalRequestPayload,
39    simple_certificate::{
40        DaCertificate, DaCertificate2, EpochRootQuorumCertificateV1, EpochRootQuorumCertificateV2,
41        NextEpochQuorumCertificate2, QuorumCertificate2, UpgradeCertificate,
42        ViewSyncCommitCertificate, ViewSyncCommitCertificate2, ViewSyncFinalizeCertificate,
43        ViewSyncFinalizeCertificate2, ViewSyncPreCommitCertificate, ViewSyncPreCommitCertificate2,
44    },
45    simple_vote::{
46        DaVote, DaVote2, EpochRootQuorumVote, EpochRootQuorumVote2, HasEpoch, QuorumVote,
47        QuorumVote2, TimeoutVote, TimeoutVote2, UpgradeVote, ViewSyncCommitVote,
48        ViewSyncCommitVote2, ViewSyncFinalizeVote, ViewSyncFinalizeVote2, ViewSyncPreCommitVote,
49        ViewSyncPreCommitVote2,
50    },
51    traits::{
52        network::{DataRequest, ResponseMessage, ViewMessage},
53        node_implementation::NodeType,
54        signature_key::SignatureKey,
55    },
56    utils::mnemonic,
57    vote::HasViewNumber,
58};
59
60/// Incoming message
61#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
62#[serde(bound(deserialize = "", serialize = ""))]
63pub struct Message<TYPES: NodeType> {
64    /// The sender of this message
65    pub sender: TYPES::SignatureKey,
66
67    /// The message kind
68    pub kind: MessageKind<TYPES>,
69}
70
71impl<TYPES: NodeType> fmt::Debug for Message<TYPES> {
72    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
73        fmt.debug_struct("Message")
74            .field("sender", &mnemonic(&self.sender))
75            .field("kind", &self.kind)
76            .finish()
77    }
78}
79
80impl<TYPES: NodeType> HasViewNumber for Message<TYPES> {
81    /// get the view number out of a message
82    fn view_number(&self) -> ViewNumber {
83        self.kind.view_number()
84    }
85}
86
87/// A wrapper type for implementing `PassType` on a vector of `Message`.
88#[derive(Clone, Debug)]
89pub struct Messages<TYPES: NodeType>(pub Vec<Message<TYPES>>);
90
91/// A message type agnostic description of a message's purpose
92#[derive(PartialEq, Copy, Clone)]
93pub enum MessagePurpose {
94    /// Message with a [quorum/DA] proposal.
95    Proposal,
96    /// Message with most recent [quorum/DA] proposal the server has
97    LatestProposal,
98    /// Message with most recent view sync certificate the server has
99    LatestViewSyncCertificate,
100    /// Message with a quorum vote.
101    Vote,
102    /// Message with a view sync vote.
103    ViewSyncVote,
104    /// Message with a view sync certificate.
105    ViewSyncCertificate,
106    /// Message with a DAC.
107    DaCertificate,
108    /// Message for internal use
109    Internal,
110    /// Data message
111    Data,
112    /// VID disperse, like [`Proposal`].
113    VidDisperse,
114    /// Message with an upgrade proposal.
115    UpgradeProposal,
116    /// Upgrade vote.
117    UpgradeVote,
118    /// A message to be passed through to external listeners
119    External,
120}
121
122// TODO (da) make it more customized to the consensus layer, maybe separating the specific message
123// data from the kind enum.
124/// Enum representation of any message type
125#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
126#[serde(bound(deserialize = "", serialize = ""))]
127pub enum MessageKind<TYPES: NodeType> {
128    /// Messages related to the consensus protocol
129    Consensus(SequencingMessage<TYPES>),
130    /// Messages relating to sharing data between nodes
131    Data(DataMessage<TYPES>),
132    /// A (still serialized) message to be passed through to external listeners
133    External(#[serde(with = "serde_bytes")] Vec<u8>),
134}
135
136/// List of keys to send a message to, or broadcast to all known keys
137pub enum RecipientList<K: SignatureKey> {
138    /// Broadcast to all
139    Broadcast,
140    /// Send a message directly to a key
141    Direct(K),
142    /// Send a message directly to many keys
143    Many(Vec<K>),
144}
145
146impl<TYPES: NodeType> MessageKind<TYPES> {
147    // Can't implement `From<I::ConsensusMessage>` directly due to potential conflict with
148    // `From<DataMessage>`.
149    /// Construct a [`MessageKind`] from [`SequencingMessage`].
150    pub fn from_consensus_message(m: SequencingMessage<TYPES>) -> Self {
151        Self::Consensus(m)
152    }
153}
154
155impl<TYPES: NodeType> From<DataMessage<TYPES>> for MessageKind<TYPES> {
156    fn from(m: DataMessage<TYPES>) -> Self {
157        Self::Data(m)
158    }
159}
160
161impl<TYPES: NodeType> ViewMessage<TYPES> for MessageKind<TYPES> {
162    fn view_number(&self) -> ViewNumber {
163        match &self {
164            MessageKind::Consensus(message) => message.view_number(),
165            MessageKind::Data(DataMessage::SubmitTransaction(_, v)) => *v,
166            MessageKind::Data(DataMessage::RequestData(msg)) => msg.view,
167            MessageKind::Data(DataMessage::DataResponse(msg)) => match msg {
168                ResponseMessage::Found(m) => m.view_number(),
169                ResponseMessage::NotFound | ResponseMessage::Denied => ViewNumber::new(1),
170            },
171            MessageKind::External(_) => ViewNumber::new(1),
172        }
173    }
174}
175
176impl<TYPES: NodeType> HasEpoch for MessageKind<TYPES> {
177    fn epoch(&self) -> Option<EpochNumber> {
178        match &self {
179            MessageKind::Consensus(message) => message.epoch_number(),
180            MessageKind::Data(DataMessage::SubmitTransaction(..) | DataMessage::RequestData(_))
181            | MessageKind::External(_) => None,
182            MessageKind::Data(DataMessage::DataResponse(msg)) => match msg {
183                ResponseMessage::Found(m) => m.epoch_number(),
184                ResponseMessage::NotFound | ResponseMessage::Denied => None,
185            },
186        }
187    }
188}
189
190#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
191#[serde(bound(deserialize = "", serialize = ""))]
192/// Messages related to both validating and sequencing consensus.
193pub enum GeneralConsensusMessage<TYPES: NodeType> {
194    /// Message with a quorum proposal.
195    Proposal(Proposal<TYPES, QuorumProposal<TYPES>>),
196
197    /// Message with a quorum vote.
198    Vote(QuorumVote<TYPES>),
199
200    /// Message with a view sync pre-commit vote
201    ViewSyncPreCommitVote(ViewSyncPreCommitVote<TYPES>),
202
203    /// Message with a view sync commit vote
204    ViewSyncCommitVote(ViewSyncCommitVote<TYPES>),
205
206    /// Message with a view sync finalize vote
207    ViewSyncFinalizeVote(ViewSyncFinalizeVote<TYPES>),
208
209    /// Message with a view sync pre-commit certificate
210    ViewSyncPreCommitCertificate(ViewSyncPreCommitCertificate<TYPES>),
211
212    /// Message with a view sync commit certificate
213    ViewSyncCommitCertificate(ViewSyncCommitCertificate<TYPES>),
214
215    /// Message with a view sync finalize certificate
216    ViewSyncFinalizeCertificate(ViewSyncFinalizeCertificate<TYPES>),
217
218    /// Message with a Timeout vote
219    TimeoutVote(TimeoutVote<TYPES>),
220
221    /// Message with an upgrade proposal
222    UpgradeProposal(Proposal<TYPES, UpgradeProposal>),
223
224    /// Message with an upgrade vote
225    UpgradeVote(UpgradeVote<TYPES>),
226
227    /// A peer node needs a proposal from the leader.
228    ProposalRequested(
229        ProposalRequestPayload<TYPES>,
230        <TYPES::SignatureKey as SignatureKey>::PureAssembledSignatureType,
231    ),
232
233    /// A replica has responded with a valid proposal.
234    ProposalResponse(Proposal<TYPES, QuorumProposal<TYPES>>),
235
236    /// Message with a quorum proposal.
237    Proposal2Legacy(Proposal<TYPES, QuorumProposal2Legacy<TYPES>>),
238
239    /// Message with a quorum vote.
240    Vote2(QuorumVote2<TYPES>),
241
242    /// Message with an epoch root quorum vote.
243    EpochRootQuorumVote(EpochRootQuorumVote<TYPES>),
244
245    /// A replica has responded with a valid proposal.
246    ProposalResponse2Legacy(Proposal<TYPES, QuorumProposal2Legacy<TYPES>>),
247
248    /// Message for the next leader containing our highest QC
249    HighQc(
250        QuorumCertificate2<TYPES>,
251        Option<NextEpochQuorumCertificate2<TYPES>>,
252    ),
253
254    /// Message containing the highest QC and the next epoch QC
255    ExtendedQc(
256        QuorumCertificate2<TYPES>,
257        NextEpochQuorumCertificate2<TYPES>,
258    ),
259
260    /// Message for the next leader containing the epoch root QC from older consensus version.
261    EpochRootQcV1(EpochRootQuorumCertificateV1<TYPES>),
262
263    /// Message with a view sync pre-commit vote
264    ViewSyncPreCommitVote2(ViewSyncPreCommitVote2<TYPES>),
265
266    /// Message with a view sync commit vote
267    ViewSyncCommitVote2(ViewSyncCommitVote2<TYPES>),
268
269    /// Message with a view sync finalize vote
270    ViewSyncFinalizeVote2(ViewSyncFinalizeVote2<TYPES>),
271
272    /// Message with a view sync pre-commit certificate
273    ViewSyncPreCommitCertificate2(ViewSyncPreCommitCertificate2<TYPES>),
274
275    /// Message with a view sync commit certificate
276    ViewSyncCommitCertificate2(ViewSyncCommitCertificate2<TYPES>),
277
278    /// Message with a view sync finalize certificate
279    ViewSyncFinalizeCertificate2(ViewSyncFinalizeCertificate2<TYPES>),
280
281    /// Message with a Timeout vote
282    TimeoutVote2(TimeoutVote2<TYPES>),
283
284    /// Message for the next leader containing the epoch root QC
285    EpochRootQc(EpochRootQuorumCertificateV2<TYPES>),
286
287    /// Message with a quorum proposal.
288    Proposal2(Proposal<TYPES, QuorumProposal2<TYPES>>),
289
290    /// A replica has responded with a valid proposal.
291    ProposalResponse2(Proposal<TYPES, QuorumProposal2<TYPES>>),
292
293    /// Message with an epoch root quorum vote.
294    EpochRootQuorumVote2(EpochRootQuorumVote2<TYPES>),
295}
296
297#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Hash, Eq)]
298#[serde(bound(deserialize = "", serialize = ""))]
299/// Messages related to the sequencing consensus protocol for the DA committee.
300pub enum DaConsensusMessage<TYPES: NodeType> {
301    /// Proposal for data availability committee
302    DaProposal(Proposal<TYPES, DaProposal<TYPES>>),
303
304    /// vote for data availability committee
305    DaVote(DaVote<TYPES>),
306
307    /// Certificate data is available
308    DaCertificate(DaCertificate<TYPES>),
309
310    /// Initiate VID dispersal.
311    ///
312    /// Like [`DaProposal`]. Use `Msg` suffix to distinguish from `VidDisperse`.
313    VidDisperseMsg(Proposal<TYPES, VidDisperseShare0<TYPES>>),
314
315    /// Proposal for data availability committee
316    DaProposal2(Proposal<TYPES, DaProposal2<TYPES>>),
317
318    /// vote for data availability committee
319    DaVote2(DaVote2<TYPES>),
320
321    /// Certificate data is available
322    DaCertificate2(DaCertificate2<TYPES>),
323
324    /// VID dispersal for AvidM Scheme.
325    VidDisperseMsg1(Proposal<TYPES, VidDisperseShare1<TYPES>>),
326
327    /// VID dispersal for AvidmGf2 Scheme.
328    VidDisperseMsg2(Proposal<TYPES, VidDisperseShare2<TYPES>>),
329}
330
331/// Messages for sequencing consensus.
332#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
333#[serde(bound(deserialize = "", serialize = ""))]
334#[allow(clippy::large_enum_variant)]
335pub enum SequencingMessage<TYPES: NodeType> {
336    /// Messages related to validating and sequencing consensus
337    General(GeneralConsensusMessage<TYPES>),
338
339    /// Messages related to the sequencing consensus protocol for the DA committee.
340    Da(DaConsensusMessage<TYPES>),
341}
342
343impl<TYPES: NodeType> SequencingMessage<TYPES> {
344    /// Get the view number this message relates to
345    fn view_number(&self) -> ViewNumber {
346        match &self {
347            SequencingMessage::General(general_message) => {
348                match general_message {
349                    GeneralConsensusMessage::Proposal(p) => {
350                        // view of leader in the leaf when proposal
351                        // this should match replica upon receipt
352                        p.data.view_number()
353                    },
354                    GeneralConsensusMessage::Proposal2Legacy(p) => {
355                        // view of leader in the leaf when proposal
356                        // this should match replica upon receipt
357                        p.data.view_number()
358                    },
359                    GeneralConsensusMessage::Proposal2(p) => {
360                        // view of leader in the leaf when proposal
361                        // this should match replica upon receipt
362                        p.data.view_number()
363                    },
364                    GeneralConsensusMessage::ProposalRequested(req, _) => req.view_number,
365                    GeneralConsensusMessage::ProposalResponse(proposal) => {
366                        proposal.data.view_number()
367                    },
368                    GeneralConsensusMessage::ProposalResponse2Legacy(proposal) => {
369                        proposal.data.view_number()
370                    },
371                    GeneralConsensusMessage::ProposalResponse2(proposal) => {
372                        proposal.data.view_number()
373                    },
374                    GeneralConsensusMessage::Vote(vote_message) => vote_message.view_number(),
375                    GeneralConsensusMessage::Vote2(vote_message) => vote_message.view_number(),
376                    GeneralConsensusMessage::TimeoutVote(message) => message.view_number(),
377                    GeneralConsensusMessage::ViewSyncPreCommitVote(message) => {
378                        message.view_number()
379                    },
380                    GeneralConsensusMessage::ViewSyncCommitVote(message) => message.view_number(),
381                    GeneralConsensusMessage::ViewSyncFinalizeVote(message) => message.view_number(),
382                    GeneralConsensusMessage::ViewSyncPreCommitCertificate(message) => {
383                        message.view_number()
384                    },
385                    GeneralConsensusMessage::ViewSyncCommitCertificate(message) => {
386                        message.view_number()
387                    },
388                    GeneralConsensusMessage::ViewSyncFinalizeCertificate(message) => {
389                        message.view_number()
390                    },
391                    GeneralConsensusMessage::TimeoutVote2(message) => message.view_number(),
392                    GeneralConsensusMessage::ViewSyncPreCommitVote2(message) => {
393                        message.view_number()
394                    },
395                    GeneralConsensusMessage::ViewSyncCommitVote2(message) => message.view_number(),
396                    GeneralConsensusMessage::ViewSyncFinalizeVote2(message) => {
397                        message.view_number()
398                    },
399                    GeneralConsensusMessage::ViewSyncPreCommitCertificate2(message) => {
400                        message.view_number()
401                    },
402                    GeneralConsensusMessage::ViewSyncCommitCertificate2(message) => {
403                        message.view_number()
404                    },
405                    GeneralConsensusMessage::ViewSyncFinalizeCertificate2(message) => {
406                        message.view_number()
407                    },
408                    GeneralConsensusMessage::UpgradeProposal(message) => message.data.view_number(),
409                    GeneralConsensusMessage::UpgradeVote(message) => message.view_number(),
410                    GeneralConsensusMessage::HighQc(qc, _)
411                    | GeneralConsensusMessage::ExtendedQc(qc, _) => qc.view_number(),
412                    GeneralConsensusMessage::EpochRootQuorumVote(vote) => vote.view_number(),
413                    GeneralConsensusMessage::EpochRootQuorumVote2(vote) => vote.view_number(),
414                    GeneralConsensusMessage::EpochRootQc(root_qc) => root_qc.view_number(),
415                    GeneralConsensusMessage::EpochRootQcV1(root_qc) => root_qc.view_number(),
416                }
417            },
418            SequencingMessage::Da(da_message) => {
419                match da_message {
420                    DaConsensusMessage::DaProposal(p) => {
421                        // view of leader in the leaf when proposal
422                        // this should match replica upon receipt
423                        p.data.view_number()
424                    },
425                    DaConsensusMessage::DaVote(vote_message) => vote_message.view_number(),
426                    DaConsensusMessage::DaCertificate(cert) => cert.view_number,
427                    DaConsensusMessage::VidDisperseMsg(disperse) => disperse.data.view_number(),
428                    DaConsensusMessage::DaProposal2(p) => {
429                        // view of leader in the leaf when proposal
430                        // this should match replica upon receipt
431                        p.data.view_number()
432                    },
433                    DaConsensusMessage::DaVote2(vote_message) => vote_message.view_number(),
434                    DaConsensusMessage::DaCertificate2(cert) => cert.view_number,
435                    DaConsensusMessage::VidDisperseMsg1(disperse) => disperse.data.view_number(),
436                    DaConsensusMessage::VidDisperseMsg2(disperse) => disperse.data.view_number(),
437                }
438            },
439        }
440    }
441
442    /// Get the epoch number this message relates to, if applicable
443    fn epoch_number(&self) -> Option<EpochNumber> {
444        match &self {
445            SequencingMessage::General(general_message) => {
446                match general_message {
447                    GeneralConsensusMessage::Proposal(p) => {
448                        // view of leader in the leaf when proposal
449                        // this should match replica upon receipt
450                        p.data.epoch()
451                    },
452                    GeneralConsensusMessage::Proposal2Legacy(p) => {
453                        // view of leader in the leaf when proposal
454                        // this should match replica upon receipt
455                        p.data.epoch()
456                    },
457                    GeneralConsensusMessage::Proposal2(p) => {
458                        // view of leader in the leaf when proposal
459                        // this should match replica upon receipt
460                        p.data.epoch()
461                    },
462                    GeneralConsensusMessage::ProposalRequested(..) => None,
463                    GeneralConsensusMessage::ProposalResponse(proposal) => proposal.data.epoch(),
464                    GeneralConsensusMessage::ProposalResponse2Legacy(proposal) => {
465                        proposal.data.epoch()
466                    },
467                    GeneralConsensusMessage::ProposalResponse2(proposal) => proposal.data.epoch(),
468                    GeneralConsensusMessage::Vote(vote_message) => vote_message.epoch(),
469                    GeneralConsensusMessage::Vote2(vote_message) => vote_message.epoch(),
470                    GeneralConsensusMessage::TimeoutVote(message) => message.epoch(),
471                    GeneralConsensusMessage::ViewSyncPreCommitVote(message) => message.epoch(),
472                    GeneralConsensusMessage::ViewSyncCommitVote(message) => message.epoch(),
473                    GeneralConsensusMessage::ViewSyncFinalizeVote(message) => message.epoch(),
474                    GeneralConsensusMessage::ViewSyncPreCommitCertificate(message) => {
475                        message.epoch()
476                    },
477                    GeneralConsensusMessage::ViewSyncCommitCertificate(message) => message.epoch(),
478                    GeneralConsensusMessage::ViewSyncFinalizeCertificate(message) => {
479                        message.epoch()
480                    },
481                    GeneralConsensusMessage::TimeoutVote2(message) => message.epoch(),
482                    GeneralConsensusMessage::ViewSyncPreCommitVote2(message) => message.epoch(),
483                    GeneralConsensusMessage::ViewSyncCommitVote2(message) => message.epoch(),
484                    GeneralConsensusMessage::ViewSyncFinalizeVote2(message) => message.epoch(),
485                    GeneralConsensusMessage::ViewSyncPreCommitCertificate2(message) => {
486                        message.epoch()
487                    },
488                    GeneralConsensusMessage::ViewSyncCommitCertificate2(message) => message.epoch(),
489                    GeneralConsensusMessage::ViewSyncFinalizeCertificate2(message) => {
490                        message.epoch()
491                    },
492                    GeneralConsensusMessage::UpgradeProposal(message) => message.data.epoch(),
493                    GeneralConsensusMessage::UpgradeVote(message) => message.epoch(),
494                    GeneralConsensusMessage::HighQc(qc, _)
495                    | GeneralConsensusMessage::ExtendedQc(qc, _) => qc.epoch(),
496                    GeneralConsensusMessage::EpochRootQuorumVote(vote) => vote.epoch(),
497                    GeneralConsensusMessage::EpochRootQuorumVote2(vote) => vote.epoch(),
498                    GeneralConsensusMessage::EpochRootQc(root_qc) => root_qc.epoch(),
499                    GeneralConsensusMessage::EpochRootQcV1(root_qc) => root_qc.epoch(),
500                }
501            },
502            SequencingMessage::Da(da_message) => {
503                match da_message {
504                    DaConsensusMessage::DaProposal(p) => {
505                        // view of leader in the leaf when proposal
506                        // this should match replica upon receipt
507                        p.data.epoch()
508                    },
509                    DaConsensusMessage::DaVote(vote_message) => vote_message.epoch(),
510                    DaConsensusMessage::DaCertificate(cert) => cert.epoch(),
511                    DaConsensusMessage::VidDisperseMsg(disperse) => disperse.data.epoch(),
512                    DaConsensusMessage::VidDisperseMsg1(disperse) => disperse.data.epoch(),
513                    DaConsensusMessage::VidDisperseMsg2(disperse) => disperse.data.epoch(),
514                    DaConsensusMessage::DaProposal2(p) => {
515                        // view of leader in the leaf when proposal
516                        // this should match replica upon receipt
517                        p.data.epoch()
518                    },
519                    DaConsensusMessage::DaVote2(vote_message) => vote_message.epoch(),
520                    DaConsensusMessage::DaCertificate2(cert) => cert.epoch(),
521                }
522            },
523        }
524    }
525}
526
527#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
528#[serde(bound(deserialize = ""))]
529#[allow(clippy::large_enum_variant)]
530/// TODO: Put `DataResponse` content in a `Box` to make enum smaller
531/// Messages related to sending data between nodes
532pub enum DataMessage<TYPES: NodeType> {
533    /// Contains a transaction to be submitted
534    /// TODO rethink this when we start to send these messages
535    /// we only need the view number for broadcast
536    SubmitTransaction(TYPES::Transaction, ViewNumber),
537    /// A request for data
538    RequestData(DataRequest<TYPES>),
539    /// A response to a data request
540    DataResponse(ResponseMessage<TYPES>),
541}
542
543#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
544#[serde(bound(deserialize = ""))]
545/// Prepare qc from the leader
546pub struct Proposal<TYPES: NodeType, PROPOSAL: HasViewNumber + HasEpoch + DeserializeOwned> {
547    // NOTE: optimization could include view number to help look up parent leaf
548    // could even do 16 bit numbers if we want
549    /// The data being proposed.
550    pub data: PROPOSAL,
551    /// The proposal must be signed by the view leader
552    pub signature: <TYPES::SignatureKey as SignatureKey>::PureAssembledSignatureType,
553    /// Phantom for TYPES
554    pub _pd: PhantomData<TYPES>,
555}
556
557impl<TYPES: NodeType, PROPOSAL: HasViewNumber + HasEpoch + DeserializeOwned>
558    Proposal<TYPES, PROPOSAL>
559{
560    pub fn new(
561        data: PROPOSAL,
562        signature: <TYPES::SignatureKey as SignatureKey>::PureAssembledSignatureType,
563    ) -> Self {
564        Self {
565            data,
566            signature,
567            _pd: PhantomData,
568        }
569    }
570}
571
572/// Convert a `Proposal` by converting the underlying proposal type
573pub fn convert_proposal<TYPES, PROPOSAL, PROPOSAL2>(
574    proposal: Proposal<TYPES, PROPOSAL>,
575) -> Proposal<TYPES, PROPOSAL2>
576where
577    TYPES: NodeType,
578    PROPOSAL: HasViewNumber + HasEpoch + DeserializeOwned,
579    PROPOSAL2: HasViewNumber + HasEpoch + DeserializeOwned + From<PROPOSAL>,
580{
581    Proposal {
582        data: proposal.data.into(),
583        signature: proposal.signature,
584        _pd: proposal._pd,
585    }
586}
587
588impl<TYPES> Proposal<TYPES, QuorumProposalWrapper<TYPES>>
589where
590    TYPES: NodeType,
591{
592    /// Checks that the signature of the quorum proposal is valid.
593    /// # Errors
594    /// Returns an error when the proposal signature is invalid.
595    pub fn validate_signature(&self, membership: &EpochMembership<TYPES>) -> Result<()> {
596        let view_number = self.data.proposal.view_number();
597        let view_leader_key = membership.leader(view_number)?;
598        let proposed_leaf = Leaf2::from_quorum_proposal(&self.data);
599
600        ensure!(
601            view_leader_key.validate(&self.signature, proposed_leaf.commit().as_ref()),
602            "Proposal signature is invalid."
603        );
604
605        Ok(())
606    }
607}
608
609impl<TYPES> Proposal<TYPES, QuorumProposal2<TYPES>>
610where
611    TYPES: NodeType,
612{
613    pub fn validate_signature(
614        &self,
615        membership_coordinator: &EpochMembershipCoordinator<TYPES>,
616    ) -> Result<()> {
617        let view_number = self.data.view_number();
618        let epoch = self.data.epoch().ok_or(error!("Epoch is not set"))?;
619        let membership = membership_coordinator.membership_for_epoch(Some(epoch))?;
620        let view_leader_key = membership.leader(view_number)?;
621        let proposed_leaf =
622            Leaf2::from_quorum_proposal(&QuorumProposalWrapper::from(self.data.clone()));
623
624        ensure!(
625            view_leader_key.validate(&self.signature, proposed_leaf.commit().as_ref()),
626            "Proposal signature is invalid."
627        );
628
629        Ok(())
630    }
631}
632
633/// A lock for an upgrade certificate decided by HotShot.
634#[derive(Clone, Debug)]
635pub struct UpgradeLock<TYPES: NodeType> {
636    decided_upgrade_certificate: Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
637    upgrade: Upgrade,
638}
639
640impl<TYPES: NodeType> UpgradeLock<TYPES> {
641    /// Create a new `UpgradeLock` for a fresh instance of HotShot
642    pub fn new(upgrade: Upgrade) -> Self {
643        Self {
644            decided_upgrade_certificate: Arc::new(RwLock::new(None)),
645            upgrade,
646        }
647    }
648
649    /// Create a new `UpgradeLock` from an optional upgrade certificate
650    pub fn from_certificate(
651        upgrade: Upgrade,
652        certificate: &Option<UpgradeCertificate<TYPES>>,
653    ) -> Self {
654        Self {
655            decided_upgrade_certificate: Arc::new(RwLock::new(certificate.clone())),
656            upgrade,
657        }
658    }
659
660    pub fn upgrade(&self) -> Upgrade {
661        self.upgrade
662    }
663
664    pub fn upgrade_view(&self) -> Option<ViewNumber> {
665        self.decided_upgrade_certificate
666            .read()
667            .as_ref()
668            .map(|cert| cert.data.new_version_first_view)
669    }
670
671    pub fn decided_upgrade_cert(&self) -> Option<UpgradeCertificate<TYPES>> {
672        self.decided_upgrade_certificate.read().clone()
673    }
674
675    pub fn set_decided_upgrade_cert<C>(&self, cert: C)
676    where
677        C: Into<Option<UpgradeCertificate<TYPES>>>,
678    {
679        *self.decided_upgrade_certificate.write() = cert.into()
680    }
681
682    /// Apply a function to the upgrade certificate.
683    pub fn apply<F>(&self, f: F)
684    where
685        F: FnOnce(&mut Option<UpgradeCertificate<TYPES>>),
686    {
687        let mut guard = self.decided_upgrade_certificate.write();
688        f(&mut *guard)
689    }
690
691    /// Calculate the version applied in a view, based on the provided upgrade lock.
692    ///
693    /// # Errors
694    /// Returns an error if we do not support the version required by the decided upgrade certificate.
695    pub fn version(&self, view: ViewNumber) -> Result<Version> {
696        if let Some(cert) = &*self.decided_upgrade_certificate.read() {
697            if view >= cert.data.new_version_first_view {
698                if cert.data.new_version == self.upgrade.target {
699                    Ok(self.upgrade.target)
700                } else {
701                    bail!("The network has upgraded to a new version that we do not support!");
702                }
703            } else {
704                Ok(self.upgrade.base)
705            }
706        } else {
707            Ok(self.upgrade.base)
708        }
709    }
710
711    /// Calculate the version applied in a view, based on the provided upgrade lock.
712    ///
713    /// This function does not fail, since it does not check that the version is supported.
714    pub fn version_infallible(&self, view: ViewNumber) -> Version {
715        if let Some(cert) = &*self.decided_upgrade_certificate.read() {
716            if view >= cert.data.new_version_first_view {
717                cert.data.new_version
718            } else {
719                cert.data.old_version
720            }
721        } else {
722            self.upgrade.base
723        }
724    }
725
726    /// Return whether epochs are enabled in the given view
727    pub fn epochs_enabled(&self, view: ViewNumber) -> bool {
728        self.version_infallible(view) >= EPOCH_VERSION
729    }
730
731    /// Return whether `QuorumProposal2Legacy` is the correct message type for the given view
732    pub fn proposal2_legacy_version(&self, view: ViewNumber) -> bool {
733        let version = self.version_infallible(view);
734        version >= EPOCH_VERSION && version < DRB_AND_HEADER_UPGRADE_VERSION
735    }
736
737    /// Return whether `QuorumProposal2` is the correct message type for the given view
738    pub fn proposal2_version(&self, view: ViewNumber) -> bool {
739        let version = self.version_infallible(view);
740        version >= EPOCH_VERSION && version >= DRB_AND_HEADER_UPGRADE_VERSION
741    }
742
743    /// Return whether epochs are enabled in the given view
744    pub fn upgraded_drb_and_header(&self, view: ViewNumber) -> bool {
745        self.version_infallible(view) >= DRB_AND_HEADER_UPGRADE_VERSION
746    }
747
748    pub fn upgraded_vid2(&self, view: ViewNumber) -> bool {
749        self.version_infallible(view) >= NEW_PROTOCOL_VERSION
750    }
751
752    /// Whether a timeout certificate must be a `TimeoutEvidence::V3`.
753    pub fn timeout_epoch_bound(&self, timed_out_view: ViewNumber) -> bool {
754        let v = ViewNumber::new(timed_out_view.u64().saturating_add(1));
755        self.version_infallible(v) >= TIMEOUT_EPOCH_VERSION
756    }
757
758    /// Whether the certificate rule governs votes at `view`.
759    ///
760    /// Under it a node does not vote2 at a view it has sent a timeout vote
761    /// for, and a vote1 for a proposal after a timeout checks the proposal's
762    /// parent against the lock its timeout certificate carries instead of
763    /// against the voter's own votes. The certificates that carry a lock are
764    /// the epoch-binding ones, so the rule starts with them:
765    /// `certificate_rule(v) == timeout_epoch_bound(v - 1)`.
766    pub fn certificate_rule(&self, view: ViewNumber) -> bool {
767        self.version_infallible(view) >= TIMEOUT_EPOCH_VERSION
768    }
769
770    /// Serialize a message with a version number, using `message.view_number()`
771    /// and an optional decided upgrade certificate to determine the message's
772    /// version.
773    ///
774    /// # Errors
775    ///
776    /// Errors if serialization fails.
777    pub fn serialize<M: HasViewNumber + Serialize>(&self, message: &M) -> Result<Vec<u8>> {
778        let view = message.view_number();
779
780        let version = self.version(view)?;
781
782        let serialized_message = if version == self.upgrade.base || version == self.upgrade.target {
783            versions::encode(version, message)
784        } else {
785            bail!(
786                "Attempted to serialize with version {version}, which is incompatible. This \
787                 should be impossible."
788            );
789        };
790
791        serialized_message
792            .wrap()
793            .context(info!("Failed to serialize message!"))
794    }
795
796    /// Deserialize a message with a version number, using `message.view_number()`
797    /// to determine the message's version. This function will fail on improperly
798    /// versioned messages.
799    ///
800    /// Returns both the deserialized message and the version of the message
801    ///
802    /// # Errors
803    ///
804    /// Errors if deserialization fails.
805    pub fn deserialize<M: Debug + HasViewNumber + DeserializeOwned>(
806        &self,
807        message: &[u8],
808    ) -> Result<(M, Version)> {
809        let (version, deserialized_message) = versions::decode::<M>(message)
810            .wrap()
811            .context(info!("Failed to read version and message!"))?;
812
813        if EXTERNAL_MESSAGE_VERSION != version
814            && self.upgrade.base != version
815            && self.upgrade.target != version
816        {
817            bail!(warn!(
818                "Received a message with state version {version} which is invalid for its view: \
819                 {:?}",
820                deserialized_message
821            ));
822        }
823
824        // If the message is version 0.0, just return the deserialized message and the version.
825        // We don't care about it matching the expected version for the view number.
826        if version == EXTERNAL_MESSAGE_VERSION {
827            return Ok((deserialized_message, version));
828        }
829
830        // Get the view number associated with the message
831        let view = deserialized_message.view_number();
832
833        // Get the expected version for the message based on the view number
834        let expected_version = self.version(view)?;
835
836        // Check that the actual version matches the expected version
837        if version != expected_version {
838            return Err(error!(format!(
839                "Message has invalid version number for its view. Expected: {expected_version}, \
840                 Actual: {version}, View: {view:?}\n\n{deserialized_message:?}"
841            )));
842        };
843
844        Ok((deserialized_message, version))
845    }
846}