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 @@ -31,6 +31,8 @@

-[#5598](https://github.com/ChainSafe/forest/pull/5598) Add `forest-cli chain prune snap` command for garbage collecting the database with a new snapshot garbage collector.

-[#5629](https://github.com/ChainSafe/forest/pull/5629) Save default RPC token and consume it automatically.

### Changed

-[#5616](https://github.com/ChainSafe/forest/pull/5616) Remove the initial background task for populating Ethereum mappings. Use `forest-tool index backfill` to perform this operation offline instead.
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,14 @@ without any prompts.

### Interacting with Forest via CLI

When the Forest daemon is started, an admin token will be displayed
(alternatively, use `--save-token <token>` flag to save it on disk). You will
need this for commands that require a higher level of authorization (like a
When the Forest daemon is started, an admin token will be displayed and saved to
data directory by default. (alternatively, use `--save-token <token>` flag to save it on disk).
You will need this for commands that require a higher level of authorization (like a
password). Forest, as mentioned above, uses multiaddresses for networking. This
is no different in the CLI. To set the host and the port to use, if not using
the default port or using a remote host, set the `FULLNODE_API_INFO` environment
variable. This is also where you can set a token for authentication.
variable. This is also where you can set a token for authentication. Note that the token is
automatically set for CLI if it is invoked on the same host of the daemon.

```
FULLNODE_API_INFO="<token goes here>:/ip4/<host>/tcp/<port>/http
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/users/knowledge_base/jwt_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ The admin token is assumed to be stored in `/tmp/token` for the following exampl

### via `forest-cli`

The most straightforward way to use tokens is to pass them to the `forest-cli` tool. This can be done either by passing it via the `--token` flag or by setting the `FULLNODE_API_INFO` environment variable.
The most straightforward way to use tokens is to pass them to the `forest-cli` tool. This can be done either by passing it via the `--token` flag or by setting the `FULLNODE_API_INFO` environment variable. Note that the token is automatically set for CLI if it is invoked on the same host of the daemon.

```bash
forest-cli --token $(cat /tmp/token) shutdown
Expand Down
5 changes: 1 addition & 4 deletions scripts/tests/calibnet_no_discovery_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@ function shutdown {

trap shutdown EXIT

$FOREST_PATH --chain calibnet --encrypt-keystore false --mdns false --kademlia false --auto-download-snapshot --save-token ./admin_token --exit-after-init
$FOREST_PATH --chain calibnet --encrypt-keystore false --mdns false --kademlia false --auto-download-snapshot --exit-after-init
$FOREST_PATH --chain calibnet --encrypt-keystore false --mdns false --kademlia false --auto-download-snapshot --log-dir "$LOG_DIRECTORY" &
FOREST_NODE_PID=$!

FULLNODE_API_INFO="$(cat admin_token):/ip4/127.0.0.1/tcp/2345/http"
export FULLNODE_API_INFO

forest_wait_api

# Verify that one of the seed nodes has been connected to
Expand Down
2 changes: 1 addition & 1 deletion scripts/tests/calibnet_other_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fi

forest_check_db_stats
echo "Run snapshot GC"
forest_run_snap_gc
$FOREST_CLI_PATH chain prune snap
forest_wait_api
echo "Wait the node to sync"
forest_wait_for_sync
Expand Down
8 changes: 1 addition & 7 deletions scripts/tests/calibnet_stateless_rpc_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ function forest_run_node_stateless_detached_with_filter_list {
pkill -9 forest || true
local filter_list=$1

$FOREST_PATH --detach --chain calibnet --encrypt-keystore false --log-dir "$LOG_DIRECTORY" --save-token ./admin_token --skip-load-actors --stateless --rpc-filter-list "$filter_list"

ADMIN_TOKEN=$(cat admin_token)
FULLNODE_API_INFO="$ADMIN_TOKEN:/ip4/127.0.0.1/tcp/2345/http"

export ADMIN_TOKEN
export FULLNODE_API_INFO
$FOREST_PATH --detach --chain calibnet --encrypt-keystore false --log-dir "$LOG_DIRECTORY" --skip-load-actors --stateless --rpc-filter-list "$filter_list"
}

# Tests the RPC method `Filecoin.ChainHead` and checks if the status code matches the expected code.
Expand Down
14 changes: 1 addition & 13 deletions scripts/tests/harness.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function forest_query_format {

function forest_run_node_detached {
echo "Running forest in detached mode"
$FOREST_PATH --chain calibnet --encrypt-keystore false --log-dir "$LOG_DIRECTORY" --detach --save-token ./admin_token --track-peak-rss
$FOREST_PATH --chain calibnet --encrypt-keystore false --log-dir "$LOG_DIRECTORY" --detach --track-peak-rss
}

function forest_run_node_stateless_detached {
Expand Down Expand Up @@ -112,12 +112,6 @@ function forest_init {
forest_check_db_stats
forest_run_node_detached

ADMIN_TOKEN=$(cat admin_token)
FULLNODE_API_INFO="$ADMIN_TOKEN:/ip4/127.0.0.1/tcp/2345/http"

export ADMIN_TOKEN
export FULLNODE_API_INFO

forest_wait_api
forest_wait_for_sync
forest_check_db_stats
Expand All @@ -133,12 +127,6 @@ function forest_init_stateless {
export FULLNODE_API_INFO
}

function forest_run_snap_gc {
ADMIN_TOKEN=$(cat admin_token)
FULLNODE_API_INFO="$ADMIN_TOKEN:/ip4/127.0.0.1/tcp/2345/http"
$FOREST_CLI_PATH chain prune snap
}

function forest_print_logs_and_metrics {
echo "Get and print metrics"
wget -O metrics.log http://localhost:6116/metrics
Expand Down
6 changes: 6 additions & 0 deletions src/cli_shared/cli/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,9 @@ impl Default for Client {
}
}
}

impl Client {
pub fn default_rpc_token_path(&self) -> PathBuf {
self.data_dir.join("token")
}
}
13 changes: 11 additions & 2 deletions src/daemon/context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2019-2025 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use crate::auth::{ADMIN, create_token, generate_priv_key};
use crate::chain::ChainStore;
use crate::cli_shared::chain_path;
Expand Down Expand Up @@ -157,7 +158,7 @@ async fn load_or_create_keystore_and_configure_jwt(
if keystore.get(JWT_IDENTIFIER).is_err() {
keystore.put(JWT_IDENTIFIER, generate_priv_key())?;
}
let admin_jwt = handle_admin_token(opts, &keystore)?;
let admin_jwt = handle_admin_token(opts, config, &keystore)?;
let keystore = Arc::new(RwLock::new(keystore));
Ok((keystore, admin_jwt))
}
Expand Down Expand Up @@ -315,7 +316,11 @@ fn create_password(prompt: &str) -> dialoguer::Result<String> {

/// Generates, prints and optionally writes to a file the administrator JWT
/// token.
fn handle_admin_token(opts: &CliOpts, keystore: &KeyStore) -> anyhow::Result<String> {
fn handle_admin_token(
opts: &CliOpts,
config: &Config,
keystore: &KeyStore,
) -> anyhow::Result<String> {
let ki = keystore.get(JWT_IDENTIFIER)?;
// Lotus admin tokens do not expire but Forest requires all JWT tokens to
// have an expiration date. So we set the expiration date to 100 years in
Expand All @@ -327,6 +332,10 @@ fn handle_admin_token(opts: &CliOpts, keystore: &KeyStore) -> anyhow::Result<Str
token_exp,
)?;
info!("Admin token: {token}");
let default_token_path = config.client.default_rpc_token_path();
if let Err(e) = std::fs::write(&default_token_path, &token) {
tracing::warn!("Failed to save the default admin token file: {e}");
}
if let Some(path) = opts.save_token.as_ref() {
if let Some(dir) = path.parent() {
if !dir.is_dir() {
Expand Down
16 changes: 16 additions & 0 deletions src/rpc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ impl Client {
if token.is_some() && base_url.set_password(token).is_err() {
bail!("couldn't set override password")
}
// Set default token if not provided
if token.is_none() && base_url.password().is_none() {
let client_config = crate::cli_shared::cli::Client::default();
let default_token_path = client_config.default_rpc_token_path();
if default_token_path.is_file() {
if let Ok(token) = std::fs::read_to_string(&default_token_path) {
if base_url.set_password(Some(token.trim())).is_ok() {
tracing::info!("Loaded the default RPC token");
} else {
tracing::warn!("Failed to set the default RPC token");
}
} else {
tracing::warn!("Failed to load the default token file");
}
}
}
Ok(Self::from_url(base_url))
}
pub fn from_url(mut base_url: Url) -> Self {
Expand Down
Loading