diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc80be53f1..e9fbd8d2682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,11 @@ ### Breaking +- [#4952](https://github.com/ChainSafe/forest/pull/4952) Extended the + `forest-cli chain head` command to allow for specifying number of last tipsets + to display. This change is breaking as the output now contains the epoch of + tipsets. + ### Added - [#4937](https://github.com/ChainSafe/forest/pull/4937) Added diff --git a/scripts/tests/calibnet_other_check.sh b/scripts/tests/calibnet_other_check.sh index 10c552f2bb2..c692bf3f7f4 100755 --- a/scripts/tests/calibnet_other_check.sh +++ b/scripts/tests/calibnet_other_check.sh @@ -29,6 +29,10 @@ echo "Test dev commands (which could brick the node/cause subsequent snapshots t echo "Test subcommand: chain set-head" $FOREST_CLI_PATH chain set-head --epoch -10 --force +echo "Test subcommand: chain head" +$FOREST_CLI_PATH chain head +$FOREST_CLI_PATH chain head --tipsets 10 + echo "Test subcommand: info show" $FOREST_CLI_PATH info show diff --git a/scripts/tests/calibnet_stateless_mode_check.sh b/scripts/tests/calibnet_stateless_mode_check.sh index 82198d3273b..3d57334934b 100755 --- a/scripts/tests/calibnet_stateless_mode_check.sh +++ b/scripts/tests/calibnet_stateless_mode_check.sh @@ -8,7 +8,7 @@ source "$(dirname "$0")/harness.sh" forest_init_stateless echo "Verifying the heaviest tipset to be the genesis" -HEAD_CID=$($FOREST_CLI_PATH chain head) +HEAD_CID=$($FOREST_CLI_PATH chain head | tail -n +2) assert_eq "$HEAD_CID" "bafy2bzacecyaggy24wol5ruvs6qm73gjibs2l2iyhcqmvi7r7a4ph7zx3yqd4" # Example format: /ip4/127.0.0.1/tcp/41937/p2p/12D3KooWAB9z7vZ1x1v9t4BViVkX1Hy1ScoRnWV2GgGy5ec6pfUZ diff --git a/src/cli/subcommands/chain_cmd.rs b/src/cli/subcommands/chain_cmd.rs index fd8b7501802..8049604221f 100644 --- a/src/cli/subcommands/chain_cmd.rs +++ b/src/cli/subcommands/chain_cmd.rs @@ -5,7 +5,7 @@ use crate::blocks::{Tipset, TipsetKey}; use crate::lotus_json::HasLotusJson; use crate::message::ChainMessage; use crate::rpc::{self, prelude::*}; -use anyhow::bail; +use anyhow::{bail, ensure}; use cid::Cid; use clap::Subcommand; use nunny::Vec as NonEmpty; @@ -24,7 +24,12 @@ pub enum ChainCommands { Genesis, /// Prints out the canonical head of the chain - Head, + Head { + /// Print the first `n` tipsets from the head (inclusive). + /// Tipsets are categorized by epoch in descending order. + #[arg(short = 'n', long, default_value = "1")] + tipsets: u64, + }, /// Reads and prints out a message referenced by the specified CID from the /// chain block store @@ -63,7 +68,7 @@ impl ChainCommands { print_pretty_lotus_json(ChainGetBlock::call(&client, (cid,)).await?) } Self::Genesis => print_pretty_lotus_json(ChainGetGenesis::call(&client, ()).await?), - Self::Head => print_rpc_res_cids(ChainHead::call(&client, ()).await?), + Self::Head { tipsets } => print_chain_head(&client, tipsets).await, Self::Message { cid } => { let bytes = ChainReadObj::call(&client, (cid,)).await?; match fvm_ipld_encoding::from_slice::(&bytes)? { @@ -145,3 +150,16 @@ fn maybe_confirm(no_confirm: bool, prompt: impl Into) -> anyhow::Result< false => bail!("Operation cancelled by user"), } } + +/// Print the first `n` tipsets from the head (inclusive). +async fn print_chain_head(client: &rpc::Client, n: u64) -> anyhow::Result<()> { + ensure!(n > 0, "number of tipsets must be positive"); + let current_epoch = ChainHead::call(client, ()).await?.epoch() as u64; + + for epoch in (current_epoch.saturating_sub(n - 1)..=current_epoch).rev() { + let tipset = tipset_by_epoch_or_offset(client, epoch.try_into()?).await?; + println!("[{}]", epoch); + print_rpc_res_cids(tipset)?; + } + Ok(()) +}