1use alloy::{
2 network::Ethereum,
3 primitives::{B256, U256},
4 providers::{
5 fillers::{FillProvider, JoinFill, RecommendedFillers},
6 Identity, RootProvider,
7 },
8 transports::http::{Client, Http},
9};
10use alloy_compat::ethers_serde;
11use async_broadcast::{InactiveReceiver, Sender};
12use clap::Parser;
13use derive_more::Deref;
14use hotshot_types::traits::metrics::{Counter, Gauge, Metrics, NoMetrics};
15use lru::LruCache;
16use parking_lot::RwLock;
17use serde::{Deserialize, Serialize};
18use std::{
19 num::NonZeroUsize,
20 sync::Arc,
21 time::{Duration, Instant},
22};
23use tokio::{
24 sync::{Mutex, Notify},
25 task::JoinHandle,
26};
27use url::Url;
28
29use crate::v0::utils::parse_duration;
30
31#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Hash, PartialEq, Eq)]
32pub struct L1BlockInfo {
33 pub number: u64,
34 #[serde(with = "ethers_serde::u256")]
35 pub timestamp: U256,
36 #[serde(with = "ethers_serde::b256")]
37 pub hash: B256,
38}
39
40#[derive(Clone, Copy, Debug, PartialOrd, Ord, Hash, PartialEq, Eq)]
41pub(crate) struct L1BlockInfoWithParent {
42 pub(crate) info: L1BlockInfo,
43 pub(crate) parent_hash: B256,
44}
45
46#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Hash, PartialEq, Eq)]
47pub struct L1Snapshot {
48 pub head: u64,
55
56 pub finalized: Option<L1BlockInfo>,
65}
66
67#[derive(Clone, Debug, Parser)]
69pub struct L1ClientOptions {
70 #[clap(
72 long,
73 env = "ESPRESSO_SEQUENCER_L1_RETRY_DELAY",
74 default_value = "1s",
75 value_parser = parse_duration,
76 )]
77 pub l1_retry_delay: Duration,
78
79 #[clap(
81 long,
82 env = "ESPRESSO_SEQUENCER_L1_POLLING_INTERVAL",
83 default_value = "7s",
84 value_parser = parse_duration,
85 )]
86 pub l1_polling_interval: Duration,
87
88 #[clap(
90 long,
91 env = "ESPRESSO_SEQUENCER_L1_BLOCKS_CACHE_SIZE",
92 default_value = "100"
93 )]
94 pub l1_blocks_cache_size: NonZeroUsize,
95
96 #[clap(
98 long,
99 env = "ESPRESSO_SEQUENCER_L1_EVENTS_CHANNEL_CAPACITY",
100 default_value = "100"
101 )]
102 pub l1_events_channel_capacity: usize,
103
104 #[clap(
106 long,
107 env = "ESPRESSO_SEQUENCER_L1_EVENTS_MAX_BLOCK_RANGE",
108 default_value = "10000"
109 )]
110 pub l1_events_max_block_range: u64,
111
112 #[clap(
114 long,
115 env = "ESPRESSO_SEQUENCER_L1_SUBSCRIPTION_TIMEOUT",
116 default_value = "1m",
117 value_parser = parse_duration,
118 )]
119 pub subscription_timeout: Duration,
120
121 #[clap(
123 long,
124 env = "ESPRESSO_SEQUENCER_L1_FREQUENT_FAILURE_TOLERANCE",
125 default_value = "1m",
126 value_parser = parse_duration,
127 )]
128 pub l1_frequent_failure_tolerance: Duration,
129
130 #[clap(
133 long,
134 env = "ESPRESSO_SEQUENCER_L1_CONSECUTIVE_FAILURE_TOLERANCE",
135 default_value = "10"
136 )]
137 pub l1_consecutive_failure_tolerance: usize,
138
139 #[clap(
141 long,
142 env = "ESPRESSO_SEQUENCER_L1_FAILOVER_REVERT",
143 default_value = "30m",
144 value_parser = parse_duration,
145 )]
146 pub l1_failover_revert: Duration,
147
148 #[clap(
152 long,
153 env = "ESPRESSO_SEQUENCER_L1_RATE_LIMIT_DELAY",
154 value_parser = parse_duration,
155 )]
156 pub l1_rate_limit_delay: Option<Duration>,
157
158 #[clap(long, env = "ESPRESSO_SEQUENCER_L1_WS_PROVIDER", value_delimiter = ',')]
162 pub l1_ws_provider: Option<Vec<Url>>,
163
164 #[clap(
168 long,
169 env = "ESPRESSO_SEQUENCER_L1_STAKE_TABLE_UPDATE_INTERVAL",
170 default_value = "60m",
171 value_parser = parse_duration,
172 )]
173 pub stake_table_update_interval: Duration,
174
175 #[clap(long, env = "ESPRESSO_SEQUENCER_L1_FINALIZED_SAFETY_MARGIN")]
189 pub l1_finalized_safety_margin: Option<u64>,
190
191 #[clap(skip = Arc::<Box<dyn Metrics>>::new(Box::new(NoMetrics)))]
192 pub metrics: Arc<Box<dyn Metrics>>,
193}
194
195pub type L1Provider = FillProvider<
197 JoinFill<Identity, <Ethereum as RecommendedFillers>::RecommendedFillers>,
198 RootProvider,
199>;
200
201#[derive(Clone, Debug, Deref)]
202pub struct L1Client {
210 #[deref]
212 pub provider: L1Provider,
213 pub transport: SwitchingTransport,
216 pub(crate) state: Arc<Mutex<L1State>>,
218 pub(crate) sender: Sender<L1Event>,
220 pub(crate) receiver: InactiveReceiver<L1Event>,
222 pub(crate) update_task: Arc<L1UpdateTask>,
224}
225
226#[derive(Debug)]
228pub(crate) struct L1State {
229 pub(crate) snapshot: L1Snapshot,
230 pub(crate) finalized: LruCache<u64, L1BlockInfoWithParent>,
231 pub(crate) last_finalized: Option<u64>,
232}
233
234#[derive(Clone, Debug)]
235pub(crate) enum L1Event {
236 NewHead { head: u64 },
237 NewFinalized { finalized: L1BlockInfoWithParent },
238}
239
240#[derive(Debug, Default)]
241pub(crate) struct L1UpdateTask(pub(crate) Mutex<Option<JoinHandle<()>>>);
242
243#[derive(Clone, Debug)]
244pub(crate) struct L1ClientMetrics {
245 pub(crate) head: Arc<dyn Gauge>,
246 pub(crate) finalized: Arc<dyn Gauge>,
247 pub(crate) reconnects: Arc<dyn Counter>,
248 pub(crate) failovers: Arc<dyn Counter>,
249 pub(crate) failures: Arc<Vec<Box<dyn Counter>>>,
250}
251
252#[derive(Clone, Debug)]
257pub struct SwitchingTransport {
258 pub(crate) current_transport: Arc<RwLock<SingleTransport>>,
260 pub(crate) urls: Arc<Vec<Url>>,
262 pub(crate) opt: Arc<L1ClientOptions>,
263 pub(crate) metrics: L1ClientMetrics,
264 pub(crate) switch_notify: Arc<Notify>,
265}
266
267#[derive(Debug, Clone)]
270pub(crate) struct SingleTransport {
271 pub(crate) generation: usize,
272 pub(crate) client: Http<Client>,
273 pub(crate) status: Arc<RwLock<SingleTransportStatus>>,
274 pub(crate) revert_at: Option<Instant>,
276}
277
278#[derive(Debug, Default)]
280pub(crate) struct SingleTransportStatus {
281 pub(crate) last_failure: Option<Instant>,
282 pub(crate) consecutive_failures: usize,
283 pub(crate) rate_limited_until: Option<Instant>,
284 pub(crate) shutting_down: bool,
286}