Skip to content

Remove internal hardened BIP32 derivation via Bitcoin Core HD-key RPCs #6

Description

@BenWestgate

Follow-up to #3.

Goal

Remove the temporary Python implementation of hardened BIP32 child derivation from the Bitcoin Core integration. Python should stop at validating/serializing the BIP32 root xprv/tprv; Bitcoin Core should perform hardened derivation and public-key work.

Verified Core 31.1 behavior

Bitcoin Core v31.1.0 has createwalletdescriptor and gethdkeys, but does not expose addhdkey, derivehdkey, or sethdseed RPCs.

A root key can nevertheless be made known to a descriptor wallet by importing one inactive descriptor containing the root xprv/tprv. gethdkeys then returns the corresponding xpub/tpub, and createwalletdescriptor successfully derives the standard account-0 descriptors with origin metadata.

Using the public synthetic BIP93 test seed, this was verified on both networks:

  • mainnet: 44h/0h/0h, 49h/0h/0h, 84h/0h/0h, 86h/0h/0h
  • regtest: 44h/1h/0h, 49h/1h/0h, 84h/1h/0h, 86h/1h/0h

So createwalletdescriptor works on non-mainnet as well as mainnet. Its fixed account 0h is acceptable for the current codex32 integration.

Remaining problem

createwalletdescriptor is wallet-stateful: the wallet must already know the private HD key, and the RPC adds descriptors to that wallet. That is usable for private-wallet initialization, but it is not a clean stateless replacement for public_descriptors() / watch-only export. Using a scratch wallet would persist secret material and introduces cleanup/lifecycle concerns.

The newer derivehdkey design would be the clean primitive once it exists in a Bitcoin Core release that codex32 supports, because it can return the derived xpub plus key origin without requiring Python to implement hardened child derivation.

Implementation plan

  1. Keep only stdlib BIP32 root validation and root xprv/tprv serialization in Python.
  2. Where a private target wallet is already part of the operation, evaluate replacing Python hardened derivation with the Core 31.1 importdescriptors → gethdkeys → createwalletdescriptor flow, restricted to account 0.
  3. Do not introduce a persistent scratch wallet merely to support watch-only/public export without an explicit security review of secret-on-disk lifecycle and cleanup.
  4. When the minimum supported Bitcoin Core release includes derivehdkey, use it for explicit hardened paths and consume Core's returned origin + xpub/tpub.
  5. Remove _hardened_child and non-root child-xprv serialization from codex32 once all public descriptor and multisig-account derivation paths use Core.
  6. Add real-Core integration coverage on mainnet-style and non-mainnet extended-key versions, including BIP44/49/84/86 account-0 origins and BIP48 multisig derivation.
  7. Keep tests asserting that backup/recovery remains free of third-party/native secp256k1 dependencies.

Acceptance criteria

  • Python implements no general or hardened BIP32 child derivation for wallet interoperability.
  • Root seed → root xprv/tprv remains stdlib-only.
  • Hardened account derivation and public-key conversion occur in Bitcoin Core.
  • Mainnet and non-mainnet descriptor origins are verified against real Core.
  • No new persistent scratch wallet containing the root secret is introduced without an explicit security decision.

Activity

  1. added a commit that references this issue on Sep 17, 2026
    3d2acda
  2. added
    enhancementNew feature or request
    gate: adversarial reviewResolve, merge, or explicitly defer before the next full adversarial review.
    on Sep 21, 2026
  3. BenWestgate commented on Sep 25, 2026

    @BenWestgate
    OwnerAuthor

    Current-state audit (updated 2026-09-27): reviewability-v1 already implements the #6 production boundary. Python stops at stdlib BIP32 root validation/root xprv serialization; Bitcoin Core v32 gethdkeys/derivehdkey performs hardened account derivation and public-key conversion. No scratch-wallet fallback is used.

    PR #7 is now refreshed onto reviewability-v1 at head 4ceedeef, mergeable, with a green Python 3.10–3.15/full test matrix. It removes the remaining Python bip32/Coincurve oracle from tests and CI. PR #51 is restacked on that exact head at 0466510, is mergeable, and its pinned real-Core v32.0rc2 fixture-verification job passes. The prior conflict/no-fresh-run blocker is resolved.

    Review/merge #7 first, then retarget/merge #51 onto reviewability-v1. After both land, #6’s implementation and independent real-Core coverage are complete for the v1 gate.

  4. added
    area: ciContinuous integration and workflow configuration.
    area: wallet/coreWallet integration and Bitcoin Core boundaries.
    on Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: ciContinuous integration and workflow configuration.area: wallet/coreWallet integration and Bitcoin Core boundaries.enhancementNew feature or requestgate: adversarial reviewResolve, merge, or explicitly defer before the next full adversarial review.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions