mithril_signer/
configuration.rs

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