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}