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