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 hotshot_utils::anytrace::*;
12use serde::{Deserialize, Serialize};
13
14use crate::{
15 data::{
16 vid_disperse::ADVZDisperseShare, DaProposal, DaProposal2, Leaf, Leaf2, QuorumProposal,
17 QuorumProposalWrapper, UpgradeProposal, VidDisperseShare,
18 },
19 error::HotShotError,
20 message::{convert_proposal, Proposal},
21 simple_certificate::{
22 LightClientStateUpdateCertificate, QuorumCertificate, QuorumCertificate2,
23 },
24 traits::{node_implementation::NodeType, ValidatedState},
25};
26
27/// A status event emitted by a `HotShot` instance
28///
29/// This includes some metadata, such as the stage and view number that the event was generated in,
30/// as well as an inner [`EventType`] describing the event proper.
31#[derive(Clone, Debug, Serialize, Deserialize)]
32#[serde(bound(deserialize = "TYPES: NodeType"))]
33pub struct Event<TYPES: NodeType> {
34 /// The view number that this event originates from
35 pub view_number: TYPES::View,
36 /// The underlying event
37 pub event: EventType<TYPES>,
38}
39
40impl<TYPES: NodeType> Event<TYPES> {
41 pub fn to_legacy(self) -> anyhow::Result<LegacyEvent<TYPES>> {
42 Ok(LegacyEvent {
43 view_number: self.view_number,
44 event: self.event.to_legacy()?,
45 })
46 }
47}
48
49/// The pre-epoch version of an Event
50#[derive(Clone, Debug, Serialize, Deserialize)]
51#[serde(bound(deserialize = "TYPES: NodeType"))]
52pub struct LegacyEvent<TYPES: NodeType> {
53 /// The view number that this event originates from
54 pub view_number: TYPES::View,
55 /// The underlying event
56 pub event: LegacyEventType<TYPES>,
57}
58
59/// Decided leaf with the corresponding state and VID info.
60#[derive(Clone, Debug, Serialize, Deserialize)]
61#[serde(bound(deserialize = "TYPES: NodeType"))]
62pub struct LeafInfo<TYPES: NodeType> {
63 /// Decided leaf.
64 pub leaf: Leaf2<TYPES>,
65 /// Validated state.
66 pub state: Arc<<TYPES as NodeType>::ValidatedState>,
67 /// Optional application-specific state delta.
68 pub delta: Option<Arc<<<TYPES as NodeType>::ValidatedState as ValidatedState<TYPES>>::Delta>>,
69 /// Optional VID share data.
70 pub vid_share: Option<VidDisperseShare<TYPES>>,
71 /// Optional light client state update certificate.
72 pub state_cert: Option<LightClientStateUpdateCertificate<TYPES>>,
73}
74
75impl<TYPES: NodeType> LeafInfo<TYPES> {
76 /// Constructor.
77 pub fn new(
78 leaf: Leaf2<TYPES>,
79 state: Arc<<TYPES as NodeType>::ValidatedState>,
80 delta: Option<Arc<<<TYPES as NodeType>::ValidatedState as ValidatedState<TYPES>>::Delta>>,
81 vid_share: Option<VidDisperseShare<TYPES>>,
82 state_cert: Option<LightClientStateUpdateCertificate<TYPES>>,
83 ) -> Self {
84 Self {
85 leaf,
86 state,
87 delta,
88 vid_share,
89 state_cert,
90 }
91 }
92
93 pub fn to_legacy_unsafe(self) -> anyhow::Result<LegacyLeafInfo<TYPES>> {
94 Ok(LegacyLeafInfo {
95 leaf: self.leaf.to_leaf_unsafe(),
96 state: self.state,
97 delta: self.delta,
98 vid_share: self
99 .vid_share
100 .map(|share| match share {
101 VidDisperseShare::V0(share) => Ok(share),
102 VidDisperseShare::V1(_) => Err(error!("VID share is post-epoch")),
103 })
104 .transpose()?,
105 })
106 }
107}
108
109/// Pre-epoch version of `LeafInfo`
110#[derive(Clone, Debug, Serialize, Deserialize)]
111#[serde(bound(deserialize = "TYPES: NodeType"))]
112pub struct LegacyLeafInfo<TYPES: NodeType> {
113 /// Decided leaf.
114 pub leaf: Leaf<TYPES>,
115 /// Validated state.
116 pub state: Arc<<TYPES as NodeType>::ValidatedState>,
117 /// Optional application-specific state delta.
118 pub delta: Option<Arc<<<TYPES as NodeType>::ValidatedState as ValidatedState<TYPES>>::Delta>>,
119 /// Optional VID share data.
120 pub vid_share: Option<ADVZDisperseShare<TYPES>>,
121}
122
123impl<TYPES: NodeType> LegacyLeafInfo<TYPES> {
124 /// Constructor.
125 pub fn new(
126 leaf: Leaf<TYPES>,
127 state: Arc<<TYPES as NodeType>::ValidatedState>,
128 delta: Option<Arc<<<TYPES as NodeType>::ValidatedState as ValidatedState<TYPES>>::Delta>>,
129 vid_share: Option<ADVZDisperseShare<TYPES>>,
130 ) -> Self {
131 Self {
132 leaf,
133 state,
134 delta,
135 vid_share,
136 }
137 }
138}
139
140/// The chain of decided leaves with its corresponding state and VID info.
141pub type LeafChain<TYPES> = Vec<LeafInfo<TYPES>>;
142
143/// Pre-epoch version of `LeafChain`
144pub type LegacyLeafChain<TYPES> = Vec<LegacyLeafInfo<TYPES>>;
145
146/// Utilities for converting between HotShotError and a string.
147pub mod error_adaptor {
148 use serde::{de::Deserializer, ser::Serializer};
149
150 use super::{Arc, Deserialize, HotShotError, NodeType};
151
152 /// Convert a HotShotError into a string
153 ///
154 /// # Errors
155 /// Returns `Err` if the serializer fails.
156 pub fn serialize<S: Serializer, TYPES: NodeType>(
157 elem: &Arc<HotShotError<TYPES>>,
158 serializer: S,
159 ) -> Result<S::Ok, S::Error> {
160 serializer.serialize_str(&format!("{elem}"))
161 }
162
163 /// Convert a string into a HotShotError
164 ///
165 /// # Errors
166 /// Returns `Err` if the string cannot be deserialized.
167 pub fn deserialize<'de, D: Deserializer<'de>, TYPES: NodeType>(
168 deserializer: D,
169 ) -> Result<Arc<HotShotError<TYPES>>, D::Error> {
170 let str = String::deserialize(deserializer)?;
171 Ok(Arc::new(HotShotError::FailedToDeserialize(str)))
172 }
173}
174
175/// The type and contents of a status event emitted by a `HotShot` instance
176///
177/// This enum does not include metadata shared among all variants, such as the stage and view
178/// number, and is thus always returned wrapped in an [`Event`].
179#[non_exhaustive]
180#[derive(Clone, Debug, Serialize, Deserialize)]
181#[serde(bound(deserialize = "TYPES: NodeType"))]
182#[allow(clippy::large_enum_variant)]
183pub enum EventType<TYPES: NodeType> {
184 /// A view encountered an error and was interrupted
185 Error {
186 /// The underlying error
187 #[serde(with = "error_adaptor")]
188 error: Arc<HotShotError<TYPES>>,
189 },
190 /// A new decision event was issued
191 Decide {
192 /// The chain of Leaves that were committed by this decision
193 ///
194 /// This list is sorted in reverse view number order, with the newest (highest view number)
195 /// block first in the list.
196 ///
197 /// This list may be incomplete if the node is currently performing catchup.
198 /// Vid Info for a decided view may be missing if this node never saw it's share.
199 leaf_chain: Arc<LeafChain<TYPES>>,
200 /// The QC signing the most recent leaf in `leaf_chain`.
201 ///
202 /// Note that the QC for each additional leaf in the chain can be obtained from the leaf
203 /// before it using
204 qc: Arc<QuorumCertificate2<TYPES>>,
205 /// Optional information of the number of transactions in the block, for logging purposes.
206 block_size: Option<u64>,
207 },
208 /// A replica task was canceled by a timeout interrupt
209 ReplicaViewTimeout {
210 /// The view that timed out
211 view_number: TYPES::View,
212 },
213 /// The view has finished. If values were decided on, a `Decide` event will also be emitted.
214 ViewFinished {
215 /// The view number that has just finished
216 view_number: TYPES::View,
217 },
218 /// The view timed out
219 ViewTimeout {
220 /// The view that timed out
221 view_number: TYPES::View,
222 },
223 /// New transactions were received from the network
224 /// or submitted to the network by us
225 Transactions {
226 /// The list of transactions
227 transactions: Vec<TYPES::Transaction>,
228 },
229 /// DA proposal was received from the network
230 /// or submitted to the network by us
231 DaProposal {
232 /// Contents of the proposal
233 proposal: Proposal<TYPES, DaProposal2<TYPES>>,
234 /// Public key of the leader submitting the proposal
235 sender: TYPES::SignatureKey,
236 },
237 /// Quorum proposal was received from the network
238 /// or submitted to the network by us
239 QuorumProposal {
240 /// Contents of the proposal
241 proposal: Proposal<TYPES, QuorumProposalWrapper<TYPES>>,
242 /// Public key of the leader submitting the proposal
243 sender: TYPES::SignatureKey,
244 },
245 /// Upgrade proposal was received from the network
246 /// or submitted to the network by us
247 UpgradeProposal {
248 /// Contents of the proposal
249 proposal: Proposal<TYPES, UpgradeProposal<TYPES>>,
250 /// Public key of the leader submitting the proposal
251 sender: TYPES::SignatureKey,
252 },
253
254 /// A message destined for external listeners was received
255 ExternalMessageReceived {
256 /// Public Key of the message sender
257 sender: TYPES::SignatureKey,
258 /// Serialized data of the message
259 data: Vec<u8>,
260 },
261}
262
263impl<TYPES: NodeType> EventType<TYPES> {
264 pub fn to_legacy(self) -> anyhow::Result<LegacyEventType<TYPES>> {
265 Ok(match self {
266 EventType::Error { error } => LegacyEventType::Error { error },
267 EventType::Decide {
268 leaf_chain,
269 qc,
270 block_size,
271 } => LegacyEventType::Decide {
272 leaf_chain: Arc::new(
273 leaf_chain
274 .iter()
275 .cloned()
276 .map(LeafInfo::to_legacy_unsafe)
277 .collect::<anyhow::Result<_, _>>()?,
278 ),
279 qc: Arc::new(qc.as_ref().clone().to_qc()),
280 block_size,
281 },
282 EventType::ReplicaViewTimeout { view_number } => {
283 LegacyEventType::ReplicaViewTimeout { view_number }
284 },
285 EventType::ViewFinished { view_number } => {
286 LegacyEventType::ViewFinished { view_number }
287 },
288 EventType::ViewTimeout { view_number } => LegacyEventType::ViewTimeout { view_number },
289 EventType::Transactions { transactions } => {
290 LegacyEventType::Transactions { transactions }
291 },
292 EventType::DaProposal { proposal, sender } => LegacyEventType::DaProposal {
293 proposal: convert_proposal(proposal),
294 sender,
295 },
296 EventType::QuorumProposal { proposal, sender } => LegacyEventType::QuorumProposal {
297 proposal: convert_proposal(proposal),
298 sender,
299 },
300 EventType::UpgradeProposal { proposal, sender } => {
301 LegacyEventType::UpgradeProposal { proposal, sender }
302 },
303 EventType::ExternalMessageReceived { sender, data } => {
304 LegacyEventType::ExternalMessageReceived { sender, data }
305 },
306 })
307 }
308}
309
310/// Pre-epoch version of the `EventType` enum.
311#[non_exhaustive]
312#[derive(Clone, Debug, Serialize, Deserialize)]
313#[serde(bound(deserialize = "TYPES: NodeType"))]
314#[allow(clippy::large_enum_variant)]
315pub enum LegacyEventType<TYPES: NodeType> {
316 /// A view encountered an error and was interrupted
317 Error {
318 /// The underlying error
319 #[serde(with = "error_adaptor")]
320 error: Arc<HotShotError<TYPES>>,
321 },
322 /// A new decision event was issued
323 Decide {
324 /// The chain of Leaves that were committed by this decision
325 ///
326 /// This list is sorted in reverse view number order, with the newest (highest view number)
327 /// block first in the list.
328 ///
329 /// This list may be incomplete if the node is currently performing catchup.
330 /// Vid Info for a decided view may be missing if this node never saw it's share.
331 leaf_chain: Arc<LegacyLeafChain<TYPES>>,
332 /// The QC signing the most recent leaf in `leaf_chain`.
333 ///
334 /// Note that the QC for each additional leaf in the chain can be obtained from the leaf
335 /// before it using
336 qc: Arc<QuorumCertificate<TYPES>>,
337 /// Optional information of the number of transactions in the block, for logging purposes.
338 block_size: Option<u64>,
339 },
340 /// A replica task was canceled by a timeout interrupt
341 ReplicaViewTimeout {
342 /// The view that timed out
343 view_number: TYPES::View,
344 },
345 /// The view has finished. If values were decided on, a `Decide` event will also be emitted.
346 ViewFinished {
347 /// The view number that has just finished
348 view_number: TYPES::View,
349 },
350 /// The view timed out
351 ViewTimeout {
352 /// The view that timed out
353 view_number: TYPES::View,
354 },
355 /// New transactions were received from the network
356 /// or submitted to the network by us
357 Transactions {
358 /// The list of transactions
359 transactions: Vec<TYPES::Transaction>,
360 },
361 /// DA proposal was received from the network
362 /// or submitted to the network by us
363 DaProposal {
364 /// Contents of the proposal
365 proposal: Proposal<TYPES, DaProposal<TYPES>>,
366 /// Public key of the leader submitting the proposal
367 sender: TYPES::SignatureKey,
368 },
369 /// Quorum proposal was received from the network
370 /// or submitted to the network by us
371 QuorumProposal {
372 /// Contents of the proposal
373 proposal: Proposal<TYPES, QuorumProposal<TYPES>>,
374 /// Public key of the leader submitting the proposal
375 sender: TYPES::SignatureKey,
376 },
377 /// Upgrade proposal was received from the network
378 /// or submitted to the network by us
379 UpgradeProposal {
380 /// Contents of the proposal
381 proposal: Proposal<TYPES, UpgradeProposal<TYPES>>,
382 /// Public key of the leader submitting the proposal
383 sender: TYPES::SignatureKey,
384 },
385
386 /// A message destined for external listeners was received
387 ExternalMessageReceived {
388 /// Public Key of the message sender
389 sender: TYPES::SignatureKey,
390 /// Serialized data of the message
391 data: Vec<u8>,
392 },
393}
394
395#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
396/// A list of actions that we track for nodes
397pub enum HotShotAction {
398 /// A quorum vote was sent
399 Vote,
400 /// A timeout vote was sent
401 TimeoutVote,
402 /// View Sync Vote
403 ViewSyncVote,
404 /// A quorum proposal was sent
405 Propose,
406 /// DA proposal was sent
407 DaPropose,
408 /// DA vote was sent
409 DaVote,
410 /// DA certificate was sent
411 DaCert,
412 /// VID shares were sent
413 VidDisperse,
414 /// An upgrade vote was sent
415 UpgradeVote,
416 /// An upgrade proposal was sent
417 UpgradePropose,
418}