1use std::{collections::HashMap, time::Duration};
2
3use alloy::primitives::Address;
4use anyhow::Context;
5use async_trait::async_trait;
6use committable::Commitment;
7use espresso_types::{
8 config::PublicNetworkConfig,
9 v0::traits::{PersistenceOptions, SequencerPersistence},
10 v0_3::{
11 ChainConfig, RewardAccountProofV1, RewardAccountQueryDataV1, RewardAccountV1, RewardAmount,
12 RewardMerkleTreeV1, StakeTableEvent, Validator,
13 },
14 v0_4::{RewardAccountProofV2, RewardAccountQueryDataV2, RewardAccountV2, RewardMerkleTreeV2},
15 FeeAccount, FeeAccountProof, FeeMerkleTree, Leaf2, NodeState, PubKey, Transaction,
16};
17use futures::future::{BoxFuture, Future};
18use hotshot::types::BLSPubKey;
19use hotshot_query_service::{
20 availability::{AvailabilityDataSource, VidCommonQueryData},
21 data_source::{UpdateDataSource, VersionedDataSource},
22 fetching::provider::{AnyProvider, QueryServiceProvider},
23 node::NodeDataSource,
24 status::StatusDataSource,
25};
26use hotshot_types::{
27 data::{EpochNumber, VidShare, ViewNumber},
28 light_client::LCV3StateSignatureRequestBody,
29 simple_certificate::LightClientStateUpdateCertificateV2,
30 traits::{
31 network::ConnectedNetwork,
32 node_implementation::{NodeType, Versions},
33 },
34 PeerConfig,
35};
36use indexmap::IndexMap;
37use serde::{Deserialize, Serialize};
38use tide_disco::Url;
39
40use super::{
41 fs,
42 options::{Options, Query},
43 sql, AccountQueryData, BlocksFrontier,
44};
45use crate::{persistence, state_cert::StateCertFetchError, SeqTypes, SequencerApiVersion, U256};
46
47pub trait DataSourceOptions: PersistenceOptions {
48 type DataSource: SequencerDataSource<Options = Self>;
49
50 fn enable_query_module(&self, opt: Options, query: Query) -> Options;
51}
52
53impl DataSourceOptions for persistence::sql::Options {
54 type DataSource = sql::DataSource;
55
56 fn enable_query_module(&self, opt: Options, query: Query) -> Options {
57 opt.query_sql(query, self.clone())
58 }
59}
60
61impl DataSourceOptions for persistence::fs::Options {
62 type DataSource = fs::DataSource;
63
64 fn enable_query_module(&self, opt: Options, query: Query) -> Options {
65 opt.query_fs(query, self.clone())
66 }
67}
68
69#[async_trait]
74pub trait SequencerDataSource:
75 AvailabilityDataSource<SeqTypes>
76 + NodeDataSource<SeqTypes>
77 + StatusDataSource
78 + UpdateDataSource<SeqTypes>
79 + VersionedDataSource
80 + Sized
81{
82 type Options: DataSourceOptions<DataSource = Self>;
83
84 async fn create(opt: Self::Options, provider: Provider, reset: bool) -> anyhow::Result<Self>;
86}
87
88pub type Provider = AnyProvider<SeqTypes>;
90
91pub fn provider<V: Versions>(
93 peers: impl IntoIterator<Item = Url>,
94 bind_version: SequencerApiVersion,
95) -> Provider {
96 let mut provider = Provider::default();
97 for peer in peers {
98 tracing::info!("will fetch missing data from {peer}");
99 provider = provider.with_provider(QueryServiceProvider::new(peer, bind_version));
100 }
101 provider
102}
103
104pub(crate) trait SubmitDataSource<N: ConnectedNetwork<PubKey>, P: SequencerPersistence> {
105 fn submit(&self, tx: Transaction) -> impl Send + Future<Output = anyhow::Result<()>>;
106}
107
108pub(crate) trait HotShotConfigDataSource {
109 fn get_config(&self) -> impl Send + Future<Output = PublicNetworkConfig>;
110}
111
112#[async_trait]
113pub(crate) trait StateSignatureDataSource<N: ConnectedNetwork<PubKey>> {
114 async fn get_state_signature(&self, height: u64) -> Option<LCV3StateSignatureRequestBody>;
115}
116
117pub(crate) trait NodeStateDataSource {
118 fn node_state(&self) -> impl Send + Future<Output = NodeState>;
119}
120
121pub(crate) trait TokenDataSource<T: NodeType> {
122 fn get_total_supply_l1(&self) -> impl Send + Future<Output = anyhow::Result<U256>>;
124}
125
126#[derive(Serialize, Deserialize)]
127#[serde(bound = "T: NodeType")]
128pub struct StakeTableWithEpochNumber<T: NodeType> {
129 pub epoch: Option<EpochNumber>,
130 pub stake_table: Vec<PeerConfig<T>>,
131}
132
133pub(crate) trait StakeTableDataSource<T: NodeType> {
134 fn get_stake_table(
136 &self,
137 epoch: Option<<T as NodeType>::Epoch>,
138 ) -> impl Send + Future<Output = anyhow::Result<Vec<PeerConfig<T>>>>;
139
140 fn get_stake_table_current(
142 &self,
143 ) -> impl Send + Future<Output = anyhow::Result<StakeTableWithEpochNumber<T>>>;
144
145 fn get_da_stake_table(
147 &self,
148 epoch: Option<<T as NodeType>::Epoch>,
149 ) -> impl Send + Future<Output = anyhow::Result<Vec<PeerConfig<T>>>>;
150
151 fn get_da_stake_table_current(
153 &self,
154 ) -> impl Send + Future<Output = anyhow::Result<StakeTableWithEpochNumber<T>>>;
155
156 fn get_validators(
158 &self,
159 epoch: <T as NodeType>::Epoch,
160 ) -> impl Send + Future<Output = anyhow::Result<IndexMap<Address, Validator<BLSPubKey>>>>;
161
162 fn get_block_reward(
163 &self,
164 epoch: Option<EpochNumber>,
165 ) -> impl Send + Future<Output = anyhow::Result<Option<RewardAmount>>>;
166 fn current_proposal_participation(
168 &self,
169 ) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>>;
170
171 fn previous_proposal_participation(
173 &self,
174 ) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>>;
175
176 fn get_all_validators(
177 &self,
178 epoch: <T as NodeType>::Epoch,
179 offset: u64,
180 limit: u64,
181 ) -> impl Send + Future<Output = anyhow::Result<Vec<Validator<PubKey>>>>;
182
183 fn stake_table_events(
185 &self,
186 from_l1_block: u64,
187 to_l1_block: u64,
188 ) -> impl Send + Future<Output = anyhow::Result<Vec<StakeTableEvent>>>;
189}
190
191#[async_trait]
193pub(crate) trait StateCertDataSource {
194 async fn get_state_cert_by_epoch(
195 &self,
196 epoch: u64,
197 ) -> anyhow::Result<Option<LightClientStateUpdateCertificateV2<SeqTypes>>>;
198
199 async fn insert_state_cert(
200 &self,
201 epoch: u64,
202 cert: LightClientStateUpdateCertificateV2<SeqTypes>,
203 ) -> anyhow::Result<()>;
204}
205
206pub(crate) trait CatchupDataSource: Sync {
207 fn get_account(
214 &self,
215 instance: &NodeState,
216 height: u64,
217 view: ViewNumber,
218 account: FeeAccount,
219 ) -> impl Send + Future<Output = anyhow::Result<AccountQueryData>> {
220 async move {
221 let tree = self
222 .get_accounts(instance, height, view, &[account])
223 .await?;
224 let (proof, balance) = FeeAccountProof::prove(&tree, account.into()).context(
225 format!("account {account} not available for height {height}, view {view}"),
226 )?;
227 Ok(AccountQueryData { balance, proof })
228 }
229 }
230
231 fn get_accounts(
238 &self,
239 instance: &NodeState,
240 height: u64,
241 view: ViewNumber,
242 accounts: &[FeeAccount],
243 ) -> impl Send + Future<Output = anyhow::Result<FeeMerkleTree>>;
244
245 fn get_frontier(
252 &self,
253 instance: &NodeState,
254 height: u64,
255 view: ViewNumber,
256 ) -> impl Send + Future<Output = anyhow::Result<BlocksFrontier>>;
257
258 fn get_chain_config(
259 &self,
260 commitment: Commitment<ChainConfig>,
261 ) -> impl Send + Future<Output = anyhow::Result<ChainConfig>>;
262
263 fn get_leaf_chain(
264 &self,
265 height: u64,
266 ) -> impl Send + Future<Output = anyhow::Result<Vec<Leaf2>>>;
267
268 fn get_reward_account_v2(
275 &self,
276 instance: &NodeState,
277 height: u64,
278 view: ViewNumber,
279 account: RewardAccountV2,
280 ) -> impl Send + Future<Output = anyhow::Result<RewardAccountQueryDataV2>> {
281 async move {
282 let tree = self
283 .get_reward_accounts_v2(instance, height, view, &[account])
284 .await?;
285 let (proof, balance) = RewardAccountProofV2::prove(&tree, account.into()).context(
286 format!("reward account {account} not available for height {height}, view {view}"),
287 )?;
288 Ok(RewardAccountQueryDataV2 { balance, proof })
289 }
290 }
291
292 fn get_reward_accounts_v2(
293 &self,
294 instance: &NodeState,
295 height: u64,
296 view: ViewNumber,
297 accounts: &[RewardAccountV2],
298 ) -> impl Send + Future<Output = anyhow::Result<RewardMerkleTreeV2>>;
299
300 fn get_all_reward_accounts(
301 &self,
302 height: u64,
303 offset: u64,
304 limit: u64,
305 ) -> impl Send + Future<Output = anyhow::Result<Vec<(RewardAccountV2, RewardAmount)>>>;
306
307 fn get_reward_account_v1(
308 &self,
309 instance: &NodeState,
310 height: u64,
311 view: ViewNumber,
312 account: RewardAccountV1,
313 ) -> impl Send + Future<Output = anyhow::Result<RewardAccountQueryDataV1>> {
314 async move {
315 let tree = self
316 .get_reward_accounts_v1(instance, height, view, &[account])
317 .await?;
318 let (proof, balance) = RewardAccountProofV1::prove(&tree, account.into()).context(
319 format!("reward account {account} not available for height {height}, view {view}"),
320 )?;
321 Ok(RewardAccountQueryDataV1 { balance, proof })
322 }
323 }
324
325 fn get_reward_accounts_v1(
326 &self,
327 instance: &NodeState,
328 height: u64,
329 view: ViewNumber,
330 accounts: &[RewardAccountV1],
331 ) -> impl Send + Future<Output = anyhow::Result<RewardMerkleTreeV1>>;
332
333 fn get_state_cert(
334 &self,
335 epoch: u64,
336 ) -> impl Send + Future<Output = anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>>>;
337}
338
339pub trait RequestResponseDataSource<Types: NodeType> {
340 fn request_vid_shares(
341 &self,
342 block_number: u64,
343 vid_common_data: VidCommonQueryData<Types>,
344 duration: Duration,
345 ) -> impl Future<Output = BoxFuture<'static, anyhow::Result<Vec<VidShare>>>> + Send;
346}
347
348#[async_trait]
349pub trait StateCertFetchingDataSource<Types: NodeType> {
350 async fn request_state_cert(
351 &self,
352 epoch: u64,
353 timeout: Duration,
354 ) -> Result<LightClientStateUpdateCertificateV2<Types>, StateCertFetchError>;
355}
356
357#[cfg(any(test, feature = "testing"))]
358pub mod testing {
359 use super::{super::Options, *};
360
361 #[async_trait]
362 pub trait TestableSequencerDataSource: SequencerDataSource {
363 type Storage: Sync;
364
365 async fn create_storage() -> Self::Storage;
366 fn persistence_options(storage: &Self::Storage) -> Self::Options;
367 fn leaf_only_ds_options(
368 _storage: &Self::Storage,
369 _opt: Options,
370 ) -> anyhow::Result<Options> {
371 anyhow::bail!("not supported")
372 }
373 fn options(storage: &Self::Storage, opt: Options) -> Options;
374 }
375}