forest/tool/subcommands/
index_cmd.rs1use std::{path::PathBuf, sync::Arc};
5
6use anyhow::bail;
7use clap::Subcommand;
8
9use crate::chain::ChainStore;
10use crate::chain::index::ResolveNullTipset;
11use crate::cli_shared::{chain_path, read_config};
12use crate::daemon::db_util::load_all_forest_cars;
13use crate::daemon::db_util::{RangeSpec, backfill_db};
14use crate::db::CAR_DB_DIR_NAME;
15use crate::db::car::ManyCar;
16use crate::db::db_engine::{db_root, open_db};
17use crate::genesis::read_genesis_header;
18use crate::networks::NetworkChain;
19use crate::shim::clock::ChainEpoch;
20use crate::state_manager::StateManager;
21use crate::tool::offline_server::server::handle_chain_config;
22
23#[derive(Debug, Subcommand)]
24pub enum IndexCommands {
25 Backfill {
27 #[arg(short, long)]
29 config: Option<PathBuf>,
30 #[arg(long)]
32 chain: Option<NetworkChain>,
33 #[arg(long)]
35 from: Option<ChainEpoch>,
36 #[arg(long)]
38 to: Option<ChainEpoch>,
39 #[arg(long, conflicts_with = "to")]
41 n_tipsets: Option<usize>,
42 },
43}
44
45impl IndexCommands {
46 pub async fn run(&self) -> anyhow::Result<()> {
47 match self {
48 Self::Backfill {
49 config,
50 chain,
51 from,
52 to,
53 n_tipsets,
54 } => {
55 let spec = match (to, n_tipsets) {
56 (Some(x), None) => RangeSpec::To(*x),
57 (None, Some(x)) => RangeSpec::NumTipsets(*x),
58 (None, None) => {
59 bail!("You must provide either '--to' or '--n-tipsets'.");
60 }
61 _ => unreachable!(), };
63
64 let (_, config) = read_config(config.as_ref(), chain.clone())?;
65
66 let chain_data_path = chain_path(&config);
67 let db_root_dir = db_root(&chain_data_path)?;
68
69 let db_writer = Arc::new(open_db(db_root_dir.clone(), config.db_config())?);
70 let db = Arc::new(ManyCar::new(db_writer.clone()));
71 let forest_car_db_dir = db_root_dir.join(CAR_DB_DIR_NAME);
72
73 load_all_forest_cars(&db, &forest_car_db_dir)?;
74
75 let chain_config = Arc::new(handle_chain_config(&config.chain)?);
76 let genesis_header = read_genesis_header(
77 None,
78 chain_config.genesis_bytes(&db).await?.as_deref(),
79 &db,
80 )
81 .await?;
82
83 let chain_store = Arc::new(ChainStore::new(
84 db.clone(),
85 db.clone(),
86 db.clone(),
87 db.writer().clone(),
88 chain_config,
89 genesis_header.clone(),
90 )?);
91
92 let state_manager = Arc::new(StateManager::new(chain_store.clone())?);
93
94 let head_ts = chain_store.heaviest_tipset();
95
96 println!("Database path: {}", db_root_dir.display());
97 println!("From epoch: {}", from.unwrap_or_else(|| head_ts.epoch()));
98 println!("{spec}");
99 println!("Head epoch: {}", head_ts.epoch());
100
101 let from_ts = if let Some(from) = from {
102 chain_store.chain_index().tipset_by_height(
103 *from,
104 head_ts,
105 ResolveNullTipset::TakeOlder,
106 )?
107 } else {
108 head_ts
109 };
110
111 backfill_db(&state_manager, &from_ts, spec).await?;
112
113 Ok(())
114 }
115 }
116 }
117}