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