1use crate::{
5 chain::{ChainStore, index::ResolveNullTipset},
6 cli_shared::{chain_path, read_config},
7 db::{
8 MemoryDB, SettingsStoreExt,
9 car::{AnyCar, ManyCar},
10 db_engine::db_root,
11 },
12 genesis::read_genesis_header,
13 interpreter::VMTrace,
14 networks::{ChainConfig, NetworkChain},
15 shim::clock::ChainEpoch,
16 state_manager::{StateManager, StateOutput},
17};
18use std::{num::NonZeroUsize, path::PathBuf, sync::Arc, time::Instant};
19
20#[derive(Debug, clap::Subcommand)]
22pub enum StateCommand {
23 Compute(ComputeCommand),
24 ReplayCompute(ReplayComputeCommand),
25}
26
27impl StateCommand {
28 pub async fn run(self) -> anyhow::Result<()> {
29 match self {
30 Self::Compute(cmd) => cmd.run().await,
31 Self::ReplayCompute(cmd) => cmd.run().await,
32 }
33 }
34}
35
36#[derive(Debug, clap::Args)]
38pub struct ComputeCommand {
39 #[arg(long, required = true)]
41 epoch: ChainEpoch,
42 #[arg(long, required = true)]
44 chain: NetworkChain,
45 #[arg(long)]
47 db: Option<PathBuf>,
48 #[arg(long)]
50 export_db_to: Option<PathBuf>,
51}
52
53impl ComputeCommand {
54 pub async fn run(self) -> anyhow::Result<()> {
55 let Self {
56 epoch,
57 chain,
58 db,
59 export_db_to,
60 } = self;
61 let db_root_path = if let Some(db) = db {
62 db
63 } else {
64 let (_, config) = read_config(None, Some(chain.clone()))?;
65 db_root(&chain_path(&config))?
66 };
67 let db = super::api_cmd::generate_test_snapshot::load_db(&db_root_path)?;
68 let chain_config = Arc::new(ChainConfig::from_chain(&chain));
69 let genesis_header =
70 read_genesis_header(None, chain_config.genesis_bytes(&db).await?.as_deref(), &db)
71 .await?;
72 let chain_store = Arc::new(ChainStore::new(
73 db.clone(),
74 db.clone(),
75 db.clone(),
76 db.clone(),
77 chain_config,
78 genesis_header,
79 )?);
80 let ts = chain_store.chain_index().tipset_by_height(
81 epoch,
82 chain_store.heaviest_tipset(),
83 ResolveNullTipset::TakeOlder,
84 )?;
85 let epoch = ts.epoch();
86 SettingsStoreExt::write_obj(&db.tracker, crate::db::setting_keys::HEAD_KEY, ts.key())?;
87 let state_manager = Arc::new(StateManager::new(chain_store.clone())?);
88
89 let StateOutput {
90 state_root,
91 receipt_root,
92 ..
93 } = state_manager
94 .compute_tipset_state(ts, crate::state_manager::NO_CALLBACK, VMTrace::NotTraced)
95 .await?;
96 let mut db_snapshot = vec![];
97 db.export_forest_car(&mut db_snapshot).await?;
98 println!(
99 "epoch: {epoch}, state_root: {state_root}, receipt_root: {receipt_root}, db_snapshot_size: {}",
100 human_bytes::human_bytes(db_snapshot.len() as f64)
101 );
102 if let Some(export_db_to) = export_db_to {
103 std::fs::write(export_db_to, db_snapshot)?;
104 }
105 Ok(())
106 }
107}
108
109#[derive(Debug, clap::Args)]
112pub struct ReplayComputeCommand {
113 snapshot: PathBuf,
115 #[arg(long, required = true)]
117 chain: NetworkChain,
118 #[arg(short, long, default_value_t = NonZeroUsize::new(1).unwrap())]
120 n: NonZeroUsize,
121}
122
123impl ReplayComputeCommand {
124 pub async fn run(self) -> anyhow::Result<()> {
125 let Self { snapshot, chain, n } = self;
126 let snap_car = AnyCar::try_from(&snapshot)?;
127 let ts = Arc::new(snap_car.heaviest_tipset()?);
128 let epoch = ts.epoch();
129 let db = Arc::new(ManyCar::new(MemoryDB::default()).with_read_only(snap_car)?);
130 let chain_config = Arc::new(ChainConfig::from_chain(&chain));
131 let genesis_header =
132 read_genesis_header(None, chain_config.genesis_bytes(&db).await?.as_deref(), &db)
133 .await?;
134 let chain_store = Arc::new(ChainStore::new(
135 db.clone(),
136 db.clone(),
137 db.clone(),
138 db.clone(),
139 chain_config,
140 genesis_header,
141 )?);
142 let state_manager = Arc::new(StateManager::new(chain_store.clone())?);
143 for _ in 0..n.get() {
144 let start = Instant::now();
145 let StateOutput {
146 state_root,
147 receipt_root,
148 ..
149 } = state_manager
150 .compute_tipset_state(
151 ts.clone(),
152 crate::state_manager::NO_CALLBACK,
153 VMTrace::NotTraced,
154 )
155 .await?;
156 println!(
157 "epoch: {epoch}, state_root: {state_root}, receipt_root: {receipt_root}, took {}.",
158 humantime::format_duration(start.elapsed())
159 );
160 }
161 Ok(())
162 }
163}