1use crate::{
10 arg_enums::Database, error::Result, DatabaseParams, ImportParams, KeystoreParams,
11 NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, RpcEndpoint, SharedParams,
12 SubstrateCli,
13};
14use log::warn;
15use names::{Generator, Name};
16use soil_client::tracing::logging::LoggerBuilder;
17use soil_service::{
18 config::{
19 BasePath, Configuration, DatabaseSource, ExecutorConfiguration, IpNetwork, KeystoreConfig,
20 NetworkConfiguration, NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode,
21 Role, RpcBatchRequestConfig, RpcConfiguration, RpcMethods, TelemetryEndpoints,
22 TransactionPoolOptions, WasmExecutionMethod,
23 },
24 BlocksPruning, ChainSpec, TracingReceiver,
25};
26use std::{num::NonZeroU32, path::PathBuf};
27
28pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64;
30
31pub(crate) const DEFAULT_NETWORK_CONFIG_PATH: &str = "network";
33
34const RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT: u64 = 10_000;
36
37pub const RPC_DEFAULT_PORT: u16 = 9944;
39pub const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
41pub const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
43pub const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 15;
45pub const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 100;
47pub const RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN: u32 = 64;
50
51pub trait DefaultConfigurationValues {
56 fn p2p_listen_port() -> u16 {
60 30333
61 }
62
63 fn rpc_listen_port() -> u16 {
67 RPC_DEFAULT_PORT
68 }
69
70 fn prometheus_listen_port() -> u16 {
74 9615
75 }
76}
77
78impl DefaultConfigurationValues for () {}
79
80pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
82 fn shared_params(&self) -> &SharedParams;
84
85 fn import_params(&self) -> Option<&ImportParams> {
87 None
88 }
89
90 fn pruning_params(&self) -> Option<&PruningParams> {
92 self.import_params().map(|x| &x.pruning_params)
93 }
94
95 fn keystore_params(&self) -> Option<&KeystoreParams> {
97 None
98 }
99
100 fn network_params(&self) -> Option<&NetworkParams> {
102 None
103 }
104
105 fn offchain_worker_params(&self) -> Option<&OffchainWorkerParams> {
107 None
108 }
109
110 fn node_key_params(&self) -> Option<&NodeKeyParams> {
112 self.network_params().map(|x| &x.node_key_params)
113 }
114
115 fn database_params(&self) -> Option<&DatabaseParams> {
117 self.import_params().map(|x| &x.database_params)
118 }
119
120 fn base_path(&self) -> Result<Option<BasePath>> {
124 self.shared_params().base_path()
125 }
126
127 fn is_dev(&self) -> Result<bool> {
131 Ok(self.shared_params().is_dev())
132 }
133
134 fn role(&self, _is_dev: bool) -> Result<Role> {
138 Ok(Role::Full)
139 }
140
141 fn transaction_pool(&self, _is_dev: bool) -> Result<TransactionPoolOptions> {
145 Ok(Default::default())
146 }
147
148 fn network_config(
154 &self,
155 chain_spec: &Box<dyn ChainSpec>,
156 is_dev: bool,
157 is_validator: bool,
158 net_config_dir: PathBuf,
159 client_id: &str,
160 node_name: &str,
161 node_key: NodeKeyConfig,
162 default_listen_port: u16,
163 ) -> Result<NetworkConfiguration> {
164 let network_config = if let Some(network_params) = self.network_params() {
165 network_params.network_config(
166 chain_spec,
167 is_dev,
168 is_validator,
169 Some(net_config_dir),
170 client_id,
171 node_name,
172 node_key,
173 default_listen_port,
174 )
175 } else {
176 NetworkConfiguration::new(node_name, client_id, node_key, Some(net_config_dir))
177 };
178
179 Ok(network_config)
184 }
185
186 fn keystore_config(&self, config_dir: &PathBuf) -> Result<KeystoreConfig> {
191 self.keystore_params()
192 .map(|x| x.keystore_config(config_dir))
193 .unwrap_or_else(|| Ok(KeystoreConfig::InMemory))
194 }
195
196 fn database_cache_size(&self) -> Result<Option<usize>> {
200 Ok(self.database_params().map(|x| x.database_cache_size()).unwrap_or_default())
201 }
202
203 fn database(&self) -> Result<Option<Database>> {
207 Ok(self.database_params().and_then(|x| x.database()))
208 }
209
210 fn database_config(
212 &self,
213 base_path: &PathBuf,
214 cache_size: usize,
215 database: Database,
216 ) -> Result<DatabaseSource> {
217 let role_dir = "full";
218 let rocksdb_path = base_path.join("db").join(role_dir);
219 let paritydb_path = base_path.join("paritydb").join(role_dir);
220 Ok(match database {
221 #[cfg(feature = "rocksdb")]
222 Database::RocksDb => DatabaseSource::RocksDb { path: rocksdb_path, cache_size },
223 Database::ParityDb => DatabaseSource::ParityDb { path: paritydb_path },
224 Database::ParityDbDeprecated => {
225 eprintln!(
226 "WARNING: \"paritydb-experimental\" database setting is deprecated and will be removed in future releases. \
227 Please update your setup to use the new value: \"paritydb\"."
228 );
229 DatabaseSource::ParityDb { path: paritydb_path }
230 },
231 Database::Auto => DatabaseSource::Auto { paritydb_path, rocksdb_path, cache_size },
232 })
233 }
234
235 fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
240 Ok(self.import_params().map(|x| x.trie_cache_maximum_size()).unwrap_or_default())
241 }
242
243 fn warm_up_trie_cache(&self) -> Result<Option<soil_service::config::TrieCacheWarmUpStrategy>> {
247 Ok(self
248 .import_params()
249 .map(|x| x.warm_up_trie_cache().map(|x| x.into()))
250 .unwrap_or_default())
251 }
252
253 fn state_pruning(&self) -> Result<Option<PruningMode>> {
258 self.pruning_params()
259 .map(|x| x.state_pruning())
260 .unwrap_or_else(|| Ok(Default::default()))
261 }
262
263 fn blocks_pruning(&self) -> Result<BlocksPruning> {
268 self.pruning_params()
269 .map(|x| x.blocks_pruning())
270 .unwrap_or_else(|| Ok(BlocksPruning::KeepFinalized))
271 }
272
273 fn chain_id(&self, is_dev: bool) -> Result<String> {
277 Ok(self.shared_params().chain_id(is_dev))
278 }
279
280 fn node_name(&self) -> Result<String> {
284 Ok(generate_node_name())
285 }
286
287 fn wasm_method(&self) -> Result<WasmExecutionMethod> {
292 Ok(self.import_params().map(|x| x.wasm_method()).unwrap_or_default())
293 }
294
295 fn wasm_runtime_overrides(&self) -> Option<PathBuf> {
299 self.import_params().map(|x| x.wasm_runtime_overrides()).unwrap_or_default()
300 }
301
302 fn rpc_addr(&self, _default_listen_port: u16) -> Result<Option<Vec<RpcEndpoint>>> {
304 Ok(None)
305 }
306
307 fn rpc_methods(&self) -> Result<RpcMethods> {
312 Ok(Default::default())
313 }
314
315 fn rpc_max_connections(&self) -> Result<u32> {
317 Ok(RPC_DEFAULT_MAX_CONNECTIONS)
318 }
319
320 fn rpc_cors(&self, _is_dev: bool) -> Result<Option<Vec<String>>> {
324 Ok(Some(Vec::new()))
325 }
326
327 fn rpc_max_request_size(&self) -> Result<u32> {
329 Ok(RPC_DEFAULT_MAX_REQUEST_SIZE_MB)
330 }
331
332 fn rpc_max_response_size(&self) -> Result<u32> {
334 Ok(RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)
335 }
336
337 fn rpc_max_subscriptions_per_connection(&self) -> Result<u32> {
339 Ok(RPC_DEFAULT_MAX_SUBS_PER_CONN)
340 }
341
342 fn rpc_buffer_capacity_per_connection(&self) -> Result<u32> {
344 Ok(RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)
345 }
346
347 fn rpc_batch_config(&self) -> Result<RpcBatchRequestConfig> {
349 Ok(RpcBatchRequestConfig::Unlimited)
350 }
351
352 fn rpc_rate_limit(&self) -> Result<Option<NonZeroU32>> {
354 Ok(None)
355 }
356
357 fn rpc_rate_limit_whitelisted_ips(&self) -> Result<Vec<IpNetwork>> {
359 Ok(vec![])
360 }
361
362 fn rpc_rate_limit_trust_proxy_headers(&self) -> Result<bool> {
364 Ok(false)
365 }
366
367 fn prometheus_config(
371 &self,
372 _default_listen_port: u16,
373 _chain_spec: &Box<dyn ChainSpec>,
374 ) -> Result<Option<PrometheusConfig>> {
375 Ok(None)
376 }
377
378 fn telemetry_endpoints(
382 &self,
383 chain_spec: &Box<dyn ChainSpec>,
384 ) -> Result<Option<TelemetryEndpoints>> {
385 Ok(chain_spec.telemetry_endpoints().clone())
386 }
387
388 fn default_heap_pages(&self) -> Result<Option<u64>> {
392 Ok(None)
393 }
394
395 fn offchain_worker(&self, role: &Role) -> Result<OffchainWorkerConfig> {
399 self.offchain_worker_params()
400 .map(|x| x.offchain_worker(role))
401 .unwrap_or_else(|| Ok(OffchainWorkerConfig::default()))
402 }
403
404 fn force_authoring(&self) -> Result<bool> {
408 Ok(Default::default())
409 }
410
411 fn disable_grandpa(&self) -> Result<bool> {
415 Ok(Default::default())
416 }
417
418 fn dev_key_seed(&self, _is_dev: bool) -> Result<Option<String>> {
422 Ok(Default::default())
423 }
424
425 fn tracing_targets(&self) -> Result<Option<String>> {
430 Ok(self.shared_params().tracing_targets())
431 }
432
433 fn tracing_receiver(&self) -> Result<TracingReceiver> {
438 Ok(self.shared_params().tracing_receiver())
439 }
440
441 fn node_key(&self, net_config_dir: &PathBuf) -> Result<NodeKeyConfig> {
446 let is_dev = self.is_dev()?;
447 let role = self.role(is_dev)?;
448 self.node_key_params()
449 .map(|x| x.node_key(net_config_dir, role, is_dev))
450 .unwrap_or_else(|| Ok(Default::default()))
451 }
452
453 fn max_runtime_instances(&self) -> Result<Option<usize>> {
457 Ok(Default::default())
458 }
459
460 fn runtime_cache_size(&self) -> Result<u8> {
464 Ok(2)
465 }
466
467 fn announce_block(&self) -> Result<bool> {
471 Ok(true)
472 }
473
474 fn create_configuration<C: SubstrateCli>(
476 &self,
477 cli: &C,
478 tokio_handle: tokio::runtime::Handle,
479 ) -> Result<Configuration> {
480 let is_dev = self.is_dev()?;
481 let chain_id = self.chain_id(is_dev)?;
482 let chain_spec = cli.load_spec(&chain_id)?;
483 let base_path = base_path_or_default(self.base_path()?, &C::executable_name());
484 let config_dir = build_config_dir(&base_path, chain_spec.id());
485 let net_config_dir = build_net_config_dir(&config_dir);
486 let client_id = C::client_id();
487 let database_cache_size = self.database_cache_size()?.unwrap_or(1024);
488 let database = self.database()?.unwrap_or(
489 #[cfg(feature = "rocksdb")]
490 {
491 Database::RocksDb
492 },
493 #[cfg(not(feature = "rocksdb"))]
494 {
495 Database::ParityDb
496 },
497 );
498 let node_key = self.node_key(&net_config_dir)?;
499 let role = self.role(is_dev)?;
500 let max_runtime_instances = self.max_runtime_instances()?.unwrap_or(8);
501 let is_validator = role.is_authority();
502 let keystore = self.keystore_config(&config_dir)?;
503 let telemetry_endpoints = self.telemetry_endpoints(&chain_spec)?;
504 let runtime_cache_size = self.runtime_cache_size()?;
505
506 let rpc_addrs: Option<Vec<soil_service::config::RpcEndpoint>> = self
507 .rpc_addr(DCV::rpc_listen_port())?
508 .map(|addrs| addrs.into_iter().map(Into::into).collect());
509
510 Ok(Configuration {
511 impl_name: C::impl_name(),
512 impl_version: C::impl_version(),
513 tokio_handle,
514 transaction_pool: self.transaction_pool(is_dev)?,
515 network: self.network_config(
516 &chain_spec,
517 is_dev,
518 is_validator,
519 net_config_dir,
520 client_id.as_str(),
521 self.node_name()?.as_str(),
522 node_key,
523 DCV::p2p_listen_port(),
524 )?,
525 keystore,
526 database: self.database_config(&config_dir, database_cache_size, database)?,
527 data_path: config_dir,
528 trie_cache_maximum_size: self.trie_cache_maximum_size()?,
529 warm_up_trie_cache: self.warm_up_trie_cache()?,
530 state_pruning: self.state_pruning()?,
531 blocks_pruning: self.blocks_pruning()?,
532 executor: ExecutorConfiguration {
533 wasm_method: self.wasm_method()?,
534 default_heap_pages: self.default_heap_pages()?,
535 max_runtime_instances,
536 runtime_cache_size,
537 },
538 wasm_runtime_overrides: self.wasm_runtime_overrides(),
539 rpc: RpcConfiguration {
540 addr: rpc_addrs,
541 methods: self.rpc_methods()?,
542 max_connections: self.rpc_max_connections()?,
543 cors: self.rpc_cors(is_dev)?,
544 max_request_size: self.rpc_max_request_size()?,
545 max_response_size: self.rpc_max_response_size()?,
546 id_provider: None,
547 max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?,
548 port: DCV::rpc_listen_port(),
549 message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?,
550 batch_config: self.rpc_batch_config()?,
551 rate_limit: self.rpc_rate_limit()?,
552 rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips()?,
553 rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers()?,
554 request_logger_limit: if is_dev { 1024 * 1024 } else { 1024 },
555 },
556 prometheus_config: self
557 .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?,
558 telemetry_endpoints,
559 offchain_worker: self.offchain_worker(&role)?,
560 force_authoring: self.force_authoring()?,
561 disable_grandpa: self.disable_grandpa()?,
562 dev_key_seed: self.dev_key_seed(is_dev)?,
563 tracing_targets: self.tracing_targets()?,
564 tracing_receiver: self.tracing_receiver()?,
565 chain_spec,
566 announce_block: self.announce_block()?,
567 role,
568 base_path,
569 })
570 }
571
572 fn log_filters(&self) -> Result<String> {
579 Ok(self.shared_params().log_filters().join(","))
580 }
581
582 fn detailed_log_output(&self) -> Result<bool> {
584 Ok(self.shared_params().detailed_log_output())
585 }
586
587 fn enable_log_reloading(&self) -> Result<bool> {
589 Ok(self.shared_params().enable_log_reloading())
590 }
591
592 fn disable_log_color(&self) -> Result<bool> {
594 Ok(self.shared_params().disable_log_color())
595 }
596
597 fn init<F>(&self, support_url: &String, impl_version: &String, logger_hook: F) -> Result<()>
626 where
627 F: FnOnce(&mut LoggerBuilder),
628 {
629 subsoil::panic_handler::set(support_url, impl_version);
630
631 let mut logger = LoggerBuilder::new(self.log_filters()?);
632 logger
633 .with_log_reloading(self.enable_log_reloading()?)
634 .with_detailed_output(self.detailed_log_output()?);
635
636 if let Some(tracing_targets) = self.tracing_targets()? {
637 let tracing_receiver = self.tracing_receiver()?;
638 logger.with_profiling(tracing_receiver, tracing_targets);
639 }
640
641 if self.disable_log_color()? {
642 logger.with_colors(false);
643 }
644
645 logger_hook(&mut logger);
647
648 logger.init()?;
649
650 match fdlimit::raise_fd_limit() {
651 Ok(fdlimit::Outcome::LimitRaised { to, .. }) => {
652 if to < RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT {
653 warn!(
654 "Low open file descriptor limit configured for the process. \
655 Current value: {:?}, recommended value: {:?}.",
656 to, RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT,
657 );
658 }
659 },
660 Ok(fdlimit::Outcome::Unsupported) => {
661 },
663 Err(error) => {
664 warn!(
665 "Failed to configure file descriptor limit for the process: \
666 {}, recommended value: {:?}.",
667 error, RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT,
668 );
669 },
670 }
671
672 Ok(())
673 }
674}
675
676pub fn generate_node_name() -> String {
678 loop {
679 let node_name = Generator::with_naming(Name::Numbered)
680 .next()
681 .expect("RNG is available on all supported platforms; qed");
682 let count = node_name.chars().count();
683
684 if count < NODE_NAME_MAX_LENGTH {
685 return node_name;
686 }
687 }
688}
689
690pub(crate) fn base_path_or_default(
692 base_path: Option<BasePath>,
693 executable_name: &String,
694) -> BasePath {
695 base_path.unwrap_or_else(|| BasePath::from_project("", "", executable_name))
696}
697
698pub(crate) fn build_config_dir(base_path: &BasePath, chain_spec_id: &str) -> PathBuf {
700 base_path.config_dir(chain_spec_id)
701}
702
703pub(crate) fn build_net_config_dir(config_dir: &PathBuf) -> PathBuf {
705 config_dir.join(DEFAULT_NETWORK_CONFIG_PATH)
706}
707
708pub(crate) fn build_network_key_dir_or_default(
711 base_path: Option<BasePath>,
712 chain_spec_id: &str,
713 executable_name: &String,
714) -> PathBuf {
715 let config_dir =
716 build_config_dir(&base_path_or_default(base_path, executable_name), chain_spec_id);
717 build_net_config_dir(&config_dir)
718}