1use 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#[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)]
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)]
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 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 Ok(config)
176 }
177
178 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 self.save_config(&output_path)?;
223 Ok(())
224 }
225
226 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 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 }
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 }
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 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 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 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 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(_)) => { }
416 _ => panic!("A ParseRoleError should have been thrown on the invalid role type!"),
417 }
418 }
419
420 }