Skip to content

feat(wallet)!: apply FRC-0102 envelope to sign/verify by default#6967

Open
0xDevNinja wants to merge 1 commit into
ChainSafe:mainfrom
0xDevNinja:0xdevninja/issue-6442-frc0102-wallet
Open

feat(wallet)!: apply FRC-0102 envelope to sign/verify by default#6967
0xDevNinja wants to merge 1 commit into
ChainSafe:mainfrom
0xDevNinja:0xdevninja/issue-6442-frc0102-wallet

Conversation

@0xDevNinja
Copy link
Copy Markdown

@0xDevNinja 0xDevNinja commented Apr 24, 2026

Summary of changes

Changes introduced in this pull request:

  • Wrap the decoded message with the FRC-0102 Filecoin signing envelope (\x19Filecoin Signed Message:\n<len>) in forest-wallet sign and forest-wallet verify before handing it to the signer/verifier. Aligns Forest with Ledger Filecoin, Filsnap, iso-filecoin and Lotus (feat(cli): add FRC-102 compatible EIP-191 signing via --fevm flag (#13256) filecoin-project/lotus#13388).
  • Add a --raw flag to both sign and verify that restores the previous raw-bytes behaviour. --raw is also the correct choice when signing on-chain Filecoin messages, which must not be wrapped.
  • Add a wrap_frc0102(&[u8]) -> Vec<u8> helper with a const FRC_0102_FILECOIN_PREFIX: &[u8] in wallet_cmd.rs.
  • Expand the unit test suite with 7 cases: empty/short/long/binary/length-boundary/embedded-newline format checks, plus a secp256k1 round-trip that asserts verify(wrapped) succeeds and verify(raw) fails.
  • CHANGELOG.md: Breaking entry that also calls out (a) pre-FRC-0102 nodes cannot verify the new default without --raw, and (b) on-chain messages must still use --raw.

The envelope is applied client-side only. Forest's and Lotus's WalletSign / WalletVerify RPCs treat their input as opaque bytes and do not wrap, so a single wrap here is correct against today's daemons. If that ever changes server-side, users will want --raw to avoid a double envelope.

Reference issue to close (if applicable)

Closes #6442

Other information and links

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:

  • Issue triage, reading of the Lotus reference implementation (feat(cli): add FRC-102 compatible EIP-191 signing via --fevm flag (#13256) filecoin-project/lotus#13388), implementation, self-review (two 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 -- wrap_frc0102 frc0102_roundtrip (7/7 pass) are all green.
  • End-to-end CLI verification against a running daemon has not been performed locally; that path goes through an unchanged wallet_sign / wallet_verify that receives the already-wrapped bytes.
  • I have reviewed every diff line and take full responsibility for correctness.

Summary by CodeRabbit

  • Breaking Changes

    • Signed messages now use the FRC-0102 Filecoin signing envelope by default; signatures from pre-FRC-0102 releases may not verify unless --raw is used.
  • New Features

    • Added a --raw option to control envelope wrapping for both sign and verify commands.
  • Documentation

    • Added a “Breaking” note to the changelog documenting the behavior change.
  • Chores

    • Updated wordlist with two new entries.

Review Change Stack

@0xDevNinja 0xDevNinja requested a review from a team as a code owner April 24, 2026 10:39
@0xDevNinja 0xDevNinja requested review from akaladarshi and hanabi1224 and removed request for a team April 24, 2026 10:39
@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

Walkthrough

Adds conditional FRC-0102 envelope wrapping to forest-wallet sign and verify (default on), introduces --raw to preserve prior raw-bytes behavior, updates the wallet signing backend to accept message bytes, and adds unit tests and a breaking changelog notice.

Changes

FRC-0102 Envelope Implementation

Layer / File(s) Summary
CLI imports and backend plumbing
src/wallet/subcommands/wallet_cmd.rs
Removes base64 import, changes the local signing helper to accept Vec<u8>, adds --raw flags to Sign and Verify, and updates sign/verify execution to hex-decode and conditionally wrap bytes before calling the backend.
Wrap implementation and tests
src/wallet/subcommands/wallet_cmd.rs
Adds FRC_0102_FILECOIN_PREFIX and wrap_frc0102(msg) plus comprehensive unit tests covering wire-format correctness (empty, length boundaries, binary/newline) and a secp256k1 roundtrip that distinguishes wrapped vs raw verification.
Changelog and dictionary
CHANGELOG.md, .config/forest.dic
Adds a breaking CHANGELOG entry noting default FRC-0102 wrapping and compatibility notes; increments .config/forest.dic count and adds Filsnap and interoperating entries.

Sequence Diagram

sequenceDiagram
  participant CLI as forest-wallet CLI
  participant Wrap as wrap_frc0102
  participant Backend as WalletBackend
  participant Signer as crypto signing/verification
  CLI->>CLI: parse message hex, check --raw
  CLI->>Wrap: wrap_frc0102(msg) when raw==false
  CLI->>Backend: pass message bytes to wallet_sign/wallet_verify
  Backend->>Signer: perform signature or verification on provided bytes
  Signer-->>Backend: signature/verification result
  Backend-->>CLI: return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • hanabi1224
  • akaladarshi
  • LesnyRumcajs
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(wallet)!: apply FRC-0102 envelope to sign/verify by default' accurately summarizes the main breaking change introduced in this PR.
Linked Issues check ✅ Passed The PR fully addresses all completion criteria from issue #6442: implementation of FRC-0102 wrapping in sign/verify commands, comprehensive tests, and updated documentation.
Out of Scope Changes check ✅ Passed All changes are directly related to FRC-0102 implementation. Dictionary updates for 'Filsnap' and 'interoperating' are necessary additions supporting the PR changes.

✏️ 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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/wallet/subcommands/wallet_cmd.rs`:
- Around line 475-491: The Sign branch is base64-encoding the wrapped message
(BASE64_STANDARD.encode) before sending to backend.wallet_sign, which causes
remote wallets to sign the base64 text bytes instead of the raw/wrapped bytes
used by verify; change the flow in the Self::Sign arm so that
backend.wallet_sign receives the raw byte payload (i.e., the result of
wrap_frc0102(&message) or message when raw is true) rather than the base64
text—move or remove the BASE64_STANDARD.encode call and only base64-encode
when/if the backend API truly expects a base64 string, ensuring
functions/variables mentioned (Self::Sign, wrap_frc0102, BASE64_STANDARD.encode,
backend.wallet_sign) are updated consistently so sign and verify operate on
identical byte sequences.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8b670a84-4ce2-4479-bc7e-ef7c7caf0fd7

📥 Commits

Reviewing files that changed from the base of the PR and between 65c39a0 and 24b9f00.

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

Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

❌ Patch coverage is 85.93750% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.19%. Comparing base (8088a29) to head (11ad84a).
⚠️ Report is 5 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/wallet/subcommands/wallet_cmd.rs 85.93% 9 Missing ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
src/wallet/subcommands/wallet_cmd.rs 33.53% <85.93%> (+8.07%) ⬆️

... and 11 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 8088a29...11ad84a. 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.

@akaladarshi
Copy link
Copy Markdown
Collaborator

Hi @0xDevNinja
Thanks for raising the PR for this.
It seems the linter is failing in CI and there is a comment form coderabbit AI could you please check and fix it if needed.

@akaladarshi
Copy link
Copy Markdown
Collaborator

Any update on this @0xDevNinja ?

@0xDevNinja 0xDevNinja force-pushed the 0xdevninja/issue-6442-frc0102-wallet branch from 24b9f00 to 4514566 Compare May 13, 2026 10:20
@0xDevNinja
Copy link
Copy Markdown
Author

Hey, sorry for the delay. Just pushed an update:

  • Spellcheck: added Filsnap and interoperating to .config/forest.dic (both real Filecoin/English terms, didn't want to rephrase).
  • CodeRabbit's sign/verify parity flag was valid — in remote mode sign was base64-encoding the wrapped bytes while verify consumed raw bytes, so a remote daemon would have signed the base64 text instead of the message. Reworked wallet_sign to take Vec<u8> and pass the payload through unchanged so both paths operate on the same bytes. Dropped the now-unused BASE64_STANDARD import.
  • Rebased onto main to clear the behind state.

Let me know if anything else stands out.

@0xDevNinja 0xDevNinja force-pushed the 0xdevninja/issue-6442-frc0102-wallet branch 2 times, most recently from 2a78947 to c985118 Compare May 14, 2026 07:50
Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated

/// FRC-0102 envelope prefix for Filecoin `personal_sign`-style messages.
///
/// See <https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0102.md>.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Link to the FRC should be an internal doc not user facing.

Also keep the comment simple:

Suggested change
/// See <https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0102.md>.
/// FRC-0102 envelope prefix format: `0x19 || "Filecoin Signed Message:\n" || ascii(len(msg)) || msg`.

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.

Done — dropped the FRC link, collapsed to the one-line wire-format note. ff406be63

Comment thread CHANGELOG.md Outdated

### Breaking

- [#6442](https://github.com/ChainSafe/forest/issues/6442): `forest-wallet sign` and `forest-wallet verify` now apply the FRC-0102 signing envelope (`\x19Filecoin Signed Message:\n<len>`) to the message by default, for interoperability with Ledger, Filsnap and other Filecoin wallets. Signatures produced with the new default are NOT verifiable by pre-FRC-0102 Forest/Lotus releases without passing `--raw` on the verifier side; use `--raw` on both sides to reproduce the previous raw-bytes behaviour (required when signing on-chain Filecoin messages, which must not be wrapped).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Don't need to add internal details in the ChangeLog, keep it simple.

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.

Trimmed to two sentences. ff406be63

Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated
// opaque bytes — neither wraps — so a single wrap here is
// correct against today's Forest/Lotus daemons. If a daemon
// ever starts wrapping server-side, this will need revisiting
// (prefer `--raw` in that case to avoid a double envelope).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We have already explain the raw flag above no need to again explain what it does and how it does.

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 the duplicated rationale. ff406be63

Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated
const FRC_0102_FILECOIN_PREFIX: &[u8] = b"\x19Filecoin Signed Message:\n";

/// Wraps `msg` with the FRC-0102 Filecoin signing envelope so that signatures
/// produced by `forest-wallet sign` are interoperable with Ledger, Filsnap,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Leave the unnecessary comment regarding Ledger and Filsnap, only mention what the function is for.

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 the Ledger/Filsnap mention; doc is now a single sentence. ff406be63

Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated
signature.verify(&wrapped, &key.address).unwrap();

// `verify` on the raw bytes must fail (proves the envelope is actually
// part of the signed payload and we are not accidentally stripping it).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

assert message already explains this, no need for the extra comment.

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. ff406be63

Comment thread src/wallet/subcommands/wallet_cmd.rs Outdated
Comment on lines +670 to +675
let len = msg.len().to_string();
let mut out = Vec::with_capacity(FRC_0102_FILECOIN_PREFIX.len() + len.len() + msg.len());
out.extend_from_slice(FRC_0102_FILECOIN_PREFIX);
out.extend_from_slice(len.as_bytes());
out.extend_from_slice(msg);
out
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You can keep it little simple here:

Suggested change
let len = msg.len().to_string();
let mut out = Vec::with_capacity(FRC_0102_FILECOIN_PREFIX.len() + len.len() + msg.len());
out.extend_from_slice(FRC_0102_FILECOIN_PREFIX);
out.extend_from_slice(len.as_bytes());
out.extend_from_slice(msg);
out
let len = msg.len().to_string();
[FRC_0102_FILECOIN_PREFIX, len.as_bytes(), msg].concat()

or

Suggested change
let len = msg.len().to_string();
let mut out = Vec::with_capacity(FRC_0102_FILECOIN_PREFIX.len() + len.len() + msg.len());
out.extend_from_slice(FRC_0102_FILECOIN_PREFIX);
out.extend_from_slice(len.as_bytes());
out.extend_from_slice(msg);
out
let mut out = format!("{FRC_0102_FILECOIN_PREFIX}{}", msg.len()).into_bytes();
out.extend_from_slice(msg);
out

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.

Switched to [FRC_0102_FILECOIN_PREFIX, len.as_bytes(), msg].concat(). ff406be63

@0xDevNinja 0xDevNinja force-pushed the 0xdevninja/issue-6442-frc0102-wallet branch from c985118 to ff406be Compare May 14, 2026 09:50
@0xDevNinja 0xDevNinja requested a review from akaladarshi May 14, 2026 09:54
`forest-wallet sign` and `forest-wallet verify` now wrap the decoded
message with the FRC-0102 Filecoin signing envelope
(`\x19Filecoin Signed Message:\n<len>`) before handing it to the signer
/ verifier. This aligns Forest with the Ledger Filecoin app, Filsnap,
iso-filecoin and Lotus (filecoin-project/lotus#13388), so signatures
produced by any of these wallets over the same bytes now round-trip.

A new `--raw` flag restores the prior raw-bytes behaviour on both
commands. `--raw` is also the correct choice when signing on-chain
Filecoin messages, which must not be wrapped.

The envelope is applied client-side only; the Forest and Lotus
`WalletSign` / `WalletVerify` RPCs treat their input as opaque bytes,
so a single wrap here is correct against today's daemons.

BREAKING CHANGE: signatures produced by the default invocation are not
verifiable by pre-FRC-0102 Forest or Lotus releases without `--raw` on
the verifier side.

Refs ChainSafe#6442
@0xDevNinja 0xDevNinja force-pushed the 0xdevninja/issue-6442-frc0102-wallet branch from ff406be to 11ad84a Compare May 14, 2026 10:36
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.

Implement FRC-102 in forest-wallet for consistency across Filecoin wallets

2 participants