Skip to main content

hotshot_types/
lib.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//! Types and Traits for the `HotShot` consensus module
8// False positive from displaydoc derive macro on struct fields
9#![allow(unused_assignments)]
10use std::{fmt::Debug, num::NonZeroUsize, time::Duration};
11
12use alloy_primitives::U256;
13use bincode::Options;
14use displaydoc::Display;
15use serde::{Deserialize, Serialize};
16use stake_table::HSStakeTable;
17use tracing::error;
18use traits::{
19    node_implementation::NodeType,
20    signature_key::{SignatureKey, StateSignatureKey},
21};
22use url::Url;
23use vbs::version::Version;
24use vec1::Vec1;
25
26use crate::{addr::NetAddr, utils::bincode_opts};
27
28pub mod addr;
29pub mod bundle;
30pub mod consensus;
31pub mod constants;
32pub mod data;
33/// Holds the types and functions for DRB computation.
34pub mod drb;
35/// Epoch Membership wrappers
36pub mod epoch_membership;
37pub mod error;
38pub mod event;
39/// Holds the configuration file specification for a HotShot node.
40pub mod hotshot_config_file;
41pub mod initializer;
42pub mod light_client;
43pub mod message;
44pub mod new_protocol;
45
46/// Holds the network configuration specification for HotShot nodes.
47pub mod network;
48pub mod qc;
49pub mod request_response;
50pub mod signature_key;
51pub mod simple_certificate;
52pub mod simple_vote;
53pub mod stake_table;
54pub mod traits;
55
56pub mod storage_metrics;
57/// Holds the upgrade configuration specification for HotShot nodes.
58pub mod upgrade_config;
59pub mod utils;
60pub mod vid;
61pub mod vote;
62pub mod x25519;
63
64/// config for validator, including public key, private key, stake value
65#[derive(Clone, Debug, Display)]
66pub struct ValidatorConfig<TYPES: NodeType> {
67    /// The validator's public key and stake value
68    pub public_key: TYPES::SignatureKey,
69    /// The validator's private key, should be in the mempool, not public
70    pub private_key: <TYPES::SignatureKey as SignatureKey>::PrivateKey,
71    /// The validator's stake
72    pub stake_value: U256,
73    /// the validator's key pairs for state verification
74    pub state_public_key: TYPES::StateSignatureKey,
75    /// the validator's key pairs for state verification
76    pub state_private_key: <TYPES::StateSignatureKey as StateSignatureKey>::StatePrivateKey,
77    /// Whether or not this validator is DA
78    pub is_da: bool,
79    /// X25519 keypair for network.
80    pub x25519_keypair: Option<x25519::Keypair>,
81    /// Network address.
82    pub p2p_addr: Option<NetAddr>,
83}
84
85impl<TYPES: NodeType> ValidatorConfig<TYPES> {
86    /// generate validator config from input seed, index, stake value, and whether it's DA
87    pub fn generated_from_seed_indexed(
88        seed: [u8; 32],
89        index: u64,
90        stake_value: U256,
91        is_da: bool,
92    ) -> Self {
93        let (public_key, private_key) =
94            TYPES::SignatureKey::generated_from_seed_indexed(seed, index);
95        let (state_public_key, state_private_key) =
96            TYPES::StateSignatureKey::generated_from_seed_indexed(seed, index);
97        Self {
98            public_key,
99            private_key,
100            stake_value,
101            state_public_key,
102            state_private_key,
103            is_da,
104            p2p_addr: None,
105            x25519_keypair: None,
106        }
107    }
108
109    /// get the public config of the validator
110    pub fn public_config(&self) -> PeerConfig<TYPES> {
111        PeerConfig {
112            stake_table_entry: self.public_key.stake_table_entry(self.stake_value),
113            state_ver_key: self.state_public_key.clone(),
114            connect_info: self
115                .x25519_keypair
116                .as_ref()
117                .map(|k| k.public_key())
118                .and_then(|p| {
119                    let a = self.p2p_addr.clone()?;
120                    Some(PeerConnectInfo {
121                        x25519_key: p,
122                        p2p_addr: a,
123                    })
124                }),
125        }
126    }
127
128    /// Create a default `ValidatorConfig` for testing.
129    pub fn test_default() -> Self {
130        Self::generated_from_seed_indexed([0u8; 32], 0, U256::from(1), true)
131    }
132}
133
134#[derive(Clone, Debug, Display, PartialEq, Eq, Hash, Serialize, Deserialize)]
135pub struct PeerConnectInfo {
136    /// Public X25519 key for network communication.
137    pub x25519_key: x25519::PublicKey,
138    /// Network address.
139    pub p2p_addr: NetAddr,
140}
141
142/// Structure of peers' config, including public key, stake value, and state key.
143#[derive(Clone, Display, PartialEq, Eq, Hash, Serialize, Deserialize)]
144#[serde(bound(deserialize = ""))]
145pub struct PeerConfig<TYPES: NodeType> {
146    /// The peer's public key and stake value. The key is the BLS Public Key used to
147    /// verify Stake Holder in the application layer.
148    pub stake_table_entry: <TYPES::SignatureKey as SignatureKey>::StakeTableEntry,
149    /// The peer's state public key. This is the Schnorr Public Key used to
150    /// verify HotShot state in the state-prover.
151    pub state_ver_key: TYPES::StateSignatureKey,
152    pub connect_info: Option<PeerConnectInfo>,
153}
154
155impl<TYPES: NodeType> PeerConfig<TYPES> {
156    /// Create a default `PeerConfig` for testing.
157    pub fn test_default() -> Self {
158        let default_validator_config = ValidatorConfig::<TYPES>::test_default();
159        default_validator_config.public_config()
160    }
161
162    /// Serialize a peer's config to bytes
163    pub fn to_bytes(config: &Self) -> Vec<u8> {
164        let x = bincode_opts().serialize(config);
165        match x {
166            Ok(x) => x,
167            Err(e) => {
168                error!(?e, "Failed to serialize public key");
169                vec![]
170            },
171        }
172    }
173
174    /// Deserialize a peer's config from bytes
175    /// # Errors
176    /// Will return `None` if deserialization fails
177    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
178        let x: Result<PeerConfig<TYPES>, _> = bincode_opts().deserialize(bytes);
179        match x {
180            Ok(pub_key) => Some(pub_key),
181            Err(e) => {
182                error!(?e, "Failed to deserialize public key");
183                None
184            },
185        }
186    }
187}
188
189impl<TYPES: NodeType> Debug for PeerConfig<TYPES> {
190    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191        f.debug_struct("PeerConfig")
192            .field("stake_table_entry", &self.stake_table_entry)
193            .field("state_ver_key", &format_args!("{}", self.state_ver_key))
194            .field("connect_info", &self.connect_info)
195            .finish()
196    }
197}
198
199#[derive(Clone, derive_more::Debug, serde::Serialize, serde::Deserialize)]
200#[serde(bound(deserialize = ""))]
201pub struct VersionedDaCommittee<TYPES: NodeType> {
202    #[serde(with = "version_ser")]
203    pub start_version: Version,
204    pub start_epoch: u64,
205    pub committee: Vec<PeerConfig<TYPES>>,
206}
207
208/// Holds configuration for a `HotShot`
209#[derive(Clone, derive_more::Debug, serde::Serialize, serde::Deserialize)]
210#[serde(bound(deserialize = ""))]
211pub struct HotShotConfig<TYPES: NodeType> {
212    /// The proportion of nodes required before the orchestrator issues the ready signal,
213    /// expressed as (numerator, denominator)
214    pub start_threshold: (u64, u64),
215    /// Total number of nodes in the network
216    // Earlier it was total_nodes
217    pub num_nodes_with_stake: NonZeroUsize,
218    /// List of known node's public keys and stake value for certificate aggregation, serving as public parameter
219    pub known_nodes_with_stake: Vec<PeerConfig<TYPES>>,
220    /// All public keys known to be DA nodes
221    pub known_da_nodes: Vec<PeerConfig<TYPES>>,
222    /// All public keys known to be DA nodes, by start epoch
223    pub da_committees: Vec<VersionedDaCommittee<TYPES>>,
224    /// List of DA committee (staking)nodes for static DA committee
225    pub da_staked_committee_size: usize,
226    /// Number of fixed leaders for GPU VID, normally it will be 0, it's only used when running GPU VID
227    pub fixed_leader_for_gpuvid: usize,
228    /// Base duration for next-view timeout, in milliseconds
229    pub next_view_timeout: u64,
230    /// Duration of view sync round timeouts
231    pub view_sync_timeout: Duration,
232    /// Number of network bootstrap nodes
233    pub num_bootstrap: usize,
234    /// The maximum amount of time a leader can wait to get a block from a builder
235    pub builder_timeout: Duration,
236    /// time to wait until we request data associated with a proposal
237    pub data_request_delay: Duration,
238    /// Builder API base URL
239    pub builder_urls: Vec1<Url>,
240    /// View to start proposing an upgrade
241    pub start_proposing_view: u64,
242    /// View to stop proposing an upgrade. To prevent proposing an upgrade, set stop_proposing_view <= start_proposing_view.
243    pub stop_proposing_view: u64,
244    /// View to start voting on an upgrade
245    pub start_voting_view: u64,
246    /// View to stop voting on an upgrade. To prevent voting on an upgrade, set stop_voting_view <= start_voting_view.
247    pub stop_voting_view: u64,
248    /// Unix time in seconds at which we start proposing an upgrade
249    pub start_proposing_time: u64,
250    /// Unix time in seconds at which we stop proposing an upgrade. To prevent proposing an upgrade, set stop_proposing_time <= start_proposing_time.
251    pub stop_proposing_time: u64,
252    /// Unix time in seconds at which we start voting on an upgrade
253    pub start_voting_time: u64,
254    /// Unix time in seconds at which we stop voting on an upgrade. To prevent voting on an upgrade, set stop_voting_time <= start_voting_time.
255    pub stop_voting_time: u64,
256    /// Number of blocks in an epoch, zero means there are no epochs
257    pub epoch_height: u64,
258    /// Epoch start block
259    #[serde(default = "default_epoch_start_block")]
260    pub epoch_start_block: u64,
261    /// Stake table capacity for light client use
262    #[serde(default = "default_stake_table_capacity")]
263    pub stake_table_capacity: usize,
264    /// number of iterations in the DRB calculation
265    pub drb_difficulty: u64,
266    /// number of iterations in the DRB calculation
267    pub drb_upgrade_difficulty: u64,
268}
269
270fn default_epoch_start_block() -> u64 {
271    1
272}
273
274fn default_stake_table_capacity() -> usize {
275    crate::light_client::DEFAULT_STAKE_TABLE_CAPACITY
276}
277
278impl<TYPES: NodeType> HotShotConfig<TYPES> {
279    /// Update a hotshot config to have a view-based upgrade.
280    pub fn set_view_upgrade(&mut self, view: u64) {
281        self.start_proposing_view = view;
282        self.stop_proposing_view = view + 1;
283        self.start_voting_view = view.saturating_sub(1);
284        self.stop_voting_view = view + 10;
285        self.start_proposing_time = 0;
286        self.stop_proposing_time = u64::MAX;
287        self.start_voting_time = 0;
288        self.stop_voting_time = u64::MAX;
289    }
290
291    /// Return the `known_nodes_with_stake` as a `HSStakeTable`
292    pub fn hotshot_stake_table(&self) -> HSStakeTable<TYPES> {
293        self.known_nodes_with_stake.clone().into()
294    }
295}
296
297/// `serde_bytes` for `Arc<[u8]>`, which it has no impl for.
298///
299/// serde's slice impl is `collect_seq`, one call per byte. The encoding is the same either way,
300/// so the swap is wire-compatible; `serde_bytes`' visitor still accepts a sequence.
301pub mod arc_bytes {
302    use std::{borrow::Cow, sync::Arc};
303
304    use serde::{Deserializer, Serializer};
305
306    /// # Errors
307    ///
308    /// Errors if the underlying serializer does.
309    pub fn serialize<S>(bytes: &Arc<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
310    where
311        S: Serializer,
312    {
313        serde_bytes::serialize(bytes.as_ref(), serializer)
314    }
315
316    /// # Errors
317    ///
318    /// Errors if the underlying deserializer does.
319    pub fn deserialize<'de, D>(deserializer: D) -> Result<Arc<[u8]>, D::Error>
320    where
321        D: Deserializer<'de>,
322    {
323        serde_bytes::deserialize::<Cow<[u8]>, _>(deserializer).map(|bytes| Arc::from(&*bytes))
324    }
325}
326
327pub mod version_ser {
328
329    use serde::{Deserialize, Deserializer, Serializer, de};
330    use vbs::version::Version;
331
332    pub fn serialize<S>(ver: &Version, serializer: S) -> Result<S::Ok, S::Error>
333    where
334        S: Serializer,
335    {
336        serializer.serialize_str(&ver.to_string())
337    }
338
339    pub fn deserialize<'de, D>(deserializer: D) -> Result<Version, D::Error>
340    where
341        D: Deserializer<'de>,
342    {
343        let version_str = String::deserialize(deserializer)?;
344
345        let version: Vec<_> = version_str.split('.').collect();
346
347        let version = Version {
348            major: version[0]
349                .parse()
350                .map_err(|_| de::Error::custom("invalid version format"))?,
351            minor: version[1]
352                .parse()
353                .map_err(|_| de::Error::custom("invalid version format"))?,
354        };
355
356        Ok(version)
357    }
358}