From fdce79f33dfcad8efda9194841d78e63f7b780b0 Mon Sep 17 00:00:00 2001 From: Aryan Tikarya Date: Fri, 31 Jan 2025 12:22:21 +0530 Subject: [PATCH 1/4] feat: add format flag in chain head command --- src/cli/subcommands/chain_cmd.rs | 46 +++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/cli/subcommands/chain_cmd.rs b/src/cli/subcommands/chain_cmd.rs index 4ee47291ac42..80cec68f01d8 100644 --- a/src/cli/subcommands/chain_cmd.rs +++ b/src/cli/subcommands/chain_cmd.rs @@ -12,6 +12,12 @@ use nunny::Vec as NonEmpty; use super::{print_pretty_lotus_json, print_rpc_res_cids}; +#[derive(Debug, Clone, clap::ValueEnum)] +pub enum Format { + Json, + Text, +} + #[derive(Debug, Subcommand)] pub enum ChainCommands { /// Retrieves and prints out the block specified by the given CID @@ -29,6 +35,8 @@ pub enum ChainCommands { /// Tipsets are categorized by epoch in descending order. #[arg(short = 'n', long, default_value = "1")] tipsets: u64, + #[arg(short, long, default_value = "text")] + format: Format, }, /// Reads and prints out a message referenced by the specified CID from the @@ -68,7 +76,7 @@ impl ChainCommands { print_pretty_lotus_json(ChainGetBlock::call(&client, (cid,)).await?) } Self::Genesis => print_pretty_lotus_json(ChainGetGenesis::call(&client, ()).await?), - Self::Head { tipsets } => print_chain_head(&client, tipsets).await, + Self::Head { tipsets, format } => print_chain_head(&client, tipsets, format).await, Self::Message { cid } => { let bytes = ChainReadObj::call(&client, (cid,)).await?; match fvm_ipld_encoding::from_slice::(&bytes)? { @@ -151,15 +159,41 @@ fn maybe_confirm(no_confirm: bool, prompt: impl Into) -> anyhow::Result< } } -/// Print the first `n` tipsets from the head (inclusive). -async fn print_chain_head(client: &rpc::Client, n: u64) -> anyhow::Result<()> { +#[derive(Debug, serde::Serialize)] +struct TipsetInfo { + epoch: u64, + cids: Vec, +} + +async fn collect_tipsets(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; - + let mut result = Vec::with_capacity(n as usize); 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)?; + result.push(TipsetInfo { + epoch, + cids: tipset.cids().iter().map(|cid| cid.to_string()).collect(), + }); + } + Ok(result) +} + +/// Print the first `n` tipsets from the head (inclusive). +async fn print_chain_head(client: &rpc::Client, n: u64, format: Format) -> anyhow::Result<()> { + let result = collect_tipsets(client, n).await?; + match format { + Format::Json => { + println!("{}", serde_json::to_string_pretty(&result)?); + } + Format::Text => { + result.iter().for_each(|epoch_info| { + println!("[{}]", epoch_info.epoch); + epoch_info.cids.iter().for_each(|cid| { + println!("{}", cid); + }); + }); + } } Ok(()) } From 71a732179ad3838a2c03e9b83acedd433ac80a8c Mon Sep 17 00:00:00 2001 From: Aryan Tikarya Date: Fri, 31 Jan 2025 23:07:09 +0530 Subject: [PATCH 2/4] refactor: remove unused code --- src/cli/subcommands/chain_cmd.rs | 2 +- src/cli/subcommands/mod.rs | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/cli/subcommands/chain_cmd.rs b/src/cli/subcommands/chain_cmd.rs index 80cec68f01d8..922e6290ea24 100644 --- a/src/cli/subcommands/chain_cmd.rs +++ b/src/cli/subcommands/chain_cmd.rs @@ -10,7 +10,7 @@ use cid::Cid; use clap::Subcommand; use nunny::Vec as NonEmpty; -use super::{print_pretty_lotus_json, print_rpc_res_cids}; +use super::{print_pretty_lotus_json}; #[derive(Debug, Clone, clap::ValueEnum)] pub enum Format { diff --git a/src/cli/subcommands/mod.rs b/src/cli/subcommands/mod.rs index 1411dc72269b..f39bc56c079c 100644 --- a/src/cli/subcommands/mod.rs +++ b/src/cli/subcommands/mod.rs @@ -25,7 +25,7 @@ use std::io::Write; pub(crate) use crate::cli_shared::cli::Config; use crate::cli_shared::cli::HELP_MESSAGE; use crate::utils::version::FOREST_VERSION_STRING; -use crate::{blocks::Tipset, lotus_json::HasLotusJson}; +use crate::{lotus_json::HasLotusJson}; use clap::Parser; use tracing::error; @@ -124,15 +124,6 @@ pub(super) fn print_pretty_lotus_json(obj: T) -> anyhow::Result Ok(()) } -/// Prints a tipset from a HTTP JSON-RPC response result -pub(super) fn print_rpc_res_cids(tipset: Tipset) -> anyhow::Result<()> { - for cid in &tipset.cids() { - println!("{cid}"); - } - - Ok(()) -} - /// Prints a bytes HTTP JSON-RPC response result pub(super) fn print_rpc_res_bytes(obj: Vec) -> anyhow::Result<()> { println!("{}", String::from_utf8(obj)?); From 5fb540b7c05b6b8a234df0b0a3ffa5d459cac04d Mon Sep 17 00:00:00 2001 From: Aryan Tikarya Date: Mon, 3 Feb 2025 16:42:51 +0530 Subject: [PATCH 3/4] adress comments --- CHANGELOG.md | 2 ++ scripts/tests/calibnet_other_check.sh | 1 + src/cli/subcommands/chain_cmd.rs | 21 ++++++++++++--------- src/cli/subcommands/mod.rs | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6607ef5565c0..b546370bb877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ - [#4851](https://github.com/ChainSafe/forest/issues/4851) Add support for `FOREST_MAX_FILTER_RESULTS` in `Filecoin.EthGetLogs` RPC method. Add an `[events]` section to Forest configuration file. +- [#4954](https://github.com/ChainSafe/forest/issues/4954) Add `--format json` to `forest-cli chain head` command. + ### Changed ### Removed diff --git a/scripts/tests/calibnet_other_check.sh b/scripts/tests/calibnet_other_check.sh index c692bf3f7f49..f776a80ededa 100755 --- a/scripts/tests/calibnet_other_check.sh +++ b/scripts/tests/calibnet_other_check.sh @@ -32,6 +32,7 @@ $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 +$FOREST_CLI_PATH chain head --tipsets 5 --format json | jq 'length == 5' echo "Test subcommand: info show" $FOREST_CLI_PATH info show diff --git a/src/cli/subcommands/chain_cmd.rs b/src/cli/subcommands/chain_cmd.rs index 922e6290ea24..31221e798429 100644 --- a/src/cli/subcommands/chain_cmd.rs +++ b/src/cli/subcommands/chain_cmd.rs @@ -10,7 +10,7 @@ use cid::Cid; use clap::Subcommand; use nunny::Vec as NonEmpty; -use super::{print_pretty_lotus_json}; +use super::print_pretty_lotus_json; #[derive(Debug, Clone, clap::ValueEnum)] pub enum Format { @@ -35,7 +35,8 @@ pub enum ChainCommands { /// Tipsets are categorized by epoch in descending order. #[arg(short = 'n', long, default_value = "1")] tipsets: u64, - #[arg(short, long, default_value = "text")] + /// Format of the output. `json` or `text`. + #[arg(long, default_value = "text")] format: Format, }, @@ -165,29 +166,31 @@ struct TipsetInfo { cids: Vec, } -async fn collect_tipsets(client: &rpc::Client, n: u64) -> anyhow::Result> { +/// Collects `n` tipsets from the head (inclusive) and returns them as a list of +/// `TipsetInfo` objects. +async fn collect_n_tipsets(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; - let mut result = Vec::with_capacity(n as usize); + let mut tipsets = Vec::with_capacity(n as usize); for epoch in (current_epoch.saturating_sub(n - 1)..=current_epoch).rev() { let tipset = tipset_by_epoch_or_offset(client, epoch.try_into()?).await?; - result.push(TipsetInfo { + tipsets.push(TipsetInfo { epoch, cids: tipset.cids().iter().map(|cid| cid.to_string()).collect(), }); } - Ok(result) + Ok(tipsets) } /// Print the first `n` tipsets from the head (inclusive). async fn print_chain_head(client: &rpc::Client, n: u64, format: Format) -> anyhow::Result<()> { - let result = collect_tipsets(client, n).await?; + let tipsets = collect_n_tipsets(client, n).await?; match format { Format::Json => { - println!("{}", serde_json::to_string_pretty(&result)?); + println!("{}", serde_json::to_string_pretty(&tipsets)?); } Format::Text => { - result.iter().for_each(|epoch_info| { + tipsets.iter().for_each(|epoch_info| { println!("[{}]", epoch_info.epoch); epoch_info.cids.iter().for_each(|cid| { println!("{}", cid); diff --git a/src/cli/subcommands/mod.rs b/src/cli/subcommands/mod.rs index f39bc56c079c..970abb5e6f59 100644 --- a/src/cli/subcommands/mod.rs +++ b/src/cli/subcommands/mod.rs @@ -24,8 +24,8 @@ use std::io::Write; pub(crate) use crate::cli_shared::cli::Config; use crate::cli_shared::cli::HELP_MESSAGE; +use crate::lotus_json::HasLotusJson; use crate::utils::version::FOREST_VERSION_STRING; -use crate::{lotus_json::HasLotusJson}; use clap::Parser; use tracing::error; From f9567b09fda61c29ced759778d187852fc6041b1 Mon Sep 17 00:00:00 2001 From: Aryan Tikarya Date: Mon, 3 Feb 2025 19:34:26 +0530 Subject: [PATCH 4/4] address comment --- src/cli/subcommands/chain_cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/subcommands/chain_cmd.rs b/src/cli/subcommands/chain_cmd.rs index 31221e798429..da2178fc9d96 100644 --- a/src/cli/subcommands/chain_cmd.rs +++ b/src/cli/subcommands/chain_cmd.rs @@ -167,7 +167,7 @@ struct TipsetInfo { } /// Collects `n` tipsets from the head (inclusive) and returns them as a list of -/// `TipsetInfo` objects. +/// [`TipsetInfo`] objects. async fn collect_n_tipsets(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;