Skip to main content

hotshot_types/
event.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//! Events that a `HotShot` instance can emit
8
9use std::sync::Arc;
10
11use serde::{Deserialize, Serialize};
12
13use crate::{
14    data::{
15        DaProposal2, Leaf2, QuorumProposalWrapper, UpgradeProposal, VidDisperseShare, ViewNumber,
16    },
17    error::HotShotError,
18    message::Proposal,
19    simple_certificate::{CertificatePair, LightClientStateUpdateCertificateV2},
20    traits::{ValidatedState, node_implementation::NodeType},
21    vote::HasViewNumber,
22};
23
24/// A status event emitted by a `HotShot` instance
25///
26/// This includes some metadata, such as the stage and view number that the event was generated in,
27/// as well as an inner [`EventType`] describing the event proper.
28#[derive(Clone, Debug, Serialize, Deserialize)]
29#[serde(bound(deserialize = "TYPES: NodeType"))]
30pub struct Event<TYPES: NodeType> {
31    /// The view number that this event originates from
32    pub view_number: ViewNumber,
33    /// The underlying event
34    pub event: EventType<TYPES>,
35}
36
37/// Decided leaf with the corresponding state and VID info.
38#[derive(Clone, Debug, Serialize, Deserialize)]
39#[serde(bound(deserialize = "TYPES: NodeType"))]
40pub struct LeafInfo<TYPES: NodeType> {
41    /// Decided leaf.
42    pub leaf: Leaf2<TYPES>,
43    /// Validated state.
44    pub state: Arc<<TYPES as NodeType>::ValidatedState>,
45    /// Optional application-specific state delta.
46    pub delta: Option<Arc<<<TYPES as NodeType>::ValidatedState as ValidatedState<TYPES>>::Delta>>,
47    /// Optional VID share data.
48    pub vid_share: Option<VidDisperseShare<TYPES>>,
49    /// Optional light client state update certificate.
50    pub state_cert: Option<LightClientStateUpdateCertificateV2<TYPES>>,
51}
52
53impl<TYPES: NodeType> LeafInfo<TYPES> {
54    /// Constructor.
55    pub fn new(
56        leaf: Leaf2<TYPES>,
57        state: Arc<<TYPES as NodeType>::ValidatedState>,
58        delta: Option<Arc<<<TYPES as NodeType>::ValidatedState as ValidatedState<TYPES>>::Delta>>,
59        vid_share: Option<VidDisperseShare<TYPES>>,
60        state_cert: Option<LightClientStateUpdateCertificateV2<TYPES>>,
61    ) -> Self {
62        Self {
63            leaf,
64            state,
65            delta,
66            vid_share,
67            state_cert,
68        }
69    }
70}
71
72/// The chain of decided leaves with its corresponding state and VID info.
73pub type LeafChain<TYPES> = Vec<LeafInfo<TYPES>>;
74
75/// Utilities for converting between HotShotError and a string.
76pub mod error_adaptor {
77    use serde::{de::Deserializer, ser::Serializer};
78
79    use super::{Arc, Deserialize, HotShotError, NodeType};
80
81    /// Convert a HotShotError into a string
82    ///
83    /// # Errors
84    /// Returns `Err` if the serializer fails.
85    pub fn serialize<S: Serializer, TYPES: NodeType>(
86        elem: &Arc<HotShotError<TYPES>>,
87        serializer: S,
88    ) -> Result<S::Ok, S::Error> {
89        serializer.serialize_str(&format!("{elem}"))
90    }
91
92    /// Convert a string into a HotShotError
93    ///
94    /// # Errors
95    /// Returns `Err` if the string cannot be deserialized.
96    pub fn deserialize<'de, D: Deserializer<'de>, TYPES: NodeType>(
97        deserializer: D,
98    ) -> Result<Arc<HotShotError<TYPES>>, D::Error> {
99        let str = String::deserialize(deserializer)?;
100        Ok(Arc::new(HotShotError::FailedToDeserialize(str)))
101    }
102}
103
104/// The type and contents of a status event emitted by a `HotShot` instance
105///
106/// This enum does not include metadata shared among all variants, such as the stage and view
107/// number, and is thus always returned wrapped in an [`Event`].
108#[non_exhaustive]
109#[derive(Clone, Debug, Serialize, Deserialize)]
110#[serde(bound(deserialize = "TYPES: NodeType"))]
111#[allow(clippy::large_enum_variant)]
112pub enum EventType<TYPES: NodeType> {
113    /// A view encountered an error and was interrupted
114    Error {
115        /// The underlying error
116        #[serde(with = "error_adaptor")]
117        error: Arc<HotShotError<TYPES>>,
118    },
119    /// A new decision event was issued
120    Decide {
121        /// The chain of Leaves that were committed by this decision
122        ///
123        /// This list is sorted in reverse view number order, with the newest (highest view number)
124        /// block first in the list.
125        ///
126        /// This list may be incomplete if the node is currently performing catchup.
127        /// Vid Info for a decided view may be missing if this node never saw it's share.
128        leaf_chain: Arc<LeafChain<TYPES>>,
129        /// The QC signing the most recent leaf in `leaf_chain`.
130        ///
131        /// Note that the QC for each additional leaf in the chain can be obtained from the leaf
132        /// before it using
133        committing_qc: Arc<CertificatePair<TYPES>>,
134        /// A QC signing the leaf corresponding to `qc`.
135        ///
136        /// Together with `qc`, this forms a 2-chain, which is sufficient for a light client to
137        /// verify that the leaf chain contained in this event is in fact decided.
138        deciding_qc: Option<Arc<CertificatePair<TYPES>>>,
139        /// Optional information of the number of transactions in the block, for logging purposes.
140        block_size: Option<u64>,
141    },
142    /// A replica task was canceled by a timeout interrupt
143    ReplicaViewTimeout {
144        /// The view that timed out
145        view_number: ViewNumber,
146    },
147    /// The view has finished.  If values were decided on, a `Decide` event will also be emitted.
148    ViewFinished {
149        /// The view number that has just finished
150        view_number: ViewNumber,
151    },
152    /// The view timed out
153    ViewTimeout {
154        /// The view that timed out
155        view_number: ViewNumber,
156    },
157    /// New transactions were received from the network
158    /// or submitted to the network by us
159    Transactions {
160        /// The list of transactions
161        transactions: Vec<TYPES::Transaction>,
162    },
163    /// DA proposal was received from the network
164    /// or submitted to the network by us
165    DaProposal {
166        /// Contents of the proposal
167        proposal: Proposal<TYPES, DaProposal2<TYPES>>,
168        /// Public key of the leader submitting the proposal
169        sender: TYPES::SignatureKey,
170    },
171    /// Quorum proposal was received from the network
172    /// or submitted to the network by us
173    QuorumProposal {
174        /// Contents of the proposal
175        proposal: Proposal<TYPES, QuorumProposalWrapper<TYPES>>,
176        /// Public key of the leader submitting the proposal
177        sender: TYPES::SignatureKey,
178    },
179    /// Upgrade proposal was received from the network
180    /// or submitted to the network by us
181    UpgradeProposal {
182        /// Contents of the proposal
183        proposal: Proposal<TYPES, UpgradeProposal>,
184        /// Public key of the leader submitting the proposal
185        sender: TYPES::SignatureKey,
186    },
187
188    /// A message destined for external listeners was received
189    ExternalMessageReceived {
190        /// Public Key of the message sender
191        sender: TYPES::SignatureKey,
192        /// Serialized data of the message
193        data: Vec<u8>,
194    },
195}
196
197impl<TYPES: NodeType> std::fmt::Display for Event<TYPES> {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        write!(f, "{} (view {})", self.event, self.view_number)
200    }
201}
202
203impl<TYPES: NodeType> std::fmt::Display for EventType<TYPES> {
204    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205        match self {
206            Self::Error { error } => write!(f, "Error: {error}"),
207            Self::Decide { leaf_chain, .. } => {
208                let newest = leaf_chain.first().map(|l| l.leaf.view_number());
209                let oldest = leaf_chain.last().map(|l| l.leaf.view_number());
210                write!(
211                    f,
212                    "Decide: leaves={} oldest={:?} newest={:?}",
213                    leaf_chain.len(),
214                    oldest,
215                    newest,
216                )
217            },
218            Self::ReplicaViewTimeout { view_number } => {
219                write!(f, "ReplicaViewTimeout: view={view_number}")
220            },
221            Self::ViewFinished { view_number } => {
222                write!(f, "ViewFinished: view={view_number}")
223            },
224            Self::ViewTimeout { view_number } => {
225                write!(f, "ViewTimeout: view={view_number}")
226            },
227            Self::Transactions { transactions } => {
228                write!(f, "Transactions: count={}", transactions.len())
229            },
230            Self::DaProposal { proposal, .. } => {
231                write!(f, "DaProposal: view={}", proposal.data.view_number())
232            },
233            Self::QuorumProposal { proposal, .. } => {
234                write!(f, "QuorumProposal: view={}", proposal.data.view_number())
235            },
236            Self::UpgradeProposal { proposal, .. } => {
237                write!(
238                    f,
239                    "UpgradeProposal: view={} old_version={} new_version={}",
240                    proposal.data.view_number,
241                    proposal.data.upgrade_proposal.old_version,
242                    proposal.data.upgrade_proposal.new_version,
243                )
244            },
245            Self::ExternalMessageReceived { .. } => {
246                write!(f, "ExternalMessageReceived")
247            },
248        }
249    }
250}
251
252#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
253/// A list of actions that we track for nodes
254pub enum HotShotAction {
255    /// A quorum vote was sent
256    Vote,
257    /// A timeout vote was sent
258    TimeoutVote,
259    /// View Sync Vote
260    ViewSyncVote,
261    /// A quorum proposal was sent
262    Propose,
263    /// DA proposal was sent
264    DaPropose,
265    /// DA vote was sent
266    DaVote,
267    /// DA certificate was sent
268    DaCert,
269    /// VID shares were sent
270    VidDisperse,
271    /// An upgrade vote was sent
272    UpgradeVote,
273    /// An upgrade proposal was sent
274    UpgradePropose,
275}