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
39 loop {
40 match epoch {
41 Some(epoch) => match surf_disco::Client::<
42 tide_disco::error::ServerError,
43 StaticVersion<0, 1>,
44 >::new(sequencer_url.clone())
45 .get::<Vec<PeerConfig<SeqTypes>>>(&format!("node/stake-table/{}", epoch.u64()))
46 .send()
47 .await
48 {
49 Ok(resp) => break Ok(resp.into()),
50 Err(e) => {
51 let url = sequencer_url
52 .join(&format!("node/stake-table/{}", epoch.u64()))
53 .unwrap();
54 tracing::error!(%url, "Failed to fetch the stake table: {e}");
55 sleep(Duration::from_secs(5)).await;
56 },
57 },
58 None => {
59 let url = sequencer_url.join("config/hotshot").unwrap();
60 match reqwest::get(url.clone()).await {
61 Ok(resp) => {
62 let value: serde_json::Value = resp.json().await.with_context(|| {
63 format!("Failed to parse the json object from url {url}")
64 })?;
65 let known_nodes_with_stake =
66 serde_json::from_str::<Vec<PeerConfig<SeqTypes>>>(
67 &value["config"]["known_nodes_with_stake"].to_string(),
68 )
69 .with_context(|| "Failed to parse the stake table")?;
70 break Ok(known_nodes_with_stake.into());
71 },
72 Err(e) => {
73 tracing::error!(%url, "Failed to fetch the network config: {e}");
74 sleep(Duration::from_secs(5)).await;
75 },
76 }
77 },
78 }
79 }
80}
81
82#[inline]
83pub fn light_client_genesis_from_stake_table(
85 st: &HSStakeTable<SeqTypes>,
86 stake_table_capacity: usize,
87) -> anyhow::Result<(LightClientStateSol, StakeTableStateSol)> {
88 let st_state = st.commitment(stake_table_capacity)?;
89 Ok((
90 LightClientStateSol {
91 viewNum: 0,
92 blockHeight: 0,
93 blockCommRoot: U256::from(0u32),
94 },
95 StakeTableStateSol {
96 blsKeyComm: field_to_u256(st_state.bls_key_comm),
97 schnorrKeyComm: field_to_u256(st_state.schnorr_key_comm),
98 amountComm: field_to_u256(st_state.amount_comm),
99 threshold: field_to_u256(st_state.threshold),
100 },
101 ))
102}
103
104pub async fn fetch_epoch_config_from_sequencer(sequencer_url: &Url) -> anyhow::Result<(u64, u64)> {
107 loop {
109 let url = sequencer_url.join("config/hotshot").unwrap();
110 match reqwest::get(url.clone()).await {
111 Ok(resp) => {
112 let value: serde_json::Value = resp
113 .json()
114 .await
115 .with_context(|| format!("Failed to parse the json object from url {url}"))?;
116 let blocks_per_epoch =
117 value["config"]["epoch_height"]
118 .as_u64()
119 .ok_or(anyhow::anyhow!(
120 "Failed to parse epoch_height from hotshot config"
121 ))?;
122 let epoch_start_block =
123 value["config"]["epoch_start_block"]
124 .as_u64()
125 .ok_or(anyhow::anyhow!(
126 "Failed to parse epoch_start_block from hotshot config"
127 ))?;
128 break Ok((blocks_per_epoch, epoch_start_block));
129 },
130 Err(e) => {
131 tracing::error!(%url, "Failed to fetch the network config: {e}");
132 sleep(Duration::from_secs(5)).await;
133 },
134 }
135 }
136}