Skip to content

wallet: Add deriveHDKey interface - #36070

Open
PraneethGunas wants to merge 2 commits into
bitcoin:masterfrom
PraneethGunas:derivehdkey-interface
Open

wallet: Add deriveHDKey interface#36070
PraneethGunas wants to merge 2 commits into
bitcoin:masterfrom
PraneethGunas:derivehdkey-interface

Conversation

@PraneethGunas

@PraneethGunas PraneethGunas commented Aug 24, 2026

Copy link
Copy Markdown

This PR adds a wallet interface for derivehdkey.

The motivation is the same as #35436 and #34861. The derivehdkey RPC exists
(#32784), but the GUI does not go through RPC, so the logic is currently out of
its reach. A dedicated wallet interface makes it available for multisig setup.
Alongside the addhdkey interface, this lets the GUI add an HD key and derive a
shareable xpub from it. Tracked as the deriveHDKey item in #35645.

Key changes:

  • Add CWallet::DeriveHDKey(), which selects the HD key and derives it at the
    requested path.
  • Update the derivehdkey RPC to call it, rather than repeating the watch-only,
    hardened path and unlock checks. Only the xpub argument parsing stays in the RPC.
  • Add interfaces::Wallet::deriveHDKey(), which returns the derived xpub and its
    key origin. Private key material does not cross the interface.
  • Add unit test coverage for interfaces::Wallet::deriveHDKey().

The RPC maps WalletErrorCode::UnlockNeeded to -13 so callers can prompt for a passphrase and retry, and everything else to -4, moving the unhardened path and failed derivation cases from -8 and the key selection cases from -5

@DrahtBot DrahtBot changed the title wallet: Add deriveHDKey interface wallet: Add deriveHDKey interface Aug 24, 2026
@DrahtBot

DrahtBot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/36070.

Reviews

See the guideline and AI policy for information on the review process.

Type Reviewers
Concept ACK polespinasa, jeanpablojp

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #36126 (wallet, rpc: Implements set key label functionality by polespinasa)
  • #35436 (wallet: Add addHDkey interface by pseudoramdom)
  • #35377 (wallet: Allow importing of descriptors without private keys when the wallet has the private keys by achow101)
  • #34861 (wallet: Add importdescriptors interface by polespinasa)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@polespinasa

polespinasa commented Aug 25, 2026

Copy link
Copy Markdown
Member

concept ACK

will review soon :)

@jeanpablojp jeanpablojp 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.

Concept ACK

Left some comments.

Comment thread src/wallet/test/wallet_interfaces_tests.cpp Outdated
Comment thread src/wallet/rpc/wallet.cpp Outdated

@polespinasa polespinasa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

did a first swift review

Comment thread src/wallet/rpc/wallet.cpp Outdated
Comment thread src/wallet/wallet.h Outdated
Comment thread src/wallet/wallet.cpp Outdated
return *xprv;
}

util::Expected<std::pair<CExtKey, KeyOriginInfo>, WalletError> CWallet::DeriveHDKey(const std::vector<uint32_t>& path, const std::optional<CExtPubKey>& hdkey) const

@polespinasa polespinasa Aug 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

in a1250d2 wallet: Add deriveHDKey interface

All this is duplicated from RPC code. Why not just make both the interface and the RPC rely on the same code? Because of both using different code functions, they both return different error codes for same errors.

Also probably this commit could be split in different commits. First a preparatory commit that creates the DeriveHDKey function + testing, then a commit rebasing the RPC to use it, then a commit creating the interface. You can check #34861 for a commit structure idea :)

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. Split into "Add CWallet::DeriveHDKey" and "Add deriveHDKey interface".

On sharing code with the RPC: it already calls SelectHDKey(). What's left is the watch-only, hardened-path and unlock guards, which pin the error codes wallet_derivehdkey.py asserts (-4, -8, -13).

DeriveHDKey() returns GenericError for every failure, so routing the rest through it would turn "Unable to derive HD key at the requested path" from -8 into -5. Keeping -8 would need a new WalletErrorCode just so one caller can pick a different number, which src/wallet/types.h:48-50 warns against.

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.

Now both the interface and the RPC rely on the same CWallet::DeriveHDKey

@jeanpablojp jeanpablojp 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.

Reviewed again. Left one more comment on the new test.

Comment thread src/wallet/test/wallet_tests.cpp Outdated
@DrahtBot

DrahtBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

🚧 At least one of the CI tasks failed.
Task iwyu: https://github.com/bitcoin/bitcoin/actions/runs/33223334574/job/99831020528
LLM reason (✨ experimental): CI failed because the IWYU (include-what-you-use) check reported include issues and intentionally exited with a failure.

Hints

Try to run the tests locally, according to the documentation. However, a CI failure may still
happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

@hebasto

hebasto commented Sep 2, 2026

Copy link
Copy Markdown
Member

From https://github.com/bitcoin/bitcoin/actions/runs/33532997440/job/100190403622?pr=36070:

--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -34,19 +34,16 @@
 #include <vector>
 
 class ArgsManager;
-class CKeyID;
-class CPubKey;
 class CScript;
 class PartiallySignedTransaction;
 class uint256;
 enum class FeeReason;
 enum class OutputType;
 struct bilingual_str;
+
 namespace wallet {
-struct CreatedTransactionResult;
 class CCoinControl;
 class CWallet;
-enum class AddressPurpose;
 struct CRecipient;
 struct WalletContext;
 } // namespace wallet

@achow101

achow101 commented Sep 9, 2026

Copy link
Copy Markdown
Member

Why is SelectHDKey being split into a separate function? This PR should only create one function: DeriveHDKey, and that should be called by RPC. There should not need to be a RPC that is essentially duplicating code of CWallet::DeriveHDKey. This also allows the unit tests to be dropped as the functional tests (can) cover those cases.

@PraneethGunas

Copy link
Copy Markdown
Author

Why is SelectHDKey being split into a separate function? This PR should only create one function: DeriveHDKey, and that should be called by RPC. There should not need to be a RPC that is essentially duplicating code of CWallet::DeriveHDKey. This also allows the unit tests to be dropped as the functional tests (can) cover those cases.

It was done so that the the RPC error codes and messages are preserved. Also, looking at the discussions in #35436 (comment) it makes sense to collapse the error codes down based on how the errors are logged/displayed #35436 (comment)

The derivehdkey RPC now calls it instead of repeating the watch-only, hardened
path and unlock checks, so those errors change from -8 and -5 to -4.
Only the xpub is returned. interfaces::Wallet is not authenticated, so the
private key is not passed over it.
@PraneethGunas

Copy link
Copy Markdown
Author

Rebased on master and rebuilt the history

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants