blob_indexer/
context.rs

1use std::{sync::Arc, time::Duration};
2
3use alloy::{
4    network::Ethereum,
5    providers::{Provider, ProviderBuilder},
6};
7use anyhow::{anyhow, bail, Result as AnyhowResult};
8use backoff::ExponentialBackoffBuilder;
9use dyn_clone::DynClone;
10
11use crate::{
12    clients::{
13        beacon::{BeaconClient, CommonBeaconClient, Config as BeaconClientConfig},
14        blobscan::{BlobscanClient, CommonBlobscanClient, Config as BlobscanClientConfig},
15    },
16    network::{Network, NetworkName},
17};
18
19pub struct SyncingSettings {
20    pub concurrency: u32,
21    pub checkpoint_size: u32,
22    pub disable_checkpoints: bool,
23}
24
25// #[cfg(test)]
26// use crate::clients::{beacon::MockCommonBeaconClient, blobscan::MockCommonBlobscanClient};
27
28pub trait CommonContext: Send + Sync + DynClone {
29    fn beacon_client(&self) -> &dyn CommonBeaconClient;
30    fn blobscan_client(&self) -> &dyn CommonBlobscanClient;
31    fn network(&self) -> &Network;
32    fn provider(&self) -> &dyn Provider<Ethereum>;
33    fn syncing_settings(&self) -> &SyncingSettings;
34}
35
36dyn_clone::clone_trait_object!(CommonContext);
37// dyn_clone::clone_trait_object!(CommonContext<MockProvider>);
38
39struct ContextRef {
40    pub network: Network,
41    pub beacon_client: Box<dyn CommonBeaconClient>,
42    pub blobscan_client: Box<dyn CommonBlobscanClient>,
43    pub provider: Box<dyn Provider<Ethereum>>,
44    pub syncing_settings: SyncingSettings,
45}
46
47#[derive(Clone)]
48pub struct Context {
49    inner: Arc<ContextRef>,
50}
51
52pub struct ContextConfig {
53    pub network: Network,
54    pub beacon_api_base_url: String,
55    pub blobscan_api_base_url: String,
56    pub blobscan_secret_key: String,
57    pub execution_node_base_url: String,
58    pub syncing_settings: SyncingSettings,
59}
60
61impl Context {
62    pub async fn try_new(config: ContextConfig) -> AnyhowResult<Self> {
63        let exp_backoff = Some(ExponentialBackoffBuilder::default().build());
64        let client = reqwest::Client::builder()
65            .timeout(Duration::from_secs(16))
66            .build()?;
67        let provider = ProviderBuilder::new()
68            .network::<Ethereum>()
69            .connect_http(config.execution_node_base_url.parse()?);
70
71        let ctx = Self {
72            inner: Arc::new(ContextRef {
73                network: config.network,
74                syncing_settings: config.syncing_settings,
75                blobscan_client: Box::new(BlobscanClient::try_with_client(
76                    client.clone(),
77                    BlobscanClientConfig {
78                        base_url: config.blobscan_api_base_url.clone(),
79                        secret_key: config.blobscan_secret_key.clone(),
80                        exp_backoff: exp_backoff.clone(),
81                    },
82                )?),
83                beacon_client: Box::new(BeaconClient::try_with_client(
84                    client,
85                    BeaconClientConfig {
86                        base_url: config.beacon_api_base_url.clone(),
87                        exp_backoff,
88                    },
89                )?),
90                // Provider::<HttpProvider>::try_from(execution_node_endpoint)?
91                provider: Box::new(provider),
92            }),
93        };
94
95        ctx.validate_clients_consistency().await?;
96
97        Ok(ctx)
98    }
99
100    async fn validate_clients_consistency(&self) -> AnyhowResult<()> {
101        let execution_chain_id = self.provider().get_chain_id().await?;
102        let consensus_spec = self.beacon_client().get_spec().await?;
103        let network = self.network();
104
105        match consensus_spec {
106            Some(spec) => {
107                let deposit_network_id = spec.deposit_network_id;
108                if deposit_network_id != execution_chain_id {
109                    bail!(
110                        "Execution and Consensus clients mismatch: \n consensus deposit_network_id = {deposit_network_id},  execution chain_id = {execution_chain_id}"
111                    );
112                }
113
114                if let NetworkName::Preset(p) = network.name {
115                    if network.chain_id != execution_chain_id {
116                        bail!("Environment network mismatch for '{p}': expected chain_id={}, got {} from execution client", network.chain_id, execution_chain_id);
117                    }
118                }
119            }
120            None => {
121                return Err(anyhow!("No consensus spec found"));
122            }
123        };
124
125        Ok(())
126    }
127}
128
129impl CommonContext for Context {
130    fn beacon_client(&self) -> &dyn CommonBeaconClient {
131        self.inner.beacon_client.as_ref()
132    }
133
134    fn blobscan_client(&self) -> &dyn CommonBlobscanClient {
135        self.inner.blobscan_client.as_ref()
136    }
137
138    fn provider(&self) -> &dyn Provider<Ethereum> {
139        self.inner.provider.as_ref()
140    }
141
142    fn syncing_settings(&self) -> &SyncingSettings {
143        &self.inner.syncing_settings
144    }
145
146    fn network(&self) -> &Network {
147        &self.inner.network
148    }
149}
150
151// #[cfg(test)]
152// impl Context<MockProvider> {
153//     pub fn new(
154//         beacon_client: Option<MockCommonBeaconClient>,
155//         blobscan_client: Option<MockCommonBlobscanClient>,
156//         provider: Option<Provider<MockProvider>>,
157//     ) -> Box<Self> {
158//         Box::new(Self {
159//             inner: Arc::new(ContextRef {
160//                 beacon_client: Box::new(beacon_client.unwrap_or(MockCommonBeaconClient::new())),
161//                 blobscan_client: Box::new(
162//                     blobscan_client.unwrap_or(MockCommonBlobscanClient::new()),
163//                 ),
164//                 provider: provider.unwrap_or(Provider::mocked().0),
165//             }),
166//         })
167//     }
168// }
169
170// #[cfg(test)]
171// impl CommonContext<MockProvider> for Context<MockProvider> {
172//     fn beacon_client(&self) -> &dyn CommonBeaconClient {
173//         self.inner.beacon_client.as_ref()
174//     }
175
176//     fn blobscan_client(&self) -> &dyn CommonBlobscanClient {
177//         self.inner.blobscan_client.as_ref()
178//     }
179
180//     fn provider(&self) -> &Provider<MockProvider> {
181//         &self.inner.provider
182//     }
183// }