1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
// This file is part of the HotShot repository.

// You should have received a copy of the MIT License
// along with the HotShot repository. If not, see <https://mit-license.org/>.

use std::{num::NonZeroUsize, time::Duration};

use url::Url;
use vec1::Vec1;

use crate::{
    constants::REQUEST_DATA_DELAY, traits::signature_key::SignatureKey,
    upgrade_config::UpgradeConfig, HotShotConfig, PeerConfig, ValidatorConfig,
};

/// Default builder URL, used as placeholder
fn default_builder_urls() -> Vec1<Url> {
    vec1::vec1![Url::parse("http://0.0.0.0:3311").unwrap()]
}

/// Holds configuration for a `HotShot`
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(bound(deserialize = ""))]
pub struct HotShotConfigFile<KEY: SignatureKey> {
    /// The proportion of nodes required before the orchestrator issues the ready signal,
    /// expressed as (numerator, denominator)
    pub start_threshold: (u64, u64),
    /// Total number of staked nodes in the network
    pub num_nodes_with_stake: NonZeroUsize,
    #[serde(skip)]
    /// The known nodes' public key and stake value
    pub known_nodes_with_stake: Vec<PeerConfig<KEY>>,
    #[serde(skip)]
    /// The known DA nodes' public key and stake values
    pub known_da_nodes: Vec<PeerConfig<KEY>>,
    /// Number of staking DA nodes
    pub staked_da_nodes: usize,
    /// Number of fixed leaders for GPU VID
    pub fixed_leader_for_gpuvid: usize,
    /// Base duration for next-view timeout, in milliseconds
    pub next_view_timeout: u64,
    /// Duration for view sync round timeout
    pub view_sync_timeout: Duration,
    /// Number of network bootstrap nodes
    pub num_bootstrap: usize,
    /// The maximum amount of time a leader can wait to get a block from a builder
    pub builder_timeout: Duration,
    /// Time to wait until we request data associated with a proposal
    pub data_request_delay: Option<Duration>,
    /// Builder API base URL
    #[serde(default = "default_builder_urls")]
    pub builder_urls: Vec1<Url>,
    /// Upgrade config
    pub upgrade: UpgradeConfig,
    /// Number of blocks in an epoch, zero means there are no epochs
    pub epoch_height: u64,
}

impl<KEY: SignatureKey> From<HotShotConfigFile<KEY>> for HotShotConfig<KEY> {
    fn from(val: HotShotConfigFile<KEY>) -> Self {
        HotShotConfig {
            start_threshold: val.start_threshold,
            num_nodes_with_stake: val.num_nodes_with_stake,
            known_da_nodes: val.known_da_nodes,
            known_nodes_with_stake: val.known_nodes_with_stake,
            da_staked_committee_size: val.staked_da_nodes,
            fixed_leader_for_gpuvid: val.fixed_leader_for_gpuvid,
            next_view_timeout: val.next_view_timeout,
            view_sync_timeout: val.view_sync_timeout,
            num_bootstrap: val.num_bootstrap,
            builder_timeout: val.builder_timeout,
            data_request_delay: val
                .data_request_delay
                .unwrap_or(Duration::from_millis(REQUEST_DATA_DELAY)),
            builder_urls: val.builder_urls,
            start_proposing_view: val.upgrade.start_proposing_view,
            stop_proposing_view: val.upgrade.stop_proposing_view,
            start_voting_view: val.upgrade.start_voting_view,
            stop_voting_view: val.upgrade.stop_voting_view,
            start_proposing_time: val.upgrade.start_proposing_time,
            stop_proposing_time: val.upgrade.stop_proposing_time,
            start_voting_time: val.upgrade.start_voting_time,
            stop_voting_time: val.upgrade.stop_voting_time,
            epoch_height: val.epoch_height,
        }
    }
}

impl<KEY: SignatureKey> HotShotConfigFile<KEY> {
    /// Creates a new `HotShotConfigFile` with 5 nodes and 10 DA nodes.
    ///
    /// # Panics
    ///
    /// Cannot panic, but will if `NonZeroUsize` is somehow an error.
    #[must_use]
    pub fn hotshot_config_5_nodes_10_da() -> Self {
        let staked_da_nodes: usize = 5;

        let mut known_da_nodes = Vec::new();

        let gen_known_nodes_with_stake = (0..10)
            .map(|node_id| {
                let mut cur_validator_config: ValidatorConfig<KEY> =
                    ValidatorConfig::generated_from_seed_indexed([0u8; 32], node_id, 1, false);

                if node_id < staked_da_nodes as u64 {
                    known_da_nodes.push(cur_validator_config.public_config());
                    cur_validator_config.is_da = true;
                }

                cur_validator_config.public_config()
            })
            .collect();

        Self {
            num_nodes_with_stake: NonZeroUsize::new(10).unwrap(),
            start_threshold: (1, 1),
            known_nodes_with_stake: gen_known_nodes_with_stake,
            staked_da_nodes,
            known_da_nodes,
            fixed_leader_for_gpuvid: 1,
            next_view_timeout: 10000,
            view_sync_timeout: Duration::from_millis(1000),
            num_bootstrap: 5,
            builder_timeout: Duration::from_secs(10),
            data_request_delay: Some(Duration::from_millis(REQUEST_DATA_DELAY)),
            builder_urls: default_builder_urls(),
            upgrade: UpgradeConfig::default(),
            epoch_height: 0,
        }
    }
}