1use std::{num::NonZeroUsize, time::Duration};
2
3use hotshot_types::{
4 HotShotConfig, PeerConfig, ValidatorConfig, VersionedDaCommittee,
5 addr::NetAddr,
6 network::{BuilderType, NetworkConfig, RandomBuilderConfig},
7 x25519,
8};
9use serde::{Deserialize, Serialize};
10use url::Url;
11use vec1::Vec1;
12
13use crate::{PubKey, SeqTypes};
14
15#[derive(Clone, Debug, Deserialize, Serialize)]
18pub struct PublicValidatorConfig {
19 public_key: PubKey,
20 stake_value: u64,
21 is_da: bool,
22 private_key: String,
23 state_public_key: String,
24 state_key_pair: String,
25 x25519_key: Option<x25519::PublicKey>,
26 p2p_addr: Option<NetAddr>,
27}
28
29impl From<ValidatorConfig<SeqTypes>> for PublicValidatorConfig {
30 fn from(v: ValidatorConfig<SeqTypes>) -> Self {
31 let ValidatorConfig::<SeqTypes> {
32 public_key,
33 private_key: _,
34 stake_value,
35 state_public_key,
36 state_private_key: _,
37 is_da,
38 p2p_addr,
39 x25519_keypair,
40 } = v;
41
42 Self {
43 public_key,
44 stake_value: stake_value.to::<u64>(),
45 is_da,
46 state_public_key: state_public_key.to_string(),
47 private_key: "*****".into(),
48 state_key_pair: "*****".into(),
49 x25519_key: x25519_keypair.map(|x| x.public_key()),
50 p2p_addr,
51 }
52 }
53}
54
55#[derive(Clone, Debug, Deserialize, Serialize)]
59pub struct PublicHotShotConfig {
60 start_threshold: (u64, u64),
61 num_nodes_with_stake: NonZeroUsize,
62 known_nodes_with_stake: Vec<PeerConfig<SeqTypes>>,
63 known_da_nodes: Vec<PeerConfig<SeqTypes>>,
64 #[serde(default)]
65 da_committees: Vec<VersionedDaCommittee<SeqTypes>>,
66 da_staked_committee_size: usize,
67 fixed_leader_for_gpuvid: usize,
68 next_view_timeout: u64,
69 view_sync_timeout: Duration,
70 num_bootstrap: usize,
71 builder_timeout: Duration,
72 data_request_delay: Duration,
73 builder_urls: Vec1<Url>,
74 start_proposing_view: u64,
75 stop_proposing_view: u64,
76 start_voting_view: u64,
77 stop_voting_view: u64,
78 start_proposing_time: u64,
79 stop_proposing_time: u64,
80 start_voting_time: u64,
81 stop_voting_time: u64,
82 epoch_height: u64,
83 epoch_start_block: u64,
84 #[serde(default = "default_stake_table_capacity")]
85 stake_table_capacity: usize,
86 #[serde(default = "default_drb_difficulty")]
87 drb_difficulty: u64,
88 #[serde(default = "default_drb_upgrade_difficulty")]
89 drb_upgrade_difficulty: u64,
90}
91
92fn default_stake_table_capacity() -> usize {
93 hotshot_types::light_client::DEFAULT_STAKE_TABLE_CAPACITY
94}
95
96fn default_drb_difficulty() -> u64 {
98 0
99}
100
101fn default_drb_upgrade_difficulty() -> u64 {
103 0
104}
105
106impl From<HotShotConfig<SeqTypes>> for PublicHotShotConfig {
107 fn from(v: HotShotConfig<SeqTypes>) -> Self {
108 let HotShotConfig::<SeqTypes> {
112 start_threshold,
113 num_nodes_with_stake,
114 known_nodes_with_stake,
115 known_da_nodes,
116 da_committees,
117 da_staked_committee_size,
118 fixed_leader_for_gpuvid,
119 next_view_timeout,
120 view_sync_timeout,
121 num_bootstrap,
122 builder_timeout,
123 data_request_delay,
124 builder_urls,
125 start_proposing_view,
126 stop_proposing_view,
127 start_voting_view,
128 stop_voting_view,
129 start_proposing_time,
130 stop_proposing_time,
131 start_voting_time,
132 stop_voting_time,
133 epoch_height,
134 epoch_start_block,
135 stake_table_capacity,
136 drb_difficulty,
137 drb_upgrade_difficulty,
138 } = v;
139
140 Self {
141 start_threshold,
142 num_nodes_with_stake,
143 known_nodes_with_stake,
144 known_da_nodes,
145 da_committees,
146 da_staked_committee_size,
147 fixed_leader_for_gpuvid,
148 next_view_timeout,
149 view_sync_timeout,
150 num_bootstrap,
151 builder_timeout,
152 data_request_delay,
153 builder_urls,
154 start_proposing_view,
155 stop_proposing_view,
156 start_voting_view,
157 stop_voting_view,
158 start_proposing_time,
159 stop_proposing_time,
160 start_voting_time,
161 stop_voting_time,
162 epoch_height,
163 epoch_start_block,
164 stake_table_capacity,
165 drb_difficulty,
166 drb_upgrade_difficulty,
167 }
168 }
169}
170
171impl PublicHotShotConfig {
172 pub fn into_hotshot_config(self) -> HotShotConfig<SeqTypes> {
173 HotShotConfig {
174 start_threshold: self.start_threshold,
175 num_nodes_with_stake: self.num_nodes_with_stake,
176 known_nodes_with_stake: self.known_nodes_with_stake,
177 known_da_nodes: self.known_da_nodes,
178 da_committees: self.da_committees,
179 da_staked_committee_size: self.da_staked_committee_size,
180 fixed_leader_for_gpuvid: self.fixed_leader_for_gpuvid,
181 next_view_timeout: self.next_view_timeout,
182 view_sync_timeout: self.view_sync_timeout,
183 num_bootstrap: self.num_bootstrap,
184 builder_timeout: self.builder_timeout,
185 data_request_delay: self.data_request_delay,
186 builder_urls: self.builder_urls,
187 start_proposing_view: self.start_proposing_view,
188 stop_proposing_view: self.stop_proposing_view,
189 start_voting_view: self.start_voting_view,
190 stop_voting_view: self.stop_voting_view,
191 start_proposing_time: self.start_proposing_time,
192 stop_proposing_time: self.stop_proposing_time,
193 start_voting_time: self.start_voting_time,
194 stop_voting_time: self.stop_voting_time,
195 epoch_height: self.epoch_height,
196 epoch_start_block: self.epoch_start_block,
197 stake_table_capacity: self.stake_table_capacity,
198 drb_difficulty: self.drb_difficulty,
199 drb_upgrade_difficulty: self.drb_upgrade_difficulty,
200 }
201 }
202
203 pub fn known_nodes_with_stake(&self) -> Vec<PeerConfig<SeqTypes>> {
204 self.known_nodes_with_stake.clone()
205 }
206
207 pub fn known_da_nodes(&self) -> Vec<PeerConfig<SeqTypes>> {
208 self.known_da_nodes.clone()
209 }
210
211 pub fn blocks_per_epoch(&self) -> u64 {
212 self.epoch_height
213 }
214
215 pub fn epoch_start_block(&self) -> u64 {
216 self.epoch_start_block
217 }
218}
219
220#[derive(Clone, Debug, Deserialize, Serialize)]
221pub struct PublicNetworkConfig {
222 rounds: usize,
223 indexed_da: bool,
224 transactions_per_round: usize,
225 manual_start_password: Option<String>,
226 num_bootrap: usize,
227 next_view_timeout: u64,
228 view_sync_timeout: Duration,
229 builder_timeout: Duration,
230 data_request_delay: Duration,
231 node_index: u64,
232 seed: [u8; 32],
233 transaction_size: usize,
234 key_type_name: String,
235 config: PublicHotShotConfig,
236 #[serde(default, skip_deserializing)]
239 libp2p_config: LegacyLibp2pConfig,
240 commit_sha: String,
241 builder: BuilderType,
242 random_builder: Option<RandomBuilderConfig>,
243}
244
245#[derive(Clone, Debug, Default, Serialize)]
247struct LegacyLibp2pConfig {
248 bootstrap_nodes: Vec<()>,
249}
250
251impl From<NetworkConfig<SeqTypes>> for PublicNetworkConfig {
252 fn from(cfg: NetworkConfig<SeqTypes>) -> Self {
253 Self {
254 rounds: cfg.rounds,
255 indexed_da: cfg.indexed_da,
256 transactions_per_round: cfg.transactions_per_round,
257 manual_start_password: Some("*****".into()),
258 num_bootrap: cfg.num_bootrap,
259 next_view_timeout: cfg.next_view_timeout,
260 view_sync_timeout: cfg.view_sync_timeout,
261 builder_timeout: cfg.builder_timeout,
262 data_request_delay: cfg.data_request_delay,
263 node_index: cfg.node_index,
264 seed: cfg.seed,
265 transaction_size: cfg.transaction_size,
266 key_type_name: cfg.key_type_name,
267 config: cfg.config.into(),
268 libp2p_config: LegacyLibp2pConfig::default(),
269 commit_sha: cfg.commit_sha,
270 builder: cfg.builder,
271 random_builder: cfg.random_builder,
272 }
273 }
274}
275
276impl PublicNetworkConfig {
277 pub fn into_network_config(
278 self,
279 my_own_validator_config: ValidatorConfig<SeqTypes>,
280 ) -> anyhow::Result<NetworkConfig<SeqTypes>> {
281 let node_index = self
282 .config
283 .known_nodes_with_stake
284 .iter()
285 .position(|peer| peer.stake_table_entry.stake_key == my_own_validator_config.public_key)
286 .unwrap_or(0) as u64;
287
288 Ok(NetworkConfig {
289 rounds: self.rounds,
290 indexed_da: self.indexed_da,
291 transactions_per_round: self.transactions_per_round,
292 manual_start_password: self.manual_start_password,
293 num_bootrap: self.num_bootrap,
294 next_view_timeout: self.next_view_timeout,
295 view_sync_timeout: self.view_sync_timeout,
296 builder_timeout: self.builder_timeout,
297 data_request_delay: self.data_request_delay,
298 node_index,
299 seed: self.seed,
300 transaction_size: self.transaction_size,
301 key_type_name: self.key_type_name,
302 config: self.config.into_hotshot_config(),
303 commit_sha: self.commit_sha,
304 builder: self.builder,
305 random_builder: self.random_builder,
306 public_keys: Vec::new(),
307 })
308 }
309
310 pub fn hotshot_config(&self) -> PublicHotShotConfig {
311 self.config.clone()
312 }
313
314 pub fn commit_sha(&self) -> &str {
318 &self.commit_sha
319 }
320
321 pub fn indexed_da(&self) -> bool {
322 self.indexed_da
323 }
324
325 pub fn builder(&self) -> BuilderType {
326 self.builder
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::{NetworkConfig, PublicNetworkConfig};
333 use crate::SeqTypes;
334
335 #[test]
339 fn every_field_is_accounted_for_by_the_v2_config_api() {
340 let PublicNetworkConfig {
341 config: _,
343 commit_sha: _,
344 indexed_da: _,
345 builder: _,
346 libp2p_config: _,
350 rounds: _,
351 transactions_per_round: _,
352 transaction_size: _,
353 node_index: _,
354 seed: _,
355 random_builder: _,
356 manual_start_password: _,
357 key_type_name: _,
358 num_bootrap: _,
359 next_view_timeout: _,
360 view_sync_timeout: _,
361 builder_timeout: _,
362 data_request_delay: _,
363 } = PublicNetworkConfig::from(NetworkConfig::<SeqTypes>::default());
364 }
365
366 #[test]
369 fn serves_an_empty_libp2p_section_for_older_peers() {
370 let config = PublicNetworkConfig::from(NetworkConfig::<SeqTypes>::default());
371 let json = serde_json::to_value(&config).unwrap();
372 assert_eq!(
373 json["libp2p_config"],
374 serde_json::json!({ "bootstrap_nodes": [] })
375 );
376 let mut with_peers = json.clone();
378 with_peers["libp2p_config"] =
379 serde_json::json!({ "bootstrap_nodes": [["12D3KooW", "/ip4/10.0.0.1/tcp/1769"]] });
380 let parsed: PublicNetworkConfig = serde_json::from_value(with_peers).unwrap();
381 assert!(parsed.libp2p_config.bootstrap_nodes.is_empty());
382 }
383
384 #[test]
385 fn test_deserialize_from_old_config() {
386 let json_str = r#"
388 {
389 "rounds": 100,
390 "indexed_da": false,
391 "transactions_per_round": 10,
392 "manual_start_password": "*****",
393 "num_bootrap": 5,
394 "next_view_timeout": 10,
395 "view_sync_timeout": {
396 "secs": 2,
397 "nanos": 0
398 },
399 "builder_timeout": {
400 "secs": 10,
401 "nanos": 0
402 },
403 "data_request_delay": {
404 "secs": 2,
405 "nanos": 500000000
406 },
407 "node_index": 1,
408 "seed": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
409 "transaction_size": 100,
410 "key_type_name": "jf_signature::bls_over_bn254::VerKey",
411 "libp2p_config": {
412 "bootstrap_nodes": []
413 },
414 "config": {
415 "start_threshold": [100, 100],
416 "num_nodes_with_stake": 100,
417 "known_nodes_with_stake": [],
418 "known_da_nodes": [],
419 "da_staked_committee_size": 100,
420 "fixed_leader_for_gpuvid": 1,
421 "next_view_timeout": 12000,
422 "view_sync_timeout": {
423 "secs": 1,
424 "nanos": 0
425 },
426 "num_bootstrap": 5,
427 "builder_timeout": {
428 "secs": 8,
429 "nanos": 0
430 },
431 "data_request_delay": {
432 "secs": 5,
433 "nanos": 0
434 },
435 "builder_urls": [
436 "https://builder.decaf.testnet.espresso.network/"
437 ],
438 "start_proposing_view": 0,
439 "stop_proposing_view": 0,
440 "start_voting_view": 0,
441 "stop_voting_view": 0,
442 "start_proposing_time": 0,
443 "stop_proposing_time": 0,
444 "start_voting_time": 0,
445 "stop_voting_time": 0,
446 "epoch_height": 3000,
447 "epoch_start_block": 3160636
448 },
449 "cdn_marshal_address": null,
450 "combined_network_config": null,
451 "commit_sha": "",
452 "builder": "Simple",
453 "random_builder": null
454}
455 "#;
456 let _public_config: PublicNetworkConfig = serde_json::from_str(json_str).unwrap();
457 }
458}