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 Certificate2, FeeAccount, FeeAccountProof, FeeMerkleTree, Leaf2, NodeState, PubKey,
9 Transaction,
10 config::PublicNetworkConfig,
11 v0::traits::{PersistenceOptions, SequencerPersistence},
12 v0_3::{
13 AuthenticatedValidator, ChainConfig, RegisteredValidator, RewardAccountProofV1,
14 RewardAccountQueryDataV1, RewardAccountV1, RewardAmount, RewardMerkleTreeV1,
15 StakeTableEvent,
16 },
17 v0_4::{RewardAccountProofV2, RewardAccountQueryDataV2, RewardAccountV2, RewardMerkleTreeV2},
18};
19use futures::future::{BoxFuture, Future};
20use hotshot::types::BLSPubKey;
21use hotshot_query_service::{
22 availability::{AvailabilityDataSource, BlockQueryData, LeafQueryData, VidCommonQueryData},
23 data_source::{UpdateDataSource, VersionedDataSource},
24 fetching::provider::AnyProvider,
25 node::NodeDataSource,
26 status::StatusDataSource,
27};
28use hotshot_types::{
29 PeerConfig,
30 data::{EpochNumber, VidShare, ViewNumber},
31 light_client::{LCV3StateSignatureRequestBody, StateVerKey},
32 simple_certificate::LightClientStateUpdateCertificateV2,
33 traits::{network::ConnectedNetwork, node_implementation::NodeType},
34 x25519,
35};
36use indexmap::IndexMap;
37use light_client::{state::LightClientOptions, storage::LightClientSqliteOptions};
38use serde::{Deserialize, Serialize};
39use url::Url;
40
41use super::{
42 AccountQueryData, BlocksFrontier, fs,
43 options::{Options, Query},
44 sql,
45};
46use crate::{
47 SeqTypes, U256,
48 api::{ApiState, LightClientProvider},
49 persistence,
50 state_cert::StateCertFetchError,
51};
52
53pub trait DataSourceOptions: PersistenceOptions {
54 type DataSource: SequencerDataSource<Options = Self>;
55
56 fn enable_query_module(&self, opt: Options, query: Query) -> Options;
57}
58
59impl DataSourceOptions for persistence::sql::Options {
60 type DataSource = sql::DataSource;
61
62 fn enable_query_module(&self, opt: Options, query: Query) -> Options {
63 opt.query_sql(query, self.clone())
64 }
65}
66
67impl DataSourceOptions for persistence::fs::Options {
68 type DataSource = fs::DataSource;
69
70 fn enable_query_module(&self, opt: Options, query: Query) -> Options {
71 opt.query_fs(query, self.clone())
72 }
73}
74
75#[async_trait]
80pub trait SequencerDataSource:
81 AvailabilityDataSource<SeqTypes>
82 + NodeDataSource<SeqTypes>
83 + StatusDataSource
84 + UpdateDataSource<SeqTypes>
85 + VersionedDataSource
86 + Sized
87{
88 type Options: DataSourceOptions<DataSource = Self>;
89
90 async fn create(opt: Self::Options, provider: Provider, reset: bool) -> anyhow::Result<Self>;
92}
93
94pub type Provider = AnyProvider<SeqTypes>;
96
97pub(super) async fn provider<N, P>(
99 peers: impl IntoIterator<Item = Url>,
100 state: &ApiState<N, P>,
101 opt: LightClientOptions,
102 db_opt: LightClientSqliteOptions,
103) -> anyhow::Result<Provider>
104where
105 N: ConnectedNetwork<PubKey>,
106 P: SequencerPersistence,
107{
108 Ok(Provider::default()
109 .with_provider(LightClientProvider::new(peers, state.clone(), opt, db_opt).await?))
110}
111
112pub(crate) trait SubmitDataSource<N: ConnectedNetwork<PubKey>, P: SequencerPersistence> {
113 fn submit(&self, tx: Transaction) -> impl Send + Future<Output = anyhow::Result<()>>;
114}
115
116pub(crate) trait HotShotConfigDataSource {
117 fn get_config(&self) -> impl Send + Future<Output = PublicNetworkConfig>;
118}
119
120#[async_trait]
121pub(crate) trait StateSignatureDataSource<N: ConnectedNetwork<PubKey>> {
122 async fn get_state_signature(&self, height: u64) -> Option<LCV3StateSignatureRequestBody>;
123}
124
125pub(crate) trait NodeStateDataSource {
126 fn node_state(&self) -> impl Send + Future<Output = NodeState>;
127}
128
129#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
130pub struct NodePublicKeys {
131 pub eth_account: Option<Address>,
132 pub consensus_key: BLSPubKey,
133 pub state_ver_key: StateVerKey,
134 #[serde(with = "x25519_tagged")]
135 pub x25519_key: Option<x25519::PublicKey>,
136}
137
138mod x25519_tagged {
139 use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
140
141 use super::x25519;
142
143 pub fn serialize<S: Serializer>(
144 key: &Option<x25519::PublicKey>,
145 s: S,
146 ) -> Result<S::Ok, S::Error> {
147 key.as_ref().map(ToString::to_string).serialize(s)
148 }
149
150 pub fn deserialize<'de, D: Deserializer<'de>>(
151 d: D,
152 ) -> Result<Option<x25519::PublicKey>, D::Error> {
153 Option::<String>::deserialize(d)?
154 .map(|s| s.parse().map_err(D::Error::custom))
155 .transpose()
156 }
157}
158
159pub(crate) trait NodeKeysDataSource {
160 fn node_public_keys(&self) -> impl Send + Future<Output = NodePublicKeys>;
161}
162
163pub(crate) trait TokenDataSource<T: NodeType> {
164 fn get_initial_supply_l1(&self) -> impl Send + Future<Output = anyhow::Result<U256>>;
165 fn get_total_supply_l1(&self) -> impl Send + Future<Output = anyhow::Result<U256>>;
166 fn get_decided_header(&self) -> impl Send + Future<Output = espresso_types::Header>;
167}
168
169#[derive(Serialize, Deserialize)]
170#[serde(bound = "T: NodeType")]
171pub struct StakeTableWithEpochNumber<T: NodeType> {
172 pub epoch: Option<EpochNumber>,
173 pub stake_table: Vec<PeerConfig<T>>,
174}
175
176pub(crate) trait StakeTableDataSource<T: NodeType> {
177 fn get_stake_table(
179 &self,
180 epoch: Option<EpochNumber>,
181 ) -> impl Send + Future<Output = anyhow::Result<Vec<PeerConfig<T>>>>;
182
183 fn get_stake_table_current(
185 &self,
186 ) -> impl Send + Future<Output = anyhow::Result<StakeTableWithEpochNumber<T>>>;
187
188 fn get_da_stake_table(
190 &self,
191 epoch: Option<EpochNumber>,
192 ) -> impl Send + Future<Output = anyhow::Result<Vec<PeerConfig<T>>>>;
193
194 fn get_da_stake_table_current(
196 &self,
197 ) -> impl Send + Future<Output = anyhow::Result<StakeTableWithEpochNumber<T>>>;
198
199 fn get_validators(
201 &self,
202 epoch: EpochNumber,
203 ) -> impl Send + Future<Output = anyhow::Result<IndexMap<Address, AuthenticatedValidator<BLSPubKey>>>>;
204
205 fn get_block_reward(
206 &self,
207 epoch: Option<EpochNumber>,
208 ) -> impl Send + Future<Output = anyhow::Result<Option<RewardAmount>>>;
209 fn current_proposal_participation(
211 &self,
212 ) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>>;
213
214 fn proposal_participation(
216 &self,
217 epoch: EpochNumber,
218 ) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>>;
219 fn current_vote_participation(&self) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>>;
221
222 fn vote_participation(
224 &self,
225 epoch: EpochNumber,
226 ) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>>;
227
228 fn get_all_validators(
229 &self,
230 epoch: EpochNumber,
231 offset: u64,
232 limit: u64,
233 ) -> impl Send + Future<Output = anyhow::Result<Vec<RegisteredValidator<PubKey>>>>;
234
235 fn stake_table_events(
237 &self,
238 from_l1_block: u64,
239 to_l1_block: u64,
240 ) -> impl Send + Future<Output = anyhow::Result<Vec<StakeTableEvent>>>;
241}
242
243#[async_trait]
245pub(crate) trait StateCertDataSource {
246 async fn get_state_cert_by_epoch(
247 &self,
248 epoch: u64,
249 ) -> anyhow::Result<Option<LightClientStateUpdateCertificateV2<SeqTypes>>>;
250
251 async fn insert_state_cert(
252 &self,
253 epoch: u64,
254 cert: LightClientStateUpdateCertificateV2<SeqTypes>,
255 ) -> anyhow::Result<()>;
256}
257
258pub(crate) trait CatchupDataSource: Sync {
259 fn get_account(
266 &self,
267 instance: &NodeState,
268 height: u64,
269 view: ViewNumber,
270 account: FeeAccount,
271 ) -> impl Send + Future<Output = anyhow::Result<AccountQueryData>> {
272 async move {
273 let tree = self
274 .get_accounts(instance, height, view, &[account])
275 .await?;
276 let (proof, balance) = FeeAccountProof::prove(&tree, account.into()).context(
277 format!("account {account} not available for height {height}, view {view}"),
278 )?;
279 Ok(AccountQueryData { balance, proof })
280 }
281 }
282
283 fn get_accounts(
290 &self,
291 instance: &NodeState,
292 height: u64,
293 view: ViewNumber,
294 accounts: &[FeeAccount],
295 ) -> impl Send + Future<Output = anyhow::Result<FeeMerkleTree>>;
296
297 fn get_frontier(
304 &self,
305 instance: &NodeState,
306 height: u64,
307 view: ViewNumber,
308 ) -> impl Send + Future<Output = anyhow::Result<BlocksFrontier>>;
309
310 fn get_chain_config(
311 &self,
312 commitment: Commitment<ChainConfig>,
313 ) -> impl Send + Future<Output = anyhow::Result<ChainConfig>>;
314
315 fn get_leaf_chain(
316 &self,
317 height: u64,
318 ) -> impl Send + Future<Output = anyhow::Result<Vec<Leaf2>>>;
319
320 fn get_cert2(
324 &self,
325 _height: u64,
326 ) -> impl Send + Future<Output = anyhow::Result<Option<Certificate2<SeqTypes>>>> {
327 async { Ok(None) }
328 }
329
330 fn get_reward_account_v2(
337 &self,
338 instance: &NodeState,
339 height: u64,
340 view: ViewNumber,
341 account: RewardAccountV2,
342 ) -> impl Send + Future<Output = anyhow::Result<RewardAccountQueryDataV2>> {
343 async move {
344 let tree = self
345 .get_reward_accounts_v2(instance, height, view, &[account])
346 .await?;
347 let (proof, balance) = RewardAccountProofV2::prove(&tree, account.into()).context(
348 format!("reward account {account} not available for height {height}, view {view}"),
349 )?;
350 Ok(RewardAccountQueryDataV2 { balance, proof })
351 }
352 }
353
354 fn get_reward_accounts_v2(
355 &self,
356 instance: &NodeState,
357 height: u64,
358 view: ViewNumber,
359 accounts: &[RewardAccountV2],
360 ) -> impl Send + Future<Output = anyhow::Result<RewardMerkleTreeV2>>;
361
362 fn get_reward_account_v1(
363 &self,
364 instance: &NodeState,
365 height: u64,
366 view: ViewNumber,
367 account: RewardAccountV1,
368 ) -> impl Send + Future<Output = anyhow::Result<RewardAccountQueryDataV1>> {
369 async move {
370 let tree = self
371 .get_reward_accounts_v1(instance, height, view, &[account])
372 .await?;
373 let (proof, balance) = RewardAccountProofV1::prove(&tree, account.into()).context(
374 format!("reward account {account} not available for height {height}, view {view}"),
375 )?;
376 Ok(RewardAccountQueryDataV1 { balance, proof })
377 }
378 }
379
380 fn get_reward_accounts_v1(
381 &self,
382 instance: &NodeState,
383 height: u64,
384 view: ViewNumber,
385 accounts: &[RewardAccountV1],
386 ) -> impl Send + Future<Output = anyhow::Result<RewardMerkleTreeV1>>;
387
388 fn get_reward_merkle_tree_v2(
389 &self,
390 height: u64,
391 view: ViewNumber,
392 ) -> impl Send + Future<Output = anyhow::Result<Vec<u8>>>;
393
394 fn get_state_cert(
395 &self,
396 epoch: u64,
397 ) -> impl Send + Future<Output = anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>>>;
398}
399
400pub trait RequestResponseDataSource<Types: NodeType> {
401 fn request_vid_shares(
402 &self,
403 block_number: u64,
404 vid_common_data: VidCommonQueryData<Types>,
405 duration: Duration,
406 ) -> impl Future<Output = BoxFuture<'static, anyhow::Result<Vec<VidShare>>>> + Send;
407}
408
409#[async_trait]
410pub trait StateCertFetchingDataSource<Types: NodeType> {
411 async fn request_state_cert(
412 &self,
413 epoch: u64,
414 timeout: Duration,
415 ) -> Result<LightClientStateUpdateCertificateV2<Types>, StateCertFetchError>;
416}
417
418#[derive(Serialize, Deserialize, Clone, Debug)]
420pub struct TableSize {
421 pub table_name: String,
422 pub row_count: i64,
423 pub total_size_bytes: Option<i64>,
424}
425
426#[derive(Serialize, Deserialize, Clone, Debug)]
428pub struct MigrationStatus {
429 pub name: String,
430 pub started_at: chrono::DateTime<chrono::Utc>,
431 pub completed_at: Option<chrono::DateTime<chrono::Utc>>,
432 pub last_offset: Option<i64>,
433}
434
435pub(crate) trait DatabaseMetadataSource {
439 fn get_table_sizes(&self) -> impl Send + Future<Output = anyhow::Result<Vec<TableSize>>>;
441
442 fn get_migration_status(
444 &self,
445 ) -> impl Send + Future<Output = anyhow::Result<Vec<MigrationStatus>>>;
446}
447
448use std::sync::Arc;
456
457#[async_trait]
458impl<D> StateCertDataSource for Arc<D>
459where
460 D: StateCertDataSource + Sync + Send,
461{
462 async fn get_state_cert_by_epoch(
463 &self,
464 epoch: u64,
465 ) -> anyhow::Result<Option<LightClientStateUpdateCertificateV2<SeqTypes>>> {
466 (*self).get_state_cert_by_epoch(epoch).await
467 }
468
469 async fn insert_state_cert(
470 &self,
471 epoch: u64,
472 cert: LightClientStateUpdateCertificateV2<SeqTypes>,
473 ) -> anyhow::Result<()> {
474 (*self).insert_state_cert(epoch, cert).await
475 }
476}
477
478impl<Types, D> RequestResponseDataSource<Types> for Arc<D>
479where
480 Types: NodeType,
481 D: RequestResponseDataSource<Types> + Send + Sync,
482{
483 async fn request_vid_shares(
484 &self,
485 block_number: u64,
486 vid_common_data: VidCommonQueryData<Types>,
487 timeout_duration: Duration,
488 ) -> BoxFuture<'static, anyhow::Result<Vec<VidShare>>> {
489 self.as_ref()
490 .request_vid_shares(block_number, vid_common_data, timeout_duration)
491 .await
492 }
493}
494
495#[async_trait]
496impl<Types, D> StateCertFetchingDataSource<Types> for Arc<D>
497where
498 Types: NodeType,
499 D: StateCertFetchingDataSource<Types> + Sync + Send,
500{
501 async fn request_state_cert(
502 &self,
503 epoch: u64,
504 timeout: Duration,
505 ) -> Result<LightClientStateUpdateCertificateV2<Types>, StateCertFetchError> {
506 (*self).request_state_cert(epoch, timeout).await
507 }
508}
509
510#[async_trait]
511impl<T, D> StakeTableDataSource<T> for Arc<D>
512where
513 T: NodeType,
514 D: StakeTableDataSource<T> + Sync + Send,
515{
516 fn get_stake_table(
517 &self,
518 epoch: Option<EpochNumber>,
519 ) -> impl Send + Future<Output = anyhow::Result<Vec<PeerConfig<T>>>> {
520 let this = self.clone();
521 async move { (*this).get_stake_table(epoch).await }
522 }
523
524 fn get_stake_table_current(
525 &self,
526 ) -> impl Send + Future<Output = anyhow::Result<StakeTableWithEpochNumber<T>>> {
527 let this = self.clone();
528 async move { (*this).get_stake_table_current().await }
529 }
530
531 fn get_da_stake_table(
532 &self,
533 epoch: Option<EpochNumber>,
534 ) -> impl Send + Future<Output = anyhow::Result<Vec<PeerConfig<T>>>> {
535 let this = self.clone();
536 async move { (*this).get_da_stake_table(epoch).await }
537 }
538
539 fn get_da_stake_table_current(
540 &self,
541 ) -> impl Send + Future<Output = anyhow::Result<StakeTableWithEpochNumber<T>>> {
542 let this = self.clone();
543 async move { (*this).get_da_stake_table_current().await }
544 }
545
546 fn get_validators(
547 &self,
548 epoch: EpochNumber,
549 ) -> impl Send + Future<Output = anyhow::Result<IndexMap<Address, AuthenticatedValidator<BLSPubKey>>>>
550 {
551 let this = self.clone();
552 async move { (*this).get_validators(epoch).await }
553 }
554
555 fn get_block_reward(
556 &self,
557 epoch: Option<EpochNumber>,
558 ) -> impl Send + Future<Output = anyhow::Result<Option<RewardAmount>>> {
559 let this = self.clone();
560 async move { (*this).get_block_reward(epoch).await }
561 }
562
563 fn current_proposal_participation(
564 &self,
565 ) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>> {
566 let this = self.clone();
567 async move { (*this).current_proposal_participation().await }
568 }
569
570 fn proposal_participation(
571 &self,
572 epoch: EpochNumber,
573 ) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>> {
574 let this = self.clone();
575 async move { (*this).proposal_participation(epoch).await }
576 }
577
578 fn current_vote_participation(&self) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>> {
579 let this = self.clone();
580 async move { (*this).current_vote_participation().await }
581 }
582
583 fn vote_participation(
584 &self,
585 epoch: EpochNumber,
586 ) -> impl Send + Future<Output = HashMap<BLSPubKey, f64>> {
587 let this = self.clone();
588 async move { (*this).vote_participation(epoch).await }
589 }
590
591 fn get_all_validators(
592 &self,
593 epoch: EpochNumber,
594 offset: u64,
595 limit: u64,
596 ) -> impl Send + Future<Output = anyhow::Result<Vec<RegisteredValidator<PubKey>>>> {
597 let this = self.clone();
598 async move { (*this).get_all_validators(epoch, offset, limit).await }
599 }
600
601 fn stake_table_events(
602 &self,
603 from_l1_block: u64,
604 to_l1_block: u64,
605 ) -> impl Send + Future<Output = anyhow::Result<Vec<StakeTableEvent>>> {
606 let this = self.clone();
607 async move { (*this).stake_table_events(from_l1_block, to_l1_block).await }
608 }
609}
610
611pub(crate) trait PruningDataSource {
616 fn get_oldest_block(
618 &self,
619 ) -> impl Send + Future<Output = anyhow::Result<Option<BlockQueryData<SeqTypes>>>>;
620
621 fn get_oldest_leaf(
623 &self,
624 ) -> impl Send + Future<Output = anyhow::Result<Option<LeafQueryData<SeqTypes>>>>;
625}
626
627#[cfg(test)]
628mod test {
629 use hotshot_types::{light_client::StateKeyPair, traits::signature_key::SignatureKey as _};
630
631 use super::*;
632
633 #[test]
634 fn test_node_public_keys_serialize_like_stake_table() {
635 let account = Address::random();
636 let consensus_key = BLSPubKey::generated_from_seed_indexed([1; 32], 0).0;
637 let state_ver_key = StateKeyPair::generate_from_seed_indexed([2; 32], 0).ver_key();
638 let x25519_key = x25519::Keypair::generated_from_seed_indexed([3; 32], 0)
639 .unwrap()
640 .public_key();
641
642 let validator = serde_json::to_value(RegisteredValidator::<BLSPubKey> {
643 account,
644 stake_table_key: Some(consensus_key),
645 state_ver_key: Some(state_ver_key.clone()),
646 stake: U256::from(1u64),
647 commission: 0,
648 delegators: HashMap::new(),
649 authenticated: true,
650 x25519_key: Some(x25519_key),
651 p2p_addr: None,
652 })
653 .unwrap();
654
655 let keys = serde_json::to_value(NodePublicKeys {
656 eth_account: Some(account),
657 consensus_key,
658 state_ver_key,
659 x25519_key: Some(x25519_key),
660 })
661 .unwrap();
662
663 assert_eq!(keys["eth_account"], validator["account"]);
664 assert_eq!(keys["consensus_key"], validator["stake_table_key"]);
665 assert_eq!(keys["state_ver_key"], validator["state_ver_key"]);
666 assert_eq!(keys["x25519_key"], x25519_key.to_string());
667 }
668}
669
670#[cfg(any(test, feature = "testing"))]
671pub mod testing {
672 use super::{super::Options, *};
673
674 #[async_trait]
675 pub trait TestableSequencerDataSource: SequencerDataSource {
676 type Storage: Sync;
677
678 async fn create_storage() -> Self::Storage;
679 fn persistence_options(storage: &Self::Storage) -> Self::Options;
680 fn leaf_only_ds_options(
681 _storage: &Self::Storage,
682 _opt: Options,
683 ) -> anyhow::Result<Options> {
684 anyhow::bail!("not supported")
685 }
686 fn options(storage: &Self::Storage, opt: Options) -> Options;
687 }
688}