1#[cfg(feature = "node")]
2use std::time::Instant;
3use std::{num::NonZeroUsize, sync::Arc, time::Duration};
4
5use alloy::primitives::{B256, U256};
6#[cfg(feature = "node")]
7use alloy::{
8 network::Ethereum,
9 providers::{
10 Identity, Provider, RootProvider,
11 fillers::{FillProvider, JoinFill, RecommendedFillers},
12 },
13 transports::http::{Client, Http},
14};
15use alloy_compat::ethers_serde;
16#[cfg(feature = "node")]
17use async_broadcast::{InactiveReceiver, Sender};
18use clap::Parser;
19#[cfg(feature = "node")]
20use derive_more::Deref;
21#[cfg(feature = "node")]
22use hotshot_types::traits::metrics::{Counter, Gauge};
23use hotshot_types::traits::metrics::{Metrics, NoMetrics};
24#[cfg(feature = "node")]
25use lru::LruCache;
26#[cfg(feature = "node")]
27use parking_lot::RwLock;
28use serde::{Deserialize, Serialize};
29#[cfg(feature = "node")]
30use tokio::{
31 sync::{Mutex, Notify},
32 task::JoinHandle,
33};
34use url::Url;
35
36use crate::v0::utils::parse_duration;
37
38#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Hash, PartialEq, Eq)]
39pub struct L1BlockInfo {
40 pub number: u64,
41 #[serde(with = "ethers_serde::u256")]
42 pub timestamp: U256,
43 #[serde(with = "ethers_serde::b256")]
44 pub hash: B256,
45}
46
47#[cfg_attr(not(feature = "node"), allow(dead_code))]
48#[derive(Clone, Copy, Debug, PartialOrd, Ord, Hash, PartialEq, Eq)]
49pub(crate) struct L1BlockInfoWithParent {
50 pub(crate) info: L1BlockInfo,
51 pub(crate) parent_hash: B256,
52}
53
54#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Hash, PartialEq, Eq)]
55pub struct L1Snapshot {
56 pub head: u64,
63
64 pub finalized: Option<L1BlockInfo>,
73}
74
75#[derive(Clone, Debug, Parser)]
77pub struct L1ClientOptions {
78 #[clap(
80 long,
81 env = "ESPRESSO_L1_RETRY_DELAY",
82 default_value = "1s",
83 value_parser = parse_duration,
84 )]
85 pub l1_retry_delay: Duration,
86
87 #[clap(
89 long,
90 env = "ESPRESSO_L1_POLLING_INTERVAL",
91 default_value = "7s",
92 value_parser = parse_duration,
93 )]
94 pub l1_polling_interval: Duration,
95
96 #[clap(long, env = "ESPRESSO_L1_BLOCKS_CACHE_SIZE", default_value = "100")]
98 pub l1_blocks_cache_size: NonZeroUsize,
99
100 #[clap(
102 long,
103 env = "ESPRESSO_L1_EVENTS_CHANNEL_CAPACITY",
104 default_value = "100"
105 )]
106 pub l1_events_channel_capacity: usize,
107
108 #[clap(
110 long,
111 env = "ESPRESSO_L1_EVENTS_MAX_BLOCK_RANGE",
112 default_value = "10000"
113 )]
114 pub l1_events_max_block_range: u64,
115
116 #[clap(
118 long,
119 env = "ESPRESSO_L1_SUBSCRIPTION_TIMEOUT",
120 default_value = "1m",
121 value_parser = parse_duration,
122 )]
123 pub subscription_timeout: Duration,
124
125 #[clap(
127 long,
128 env = "ESPRESSO_L1_FREQUENT_FAILURE_TOLERANCE",
129 default_value = "1m",
130 value_parser = parse_duration,
131 )]
132 pub l1_frequent_failure_tolerance: Duration,
133
134 #[clap(
137 long,
138 env = "ESPRESSO_L1_CONSECUTIVE_FAILURE_TOLERANCE",
139 default_value = "10"
140 )]
141 pub l1_consecutive_failure_tolerance: usize,
142
143 #[clap(
145 long,
146 env = "ESPRESSO_L1_FAILOVER_REVERT",
147 default_value = "30m",
148 value_parser = parse_duration,
149 )]
150 pub l1_failover_revert: Duration,
151
152 #[clap(
156 long,
157 env = "ESPRESSO_L1_RATE_LIMIT_DELAY",
158 value_parser = parse_duration,
159 )]
160 pub l1_rate_limit_delay: Option<Duration>,
161
162 #[clap(long, env = "ESPRESSO_L1_WS_PROVIDER", value_delimiter = ',')]
166 pub l1_ws_provider: Option<Vec<Url>>,
167
168 #[clap(
172 long,
173 env = "ESPRESSO_NODE_L1_STAKE_TABLE_UPDATE_INTERVAL",
174 default_value = "60m",
175 value_parser = parse_duration,
176 )]
177 pub stake_table_update_interval: Duration,
178
179 #[clap(
187 long,
188 env = "ESPRESSO_L1_EVENTS_MAX_RETRY_DURATION",
189 default_value = "20m",
190 value_parser = parse_duration,
191 )]
192 pub l1_events_max_retry_duration: Duration,
193
194 #[clap(long, env = "ESPRESSO_L1_FINALIZED_SAFETY_MARGIN")]
208 pub l1_finalized_safety_margin: Option<u64>,
209
210 #[clap(skip = Arc::<Box<dyn Metrics>>::new(Box::new(NoMetrics)))]
211 pub metrics: Arc<Box<dyn Metrics>>,
212}
213
214#[cfg(feature = "node")]
216pub type L1Provider = FillProvider<
217 JoinFill<Identity, <Ethereum as RecommendedFillers>::RecommendedFillers>,
218 RootProvider,
219>;
220
221#[cfg(feature = "node")]
222#[derive(Clone, Debug, Deref)]
223pub struct L1Client {
231 #[deref]
233 pub provider: L1Provider,
234 pub transport: SwitchingTransport,
237 pub(crate) state: Arc<Mutex<L1State>>,
239 pub(crate) sender: Sender<L1Event>,
241 pub(crate) receiver: InactiveReceiver<L1Event>,
243 pub(crate) update_task: Arc<L1UpdateTask>,
245}
246
247#[cfg(feature = "node")]
248impl Provider for L1Client {
249 fn root(&self) -> &RootProvider {
250 self.provider.root()
251 }
252}
253
254#[cfg(feature = "node")]
256#[derive(Debug)]
257pub(crate) struct L1State {
258 pub(crate) snapshot: L1Snapshot,
259 pub(crate) finalized: LruCache<u64, L1BlockInfoWithParent>,
260 pub(crate) last_finalized: Option<u64>,
261}
262
263#[cfg(feature = "node")]
264#[derive(Clone, Debug)]
265pub(crate) enum L1Event {
266 NewHead { head: u64 },
267 NewFinalized { finalized: L1BlockInfoWithParent },
268}
269
270#[cfg(feature = "node")]
271#[derive(Debug, Default)]
272pub(crate) struct L1UpdateTask(pub(crate) Mutex<Option<JoinHandle<()>>>);
273
274#[cfg(feature = "node")]
275#[derive(Clone, Debug)]
276pub(crate) struct L1ClientMetrics {
277 pub(crate) head: Arc<dyn Gauge>,
278 pub(crate) finalized: Arc<dyn Gauge>,
279 pub(crate) reconnects: Arc<dyn Counter>,
280 pub(crate) failovers: Arc<dyn Counter>,
281 pub(crate) failures: Arc<Vec<Box<dyn Counter>>>,
282}
283
284#[cfg(feature = "node")]
289#[derive(Clone, Debug)]
290pub struct SwitchingTransport {
291 pub(crate) current_transport: Arc<RwLock<SingleTransport>>,
293 pub(crate) urls: Arc<Vec<Url>>,
295 pub(crate) opt: Arc<L1ClientOptions>,
296 pub(crate) metrics: L1ClientMetrics,
297 pub(crate) switch_notify: Arc<Notify>,
298}
299
300#[cfg(feature = "node")]
303#[derive(Debug, Clone)]
304pub(crate) struct SingleTransport {
305 pub(crate) generation: usize,
306 pub(crate) client: Http<Client>,
307 pub(crate) status: Arc<RwLock<SingleTransportStatus>>,
308 pub(crate) revert_at: Option<Instant>,
310}
311
312#[cfg(feature = "node")]
314#[derive(Debug, Default)]
315pub(crate) struct SingleTransportStatus {
316 pub(crate) last_failure: Option<Instant>,
317 pub(crate) consecutive_failures: usize,
318 pub(crate) rate_limited_until: Option<Instant>,
319 pub(crate) shutting_down: bool,
321}