1use alloc::collections::{btree_map, BTreeMap};
10use core::{fmt, str::FromStr};
11use std::{
12 fs,
13 path::{Path, PathBuf},
14};
15
16use serde::{de, de::Error as _, ser, Deserialize, Serialize};
17use tendermint::{genesis::Genesis, node, Moniker, Timeout};
18
19use crate::{net, node_key::NodeKey, prelude::*, Error};
20
21#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
23pub struct TendermintConfig {
24 pub proxy_app: net::Address,
27
28 pub moniker: Moniker,
30
31 pub fast_sync: bool,
35
36 pub db_backend: DbBackend,
38
39 pub db_dir: PathBuf,
41
42 pub log_level: LogLevel,
44
45 pub log_format: LogFormat,
47
48 pub genesis_file: PathBuf,
50
51 pub priv_validator_key_file: Option<PathBuf>,
54
55 pub priv_validator_state_file: PathBuf,
57
58 #[serde(
61 deserialize_with = "deserialize_optional_value",
62 serialize_with = "serialize_optional_value"
63 )]
64 pub priv_validator_laddr: Option<net::Address>,
65
66 pub node_key_file: PathBuf,
69
70 pub abci: AbciMode,
72
73 pub filter_peers: bool,
76
77 pub rpc: RpcConfig,
79
80 pub p2p: P2PConfig,
82
83 pub mempool: MempoolConfig,
85
86 pub consensus: ConsensusConfig,
88
89 #[serde(default)]
92 pub storage: StorageConfig,
93
94 pub tx_index: TxIndexConfig,
96
97 pub instrumentation: InstrumentationConfig,
99
100 pub statesync: StatesyncConfig,
102
103 pub fastsync: FastsyncConfig,
105}
106
107impl TendermintConfig {
108 pub fn parse_toml<T: AsRef<str>>(toml_string: T) -> Result<Self, Error> {
110 let res = toml::from_str(toml_string.as_ref()).map_err(Error::toml)?;
111
112 Ok(res)
113 }
114
115 pub fn load_toml_file<P>(path: &P) -> Result<Self, Error>
117 where
118 P: AsRef<Path>,
119 {
120 let toml_string = fs::read_to_string(path)
121 .map_err(|e| Error::file_io(format!("{}", path.as_ref().display()), e))?;
122
123 Self::parse_toml(toml_string)
124 }
125
126 pub fn load_genesis_file(&self, home: impl AsRef<Path>) -> Result<Genesis, Error> {
128 let path = home.as_ref().join(&self.genesis_file);
129 let genesis_json = fs::read_to_string(&path)
130 .map_err(|e| Error::file_io(format!("{}", path.display()), e))?;
131
132 let res = serde_json::from_str(genesis_json.as_ref()).map_err(Error::serde_json)?;
133
134 Ok(res)
135 }
136
137 pub fn load_node_key(&self, home: impl AsRef<Path>) -> Result<NodeKey, Error> {
139 let path = home.as_ref().join(&self.node_key_file);
140 NodeKey::load_json_file(&path)
141 }
142}
143
144#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
146pub enum DbBackend {
147 #[serde(rename = "goleveldb")]
149 GoLevelDb,
150
151 #[serde(rename = "cleveldb")]
153 CLevelDb,
154
155 #[serde(rename = "boltdb")]
157 BoltDb,
158
159 #[serde(rename = "rocksdb")]
161 RocksDb,
162
163 #[serde(rename = "badgerdb")]
165 BadgerDb,
166}
167
168#[derive(Clone, Debug, Eq, PartialEq)]
170pub struct LogLevel {
171 pub global: Option<String>,
173 components: BTreeMap<String, String>,
174}
175
176impl LogLevel {
177 pub fn get<S>(&self, key: S) -> Option<&str>
179 where
180 S: AsRef<str>,
181 {
182 self.components
183 .get(key.as_ref())
184 .or(self.global.as_ref())
185 .map(AsRef::as_ref)
186 }
187
188 pub fn iter(&self) -> LogLevelIter<'_> {
190 self.components.iter()
191 }
192}
193
194pub type LogLevelIter<'a> = btree_map::Iter<'a, String, String>;
196
197impl FromStr for LogLevel {
198 type Err = Error;
199
200 fn from_str(s: &str) -> Result<Self, Self::Err> {
201 let mut global = None;
202 let mut components = BTreeMap::new();
203
204 for level in s.split(',') {
205 let parts = level.split(':').collect::<Vec<_>>();
206
207 if parts.len() == 1 {
208 global = Some(parts[0].to_owned());
209 continue;
210 } else if parts.len() != 2 {
211 return Err(Error::parse(format!("error parsing log level: {level}")));
212 }
213
214 let key = parts[0].to_owned();
215 let value = parts[1].to_owned();
216
217 if components.insert(key, value).is_some() {
218 return Err(Error::parse(format!(
219 "duplicate log level setting for: {level}"
220 )));
221 }
222 }
223
224 Ok(LogLevel { global, components })
225 }
226}
227
228impl fmt::Display for LogLevel {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 if let Some(global) = &self.global {
231 write!(f, "{global}")?;
232 if !self.components.is_empty() {
233 write!(f, ",")?;
234 }
235 }
236 for (i, (k, v)) in self.components.iter().enumerate() {
237 write!(f, "{k}:{v}")?;
238
239 if i < self.components.len() - 1 {
240 write!(f, ",")?;
241 }
242 }
243
244 Ok(())
245 }
246}
247
248impl<'de> Deserialize<'de> for LogLevel {
249 fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
250 let levels = String::deserialize(deserializer)?;
251 Self::from_str(&levels).map_err(|e| D::Error::custom(format!("{e}")))
252 }
253}
254
255impl Serialize for LogLevel {
256 fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
257 self.to_string().serialize(serializer)
258 }
259}
260
261#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
263pub enum LogFormat {
264 #[serde(rename = "plain")]
266 Plain,
267
268 #[serde(rename = "json")]
270 Json,
271}
272
273#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
275pub enum AbciMode {
276 #[serde(rename = "socket")]
278 Socket,
279
280 #[serde(rename = "grpc")]
282 Grpc,
283}
284
285#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
287pub struct RpcConfig {
288 pub laddr: net::Address,
290
291 pub cors_allowed_origins: Vec<CorsOrigin>,
295
296 pub cors_allowed_methods: Vec<CorsMethod>,
298
299 pub cors_allowed_headers: Vec<CorsHeader>,
301
302 #[serde(
305 deserialize_with = "deserialize_optional_value",
306 serialize_with = "serialize_optional_value"
307 )]
308 pub grpc_laddr: Option<net::Address>,
309
310 pub grpc_max_open_connections: u64,
313
314 #[serde(rename = "unsafe")]
316 pub unsafe_commands: bool,
317
318 pub max_open_connections: u64,
321
322 pub max_subscription_clients: u64,
324
325 pub max_subscriptions_per_client: u64,
327
328 pub timeout_broadcast_tx_commit: Timeout,
330
331 pub max_body_bytes: u64,
333
334 pub max_header_bytes: u64,
336
337 #[serde(
339 deserialize_with = "deserialize_optional_value",
340 serialize_with = "serialize_optional_value"
341 )]
342 pub tls_cert_file: Option<PathBuf>,
343
344 #[serde(
346 deserialize_with = "deserialize_optional_value",
347 serialize_with = "serialize_optional_value"
348 )]
349 pub tls_key_file: Option<PathBuf>,
350
351 #[serde(
353 deserialize_with = "deserialize_optional_value",
354 serialize_with = "serialize_optional_value"
355 )]
356 pub pprof_laddr: Option<net::Address>,
357}
358
359#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
362pub struct CorsOrigin(String);
363
364impl AsRef<str> for CorsOrigin {
365 fn as_ref(&self) -> &str {
366 self.0.as_ref()
367 }
368}
369
370impl fmt::Display for CorsOrigin {
371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372 write!(f, "{}", &self.0)
373 }
374}
375
376#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
379pub struct CorsMethod(String);
380
381impl AsRef<str> for CorsMethod {
382 fn as_ref(&self) -> &str {
383 self.0.as_ref()
384 }
385}
386
387impl fmt::Display for CorsMethod {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 write!(f, "{}", &self.0)
390 }
391}
392
393#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
396pub struct CorsHeader(String);
397
398impl AsRef<str> for CorsHeader {
399 fn as_ref(&self) -> &str {
400 self.0.as_ref()
401 }
402}
403
404impl fmt::Display for CorsHeader {
405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406 write!(f, "{}", &self.0)
407 }
408}
409
410#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
412pub struct P2PConfig {
413 pub laddr: net::Address,
415
416 #[serde(
421 deserialize_with = "deserialize_optional_value",
422 serialize_with = "serialize_optional_value"
423 )]
424 pub external_address: Option<net::Address>,
425
426 #[serde(
428 serialize_with = "serialize_comma_separated_list",
429 deserialize_with = "deserialize_comma_separated_list"
430 )]
431 pub seeds: Vec<net::Address>,
432
433 #[serde(
435 serialize_with = "serialize_comma_separated_list",
436 deserialize_with = "deserialize_comma_separated_list"
437 )]
438 pub persistent_peers: Vec<net::Address>,
439
440 pub upnp: bool,
442
443 pub addr_book_file: PathBuf,
445
446 pub addr_book_strict: bool,
449
450 pub max_num_inbound_peers: u64,
452
453 pub max_num_outbound_peers: u64,
455
456 #[serde(
459 serialize_with = "serialize_comma_separated_list",
460 deserialize_with = "deserialize_comma_separated_list"
461 )]
462 pub unconditional_peer_ids: Vec<node::Id>,
463
464 pub persistent_peers_max_dial_period: Timeout,
466
467 pub flush_throttle_timeout: Timeout,
469
470 pub max_packet_msg_payload_size: u64,
472
473 pub send_rate: TransferRate,
475
476 pub recv_rate: TransferRate,
478
479 pub pex: bool,
481
482 pub seed_mode: bool,
487
488 #[serde(
490 serialize_with = "serialize_comma_separated_list",
491 deserialize_with = "deserialize_comma_separated_list"
492 )]
493 pub private_peer_ids: Vec<node::Id>,
494
495 pub allow_duplicate_ip: bool,
497
498 pub handshake_timeout: Timeout,
500
501 pub dial_timeout: Timeout,
503}
504
505#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
507pub struct MempoolConfig {
508 pub recheck: bool,
510
511 pub broadcast: bool,
513
514 #[serde(
516 deserialize_with = "deserialize_optional_value",
517 serialize_with = "serialize_optional_value"
518 )]
519 pub wal_dir: Option<PathBuf>,
520
521 pub size: u64,
523
524 pub max_txs_bytes: u64,
528
529 pub cache_size: u64,
531
532 #[serde(rename = "keep-invalid-txs-in-cache")]
536 pub keep_invalid_txs_in_cache: bool,
537
538 pub max_tx_bytes: u64,
541
542 pub max_batch_bytes: u64,
546}
547
548#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
550pub struct ConsensusConfig {
551 pub wal_file: PathBuf,
553
554 pub timeout_propose: Timeout,
556
557 pub timeout_propose_delta: Timeout,
559
560 pub timeout_prevote: Timeout,
562
563 pub timeout_prevote_delta: Timeout,
565
566 pub timeout_precommit: Timeout,
568
569 pub timeout_precommit_delta: Timeout,
571
572 pub timeout_commit: Timeout,
574
575 pub double_sign_check_height: u64,
581
582 pub skip_timeout_commit: bool,
584
585 pub create_empty_blocks: bool,
587
588 pub create_empty_blocks_interval: Timeout,
590
591 pub peer_gossip_sleep_duration: Timeout,
593
594 pub peer_query_maj23_sleep_duration: Timeout,
596}
597
598#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default)]
600pub struct StorageConfig {
601 #[serde(default)]
602 pub discard_abci_responses: bool,
603}
604
605#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
607pub struct TxIndexConfig {
608 #[serde(default)]
610 pub indexer: TxIndexer,
611}
612
613#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Default)]
615pub enum TxIndexer {
616 #[serde(rename = "null")]
619 Null,
620
621 #[serde(rename = "kv")]
624 #[default]
625 Kv,
626}
627
628#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
630pub struct InstrumentationConfig {
631 pub prometheus: bool,
634
635 pub prometheus_listen_addr: String,
638
639 pub max_open_connections: u64,
641
642 pub namespace: String,
644}
645
646#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
648pub struct StatesyncConfig {
649 pub enable: bool,
655
656 #[serde(
664 serialize_with = "serialize_comma_separated_list",
665 deserialize_with = "deserialize_comma_separated_list"
666 )]
667 pub rpc_servers: Vec<String>,
668
669 pub trust_height: u64,
671
672 pub trust_hash: String,
674
675 pub trust_period: String,
677
678 pub discovery_time: Timeout,
680
681 pub temp_dir: String,
684}
685
686#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
688pub struct FastsyncConfig {
689 pub version: String,
693}
694
695#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
697pub struct TransferRate(u64);
698
699impl TransferRate {
700 pub fn bytes_per_sec(self) -> u64 {
702 self.0
703 }
704}
705
706fn deserialize_optional_value<'de, D, T, E>(deserializer: D) -> Result<Option<T>, D::Error>
708where
709 D: de::Deserializer<'de>,
710 T: FromStr<Err = E>,
711 E: fmt::Display,
712{
713 let string = Option::<String>::deserialize(deserializer).map(|str| str.unwrap_or_default())?;
714
715 if string.is_empty() {
716 return Ok(None);
717 }
718
719 string
720 .parse()
721 .map(Some)
722 .map_err(|e| D::Error::custom(format!("{e}")))
723}
724
725fn serialize_optional_value<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
726where
727 S: ser::Serializer,
728 T: Serialize,
729{
730 match value {
731 Some(value) => value.serialize(serializer),
732 None => "".serialize(serializer),
733 }
734}
735
736fn deserialize_comma_separated_list<'de, D, T, E>(deserializer: D) -> Result<Vec<T>, D::Error>
738where
739 D: de::Deserializer<'de>,
740 T: FromStr<Err = E>,
741 E: fmt::Display,
742{
743 let mut result = vec![];
744 let string = String::deserialize(deserializer)?;
745
746 if string.is_empty() {
747 return Ok(result);
748 }
749
750 for item in string.split(',') {
751 result.push(item.parse().map_err(|e| D::Error::custom(format!("{e}")))?);
752 }
753
754 Ok(result)
755}
756
757fn serialize_comma_separated_list<S, T>(list: &[T], serializer: S) -> Result<S::Ok, S::Error>
759where
760 S: ser::Serializer,
761 T: ToString,
762{
763 let str_list = list.iter().map(|addr| addr.to_string()).collect::<Vec<_>>();
764 str_list.join(",").serialize(serializer)
765}