1use ckb_types::global::DATA_DIR;
8use path_clean::PathClean;
9use std::fs;
10use std::path::{Path, PathBuf};
11
12use serde::{Deserialize, Serialize};
13
14use ckb_chain_spec::ChainSpec;
15pub use ckb_logger_config::Config as LogConfig;
16pub use ckb_metrics_config::Config as MetricsConfig;
17use ckb_resource::Resource;
18
19use super::configs::*;
20#[cfg(feature = "with_sentry")]
21use super::sentry_config::SentryConfig;
22use super::{cli, legacy, ExitCode};
23
24pub enum AppConfig {
28 CKB(Box<CKBAppConfig>),
30 Miner(Box<MinerAppConfig>),
32}
33
34#[derive(Clone, Debug, Serialize)]
39#[serde(deny_unknown_fields)]
40pub struct CKBAppConfig {
41 #[serde(skip)]
43 pub bin_name: String,
44 #[serde(skip)]
46 pub root_dir: PathBuf,
47 pub data_dir: PathBuf,
49 #[serde(default)]
51 pub ancient: PathBuf,
52 pub tmp_dir: Option<PathBuf>,
54 pub logger: LogConfig,
56 #[cfg(feature = "with_sentry")]
58 #[serde(default)]
59 pub sentry: SentryConfig,
60 #[serde(default)]
64 pub metrics: MetricsConfig,
65 #[serde(default)]
69 pub memory_tracker: MemoryTrackerConfig,
70 pub chain: ChainConfig,
72
73 pub block_assembler: Option<BlockAssemblerConfig>,
75 #[serde(default)]
77 pub db: DBConfig,
78 pub network: NetworkConfig,
80 pub rpc: RpcConfig,
82 pub tx_pool: TxPoolConfig,
84 #[serde(default)]
86 pub store: StoreConfig,
87 pub alert_signature: Option<NetworkAlertConfig>,
89 #[serde(default)]
91 pub notify: NotifyConfig,
92 #[serde(default)]
94 pub indexer: IndexerConfig,
95 #[serde(default)]
97 pub fee_estimator: FeeEstimatorConfig,
98}
99
100#[derive(Clone, Debug, Serialize)]
105#[serde(deny_unknown_fields)]
106pub struct MinerAppConfig {
107 #[serde(skip)]
109 pub bin_name: String,
110 #[serde(skip)]
112 pub root_dir: PathBuf,
113 pub data_dir: PathBuf,
115 pub chain: ChainConfig,
117 pub logger: LogConfig,
119 #[cfg(feature = "with_sentry")]
121 pub sentry: SentryConfig,
122 #[serde(default)]
126 pub metrics: MetricsConfig,
127 #[serde(default)]
131 pub memory_tracker: MemoryTrackerConfig,
132
133 pub miner: MinerConfig,
135}
136
137#[derive(Clone, Debug, Serialize, Deserialize)]
139#[serde(deny_unknown_fields)]
140pub struct ChainConfig {
141 pub spec: Resource,
143}
144
145impl AppConfig {
146 pub fn load_for_subcommand<P: AsRef<Path>>(
151 root_dir: P,
152 subcommand_name: &str,
153 ) -> Result<AppConfig, ExitCode> {
154 match subcommand_name {
155 cli::CMD_MINER => {
156 let resource = ensure_ckb_dir(Resource::miner_config(root_dir.as_ref()))?;
157 let config = MinerAppConfig::load_from_slice(&resource.get()?)?;
158
159 Ok(AppConfig::with_miner(
160 config.derive_options(root_dir.as_ref())?,
161 ))
162 }
163 _ => {
164 let resource = ensure_ckb_dir(Resource::ckb_config(root_dir.as_ref()))?;
165 let config = CKBAppConfig::load_from_slice(&resource.get()?)?;
166
167 Ok(AppConfig::with_ckb(
168 config.derive_options(root_dir.as_ref(), subcommand_name)?,
169 ))
170 }
171 }
172 }
173
174 pub fn logger(&self) -> &LogConfig {
176 match self {
177 AppConfig::CKB(config) => &config.logger,
178 AppConfig::Miner(config) => &config.logger,
179 }
180 }
181
182 #[cfg(feature = "with_sentry")]
184 pub fn sentry(&self) -> &SentryConfig {
185 match self {
186 AppConfig::CKB(config) => &config.sentry,
187 AppConfig::Miner(config) => &config.sentry,
188 }
189 }
190
191 pub fn metrics(&self) -> &MetricsConfig {
193 match self {
194 AppConfig::CKB(config) => &config.metrics,
195 AppConfig::Miner(config) => &config.metrics,
196 }
197 }
198
199 pub fn memory_tracker(&self) -> &MemoryTrackerConfig {
201 match self {
202 AppConfig::CKB(config) => &config.memory_tracker,
203 AppConfig::Miner(config) => &config.memory_tracker,
204 }
205 }
206
207 pub fn chain_spec(&self) -> Result<ChainSpec, ExitCode> {
209 let spec_resource = match self {
210 AppConfig::CKB(config) => &config.chain.spec,
211 AppConfig::Miner(config) => &config.chain.spec,
212 };
213 ChainSpec::load_from(spec_resource).map_err(|err| {
214 eprintln!("{err}");
215 ExitCode::Config
216 })
217 }
218
219 pub fn into_ckb(self) -> Result<Box<CKBAppConfig>, ExitCode> {
223 match self {
224 AppConfig::CKB(config) => Ok(config),
225 _ => {
226 eprintln!("Unmatched config file");
227 Err(ExitCode::Failure)
228 }
229 }
230 }
231
232 pub fn into_miner(self) -> Result<Box<MinerAppConfig>, ExitCode> {
236 match self {
237 AppConfig::Miner(config) => Ok(config),
238 _ => {
239 eprintln!("Unmatched config file");
240 Err(ExitCode::Failure)
241 }
242 }
243 }
244
245 pub fn set_bin_name(&mut self, bin_name: String) {
247 match self {
248 AppConfig::CKB(config) => config.bin_name = bin_name,
249 AppConfig::Miner(config) => config.bin_name = bin_name,
250 }
251 }
252}
253
254impl AppConfig {
255 fn with_ckb(config: CKBAppConfig) -> AppConfig {
256 AppConfig::CKB(Box::new(config))
257 }
258 fn with_miner(config: MinerAppConfig) -> AppConfig {
259 AppConfig::Miner(Box::new(config))
260 }
261}
262
263impl CKBAppConfig {
264 pub fn load_from_slice(slice: &[u8]) -> Result<Self, ExitCode> {
266 let legacy_config: legacy::CKBAppConfig = toml::from_slice(slice)?;
267 for field in legacy_config.deprecated_fields() {
268 eprintln!(
269 "WARN: the option \"{}\" in configuration files is deprecated since v{}.",
270 field.path, field.since
271 );
272 }
273 Ok(legacy_config.into())
274 }
275
276 fn derive_options(mut self, root_dir: &Path, subcommand_name: &str) -> Result<Self, ExitCode> {
277 self.root_dir = root_dir.to_path_buf();
278
279 self.data_dir = canonicalize_data_dir(self.data_dir, root_dir);
280
281 DATA_DIR
282 .set(self.data_dir.clone())
283 .expect("DATA_DIR is empty");
284
285 self.db.adjust(root_dir, &self.data_dir, "db");
286 self.ancient = mkdir(path_specified_or_else(&self.ancient, || {
287 self.data_dir.join("ancient")
288 }))?;
289
290 self.network.path = self.data_dir.join("network");
291 if self.tmp_dir.is_none() {
292 self.tmp_dir = Some(self.data_dir.join("tmp"));
293 }
294 self.logger.log_dir = self.data_dir.join("logs");
295 self.logger.file = Path::new(&(subcommand_name.to_string() + ".log")).to_path_buf();
296
297 let tx_pool_path = mkdir(self.data_dir.join("tx_pool"))?;
298 self.tx_pool.adjust(root_dir, tx_pool_path);
299
300 let indexer_path = mkdir(self.data_dir.join("indexer"))?;
301 self.indexer.adjust(root_dir, indexer_path);
302
303 if subcommand_name == cli::CMD_RESET_DATA {
304 return Ok(self);
305 }
306
307 self.data_dir = mkdir(self.data_dir)?;
308 self.db.path = mkdir(self.db.path)?;
309 self.network.path = mkdir(self.network.path)?;
310 if let Some(tmp_dir) = self.tmp_dir {
311 self.tmp_dir = Some(mkdir(tmp_dir)?);
312 }
313 if self.logger.log_to_file {
314 mkdir(self.logger.log_dir.clone())?;
315 touch(self.logger.log_dir.join(&self.logger.file))?;
316 }
317 self.chain.spec.absolutize(root_dir);
318
319 Ok(self)
320 }
321}
322
323impl MinerAppConfig {
324 pub fn load_from_slice(slice: &[u8]) -> Result<Self, ExitCode> {
326 let legacy_config: legacy::MinerAppConfig = toml::from_slice(slice)?;
327 for field in legacy_config.deprecated_fields() {
328 eprintln!(
329 "WARN: the option \"{}\" in configuration files is deprecated since v{}.",
330 field.path, field.since
331 );
332 }
333 Ok(legacy_config.into())
334 }
335
336 fn derive_options(mut self, root_dir: &Path) -> Result<Self, ExitCode> {
337 self.root_dir = root_dir.to_path_buf();
338
339 self.data_dir = mkdir(canonicalize_data_dir(self.data_dir, root_dir))?;
340 self.logger.log_dir = self.data_dir.join("logs");
341 self.logger.file = Path::new("miner.log").to_path_buf();
342 if self.logger.log_to_file {
343 mkdir(self.logger.log_dir.clone())?;
344 touch(self.logger.log_dir.join(&self.logger.file))?;
345 }
346 self.chain.spec.absolutize(root_dir);
347
348 Ok(self)
349 }
350}
351
352fn canonicalize_data_dir(data_dir: PathBuf, root_dir: &Path) -> PathBuf {
353 if data_dir.is_absolute() {
354 data_dir
355 } else {
356 root_dir.join(data_dir)
357 }
358}
359
360fn mkdir(dir: PathBuf) -> Result<PathBuf, ExitCode> {
361 fs::create_dir_all(dir.clean())?;
362 Ok(dir)
364}
365
366fn touch(path: PathBuf) -> Result<PathBuf, ExitCode> {
367 fs::OpenOptions::new()
368 .create(true)
369 .append(true)
370 .open(&path)?;
371
372 Ok(path)
373}
374
375fn ensure_ckb_dir(r: Resource) -> Result<Resource, ExitCode> {
376 if r.exists() {
377 Ok(r)
378 } else {
379 eprintln!("Not a CKB directory; initialize one with `ckb init`.");
380 Err(ExitCode::Config)
381 }
382}
383
384fn path_specified_or_else<P: AsRef<Path>, F: FnOnce() -> PathBuf>(
385 path: P,
386 default_path: F,
387) -> PathBuf {
388 let path_ref = path.as_ref();
389 if path_ref.to_str().is_none() || path_ref.to_str() == Some("") {
390 default_path()
391 } else {
392 path_ref.to_path_buf()
393 }
394}