diem_config/config/
mod.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use rand::{rngs::StdRng, SeedableRng};
9use serde::{de::DeserializeOwned, Deserialize, Serialize};
10use std::{
11    collections::HashMap,
12    fmt,
13    fs::File,
14    io::{Read, Write},
15    path::{Path, PathBuf},
16    str::FromStr,
17};
18use thiserror::Error;
19
20mod consensus_config;
21pub use consensus_config::*;
22mod debug_interface_config;
23pub use debug_interface_config::*;
24mod error;
25pub use error::*;
26mod execution_config;
27pub use execution_config::*;
28mod logger_config;
29pub use logger_config::*;
30mod metrics_config;
31pub use metrics_config::*;
32mod mempool_config;
33pub use mempool_config::*;
34mod network_config;
35pub use network_config::*;
36mod json_rpc_config;
37pub use json_rpc_config::*;
38mod secure_backend_config;
39pub use secure_backend_config::*;
40mod state_sync_config;
41pub use state_sync_config::*;
42mod storage_config;
43pub use storage_config::*;
44mod safety_rules_config;
45pub use safety_rules_config::*;
46mod upstream_config;
47pub use upstream_config::*;
48mod test_config;
49pub use test_config::*;
50
51/// Config pulls in configuration information from the config file.
52/// This is used to set up the nodes and configure various parameters.
53/// The config file is broken up into sections for each module
54/// so that only that module can be passed around
55#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
56#[serde(deny_unknown_fields)]
57pub struct NodeConfig {
58    #[serde(default)]
59    pub base: BaseConfig,
60    #[serde(default)]
61    pub consensus: ConsensusConfig,
62    #[serde(default)]
63    pub debug_interface: DebugInterfaceConfig,
64    #[serde(default)]
65    pub execution: ExecutionConfig,
66    //#[serde(default, skip_serializing_if = "Vec::is_empty")]
67    //pub full_node_networks: Vec<NetworkConfig>,
68    #[serde(default)]
69    pub logger: LoggerConfig,
70    #[serde(default)]
71    pub metrics: MetricsConfig,
72    #[serde(default)]
73    pub mempool: MempoolConfig,
74    #[serde(default)]
75    pub json_rpc: JsonRpcConfig,
76    #[serde(default)]
77    pub state_sync: StateSyncConfig,
78    #[serde(default)]
79    pub storage: StorageConfig,
80    #[serde(default)]
81    pub test: Option<TestConfig>,
82    #[serde(default)]
83    pub upstream: UpstreamConfig,
84    //#[serde(default)]
85    //pub validator_network: Option<NetworkConfig>,
86    #[serde(default)]
87    pub failpoints: Option<HashMap<String, String>>,
88}
89
90#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
91#[serde(default, deny_unknown_fields)]
92pub struct BaseConfig {
93    data_dir: PathBuf,
94    pub role: RoleType,
95}
96
97impl Default for BaseConfig {
98    fn default() -> BaseConfig {
99        BaseConfig {
100            data_dir: PathBuf::from("./pos_db"),
101            role: RoleType::Validator,
102        }
103    }
104}
105
106#[derive(Clone, Copy, Deserialize, Eq, PartialEq, Serialize)]
107#[serde(rename_all = "snake_case")]
108pub enum RoleType {
109    Validator,
110    FullNode,
111}
112
113impl RoleType {
114    pub fn is_validator(self) -> bool { self == RoleType::Validator }
115
116    pub fn as_str(self) -> &'static str {
117        match self {
118            RoleType::Validator => "validator",
119            RoleType::FullNode => "full_node",
120        }
121    }
122}
123
124impl FromStr for RoleType {
125    type Err = ParseRoleError;
126
127    fn from_str(s: &str) -> Result<Self, Self::Err> {
128        match s {
129            "validator" => Ok(RoleType::Validator),
130            "full_node" => Ok(RoleType::FullNode),
131            _ => Err(ParseRoleError(s.to_string())),
132        }
133    }
134}
135
136impl fmt::Debug for RoleType {
137    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138        write!(f, "{}", self)
139    }
140}
141
142impl fmt::Display for RoleType {
143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        write!(f, "{}", self.as_str())
145    }
146}
147
148#[derive(Debug, Error)]
149#[error("Invalid node role: {0}")]
150pub struct ParseRoleError(String);
151
152impl NodeConfig {
153    pub fn data_dir(&self) -> &Path { &self.base.data_dir }
154
155    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
156        self.base.data_dir = data_dir.clone();
157        self.consensus.set_data_dir(data_dir.clone());
158        self.execution.set_data_dir(data_dir.clone());
159        self.metrics.set_data_dir(data_dir.clone());
160        self.storage.set_data_dir(data_dir);
161    }
162
163    /// Reads the config file and returns the configuration object in addition
164    /// to doing some post-processing of the config
165    /// Paths used in the config are either absolute or relative to the config
166    /// location
167    pub fn load<P: AsRef<Path>>(input_path: P) -> Result<Self, Error> {
168        let mut config = Self::load_config(&input_path)?;
169
170        let input_dir = RootPath::new(input_path);
171        config.execution.load(&input_dir)?;
172
173        //let mut config = config.validate_network_configs()?;
174        //config.set_data_dir(config.data_dir().to_path_buf());
175        Ok(config)
176    }
177
178    /// Checks `NetworkConfig` setups so that they exist on proper networks
179    /// Additionally, handles any strange missing default cases
180    /*fn validate_network_configs(mut self) -> Result<NodeConfig, Error> {
181        if self.base.role.is_validator() {
182            invariant(
183                self.validator_network.is_some(),
184                "Missing a validator network config for a validator node"
185                    .into(),
186            )?;
187        } else {
188            invariant(
189                self.validator_network.is_none(),
190                "Provided a validator network config for a full_node node"
191                    .into(),
192            )?;
193        }
194
195        let mut network_ids = HashSet::new();
196        if let Some(network) = &mut self.validator_network {
197            network.load_validator_network()?;
198            network_ids.insert(network.network_id.clone());
199        }
200        for network in &mut self.full_node_networks {
201            network.load_fullnode_network()?;
202
203            // Check a validator network is not included in a list of full-node
204            // networks
205            let network_id = &network.network_id;
206            invariant(
207                !matches!(network_id, NetworkId::Validator),
208                "Included a validator network in full_node_networks".into(),
209            )?;
210            network_ids.insert(network_id.clone());
211        }
212        Ok(self)
213    }*/
214
215    pub fn save<P: AsRef<Path>>(
216        &mut self, output_path: P,
217    ) -> Result<(), Error> {
218        let output_dir = RootPath::new(&output_path);
219        self.execution.save(&output_dir)?;
220        // This must be last as calling save on subconfigs may change their
221        // fields
222        self.save_config(&output_path)?;
223        Ok(())
224    }
225
226    /*pub fn randomize_ports(&mut self) {
227        self.debug_interface.randomize_ports();
228        self.json_rpc.randomize_ports();
229        self.storage.randomize_ports();
230
231        if let Some(network) = self.validator_network.as_mut() {
232            network.listen_address =
233                crate::utils::get_available_port_in_multiaddr(true);
234        }
235
236        for network in self.full_node_networks.iter_mut() {
237            network.listen_address =
238                crate::utils::get_available_port_in_multiaddr(true);
239        }
240    }*/
241
242    pub fn random() -> Self {
243        let mut rng = StdRng::from_seed([0u8; 32]);
244        Self::random_with_template(0, &NodeConfig::default(), &mut rng)
245    }
246
247    pub fn random_with_template(
248        idx: u32, template: &Self, rng: &mut StdRng,
249    ) -> Self {
250        let mut config = template.clone();
251        config.random_internal(idx, rng);
252        config
253    }
254
255    fn random_internal(&mut self, idx: u32, rng: &mut StdRng) {
256        let mut test = TestConfig::new_with_temp_dir(None);
257
258        if self.base.role == RoleType::Validator {
259            test.random_account_key(rng);
260            let peer_id = crate::utils::validator_owner_account_from_name(
261                idx.to_string().as_bytes(),
262            );
263
264            /*if self.validator_network.is_none() {
265                let network_config =
266                    NetworkConfig::network_with_id(NetworkId::Validator);
267                self.validator_network = Some(network_config);
268            }
269
270            let validator_network = self.validator_network.as_mut().unwrap();
271            validator_network.random_with_peer_id(rng, Some(peer_id));*/
272            // We want to produce this key twice
273            let mut cloned_rng = rng.clone();
274            test.random_execution_key(rng);
275
276            let mut safety_rules_test_config =
277                SafetyRulesTestConfig::new(peer_id);
278            safety_rules_test_config.random_consensus_key(rng);
279            safety_rules_test_config.random_execution_key(&mut cloned_rng);
280            self.consensus.safety_rules.test = Some(safety_rules_test_config);
281        } else {
282            /*self.validator_network = None;
283            if self.full_node_networks.is_empty() {
284                let network_config =
285                    NetworkConfig::network_with_id(NetworkId::Public);
286                self.full_node_networks.push(network_config);
287            }
288            for network in &mut self.full_node_networks {
289                network.random(rng);
290            }*/
291        }
292        self.set_data_dir(test.temp_dir().unwrap().to_path_buf());
293        self.test = Some(test);
294    }
295
296    fn default_config(serialized: &str, path: &'static str) -> Self {
297        let config = Self::parse(serialized)
298            .unwrap_or_else(|e| panic!("Error in {}: {}", path, e));
299        config
300        /*
301        config
302            .validate_network_configs()
303            .unwrap_or_else(|e| panic!("Error in {}: {}", path, e))*/
304    }
305
306    pub fn default_for_public_full_node() -> Self {
307        let contents = std::include_str!("test_data/public_full_node.yaml");
308        Self::default_config(contents, "default_for_public_full_node")
309    }
310
311    pub fn default_for_validator() -> Self {
312        let contents = std::include_str!("test_data/validator.yaml");
313        Self::default_config(contents, "default_for_validator")
314    }
315
316    pub fn default_for_validator_full_node() -> Self {
317        let contents = std::include_str!("test_data/validator_full_node.yaml");
318        Self::default_config(contents, "default_for_validator_full_node")
319    }
320}
321
322pub trait PersistableConfig: Serialize + DeserializeOwned {
323    fn load_config<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
324        let mut file = File::open(&path).map_err(|e| {
325            Error::IO(path.as_ref().to_str().unwrap().to_string(), e)
326        })?;
327        let mut contents = String::new();
328        file.read_to_string(&mut contents).map_err(|e| {
329            Error::IO(path.as_ref().to_str().unwrap().to_string(), e)
330        })?;
331        Self::parse(&contents)
332    }
333
334    fn save_config<P: AsRef<Path>>(&self, output_file: P) -> Result<(), Error> {
335        let contents = yaml_serde::to_string(&self)
336            .map_err(|e| {
337                Error::Yaml(
338                    output_file.as_ref().to_str().unwrap().to_string(),
339                    e,
340                )
341            })?
342            .into_bytes();
343        let mut file = File::create(output_file.as_ref()).map_err(|e| {
344            Error::IO(output_file.as_ref().to_str().unwrap().to_string(), e)
345        })?;
346        file.write_all(&contents).map_err(|e| {
347            Error::IO(output_file.as_ref().to_str().unwrap().to_string(), e)
348        })?;
349        Ok(())
350    }
351
352    fn parse(serialized: &str) -> Result<Self, Error> {
353        yaml_serde::from_str(&serialized)
354            .map_err(|e| Error::Yaml("config".to_string(), e))
355    }
356}
357
358impl<T: ?Sized> PersistableConfig for T where T: Serialize + DeserializeOwned {}
359
360#[derive(Debug)]
361pub struct RootPath {
362    root_path: PathBuf,
363}
364
365impl RootPath {
366    pub fn new<P: AsRef<Path>>(path: P) -> Self {
367        let root_path = if let Some(parent) = path.as_ref().parent() {
368            parent.to_path_buf()
369        } else {
370            PathBuf::from("")
371        };
372
373        Self { root_path }
374    }
375
376    /// This function assumes that the path is already a directory
377    pub fn new_path<P: AsRef<Path>>(path: P) -> Self {
378        let root_path = path.as_ref().to_path_buf();
379        Self { root_path }
380    }
381
382    /// This adds a full path when loading / storing if one is not specified
383    pub fn full_path(&self, file_path: &Path) -> PathBuf {
384        if file_path.is_relative() {
385            self.root_path.join(file_path)
386        } else {
387            file_path.to_path_buf()
388        }
389    }
390}
391
392#[cfg(test)]
393mod test {
394    use super::*;
395
396    #[test]
397    fn verify_role_type_conversion() {
398        // Verify relationship between RoleType and as_string() is reflexive
399        let validator = RoleType::Validator;
400        let full_node = RoleType::FullNode;
401        let converted_validator =
402            RoleType::from_str(validator.as_str()).unwrap();
403        let converted_full_node =
404            RoleType::from_str(full_node.as_str()).unwrap();
405        assert_eq!(converted_validator, validator);
406        assert_eq!(converted_full_node, full_node);
407    }
408
409    #[test]
410    // TODO(joshlind): once the 'matches' crate becomes stable, clean this test
411    // up!
412    fn verify_parse_role_error_on_invalid_role() {
413        let invalid_role_type = "this is not a valid role type";
414        match RoleType::from_str(invalid_role_type) {
415            Err(ParseRoleError(_)) => { /* the expected error was thrown! */ }
416            _ => panic!("A ParseRoleError should have been thrown on the invalid role type!"),
417        }
418    }
419
420    /*#[test]
421    fn verify_configs() {
422        NodeConfig::default_for_public_full_node();
423        NodeConfig::default_for_validator();
424        NodeConfig::default_for_validator_full_node();
425
426        let docker_public_full_node =
427            std::include_str!("../../../docker/compose/public_full_node/public_full_node.yaml");
428        // Only verify it is in the correct format as the values cannot be loaded for this config
429        NodeConfig::parse(docker_public_full_node).unwrap();
430
431        let contents = std::include_str!("test_data/safety_rules.yaml");
432        SafetyRulesConfig::parse(&contents)
433            .unwrap_or_else(|e| panic!("Error in safety_rules.yaml: {}", e));
434    }*/
435}