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        /// Print epoch and tipset key along with state root
38        #[arg(short, long)]
39        verbose: bool,
40    },
41    /// Read the state of an actor
42    ReadState {
43        /// Actor address to read the state of
44        actor_address: StrictAddress,
45    },
46    /// Returns the built-in actor bundle CIDs for the current network
47    ActorCids {
48        /// Format output
49        #[arg(long, default_value = "text")]
50        format: Format,
51    },
52}
53
54impl StateCommands {
55    pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> {
56        match self {
57            Self::Fetch { root, save_to_file } => {
58                let ret = client
59                    .call(
60                        StateFetchRoot::request((root, save_to_file))?.with_timeout(Duration::MAX),
61                    )
62                    .await?;
63                println!("{ret}");
64            }
65            StateCommands::Compute {
66                epoch,
67                n_epochs,
68                verbose,
69            } => {
70                let results = client
71                    .call(
72                        ForestStateCompute::request((epoch, n_epochs))?.with_timeout(Duration::MAX),
73                    )
74                    .await?;
75                for ForestComputeStateOutput {
76                    state_root,
77                    epoch,
78                    tipset_key,
79                } in results
80                {
81                    if verbose {
82                        println!("{state_root} (epoch: {epoch}, tipset key: {tipset_key})");
83                    } else {
84                        println!("{state_root}");
85                    }
86                }
87            }
88            Self::ReadState { actor_address } => {
89                let tipset = ChainHead::call(&client, ()).await?;
90                let ret = client
91                    .call(
92                        StateReadState::request((actor_address.into(), tipset.key().into()))?
93                            .with_timeout(Duration::MAX),
94                    )
95                    .await?;
96                println!("{}", ret.state.into_lotus_json_string_pretty()?);
97            }
98            Self::ActorCids { format } => {
99                let info = client.call(StateActorInfo::request(())?).await?;
100
101                match format {
102                    Format::Json => {
103                        println!("{}", serde_json::to_string_pretty(&info)?);
104                    }
105                    Format::Text => println!("{info}"),
106                }
107            }
108        }
109        Ok(())
110    }
111}