forest/dev/subcommands/
mod.rs1mod archive_missing_cmd;
5mod export_state_tree_cmd;
6mod export_tipset_lookup_cmd;
7mod state_cmd;
8mod update_checkpoints_cmd;
9
10use crate::cli_shared::cli::HELP_MESSAGE;
11use crate::networks::generate_actor_bundle;
12use crate::rpc::Client;
13use crate::state_manager::utils::state_compute::{
14 get_state_snapshot_file, list_state_snapshot_files,
15};
16use crate::utils::net::{DownloadFileOption, download_file_with_cache};
17use crate::utils::proofs_api::ensure_proof_params_downloaded;
18use crate::utils::version::FOREST_VERSION_STRING;
19use anyhow::Context as _;
20use clap::Parser;
21use directories::ProjectDirs;
22use std::borrow::Cow;
23use std::path::PathBuf;
24use std::time::Duration;
25use tokio::task::JoinSet;
26use url::Url;
27
28#[derive(Parser)]
30#[command(name = env!("CARGO_PKG_NAME"), bin_name = "forest-dev", author = env!("CARGO_PKG_AUTHORS"), version = FOREST_VERSION_STRING.as_str(), about = env!("CARGO_PKG_DESCRIPTION")
31)]
32#[command(help_template(HELP_MESSAGE))]
33pub struct Cli {
34 #[command(subcommand)]
35 pub cmd: Subcommand,
36}
37
38#[derive(clap::Subcommand)]
40pub enum Subcommand {
41 FetchTestSnapshots {
43 #[arg(long)]
45 actor_bundle: Option<PathBuf>,
46 },
47 #[command(subcommand)]
48 State(state_cmd::StateCommand),
49 UpdateCheckpoints(update_checkpoints_cmd::UpdateCheckpointsCommand),
52 ArchiveMissing(archive_missing_cmd::ArchiveMissingCommand),
54 ExportTipsetLookup(export_tipset_lookup_cmd::ExportTipsetLookupCommand),
55 ExportStateTree(export_state_tree_cmd::ExportStateTreeCommand),
56}
57
58impl Subcommand {
59 pub async fn run(self, _client: Client) -> anyhow::Result<()> {
60 match self {
61 Self::FetchTestSnapshots { actor_bundle } => fetch_test_snapshots(actor_bundle).await,
62 Self::State(cmd) => cmd.run().await,
63 Self::UpdateCheckpoints(cmd) => cmd.run().await,
64 Self::ArchiveMissing(cmd) => cmd.run().await,
65 Self::ExportTipsetLookup(cmd) => cmd.run().await,
66 Self::ExportStateTree(cmd) => cmd.run().await,
67 }
68 }
69}
70
71async fn fetch_test_snapshots(actor_bundle: Option<PathBuf>) -> anyhow::Result<()> {
72 crate::utils::proofs_api::maybe_set_proofs_parameter_cache_dir_env(
74 &crate::Config::default().client.data_dir,
75 );
76 ensure_proof_params_downloaded().await?;
77
78 if let Some(actor_bundle) = actor_bundle {
80 generate_actor_bundle(&actor_bundle).await?;
81 println!("Wrote the actors bundle to {}", actor_bundle.display());
82 }
83
84 fetch_state_tests().await?;
86
87 fetch_rpc_tests().await?;
89
90 Ok(())
91}
92
93pub async fn fetch_state_tests() -> anyhow::Result<()> {
94 let files = list_state_snapshot_files().await?;
95 let mut joinset = JoinSet::new();
96 for file in files {
97 joinset.spawn(async move { get_state_snapshot_file(&file).await });
98 }
99 for result in joinset.join_all().await {
100 if let Err(e) = result {
101 tracing::warn!("{e:#}");
102 }
103 }
104 Ok(())
105}
106
107async fn fetch_rpc_tests() -> anyhow::Result<()> {
108 let tests = include_str!("../../tool/subcommands/api_cmd/test_snapshots.txt")
109 .lines()
110 .map(|i| {
111 i.split("#").next().unwrap().trim().to_string()
113 })
114 .filter(|l| !l.is_empty() && !l.starts_with('#'));
115 let mut joinset = JoinSet::new();
116 for test in tests {
117 joinset.spawn(fetch_rpc_test_snapshot(test.into()));
118 }
119 for result in joinset.join_all().await {
120 if let Err(e) = result {
121 tracing::warn!("{e:#}");
122 }
123 }
124 Ok(())
125}
126
127pub async fn fetch_rpc_test_snapshot<'a>(name: Cow<'a, str>) -> anyhow::Result<PathBuf> {
128 let url: Url =
129 format!("https://forest-snapshots.fra1.cdn.digitaloceanspaces.com/rpc_test/{name}")
130 .parse()
131 .with_context(|| format!("Failed to parse URL for test: {name}"))?;
132 let project_dir =
133 ProjectDirs::from("com", "ChainSafe", "Forest").context("failed to get project dir")?;
134 let cache_dir = project_dir.cache_dir().join("test").join("rpc-snapshots");
135 let path = crate::utils::retry(
136 crate::utils::RetryArgs {
137 timeout: Some(Duration::from_secs(30)),
138 max_retries: Some(5),
139 delay: Some(Duration::from_secs(1)),
140 },
141 || download_file_with_cache(&url, &cache_dir, DownloadFileOption::NonResumable),
142 )
143 .await
144 .with_context(|| format!("failed to fetch rpc test snapshot {name}"))?
145 .path;
146 Ok(path)
147}