cat_gateway/settings/
chain_follower.rs

1//! Command line and environment variable settings for the service
2
3use std::{cmp::min, time::Duration};
4
5use cardano_chain_follower::{ChainSyncConfig, Network, turbo_downloader::DlConfig};
6use tracing::info;
7
8use super::str_env_var::StringEnvVar;
9
10/// Default chain to follow.
11const DEFAULT_NETWORK: NetworkFromStr = NetworkFromStr::Mainnet;
12
13/// Default number of sync tasks (must be in the range 1 to 256 inclusive.)
14const DEFAULT_SYNC_TASKS: u16 = 16;
15
16/// Maximum number of sync tasks (must be in the range 1 to 256 inclusive.)
17const MAX_SYNC_TASKS: u16 = 256;
18
19/// Default number of slots each sync task will process at one time.
20/// This default is just over one week worth of data where 1 slot == 1 second.
21const DEFAULT_SYNC_MAX_SLOTS: u64 = 700_000;
22/// Minimum the number of slots each sync task will process at one time can be set to.
23/// Note: This is just the setting minimum, a sync task may sync as few as a 1 slot.
24const MIN_SYNC_MAX_SLOTS: u64 = 10_000;
25/// Maximum the number of slots each sync task will process at one time can be set to.
26const MAX_SYNC_MAX_SLOTS: u64 = 100_000_000;
27
28/// Maximum number of DL Connections (must be in the range 1 to 256 inclusive.)
29const MAX_DL_CONNECTIONS: usize = 256;
30
31/// Maximum DL Chunk Size in MB (must be in the range 1 to 256 inclusive.)
32const MAX_DL_CHUNK_SIZE: usize = 256;
33
34/// Maximum DL Chunk Queue Ahead (must be in the range 1 to 256 inclusive.)
35const MAX_DL_CHUNK_QUEUE_AHEAD: usize = 256;
36
37/// Maximum DL Chunk Connect/Data Timeout in seconds (0 = Disabled).
38const MAX_DL_TIMEOUT: u64 = 300;
39
40/// Number of bytes in a Megabyte
41const ONE_MEGABYTE: usize = 1_048_576;
42
43/// A default devnet genesis key.
44const DEFAULT_DEVNET_GENESIS_KEY: &str = "5b33322c3235332c3138362c3230312c3137372c31312c3131372c3133352c3138372c3136372c3138312c3138382c32322c35392c3230362c3130352c3233312c3135302c3231352c33302c37382c3231322c37362c31362c3235322c3138302c37322c3133342c3133372c3234372c3136312c36385d";
45
46/// A default devnet Cardano networking magic number.
47const DEFAULT_DEVNET_MAGIC: u64 = 42;
48
49/// A default devnet Cardano networking identifier.
50const DEFAULT_DEVNET_NETWORK_ID: u64 = 42;
51
52/// A default devnet byron epoch length.
53const DEFAULT_DEVNET_BYRON_EPOCH_LENGTH: u32 = 100_000;
54
55/// A default devnet byron slot length.
56const DEFAULT_DEVNET_BYRON_SLOT_LENGTH: u32 = 1000;
57
58/// A default devnet byron known slot.
59const DEFAULT_DEVNET_BYRON_KNOWN_SLOT: u64 = 0;
60
61/// A default devnet byron known time.
62const DEFAULT_DEVNET_BYRON_KNOWN_TIME: u64 = 1_564_010_416;
63
64/// A default devnet byron known hash.
65const DEFAULT_DEVNET_BYRON_KNOWN_HASH: &str =
66    "8f8602837f7c6f8b8867dd1cbc1842cf51a27eaed2c70ef48325d00f8efb320f";
67
68/// A default devnet shelley epoch length.
69const DEFAULT_DEVNET_SHELLEY_EPOCH_LENGTH: u32 = 100;
70
71/// A default devnet shelley slot length.
72const DEFAULT_DEVNET_SHELLEY_SLOT_LENGTH: u32 = 1;
73
74/// A default devnet shelley known slot.
75const DEFAULT_DEVNET_SHELLEY_KNOWN_SLOT: u64 = 1_598_400;
76
77/// A default devnet shelley known hash.
78const DEFAULT_DEVNET_SHELLEY_KNOWN_HASH: &str =
79    "02b1c561715da9e540411123a6135ee319b02f60b9a11a603d3305556c04329f";
80
81/// A default devnet shelley known time.
82const DEFAULT_DEVNET_SHELLEY_KNOWN_TIME: u64 = 1_595_967_616;
83
84/// Configuration for the chain follower.
85#[derive(Clone)]
86pub(crate) struct EnvVars {
87    /// Cardano chain follower config
88    pub(crate) cfg: ChainSyncConfig,
89
90    /// The maximum number of sync tasks.
91    pub(crate) sync_tasks: u16,
92
93    /// The maximum number of slots a sync task will process at once.
94    pub(crate) sync_chunk_max_slots: u64,
95}
96
97#[derive(strum::EnumString, strum::VariantNames, strum::Display)]
98#[strum(ascii_case_insensitive)]
99enum NetworkFromStr {
100    /// Mainnet
101    Mainnet,
102    /// Preprod
103    Preprod,
104    /// Preview
105    Preview,
106    /// Devnet
107    Devnet,
108}
109
110impl From<NetworkFromStr> for Network {
111    #[allow(clippy::too_many_lines)]
112    fn from(value: NetworkFromStr) -> Self {
113        match value {
114            NetworkFromStr::Mainnet => Self::Mainnet,
115            NetworkFromStr::Preprod => Self::Preprod,
116            NetworkFromStr::Preview => Self::Preview,
117            NetworkFromStr::Devnet => {
118                let genesis_key = StringEnvVar::new(
119                    "CHAIN_FOLLOWER_DEVNET_GENESIS_KEY",
120                    DEFAULT_DEVNET_GENESIS_KEY.into(),
121                );
122                let magic = StringEnvVar::new_as_int(
123                    "CHAIN_FOLLOWER_DEVNET_MAGIC",
124                    DEFAULT_DEVNET_MAGIC,
125                    0,
126                    u64::MAX,
127                );
128                let network_id = StringEnvVar::new_as_int(
129                    "CHAIN_FOLLOWER_DEVNET_NETWORK_ID",
130                    DEFAULT_DEVNET_NETWORK_ID,
131                    0,
132                    u64::MAX,
133                );
134                let byron_epoch_length = StringEnvVar::new_as_int(
135                    "CHAIN_FOLLOWER_DEVNET_BYRON_EPOCH_LENGTH",
136                    DEFAULT_DEVNET_BYRON_EPOCH_LENGTH,
137                    0,
138                    u32::MAX,
139                );
140                let byron_slot_length = StringEnvVar::new_as_int(
141                    "CHAIN_FOLLOWER_DEVNET_BYRON_SLOT_LENGTH",
142                    DEFAULT_DEVNET_BYRON_SLOT_LENGTH,
143                    0,
144                    u32::MAX,
145                );
146                let byron_known_slot = StringEnvVar::new_as_int(
147                    "CHAIN_FOLLOWER_DEVNET_BYRON_KNOWN_SLOT",
148                    DEFAULT_DEVNET_BYRON_KNOWN_SLOT,
149                    0,
150                    u64::MAX,
151                );
152                let byron_known_hash = StringEnvVar::new(
153                    "CHAIN_FOLLOWER_DEVNET_BYRON_KNOWN_HASH",
154                    DEFAULT_DEVNET_BYRON_KNOWN_HASH.into(),
155                );
156                let byron_known_time = StringEnvVar::new_as_int(
157                    "CHAIN_FOLLOWER_DEVNET_BYRON_KNOWN_TIME",
158                    DEFAULT_DEVNET_BYRON_KNOWN_TIME,
159                    0,
160                    u64::MAX,
161                );
162                let shelley_epoch_length = StringEnvVar::new_as_int(
163                    "CHAIN_FOLLOWER_DEVNET_SHELLEY_EPOCH_LENGTH",
164                    DEFAULT_DEVNET_SHELLEY_EPOCH_LENGTH,
165                    0,
166                    u32::MAX,
167                );
168                let shelley_slot_length = StringEnvVar::new_as_int(
169                    "CHAIN_FOLLOWER_DEVNET_SHELLEY_SLOT_LENGTH",
170                    DEFAULT_DEVNET_SHELLEY_SLOT_LENGTH,
171                    0,
172                    u32::MAX,
173                );
174                let shelley_known_slot = StringEnvVar::new_as_int(
175                    "CHAIN_FOLLOWER_DEVNET_SHELLEY_KNOWN_SLOT",
176                    DEFAULT_DEVNET_SHELLEY_KNOWN_SLOT,
177                    0,
178                    u64::MAX,
179                );
180                let shelley_known_hash = StringEnvVar::new(
181                    "CHAIN_FOLLOWER_DEVNET_SHELLEY_KNOWN_HASH",
182                    DEFAULT_DEVNET_SHELLEY_KNOWN_HASH.into(),
183                );
184                let shelley_known_time = StringEnvVar::new_as_int(
185                    "CHAIN_FOLLOWER_DEVNET_SHELLEY_KNOWN_TIME",
186                    DEFAULT_DEVNET_SHELLEY_KNOWN_TIME,
187                    0,
188                    u64::MAX,
189                );
190                Self::Devnet {
191                    genesis_key: genesis_key.as_string(),
192                    magic,
193                    network_id,
194                    byron_epoch_length,
195                    byron_slot_length,
196                    byron_known_slot,
197                    byron_known_hash: byron_known_hash.as_string(),
198                    byron_known_time,
199                    shelley_epoch_length,
200                    shelley_slot_length,
201                    shelley_known_slot,
202                    shelley_known_hash: shelley_known_hash.as_string(),
203                    shelley_known_time,
204                }
205            },
206        }
207    }
208}
209
210impl EnvVars {
211    /// Create a config for a cassandra cluster, identified by a default namespace.
212    pub(super) fn new() -> Self {
213        let chain: Network =
214            StringEnvVar::new_as_enum("CHAIN_NETWORK", DEFAULT_NETWORK, false).into();
215
216        let sync_tasks: u16 = StringEnvVar::new_as_int(
217            "CHAIN_FOLLOWER_SYNC_TASKS",
218            DEFAULT_SYNC_TASKS,
219            1,
220            MAX_SYNC_TASKS,
221        );
222
223        let sync_slots: u64 = StringEnvVar::new_as_int(
224            "CHAIN_FOLLOWER_SYNC_MAX_SLOTS",
225            DEFAULT_SYNC_MAX_SLOTS,
226            MIN_SYNC_MAX_SLOTS,
227            MAX_SYNC_MAX_SLOTS,
228        );
229
230        let mut cfg = ChainSyncConfig::default_for(chain.clone());
231
232        cfg.mithril_cfg = cfg
233            .mithril_cfg
234            .with_dl_config(Self::new_mithril_dl_config());
235
236        if let Some(relay_address) =
237            StringEnvVar::new_optional("CHAIN_FOLLOWER_RELAY_ADDRESS", false)
238        {
239            cfg = cfg.relay(relay_address.as_string());
240        }
241        if let Some(mithril_aggregator_url) =
242            StringEnvVar::new_optional("CHAIN_FOLLOWER_MITHRIL_AGGREGATOR_URL", false)
243        {
244            cfg.mithril_cfg.aggregator_url = mithril_aggregator_url.as_string();
245        }
246
247        Self {
248            cfg,
249            sync_tasks,
250            sync_chunk_max_slots: sync_slots,
251        }
252    }
253
254    /// Setting up a Mithril `DlConfig` from the env vars.
255    fn new_mithril_dl_config() -> DlConfig {
256        let mut dl_config = DlConfig::new();
257
258        let workers = StringEnvVar::new_as_int(
259            "CHAIN_FOLLOWER_DL_CONNECTIONS",
260            dl_config.workers,
261            1,
262            MAX_DL_CONNECTIONS,
263        );
264        dl_config = dl_config.with_workers(workers);
265
266        let default_dl_chunk_size = min(1, dl_config.chunk_size / ONE_MEGABYTE);
267
268        let chunk_size = StringEnvVar::new_as_int(
269            "CHAIN_FOLLOWER_DL_CHUNK_SIZE",
270            default_dl_chunk_size,
271            1,
272            MAX_DL_CHUNK_SIZE,
273        )
274            .checked_mul(ONE_MEGABYTE)
275            .unwrap_or_else(|| {
276                info!("Too big 'CHAIN_FOLLOWER_DL_CHUNK_SIZE' value, default value {default_dl_chunk_size} is used");
277                default_dl_chunk_size
278            });
279        dl_config = dl_config.with_chunk_size(chunk_size);
280
281        let queue_ahead = StringEnvVar::new_as_int(
282            "CHAIN_FOLLOWER_DL_QUEUE_AHEAD",
283            dl_config.queue_ahead,
284            1,
285            MAX_DL_CHUNK_QUEUE_AHEAD,
286        );
287        dl_config = dl_config.with_queue_ahead(queue_ahead);
288
289        let default_dl_connect_timeout = match dl_config.connection_timeout {
290            Some(timeout) => timeout.as_secs(),
291            None => 0,
292        };
293        let dl_connect_timeout = StringEnvVar::new_as_int(
294            "CHAIN_FOLLOWER_DL_CONNECT_TIMEOUT",
295            default_dl_connect_timeout,
296            0,
297            MAX_DL_TIMEOUT,
298        );
299        if dl_connect_timeout == 0 {
300            dl_config.connection_timeout = None;
301        } else {
302            dl_config = dl_config.with_connection_timeout(Duration::from_secs(dl_connect_timeout));
303        }
304
305        let default_dl_data_timeout = match dl_config.data_read_timeout {
306            Some(timeout) => timeout.as_secs(),
307            None => 0,
308        };
309        let dl_data_timeout = StringEnvVar::new_as_int(
310            "CHAIN_FOLLOWER_DL_DATA_TIMEOUT",
311            default_dl_data_timeout,
312            0,
313            MAX_DL_TIMEOUT,
314        );
315        if dl_connect_timeout == 0 {
316            dl_config.data_read_timeout = None;
317        } else {
318            dl_config = dl_config.with_data_read_timeout(Duration::from_secs(dl_data_timeout));
319        }
320        dl_config
321    }
322
323    /// Returns the configured Network
324    pub(crate) fn chain(&self) -> &Network {
325        &self.cfg.chain
326    }
327
328    /// Log the configuration of this Chain Follower
329    pub(crate) fn log(&self) {
330        info!(
331            cfg = ?self.cfg,
332            sync_tasks = self.sync_tasks,
333            "Chain Follower Configuration"
334        );
335    }
336}