Skip to content

feat(wallet): strict address validation in forest-wallet CLI#6968

Open
0xDevNinja wants to merge 1 commit into
ChainSafe:mainfrom
0xDevNinja:0xdevninja/issue-6012-wallet-strict-address
Open

feat(wallet): strict address validation in forest-wallet CLI#6968
0xDevNinja wants to merge 1 commit into
ChainSafe:mainfrom
0xDevNinja:0xdevninja/issue-6012-wallet-strict-address

Conversation

@0xDevNinja
Copy link
Copy Markdown

@0xDevNinja 0xDevNinja commented Apr 24, 2026

Summary of changes

Changes introduced in this pull request:

  • Migrate address arguments in forest-wallet subcommands to StrictAddress (parsed at clap time). Covers balance, export, has, delete, set-default, sign, verify, and the --from option of send. Handlers destructure the typed value via field: StrictAddress(inner); runtime StrictAddress::from_str(...)? fallbacks are removed.
  • Move network detection ahead of Cli::parse_from in src/wallet/main.rs so that clap-time StrictAddress validation accepts testnet (t0...) addresses when the daemon reports a testnet chain. Client construction errors propagate (mirroring forest-cli after feat: strict checks for address args in forest-cli #6011); an unreachable daemon leaves the global CurrentNetwork at its mainnet default.
  • Tighten wallet_default_address() return type to Option<Address> (was Option<String>), removing a stringify→reparse round trip through StrictAddress::from_str in both the list and send handlers.
  • Add two clap-level regression tests (one for balance, one for sign -a ...) asserting ValueValidation on malformed addresses, guarding against an accidental revert.
  • CHANGELOG.md: Added entry listing the migrated subcommands and explicitly calling out the send target_address non-migration.

Intentional non-migrations

  • send positional target_address stays String: resolve_target_address accepts both FIL (f0.../t0...) and ETH (0x...) forms, but StrictAddress::FromStr rejects the ETH form, so typing it would break the existing ETH-recipient path. Deviates from the issue body, which assumed all address fields could be StrictAddress.
  • validate-address positional address stays String: the purpose of the subcommand is to validate arbitrary user input.

Reference issue to close (if applicable)

Closes #6012

Other information and links

  • Reference PR for the pattern: feat: strict checks for address args in forest-cli #6011 (same change for forest-cli, including the pre-parse network detection).
  • Follow-up opportunity: a typed Recipient enum (Fil(StrictAddress), Eth(EthAddress)) for send target_address, so the ETH form gets the same compile-time check.

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation. All new code adheres to the team's documentation standards,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

Outside contributions

  • I have read and agree to the CONTRIBUTING document.
  • I have read and agree to the AI Policy document. I understand that failure to comply with the guidelines will lead to rejection of the pull request.

AI Usage Disclosure

This PR was prepared with assistance from Claude Code (Anthropic). Extent:

  • Scoping (reading the linked issue + reference PR feat: strict checks for address args in forest-cli #6011), implementation, two self-review passes with an independent reviewer agent, test design, and PR drafting were AI-assisted.
  • All changes were compiled and tested locally by me before pushing — cargo fmt --all -- --check, cargo clippy --profile quick --all-targets --no-deps -- -D warnings (with FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT=1), and cargo test --lib -- wallet::subcommands (12/12 pass) are all green.
  • End-to-end CLI verification against a running daemon has not been performed locally; the change is pure clap-parse typing and a return-type tightening, both covered by the regression tests.
  • I have reviewed every diff line and take full responsibility for correctness.

Summary by CodeRabbit

  • New Features

    • Stronger validation for wallet address arguments: malformed addresses are rejected during CLI parsing.
    • CLI detects daemon network earlier and falls back safely when network lookup fails, reducing unexpected errors.
    • Send command continues to accept Ethereum-style target addresses.
  • Tests

    • Added unit tests asserting address-argument validation for wallet commands.
  • Chore

    • Added changelog entry noting stricter address validation.

Review Change Stack

@0xDevNinja 0xDevNinja requested a review from a team as a code owner April 24, 2026 13:53
@0xDevNinja 0xDevNinja requested review from LesnyRumcajs and hanabi1224 and removed request for a team April 24, 2026 13:53
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6eca7e9d-c0a2-47b5-846c-ded77220f81e

📥 Commits

Reviewing files that changed from the base of the PR and between c8005c8 and 8df812a.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/wallet/main.rs
  • src/wallet/subcommands/mod.rs
  • src/wallet/subcommands/wallet_cmd.rs
✅ Files skipped from review due to trivial changes (1)
  • src/wallet/main.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • CHANGELOG.md
  • src/wallet/subcommands/mod.rs
  • src/wallet/subcommands/wallet_cmd.rs

Walkthrough

Migrate wallet CLI address arguments to typed StrictAddress, add early RPC-based network detection before CLI parsing to set CurrentNetwork, update wallet command handlers and backend default-address return type to use typed Address, and add tests verifying parse-time rejection of malformed addresses.

Changes

Wallet strict address migration

Layer / File(s) Summary
Changelog entry
CHANGELOG.md
Adds [#6012] changelog bullet documenting stricter address validation for forest-wallet.
Early network detection
src/wallet/main.rs
Initialize rpc::Client (no token) before CLI parsing, detect daemon StateNetworkName/NetworkChain, and set CurrentNetwork when non‑Mainnet; remove prior post-parse detection.
CLI parse-time tests
src/wallet/subcommands/mod.rs
Add tests and helper asserting clap::ErrorKind::ValueValidation for malformed addresses across multiple wallet subcommands.
Command types & backend default
src/wallet/subcommands/wallet_cmd.rs
Change WalletBackend::wallet_default_address to return Option<Address>; change many command address/key fields from String to StrictAddress; change Send.from to Option<StrictAddress> (keep Send.target_address as String).
Command handlers
src/wallet/subcommands/wallet_cmd.rs
Remove runtime StrictAddress::from_str parsing in handlers and pass typed StrictAddress/Address into wallet RPC/backend calls; update List, Sign, Verify, Send flows accordingly.

Sequence Diagram

sequenceDiagram
    participant Main as main.rs
    participant RPC as rpc::Client
    participant GlobalNet as CurrentNetwork
    participant CLIParser as clap (Cli::parse_from)
    participant Handler as Command Handler

    rect rgba(100, 150, 200, 0.5)
    Note over Main,GlobalNet: Early network detection before CLI parsing
    Main->>RPC: initialize client without token
    Main->>RPC: request StateNetworkName
    RPC-->>Main: return NetworkChain
    alt parsed and not Mainnet
        Main->>GlobalNet: set CurrentNetwork to Testnet
    else parse/lookup failed
        Main-->>GlobalNet: keep default CurrentNetwork
    end
    end

    rect rgba(150, 100, 200, 0.5)
    Note over CLIParser,Handler: CLI parsing with StrictAddress validation
    Main->>CLIParser: Cli::parse_from(args) with CurrentNetwork set
    CLIParser->>CLIParser: validate StrictAddress fields
    alt valid
        CLIParser-->>Main: parsed command with typed addresses
        Main->>Handler: execute command using StrictAddress/Address
    else invalid
        CLIParser-->>Main: return clap::Error (ValueValidation)
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ChainSafe/forest#6710: Overlaps changes to src/wallet/subcommands/wallet_cmd.rs around send/address parsing and validation.
  • ChainSafe/forest#6011: Implements the same strict address validation and early network detection pattern for other CLI tooling.

Suggested reviewers

  • hanabi1224
  • LesnyRumcajs
  • akaladarshi
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: implementing strict address validation in the forest-wallet CLI, which aligns with the primary objective of the PR.
Linked Issues check ✅ Passed The PR addresses all core requirements from issue #6012: migrated string address fields to StrictAddress types for balance, export, has, delete, set-default, sign, verify, and send --from [#6012]; added early network detection before CLI parsing [#6012]; added regression tests for address validation [#6012]; and improved user experience via parse-time validation.
Out of Scope Changes check ✅ Passed All changes are tightly scoped to address validation and network detection objectives. The intentional non-migration of send target_address (to preserve ETH recipient handling) and validate-address positional address are documented and justified.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Warning

Review ran into problems

🔥 Problems

Linked repositories: Couldn't analyze filecoin-project/lotus - clone failed: Clone operation failed: Stream setup permanently failed: Sandbox setup timed out after 180000ms


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/wallet/main.rs (1)

24-28: Add a debug/warn log when the network probe fails and mainnet fallback is used.

On Line 24, probe failures are silently ignored; a small log here would make prefix-validation fallbacks much easier to diagnose.

Suggested adjustment
-    if let Ok(name) = StateNetworkName::call(&client, ()).await
-        && !matches!(NetworkChain::from_str(&name), Ok(NetworkChain::Mainnet))
-    {
-        CurrentNetwork::set_global(Network::Testnet);
-    }
+    match StateNetworkName::call(&client, ()).await {
+        Ok(name) if !matches!(NetworkChain::from_str(&name), Ok(NetworkChain::Mainnet)) => {
+            CurrentNetwork::set_global(Network::Testnet);
+        }
+        Ok(_) => {}
+        Err(err) => {
+            tracing::debug!(
+                %err,
+                "network probe failed; keeping CurrentNetwork at mainnet default for CLI parsing"
+            );
+        }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wallet/main.rs` around lines 24 - 28, The probe using
StateNetworkName::call(&client, ()).await currently swallows failures; update
the branch around StateNetworkName::call, NetworkChain::from_str and the
fallback that calls CurrentNetwork::set_global(Network::Testnet) to log a
warning or debug message when the call fails or when parsing does not yield
Mainnet. Specifically, capture the Err from StateNetworkName::call and log the
error (including the error object and context like "network probe failed,
falling back to Testnet"), and also log when NetworkChain::from_str returns
non-Mainnet to explain why CurrentNetwork::set_global(Network::Testnet) is
invoked.
src/wallet/subcommands/mod.rs (1)

48-69: Add a clap regression test for send --from malformed addresses.

balance and sign are covered, but send --from was also migrated to StrictAddress. A matching ValueValidation test would prevent regressions in that path.

Suggested test addition
     #[test]
     fn wallet_sign_rejects_malformed_address() {
         assert_eq!(
             parse_err_kind(&[
                 "forest-wallet",
                 "sign",
                 "-m",
                 "deadbeef",
                 "-a",
                 "not-an-address",
             ]),
             clap::error::ErrorKind::ValueValidation,
         );
     }
+
+    #[test]
+    fn wallet_send_from_rejects_malformed_address() {
+        assert_eq!(
+            parse_err_kind(&[
+                "forest-wallet",
+                "send",
+                "--from",
+                "not-an-address",
+                "f01234",
+                "1",
+            ]),
+            clap::error::ErrorKind::ValueValidation,
+        );
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wallet/subcommands/mod.rs` around lines 48 - 69, Add a new unit test
similar to wallet_balance_rejects_malformed_address and
wallet_sign_rejects_malformed_address that asserts parse_err_kind returns
clap::error::ErrorKind::ValueValidation for a malformed address passed to the
send command's --from flag; use the existing helper parse_err_kind and name the
test wallet_send_from_rejects_malformed_address, and supply the minimal extra
send arguments (e.g. to and amount) so the clap parser reaches --from
validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/wallet/main.rs`:
- Around line 24-28: The probe using StateNetworkName::call(&client, ()).await
currently swallows failures; update the branch around StateNetworkName::call,
NetworkChain::from_str and the fallback that calls
CurrentNetwork::set_global(Network::Testnet) to log a warning or debug message
when the call fails or when parsing does not yield Mainnet. Specifically,
capture the Err from StateNetworkName::call and log the error (including the
error object and context like "network probe failed, falling back to Testnet"),
and also log when NetworkChain::from_str returns non-Mainnet to explain why
CurrentNetwork::set_global(Network::Testnet) is invoked.

In `@src/wallet/subcommands/mod.rs`:
- Around line 48-69: Add a new unit test similar to
wallet_balance_rejects_malformed_address and
wallet_sign_rejects_malformed_address that asserts parse_err_kind returns
clap::error::ErrorKind::ValueValidation for a malformed address passed to the
send command's --from flag; use the existing helper parse_err_kind and name the
test wallet_send_from_rejects_malformed_address, and supply the minimal extra
send arguments (e.g. to and amount) so the clap parser reaches --from
validation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3f00574d-3b2b-42b7-be1a-1f0963ceca9c

📥 Commits

Reviewing files that changed from the base of the PR and between 65c39a0 and 90b968b.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/wallet/main.rs
  • src/wallet/subcommands/mod.rs
  • src/wallet/subcommands/wallet_cmd.rs

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

❌ Patch coverage is 13.79310% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.18%. Comparing base (97cea94) to head (8df812a).
⚠️ Report is 7 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/wallet/subcommands/wallet_cmd.rs 0.00% 19 Missing ⚠️
src/wallet/main.rs 0.00% 6 Missing ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
src/wallet/subcommands/mod.rs 100.00% <100.00%> (ø)
src/wallet/main.rs 0.00% <0.00%> (ø)
src/wallet/subcommands/wallet_cmd.rs 26.49% <0.00%> (+1.03%) ⬆️

... and 115 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 97cea94...8df812a. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated
Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated
Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated
Comment thread CHANGELOG.md Outdated
@akaladarshi
Copy link
Copy Markdown
Collaborator

@0xDevNinja Any update on this ?

@0xDevNinja 0xDevNinja force-pushed the 0xdevninja/issue-6012-wallet-strict-address branch from 90b968b to 7bd7a85 Compare May 13, 2026 10:57
@0xDevNinja
Copy link
Copy Markdown
Author

Hey, sorry for the delay. Pushed an update addressing the review:

  • CHANGELOG: trimmed the entry to the one-liner you suggested, dropped the per-subcommand list.
  • wallet_cmd.rs: replaced the StrictAddress(address) destructure with plain address + .into() at each call site, as suggested on the Balance arm.
  • wallet_cmd.rs:298: split the public clap doc for target_address from the implementation note about why it stays a String.
  • wallet_cmd.rs:409: dropped the redundant let default_address = default; and bound the tuple directly.
  • Rebased onto main to clear the conflict.

Left the two CodeRabbit nits as-is — the main.rs probe-failure log and the extra send --from clap test — happy to fold them in if you want them in this PR. Locally cargo test --bin forest-wallet -- wallet:: is green (27/27).

@0xDevNinja 0xDevNinja requested a review from akaladarshi May 13, 2026 12:13
@akaladarshi
Copy link
Copy Markdown
Collaborator

@0xDevNinja Linter is still failing you can verify the linter locally using mise run lint

@0xDevNinja 0xDevNinja force-pushed the 0xdevninja/issue-6012-wallet-strict-address branch from 7bd7a85 to 12f8b2d Compare May 14, 2026 07:48
@0xDevNinja
Copy link
Copy Markdown
Author

Fixed — cargo fmt --all -- --check was failing on wallet_cmd.rs:469 (wallet_verify(...).await? exceeded the line limit and rustfmt wanted it broken across three lines). Reformatted that one line, rebased on main to clear the new CHANGELOG.md conflict (NV28 entries moved into the v0.33.4 release notes upstream), and force-pushed.

Comment thread src/wallet/main.rs Outdated
Comment thread src/wallet/subcommands/mod.rs Outdated
@0xDevNinja 0xDevNinja force-pushed the 0xdevninja/issue-6012-wallet-strict-address branch from 12f8b2d to c8005c8 Compare May 14, 2026 08:19
@0xDevNinja
Copy link
Copy Markdown
Author

Both fold-ins pushed:

  • main.rs:22 — replaced the multi-line note with your one-liner: "Preliminary client without the token to check network. This needs to occur before parsing to ensure the StrictAddress validation works correctly."
  • mod.rs tests — collapsed the two wallet_balance_* / wallet_sign_* cases into the rstest parametrized form covering all eight migrated subcommands (balance, export, has, set-default, delete, sign, verify, send --from).

Local: cargo test --bin forest-wallet --lib migrated_subcommands_reject_malformed_address → 8/8 ok in 0.01s, cargo fmt --all -- --check clean.

@0xDevNinja 0xDevNinja requested a review from akaladarshi May 14, 2026 08:21
Comment thread src/wallet/subcommands/mod.rs Outdated
use rstest::rstest;

/// Guards against an accidental revert of #6012 (address args ↔ `StrictAddress`).
/// Malformed addresses must be rejected by clap at parse time, with a
Copy link
Copy Markdown
Collaborator

@akaladarshi akaladarshi May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the comments it's not helping anyone.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. 8df812a2f

Mirrors the `forest-cli` treatment introduced in ChainSafe#6011 for the
`forest-wallet` binary. Address arguments to `balance`, `export`, `has`,
`delete`, `set-default`, `sign`, `verify` and the `--from` option of
`send` are now parsed into `StrictAddress` at CLI-parse time. clap
surfaces a `ValueValidation` error on a malformed input instead of
deferring to a runtime `StrictAddress::from_str(...)?` fallback, and
each handler simply destructures the typed value via
`field: StrictAddress(inner)`.

Network detection is moved ahead of `Cli::parse_from` so that clap-time
`StrictAddress` validation accepts testnet (`t0...`) addresses when the
daemon reports a testnet chain. Client construction errors propagate
(matching `forest-cli`); an unreachable daemon leaves the global
`CurrentNetwork` at its mainnet default.

`wallet_default_address()` now returns `Option<Address>` rather than
`Option<String>`, removing a stringify-then-reparse round trip through
`StrictAddress::from_str` in both the `list` and `send` handlers.

`Send.target_address` is intentionally kept as a `String` because it
accepts ETH (`0x...`) recipients, which `StrictAddress` rejects;
`resolve_target_address` continues to handle the dispatch.
`ValidateAddress.address` also stays a `String`, since its purpose is
to validate arbitrary user input.

Two clap-level regression tests guard against an accidental revert of
the `StrictAddress` typing.

Refs ChainSafe#6012
@0xDevNinja 0xDevNinja force-pushed the 0xdevninja/issue-6012-wallet-strict-address branch from c8005c8 to 8df812a Compare May 14, 2026 10:11
@0xDevNinja 0xDevNinja requested a review from akaladarshi May 14, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: implement strict address validation in forest-wallet CLI

2 participants