mithril_signer/
configuration.rs

1use anyhow::Context;
2use config::{ConfigError, Map, Source, Value};
3#[cfg(feature = "future_dmq")]
4use mithril_dmq::DmqNetwork;
5use mithril_doc::{Documenter, DocumenterDefault, StructDoc};
6use serde::{Deserialize, Serialize};
7use std::{path::PathBuf, sync::Arc};
8
9use mithril_cardano_node_chain::chain_observer::ChainObserver;
10use mithril_cli_helper::register_config_value;
11use mithril_common::{
12    CardanoNetwork, StdResult,
13    entities::{BlockNumber, PartyId},
14};
15use mithril_era::{
16    EraReaderAdapter,
17    adapters::{EraReaderAdapterBuilder, EraReaderAdapterType},
18};
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct SignaturePublisherConfig {
22    /// Number of retry attempts when publishing the signature
23    pub retry_attempts: u8,
24
25    /// Delay (in milliseconds) between two retry attempts when publishing the signature
26    pub retry_delay_ms: u64,
27
28    /// Delay (in milliseconds) between two separate publications done by the delayer signature publisher
29    pub delayer_delay_ms: u64,
30
31    /// Whether to skip the delayer when publishing the signature
32    ///
33    /// If set to true, the signatures will be published only once:
34    /// - if the 'future_dmq` feature is used to compile, the signatures will be published only with the DMQ protocol
35    /// - if the `future_dmq` feature is not used, the signatures will be published with the regular HTTP protocol
36    pub skip_delayer: bool,
37}
38
39/// Client configuration
40#[derive(Debug, Clone, Serialize, Deserialize, Documenter)]
41pub struct Configuration {
42    /// Cardano CLI tool path
43    #[example = "`cardano-cli`"]
44    pub cardano_cli_path: PathBuf,
45
46    /// Path of the socket opened by the Cardano node
47    #[example = "`/ipc/node.socket`"]
48    pub cardano_node_socket_path: PathBuf,
49
50    /// Path of the socket opened by the DMQ node
51    #[example = "`/ipc/dmq.socket`"]
52    pub dmq_node_socket_path: Option<PathBuf>,
53
54    /// Cardano network
55    #[example = "`mainnet` or `preprod` or `devnet`"]
56    pub network: String,
57
58    /// Cardano network magic number
59    /// useful for TestNet & DevNet
60    #[example = "`1097911063` or `42`"]
61    pub network_magic: Option<u64>,
62
63    /// DMQ network magic number
64    ///
65    /// useful for TestNet & DevNet
66    #[example = "`1097911063` or `42`"]
67    pub dmq_network_magic: Option<u64>,
68
69    /// Also known as `k`, it defines the number of blocks that are required for the blockchain to
70    /// be considered final, preventing any further rollback `[default: 2160]`.
71    pub network_security_parameter: BlockNumber,
72
73    /// Blocks offset, from the tip of the chain, to exclude during the cardano transactions preload
74    /// `[default: 3000]`.
75    pub preload_security_parameter: BlockNumber,
76
77    /// Aggregator endpoint
78    #[example = "`https://aggregator.pre-release-preview.api.mithril.network/aggregator`"]
79    pub aggregator_endpoint: String,
80
81    /// Relay endpoint
82    pub relay_endpoint: Option<String>,
83
84    /// Party Id
85    ///
86    /// Used only for testing when SPO pool id is not certified
87    #[example = "`pool1pxaqe80sqpde7902er5kf6v0c7y0sv6d5g676766v2h829fvs3x`"]
88    pub party_id: Option<PartyId>,
89
90    /// Run Interval
91    #[example = "`60000`"]
92    pub run_interval: u64,
93
94    /// Directory to snapshot
95    pub db_directory: PathBuf,
96
97    /// Directory to store signer data (Stakes, Protocol initializers, ...)
98    #[example = "`./mithril-signer/stores`"]
99    pub data_stores_directory: PathBuf,
100
101    /// Store retention limit. If set to None, no limit will be set.
102    pub store_retention_limit: Option<usize>,
103
104    /// File path to the KES secret key of the pool
105    pub kes_secret_key_path: Option<PathBuf>,
106
107    /// File path to the operational certificate of the pool
108    pub operational_certificate_path: Option<PathBuf>,
109
110    /// Disable immutables digests cache.
111    pub disable_digests_cache: bool,
112
113    /// If set the existing immutables digests cache will be reset.
114    ///
115    /// Will be ignored if set in conjunction with `disable_digests_cache`.
116    pub reset_digests_cache: bool,
117
118    /// Era reader adapter type
119    pub era_reader_adapter_type: EraReaderAdapterType,
120
121    /// Era reader adapter parameters
122    pub era_reader_adapter_params: Option<String>,
123
124    /// Enable metrics server (Prometheus endpoint on /metrics).
125    pub enable_metrics_server: bool,
126
127    /// Metrics HTTP Server IP.
128    pub metrics_server_ip: String,
129
130    /// Metrics HTTP Server listening port.
131    pub metrics_server_port: u16,
132
133    /// If set no error is returned in case of unparsable block and an error log is written instead.
134    ///
135    /// Will be ignored on (pre)production networks.
136    pub allow_unparsable_block: bool,
137
138    /// If set, the signer will prune the cardano transactions in database older than the
139    /// [network_security_parameter][Self::network_security_parameter] blocks after each import
140    /// `[default: true]`.
141    pub enable_transaction_pruning: bool,
142
143    /// Chunk size for importing transactions, combined with transaction pruning it reduces the
144    /// storage footprint of the signer by reducing the number of transactions stored on disk
145    /// at any given time.
146    pub transactions_import_block_chunk_size: BlockNumber,
147
148    /// The maximum number of roll forwards during a poll of the block streamer when importing transactions.
149    pub cardano_transactions_block_streamer_max_roll_forwards_per_poll: usize,
150
151    /// Preloading refresh interval in seconds
152    pub preloading_refresh_interval_in_seconds: u64,
153
154    /// Signature publisher configuration
155    #[example = "`{ retry_attempts: 3, retry_delay_ms: 2000, delayer_delay_ms: 10000, skip_delayer: false }`"]
156    pub signature_publisher_config: SignaturePublisherConfig,
157}
158
159impl Configuration {
160    /// Create a sample configuration mainly for tests
161    #[doc(hidden)]
162    pub fn new_sample<P: Into<PartyId>>(party_id: P) -> Self {
163        let party_id: PartyId = party_id.into();
164        let signer_temp_dir =
165            mithril_common::test::crypto_helper::setup_temp_directory_for_signer(&party_id, false);
166        Self {
167            aggregator_endpoint: "http://0.0.0.0:8000".to_string(),
168            relay_endpoint: None,
169            cardano_cli_path: PathBuf::new(),
170            cardano_node_socket_path: PathBuf::new(),
171            dmq_node_socket_path: None,
172            db_directory: PathBuf::new(),
173            network: "devnet".to_string(),
174            network_magic: Some(42),
175            dmq_network_magic: Some(3141592),
176            network_security_parameter: BlockNumber(2160),
177            preload_security_parameter: BlockNumber(30),
178            party_id: Some(party_id),
179            run_interval: 5000,
180            data_stores_directory: PathBuf::new(),
181            store_retention_limit: None,
182            kes_secret_key_path: signer_temp_dir.as_ref().map(|dir| dir.join("kes.sk")),
183            operational_certificate_path: signer_temp_dir
184                .as_ref()
185                .map(|dir| dir.join("opcert.cert")),
186            disable_digests_cache: false,
187            reset_digests_cache: false,
188            era_reader_adapter_type: EraReaderAdapterType::Bootstrap,
189            era_reader_adapter_params: None,
190            enable_metrics_server: true,
191            metrics_server_ip: "0.0.0.0".to_string(),
192            metrics_server_port: 9090,
193            allow_unparsable_block: false,
194            enable_transaction_pruning: false,
195            transactions_import_block_chunk_size: BlockNumber(1000),
196            cardano_transactions_block_streamer_max_roll_forwards_per_poll: 1000,
197            preloading_refresh_interval_in_seconds: 60,
198            signature_publisher_config: SignaturePublisherConfig {
199                retry_attempts: 1,
200                retry_delay_ms: 1,
201                delayer_delay_ms: 1,
202                skip_delayer: false,
203            },
204        }
205    }
206
207    /// Return the Cardano network value from the configuration.
208    pub fn get_network(&self) -> StdResult<CardanoNetwork> {
209        CardanoNetwork::from_code(self.network.clone(), self.network_magic).with_context(|| {
210            format!(
211                "Could not read Network '{}' from configuration.",
212                &self.network
213            )
214        })
215    }
216
217    /// Return the DMQ network value from the configuration.
218    #[cfg(feature = "future_dmq")]
219    pub fn get_dmq_network(&self) -> StdResult<DmqNetwork> {
220        DmqNetwork::from_code(self.network.clone(), self.dmq_network_magic).with_context(|| {
221            format!(
222                "Could not read DMQ Network '{}' from configuration.",
223                &self.network
224            )
225        })
226    }
227
228    /// Create the SQL store directory if not exist and return the path of the
229    /// SQLite3 file.
230    pub fn get_sqlite_file(&self, sqlite_file_name: &str) -> StdResult<PathBuf> {
231        let store_dir = &self.data_stores_directory;
232
233        if !store_dir.exists() {
234            std::fs::create_dir_all(store_dir).with_context(|| {
235                format!(
236                    "Could not create directory '{}' for Sqlite3 file.",
237                    store_dir.display()
238                )
239            })?;
240        }
241
242        Ok(self.data_stores_directory.join(sqlite_file_name))
243    }
244
245    /// Create era reader adapter from configuration settings.
246    pub fn build_era_reader_adapter(
247        &self,
248        chain_observer: Arc<dyn ChainObserver>,
249    ) -> StdResult<Arc<dyn EraReaderAdapter>> {
250        EraReaderAdapterBuilder::new(
251            &self.era_reader_adapter_type,
252            &self.era_reader_adapter_params,
253        )
254        .build(chain_observer)
255        .with_context(|| {
256            format!(
257                "Configuration: can not create era reader for adapter '{}'.",
258                &self.era_reader_adapter_type
259            )
260        })
261    }
262}
263
264/// Default configuration with all the default values for configurations.
265#[derive(Debug, Clone, DocumenterDefault)]
266pub struct DefaultConfiguration {
267    /// Era reader adapter type
268    pub era_reader_adapter_type: String,
269
270    /// Metrics HTTP server IP.
271    pub metrics_server_ip: String,
272
273    /// Metrics HTTP server listening port.
274    pub metrics_server_port: u16,
275
276    /// Network security parameter
277    pub network_security_parameter: u64,
278
279    /// Transaction pruning toggle
280    pub enable_transaction_pruning: bool,
281
282    /// Preload security parameter
283    pub preload_security_parameter: u64,
284
285    /// Chunk size for importing transactions
286    pub transactions_import_block_chunk_size: u64,
287
288    /// The maximum number of roll forwards during a poll of the block streamer when importing transactions.
289    pub cardano_transactions_block_streamer_max_roll_forwards_per_poll: u32,
290}
291
292impl DefaultConfiguration {
293    fn namespace() -> String {
294        "default configuration".to_string()
295    }
296}
297
298impl Default for DefaultConfiguration {
299    fn default() -> Self {
300        Self {
301            era_reader_adapter_type: "bootstrap".to_string(),
302            metrics_server_ip: "0.0.0.0".to_string(),
303            metrics_server_port: 9090,
304            network_security_parameter: 2160, // 2160 is the mainnet value
305            preload_security_parameter: 1000,
306            enable_transaction_pruning: true,
307            transactions_import_block_chunk_size: 1500,
308            cardano_transactions_block_streamer_max_roll_forwards_per_poll: 10000,
309        }
310    }
311}
312
313impl Source for DefaultConfiguration {
314    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
315        Box::new(self.clone())
316    }
317
318    fn collect(&self) -> Result<Map<String, Value>, ConfigError> {
319        let mut result = Map::new();
320        let namespace = DefaultConfiguration::namespace();
321        let myself = self.clone();
322
323        register_config_value!(result, &namespace, myself.era_reader_adapter_type);
324        register_config_value!(result, &namespace, myself.metrics_server_ip);
325        register_config_value!(result, &namespace, myself.metrics_server_port);
326        register_config_value!(result, &namespace, myself.network_security_parameter);
327        register_config_value!(result, &namespace, myself.preload_security_parameter);
328        register_config_value!(result, &namespace, myself.enable_transaction_pruning);
329        register_config_value!(
330            result,
331            &namespace,
332            myself.transactions_import_block_chunk_size
333        );
334        register_config_value!(
335            result,
336            &namespace,
337            myself.cardano_transactions_block_streamer_max_roll_forwards_per_poll
338        );
339
340        Ok(result)
341    }
342}