Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

### Added

- [#6006](https://github.com/ChainSafe/forest/issues/6006) More strict checks for the address arguments in the `forest-cli` subcommands.

### Changed

### Removed
Expand Down
5 changes: 3 additions & 2 deletions src/bin/forest-cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2019-2025 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

fn main() -> anyhow::Result<()> {
forest::forest_main(std::env::args_os())
#[tokio::main(flavor = "multi_thread")]
async fn main() -> anyhow::Result<()> {
forest::forest_main(std::env::args_os()).await
}
59 changes: 28 additions & 31 deletions src/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,41 @@ use clap::Parser;
use std::ffi::OsString;
use std::str::FromStr as _;

pub fn main<ArgT>(args: impl IntoIterator<Item = ArgT>) -> anyhow::Result<()>
pub async fn main<ArgT>(args: impl IntoIterator<Item = ArgT>) -> anyhow::Result<()>
where
ArgT: Into<OsString> + Clone,
{
// Preliminary client without the token to check network. This needs to occur before parsing to ensure the
// `StrictAddress` validation works correctly.
let client = rpc::Client::default_or_from_env(None)?;
if let Ok(name) = StateNetworkName::call(&client, ()).await
&& !matches!(NetworkChain::from_str(&name), Ok(NetworkChain::Mainnet))
{
CurrentNetwork::set_global(Network::Testnet);
}
Comment thread
LesnyRumcajs marked this conversation as resolved.

// Capture Cli inputs
let Cli { token, cmd } = Cli::parse_from(args);

let client = rpc::Client::default_or_from_env(token.as_deref())?;

tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
logger::setup_logger(&crate::cli_shared::cli::CliOpts::default());

if let Ok(name) = StateNetworkName::call(&client, ()).await
&& !matches!(NetworkChain::from_str(&name), Ok(NetworkChain::Mainnet))
{
CurrentNetwork::set_global(Network::Testnet);
}
let (_bg_tasks, _guards) = logger::setup_logger(&crate::cli_shared::cli::CliOpts::default());

// Run command
match cmd {
Subcommand::Chain(cmd) => cmd.run(client).await,
Subcommand::Auth(cmd) => cmd.run(client).await,
Subcommand::Net(cmd) => cmd.run(client).await,
Subcommand::Sync(cmd) => cmd.run(client).await,
Subcommand::Mpool(cmd) => cmd.run(client).await,
Subcommand::State(cmd) => cmd.run(client).await,
Subcommand::Config(cmd) => cmd.run(&mut std::io::stdout()),
Subcommand::Send(cmd) => cmd.run(client).await,
Subcommand::Info(cmd) => cmd.run(client).await,
Subcommand::Snapshot(cmd) => cmd.run(client).await,
Subcommand::Shutdown(cmd) => cmd.run(client).await,
Subcommand::Healthcheck(cmd) => cmd.run(client).await,
Subcommand::F3(cmd) => cmd.run(client).await,
Subcommand::WaitApi(cmd) => cmd.run(client).await,
}
})
// Run command
match cmd {
Subcommand::Chain(cmd) => cmd.run(client).await,
Subcommand::Auth(cmd) => cmd.run(client).await,
Subcommand::Net(cmd) => cmd.run(client).await,
Subcommand::Sync(cmd) => cmd.run(client).await,
Subcommand::Mpool(cmd) => cmd.run(client).await,
Subcommand::State(cmd) => cmd.run(client).await,
Subcommand::Config(cmd) => cmd.run(&mut std::io::stdout()),
Subcommand::Send(cmd) => cmd.run(client).await,
Subcommand::Info(cmd) => cmd.run(client).await,
Subcommand::Snapshot(cmd) => cmd.run(client).await,
Subcommand::Shutdown(cmd) => cmd.run(client).await,
Subcommand::Healthcheck(cmd) => cmd.run(client).await,
Subcommand::F3(cmd) => cmd.run(client).await,
Subcommand::WaitApi(cmd) => cmd.run(client).await,
}
}
29 changes: 9 additions & 20 deletions src/cli/subcommands/mpool_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright 2019-2025 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use std::str::FromStr as _;

use crate::blocks::Tipset;
use crate::lotus_json::{HasLotusJson as _, NotNullVec};
use crate::message::SignedMessage;
Expand All @@ -27,15 +25,15 @@ pub enum MpoolCommands {
cids: bool,
/// Return messages to a given address
#[arg(long)]
to: Option<String>,
to: Option<StrictAddress>,
/// Return messages from a given address
#[arg(long)]
from: Option<String>,
from: Option<StrictAddress>,
},
/// Get the current nonce for an address
Nonce {
/// Address to check nonce for
address: Address,
address: StrictAddress,
},
/// Print mempool stats
Stat {
Expand All @@ -48,24 +46,14 @@ pub enum MpoolCommands {
},
}

fn to_addr(value: &Option<String>) -> anyhow::Result<Option<StrictAddress>> {
Ok(value
.as_ref()
.map(|s| StrictAddress::from_str(s))
.transpose()?)
}

fn filter_messages(
messages: Vec<SignedMessage>,
local_addrs: Option<HashSet<Address>>,
to: &Option<String>,
from: &Option<String>,
to: &Option<StrictAddress>,
from: &Option<StrictAddress>,
) -> anyhow::Result<Vec<SignedMessage>> {
use crate::message::Message;

let to = to_addr(to)?;
let from = to_addr(from)?;

let filtered = messages
.into_iter()
.filter(|msg| {
Expand Down Expand Up @@ -281,7 +269,7 @@ impl MpoolCommands {
Ok(())
}
Self::Nonce { address } => {
let nonce = MpoolGetNonce::call(&client, (address,)).await?;
let nonce = MpoolGetNonce::call(&client, (address.into(),)).await?;
println!("{nonce}");

Ok(())
Expand Down Expand Up @@ -390,7 +378,7 @@ mod tests {

// Filtering messages from sender2
let smsg_filtered: Vec<SignedMessage> =
filter_messages(smsg_json_vec, None, &None, &Some(sender2.to_string()))
filter_messages(smsg_json_vec, None, &None, &Some(sender2.into()))
.unwrap()
.into_iter()
.collect();
Expand Down Expand Up @@ -423,7 +411,7 @@ mod tests {

// Filtering messages to target2
let smsg_filtered: Vec<SignedMessage> =
filter_messages(smsg_json_vec, None, &Some(target2.to_string()), &None)
filter_messages(smsg_json_vec, None, &Some(target2.into()), &None)
.unwrap()
.into_iter()
.collect();
Expand All @@ -437,6 +425,7 @@ mod tests {
fn compute_statistics() {
use crate::shim::message::Message;
use fvm_ipld_encoding::RawBytes;
use std::str::FromStr;

let addr0 = Address::from_str("t3urxivigpzih5f6ih3oq3lr2jlunw3m5oehbe5efts4ub5wy2oi4fbo5cw7333a4rrffo5535tjdq24wkc2aa").unwrap();
let addr1 = Address::from_str("t410fot3vkzzorqg4alowvghvxx4mhofhtazixbm6z2i").unwrap();
Expand Down
19 changes: 3 additions & 16 deletions src/cli/subcommands/state_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
use crate::lotus_json::HasLotusJson;
use crate::rpc::state::{ForestComputeStateOutput, ForestStateCompute};
use crate::rpc::{self, prelude::*};
use crate::shim::address::{CurrentNetwork, Error, Network, StrictAddress};
use crate::shim::address::StrictAddress;
use crate::shim::clock::ChainEpoch;
use cid::Cid;
use clap::Subcommand;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;

#[derive(Debug, Subcommand)]
Expand All @@ -36,7 +35,7 @@ pub enum StateCommands {
/// Read the state of an actor
ReadState {
/// Actor address to read the state of
actor_address: String,
actor_address: StrictAddress,
},
}

Expand Down Expand Up @@ -76,21 +75,9 @@ impl StateCommands {
}
Self::ReadState { actor_address } => {
let tipset = ChainHead::call(&client, ()).await?;
let address = match StrictAddress::from_str(&actor_address) {
Ok(address) => address.into(),
Err(Error::UnknownNetwork) => {
let expected = match CurrentNetwork::get() {
Network::Mainnet => 'f',
Network::Testnet => 't',
};
anyhow::bail!("Invalid network prefix, expected '{}'", expected);
}
Err(e) => anyhow::bail!("Error parsing address: {e}"),
};

let ret = client
.call(
StateReadState::request((address, tipset.key().into()))?
StateReadState::request((actor_address.into(), tipset.key().into()))?
.with_timeout(Duration::MAX),
)
.await?;
Expand Down
Loading