saito_rust/
config_handler.rs1use figment::providers::{Format, Json};
2use figment::Figment;
3use log::{debug, error, info};
4use saito_core::core::util::configuration::{
5 get_default_issuance_writing_block_interval, BlockchainConfig, Configuration, ConsensusConfig,
6 Endpoint, PeerConfig, Server,
7};
8use serde::{Deserialize, Serialize};
9use std::io::{Error, ErrorKind};
10use std::path::Path;
11
12fn get_default_consensus() -> Option<ConsensusConfig> {
13 Some(ConsensusConfig::default())
14}
15
16#[derive(Deserialize, Debug, Serialize)]
17pub struct NodeConfigurations {
18 server: Server,
19 peers: Vec<PeerConfig>,
20 #[serde(skip)]
21 lite: bool,
22 spv_mode: Option<bool>,
23 #[serde(default = "get_default_consensus")]
24 consensus: Option<ConsensusConfig>,
25 blockchain: BlockchainConfig,
26}
27
28impl NodeConfigurations {
29 pub fn write_to_file(&self, config_file_path: String) -> Result<(), Error> {
30 let file = std::fs::File::create(config_file_path)?;
31 serde_json::to_writer_pretty(&file, &self)?;
32 Ok(())
33 }
34}
35impl Default for NodeConfigurations {
36 fn default() -> Self {
37 NodeConfigurations {
38 server: Server {
39 host: "127.0.0.1".to_string(),
40 port: 12101,
41 protocol: "http".to_string(),
42 endpoint: Endpoint {
43 host: "127.0.0.1".to_string(),
44 port: 12101,
45 protocol: "http".to_string(),
46 },
47 verification_threads: 4,
48 channel_size: 1000,
49 stat_timer_in_ms: 5000,
50 thread_sleep_time_in_ms: 10,
51 block_fetch_batch_size: 10,
52 reconnection_wait_time: 10,
53 },
54 peers: vec![],
55 lite: false,
56 spv_mode: Some(false),
57 consensus: Some(ConsensusConfig {
58 genesis_period: 100_000,
59 heartbeat_interval: 5_000,
60 prune_after_blocks: 8,
61 max_staker_recursions: 3,
62 default_social_stake: 0,
63 default_social_stake_period: 60,
64 }),
65 blockchain: BlockchainConfig {
66 last_block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
67 .to_string(),
68 last_block_id: 0,
69 last_timestamp: 0,
70 genesis_block_id: 0,
71 genesis_timestamp: 0,
72 lowest_acceptable_timestamp: 0,
73 lowest_acceptable_block_hash:
74 "0000000000000000000000000000000000000000000000000000000000000000".to_string(),
75 lowest_acceptable_block_id: 0,
76 fork_id: "0000000000000000000000000000000000000000000000000000000000000000"
77 .to_string(),
78 initial_loading_completed: false,
79 issuance_writing_block_interval: get_default_issuance_writing_block_interval(),
80 },
81 }
82 }
83}
84
85impl Configuration for NodeConfigurations {
86 fn get_server_configs(&self) -> Option<&Server> {
87 Some(&self.server)
88 }
89
90 fn get_peer_configs(&self) -> &Vec<PeerConfig> {
91 &self.peers
92 }
93
94 fn get_blockchain_configs(&self) -> &BlockchainConfig {
95 &self.blockchain
96 }
97
98 fn get_block_fetch_url(&self) -> String {
99 let endpoint = &self.get_server_configs().unwrap().endpoint;
100 endpoint.protocol.to_string()
101 + "://"
102 + endpoint.host.as_str()
103 + ":"
104 + endpoint.port.to_string().as_str()
105 }
106
107 fn is_spv_mode(&self) -> bool {
108 self.spv_mode.is_some() && self.spv_mode.unwrap()
109 }
110
111 fn is_browser(&self) -> bool {
112 false
113 }
114
115 fn replace(&mut self, config: &dyn Configuration) {
116 self.server = config.get_server_configs().cloned().unwrap();
117 self.peers = config.get_peer_configs().clone();
118 self.spv_mode = Some(config.is_spv_mode());
119 self.lite = config.is_spv_mode();
120 self.consensus = config.get_consensus_config().cloned();
121 }
122
123 fn get_consensus_config(&self) -> Option<&ConsensusConfig> {
124 self.consensus.as_ref()
125 }
126}
127
128pub struct ConfigHandler {}
129
130impl ConfigHandler {
131 pub fn load_configs(config_file_path: String) -> Result<NodeConfigurations, Error> {
132 debug!(
133 "loading configurations from path : {:?} current_dir = {:?}",
134 config_file_path,
135 std::env::current_dir()
136 );
137 let path = Path::new(config_file_path.as_str());
138 if !path.exists() {
139 info!("writing default config file to : {:?}", config_file_path);
140 if path.parent().is_some() {
141 std::fs::create_dir_all(path.parent().unwrap())?;
142 }
143 let configs = NodeConfigurations::default();
144 configs.write_to_file(config_file_path.to_string())?;
145 }
146 let configs = Figment::new()
148 .merge(Json::file(config_file_path))
149 .extract::<NodeConfigurations>();
150
151 if configs.is_err() {
152 error!("failed loading configs. {:?}", configs.err().unwrap());
153 return Err(std::io::Error::from(ErrorKind::InvalidInput));
154 }
155
156 Ok(configs.unwrap())
157 }
158}
159
160#[cfg(test)]
161mod test {
162 use std::io::ErrorKind;
163
164 use saito_core::core::util::configuration::Configuration;
165
166 use crate::config_handler::ConfigHandler;
167
168 #[test]
169 fn load_config_from_existing_file() {
170 let path = String::from("src/test/data/config_handler_tests.json");
171 let result = ConfigHandler::load_configs(path);
172 assert!(result.is_ok());
173 let configs = result.unwrap();
174 assert_eq!(
175 configs.get_server_configs().unwrap().host,
176 String::from("localhost")
177 );
178 assert_eq!(configs.get_server_configs().unwrap().port, 12101);
179 assert_eq!(
180 configs.get_server_configs().unwrap().protocol,
181 String::from("http")
182 );
183 assert_eq!(
184 configs.get_server_configs().unwrap().endpoint.host,
185 String::from("localhost")
186 );
187 assert_eq!(configs.get_server_configs().unwrap().endpoint.port, 12101);
188 assert_eq!(
189 configs.get_server_configs().unwrap().endpoint.protocol,
190 String::from("http")
191 );
192 }
193
194 #[test]
195 fn load_config_from_bad_file_format() {
196 let path = String::from("src/test/data/config_handler_tests_bad_format.xml");
197 let result = ConfigHandler::load_configs(path);
198 assert!(result.is_err());
199 assert_eq!(result.err().unwrap().kind(), ErrorKind::InvalidInput);
200 }
201
202 #[ignore]
204 #[test]
205 fn load_config_from_non_existing_file() {
206 let path = String::from("config/new_file_to_write.json");
208 let result = ConfigHandler::load_configs(path);
209 assert!(result.is_ok());
210 }
211}