espresso_contract_deployer/
network_config.rs1use std::time::Duration;
4
5use alloy::{
6 primitives::U256,
7 transports::http::reqwest::{self, Url},
8};
9use anyhow::{Context, Result};
10use espresso_types::SeqTypes;
11use hotshot_contract_adapter::{
12 field_to_u256,
13 sol_types::{LightClientStateSol, StakeTableStateSol},
14};
15use hotshot_types::{PeerConfig, data::EpochNumber, stake_table::HSStakeTable};
16use tokio::time::sleep;
17use vbs::version::StaticVersion;
18
19pub async fn light_client_genesis(
21 sequencer_url: &Url,
22 stake_table_capacity: usize,
23) -> anyhow::Result<(LightClientStateSol, StakeTableStateSol)> {
24 let st = fetch_stake_table_from_sequencer(sequencer_url, None)
25 .await
26 .with_context(|| "Failed to initialize stake table")?;
27 light_client_genesis_from_stake_table(&st, stake_table_capacity)
28}
29
30pub async fn fetch_stake_table_from_sequencer(
34 sequencer_url: &Url,
35 epoch: Option<EpochNumber>,
36) -> Result<HSStakeTable<SeqTypes>> {
37 tracing::info!("Initializing stake table from node for epoch {epoch:?}");
38 const NUM_RETRIES: usize = 5;
39
40 for i in 0..NUM_RETRIES {
41 match epoch {
42 Some(epoch) => match surf_disco::Client::<
43 tide_disco::error::ServerError,
44 StaticVersion<0, 1>,
45 >::new(sequencer_url.clone())
46 .get::<Vec<PeerConfig<SeqTypes>>>(&format!("node/stake-table/{}", epoch.u64()))
47 .send()
48 .await
49 {
50 Ok(resp) => return Ok(resp.into()),
51 Err(e) => {
52 let url = sequencer_url
53 .join(&format!("node/stake-table/{}", epoch.u64()))
54 .unwrap();
55 tracing::warn!(%url, "Failed to fetch the stake table: {e}, num_retries left: {}", NUM_RETRIES - i - 1);
56 if NUM_RETRIES - i > 1 {
57 sleep(Duration::from_secs(5)).await;
58 }
59 },
60 },
61 None => {
62 let url = sequencer_url.join("config/hotshot").unwrap();
63 match reqwest::get(url.clone()).await {
64 Ok(resp) => {
65 let value: serde_json::Value = resp.json().await.with_context(|| {
66 format!("Failed to parse the json object from url {url}")
67 })?;
68 let known_nodes_with_stake =
69 serde_json::from_str::<Vec<PeerConfig<SeqTypes>>>(
70 &value["config"]["known_nodes_with_stake"].to_string(),
71 )
72 .with_context(|| "Failed to parse the stake table")?;
73 return Ok(known_nodes_with_stake.into());
74 },
75 Err(e) => {
76 tracing::warn!(%url, "Failed to fetch the network config: {e}, num_retries left: {}", NUM_RETRIES - i - 1);
77 if NUM_RETRIES - i > 1 {
78 sleep(Duration::from_secs(5)).await;
79 }
80 },
81 }
82 },
83 }
84 }
85 Err(anyhow::anyhow!(
86 "Failed to fetch the stake table after {NUM_RETRIES} attempts"
87 ))
88}
89
90#[inline]
91pub fn light_client_genesis_from_stake_table(
93 st: &HSStakeTable<SeqTypes>,
94 stake_table_capacity: usize,
95) -> anyhow::Result<(LightClientStateSol, StakeTableStateSol)> {
96 let st_state = st.commitment(stake_table_capacity)?;
97 Ok((
98 LightClientStateSol {
99 viewNum: 0,
100 blockHeight: 0,
101 blockCommRoot: U256::from(0u32),
102 },
103 StakeTableStateSol {
104 blsKeyComm: field_to_u256(st_state.bls_key_comm),
105 schnorrKeyComm: field_to_u256(st_state.schnorr_key_comm),
106 amountComm: field_to_u256(st_state.amount_comm),
107 threshold: field_to_u256(st_state.threshold),
108 },
109 ))
110}
111
112pub async fn fetch_epoch_config_from_sequencer(sequencer_url: &Url) -> anyhow::Result<(u64, u64)> {
115 loop {
117 let url = sequencer_url.join("config/hotshot").unwrap();
118 match reqwest::get(url.clone()).await {
119 Ok(resp) => {
120 let value: serde_json::Value = resp
121 .json()
122 .await
123 .with_context(|| format!("Failed to parse the json object from url {url}"))?;
124 let blocks_per_epoch =
125 value["config"]["epoch_height"]
126 .as_u64()
127 .ok_or(anyhow::anyhow!(
128 "Failed to parse epoch_height from hotshot config"
129 ))?;
130 let epoch_start_block =
131 value["config"]["epoch_start_block"]
132 .as_u64()
133 .ok_or(anyhow::anyhow!(
134 "Failed to parse epoch_start_block from hotshot config"
135 ))?;
136 break Ok((blocks_per_epoch, epoch_start_block));
137 },
138 Err(e) => {
139 tracing::error!(%url, "Failed to fetch the network config: {e}");
140 sleep(Duration::from_secs(5)).await;
141 },
142 }
143 }
144}