Skip to main content

forest/cli/subcommands/
state_cmd.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use crate::lotus_json::HasLotusJson;
5use crate::rpc::state::{ForestComputeStateOutput, ForestStateCompute};
6use crate::rpc::{self, prelude::*};
7use crate::shim::address::StrictAddress;
8use crate::shim::clock::ChainEpoch;
9use cid::Cid;
10use clap::Subcommand;
11use std::num::NonZeroUsize;
12use std::path::PathBuf;
13use std::time::Duration;
14
15#[derive(Debug, Clone, clap::ValueEnum)]
16pub enum Format {
17    Json,
18    Text,
19}
20
21#[derive(Debug, Subcommand)]
22pub enum StateCommands {
23    Fetch {
24        root: Cid,
25        /// The `.car` file path to save the state root
26        #[arg(short, long)]
27        save_to_file: Option<PathBuf>,
28    },
29    /// Compute state trees for epochs
30    Compute {
31        /// Which epoch to compute the state transition for
32        #[arg(long)]
33        epoch: ChainEpoch,
34        /// Number of tipset epochs to compute state for. Default is 1
35        #[arg(short, long)]
36        n_epochs: Option<NonZeroUsize>,
37        /// Force recomputing the state trees regardless whether the results are cached
38        #[arg(long)]
39        force: bool,
40        /// Print epoch and tipset key along with state root
41        #[arg(short, long)]
42        verbose: bool,
43    },
44    /// Read the state of an actor
45    ReadState {
46        /// Actor address to read the state of
47        actor_address: StrictAddress,
48    },
49    /// Returns the built-in actor bundle CIDs for the current network
50    ActorCids {
51        /// Format output
52        #[arg(long, default_value = "text")]
53        format: Format,
54    },
55}
56
57impl StateCommands {
58    pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> {
59        match self {
60            Self::Fetch { root, save_to_file } => {
61                let ret = client
62                    .call(
63                        StateFetchRoot::request((root, save_to_file))?.with_timeout(Duration::MAX),
64                    )
65                    .await?;
66                println!("{ret}");
67            }
68            StateCommands::Compute {
69                epoch,
70                n_epochs,
71                force,
72                verbose,
73            } => {
74                let results = client
75                    .call(
76                        ForestStateCompute::request((epoch, n_epochs, Some(force)))?
77                            .with_timeout(Duration::MAX),
78                    )
79                    .await?;
80                for ForestComputeStateOutput {
81                    state_root,
82                    epoch,
83                    tipset_key,
84                } in results
85                {
86                    if verbose {
87                        println!("{state_root} (epoch: {epoch}, tipset key: {tipset_key})");
88                    } else {
89                        println!("{state_root}");
90                    }
91                }
92            }
93            Self::ReadState { actor_address } => {
94                let tipset = ChainHead::call(&client, ()).await?;
95                let ret = client
96                    .call(
97                        StateReadState::request((actor_address.into(), tipset.key().into()))?
98                            .with_timeout(Duration::MAX),
99                    )
100                    .await?;
101                println!("{}", ret.state.into_lotus_json_string_pretty()?);
102            }
103            Self::ActorCids { format } => {
104                let info = client.call(StateActorInfo::request(())?).await?;
105
106                match format {
107                    Format::Json => {
108                        println!("{}", serde_json::to_string_pretty(&info)?);
109                    }
110                    Format::Text => println!("{info}"),
111                }
112            }
113        }
114        Ok(())
115    }
116}