Skip to content

fix(key-wallet): actionable error when add_account(_, None) hits a keyless wallet#847

Open
lklimek wants to merge 2 commits into
devfrom
fix/add-account-actionable-error
Open

fix(key-wallet): actionable error when add_account(_, None) hits a keyless wallet#847
lklimek wants to merge 2 commits into
devfrom
fix/add-account-actionable-error

Conversation

@lklimek

@lklimek lklimek commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Why this PR exists

  • Problem: On an external-signable / watch-only wallet (one with no in-wallet private key), add_account(account_type, None) — and its BLS/EdDSA siblings — fail deep inside root_extended_priv_key() with a bare, non-actionable string: "External signable wallet has no private key". The message says what broke, never what to do.
  • What breaks without it: This cryptic error caused a downstream Dash Platform finding (Found-031) to be misdiagnosed three separate times as a production defect — when the correct usage is simply add_account(_, Some(xpub)). A keyless-wallet caller gets no signal pointing them at the right overload; the failure reads like a missing capability rather than an API misuse.

What was done

  • Added a typed error variant Error::KeylessWalletRequiresAccountKey { account_type, required_key } (manual-Display, matching the crate's existing hand-rolled Error; fully pattern-matchable).
  • In the derive-from-root branch of add_account (xpub), add_bls_account (32-byte BLS seed) and add_eddsa_account (32-byte Ed25519 seed), the keyless-wallet failure now maps to this variant, whose message names the remedy: supply the account's key material via the Some(..) argument.
  • Maps only the keyless failure — root_extended_priv_key() errors solely on keyless wallets, so the Some(..) path, all derivation logic, and every other error path are untouched. The None API shape (the legitimate hot-wallet convenience path) is preserved.

Testing

  • New unit tests: add_account(_, None) on watch-only + external-signable wallets returns the new variant and its message names the Some(..) remedy (all three siblings, feature-gated for BLS/EdDSA). 11/11 in-module pass.
  • cargo build -p key-wallet clean · cargo clippy -p key-wallet --all-targets -- -D warnings clean · cargo fmt applied.
  • Verified no exhaustive external match on Error breaks (all have _ => arms).

Breaking changes

None — additive Error variant only.

Checklist

  • Code compiles; clippy clean (-D warnings); cargo fmt applied
  • Tests added for the new behaviour
  • No secrets; additive change

🤖 Co-authored by Claudius the Magnificent AI Agent

Summary by CodeRabbit

  • New Features

    • Account creation now returns a clearer, typed error when a wallet cannot create accounts without an in-wallet private key.
    • The error message now explains what key is needed and how to provide it.
  • Bug Fixes

    • Improved handling for adding standard, BLS, and Ed25519 accounts in watch-only and external-signable wallets.
    • Added test coverage for the new keyless-wallet error behavior across supported wallet types.

… wallets

Deriving an account from an in-wallet private key on a watch-only or
external-signable wallet used to bubble up a cryptic
InvalidParameter("External signable wallet has no private key"), which
got misdiagnosed as a production bug three times instead of being read
as a usage error.

Add a typed, pattern-matchable Error::KeylessWalletRequiresAccountKey
{ account_type, required_key } and map ONLY the keyless-wallet failure
of root_extended_priv_key() to it in the derive-from-root branch of
add_account / add_bls_account / add_eddsa_account. Its Display names
its own remedy: supply the account's xpub/seed via the Some(..)
argument. The None hot-wallet convenience path, the Some(..) branch,
and every other error path are left unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BqMDY7ZZNwSvdPgBrJQQNq
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new Error::KeylessWalletRequiresAccountKey variant with account_type and required_key fields, plus a Display implementation. Updates add_account, add_bls_account, and add_eddsa_account to return this typed error when a keyless wallet lacks the required key, with corresponding unit tests.

Changes

Keyless wallet account error

Layer / File(s) Summary
Error variant and Display formatting
key-wallet/src/error.rs
Adds KeylessWalletRequiresAccountKey variant carrying account_type and required_key, with a Display arm producing a descriptive remedy message.
Account-creation methods mapped to typed error
key-wallet/src/wallet/accounts.rs
add_account, add_bls_account, and add_eddsa_account now map missing in-wallet private key failures to the new typed error with method-specific required key descriptions; doc comments updated accordingly.
Unit tests for keyless wallet error behavior
key-wallet/src/tests/unit_variant_wallet_tests.rs
New imports and feature-gated tests assert the typed error and message content for watch-only/external-signable wallets across all three account-add methods.

Estimated code review effort: 2 (Simple) | ~12 minutes

Suggested labels: ready-for-review

Suggested reviewers: xdustinface

🚥 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 matches the main change: a more actionable error for add_account(_, None) on keyless wallets.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/add-account-actionable-error

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.

@lklimek lklimek marked this pull request as ready for review July 7, 2026 08:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
key-wallet/src/wallet/accounts.rs (1)

46-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the duplicated derive-or-error pattern into a helper.

The self.root_extended_priv_key().map_err(|_| Error::KeylessWalletRequiresAccountKey { account_type, required_key: "..." })? block is duplicated verbatim (aside from the required_key string) across add_account, add_bls_account, and add_eddsa_account. A small private helper (e.g. fn require_root_priv_key(&self, account_type: AccountType, required_key: &'static str) -> Result<ExtendedPrivKey>) would remove the duplication and keep the mapping consistent if the logic ever changes.

♻️ Example helper
+    fn require_root_priv_key(
+        &self,
+        account_type: AccountType,
+        required_key: &'static str,
+    ) -> Result<ExtendedPrivKey> {
+        self.root_extended_priv_key().map_err(|_| Error::KeylessWalletRequiresAccountKey {
+            account_type,
+            required_key,
+        })
+    }

Also applies to: 111-116, 180-185

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@key-wallet/src/wallet/accounts.rs` around lines 46 - 51, The duplicate
root-key error mapping in add_account, add_bls_account, and add_eddsa_account
should be extracted into a small private helper so the derive-or-error logic is
centralized. Add a helper on the accounts implementation, such as
require_root_priv_key, that calls root_extended_priv_key and converts failures
into KeylessWalletRequiresAccountKey using the supplied AccountType and
required_key string. Then replace each inline map_err block with the helper so
the behavior stays identical while removing the repeated pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@key-wallet/src/wallet/accounts.rs`:
- Around line 46-51: The duplicate root-key error mapping in add_account,
add_bls_account, and add_eddsa_account should be extracted into a small private
helper so the derive-or-error logic is centralized. Add a helper on the accounts
implementation, such as require_root_priv_key, that calls root_extended_priv_key
and converts failures into KeylessWalletRequiresAccountKey using the supplied
AccountType and required_key string. Then replace each inline map_err block with
the helper so the behavior stays identical while removing the repeated pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 31fb0bff-18cc-45e9-844f-a361b0a93a08

📥 Commits

Reviewing files that changed from the base of the PR and between 3170ad3 and 96cebd2.

📒 Files selected for processing (3)
  • key-wallet/src/error.rs
  • key-wallet/src/tests/unit_variant_wallet_tests.rs
  • key-wallet/src/wallet/accounts.rs

@xdustinface

Copy link
Copy Markdown
Collaborator

@lklimek CI is broken here.

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.

2 participants