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