mithril_signer/
configuration.rs

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