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