Skip to content
Back to skills

Descriptors Wallet

ASecurity

Bitcoin Core descriptors wallet: createwallet, importdescriptors, multipath, ranges, watch-only, signing wallets, multisig coordination. Replaces legacy BDB wallet. USE WHEN: setting up Bitcoin Core wallet, watching xpubs, building signing pipelines via Core.

  • 31 stars
  • 0 votes
  • 2 copies
  • 4 views
  • Added September 8, 2026
ai-agentsgobashsql

Works with

  • cli

Security analysis

A100/100

Scanned September 22, 2026

npx -y skills add claude-dev-suite/claude-dev-suite --skill descriptors-wallet --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Descriptors Wallet?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Descriptors Wallet
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/claude-dev-suite-descriptors-wallet/badge)](https://www.skillsdirectory.com/skills/claude-dev-suite-descriptors-wallet)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: bitcoin-core-descriptors-wallet
description: |
  Bitcoin Core descriptors wallet: createwallet, importdescriptors,
  multipath, ranges, watch-only, signing wallets, multisig coordination.
  Replaces legacy BDB wallet.
  USE WHEN: setting up Bitcoin Core wallet, watching xpubs, building
  signing pipelines via Core.
allowed-tools: Read, Grep, Glob
---

# Descriptors Wallet (Bitcoin Core)

Descriptor wallets have been the default since Bitcoin Core 23.0.
Since Bitcoin Core 30.0 (October 2025) BDB legacy wallets can no longer
be created *or loaded* — they must be migrated. Current as of Bitcoin
Core 31.1 (July 2026).

## Create

```bash
bitcoin-cli createwallet "hot" \
  disable_private_keys=false \
  blank=false \
  passphrase="" \
  avoid_reuse=false \
  descriptors=true \
  load_on_startup=true
```

Options:
- `disable_private_keys=true` for watch-only.
- `blank=true` to create without auto-generated descriptors (you'll
  import yours).
- `passphrase` (SQLite-level encryption).
- `descriptors` has defaulted to `true` since 23.0; since 30.0 it must
  also *be* `true` — `false` errors with `descriptors argument must be
  set to "true"; it is no longer possible to create a legacy wallet.`

## Default descriptors

When created with `blank=false`, Core auto-generates:
- `pkh()` (legacy) — receive + change.
- `sh(wpkh())` (P2SH-segwit) — receive + change.
- `wpkh()` (native segwit) — receive + change.
- `tr()` (Taproot) — receive + change.

Each at standard BIP44/49/84/86 paths under a fresh internal master.

## Import descriptors

For watch-only wallet importing an xpub:
```json
[
  {
    "desc": "wpkh([d34db33f/84h/0h/0h]xpub6CV2.../<0;1>/*)#chk",
    "active": true,
    "internal": false,
    "range": [0, 999],
    "next_index": 0,
    "timestamp": "now"
  }
]
```

`active`: marks the receiving descriptor.
`internal`: change chain (alternate path).
`range`: scan window.
`timestamp`: `now` (no rescan) or unix timestamp (rescan from then).

For a signing wallet, use the privkey form:
```
{"desc": "wpkh([fp/84h/0h/0h]xprv.../<0;1>/*)#chk", ...}
```

## Inspecting

```bash
bitcoin-cli -rpcwallet=hot listdescriptors
bitcoin-cli -rpcwallet=hot listdescriptors true   # show private keys
bitcoin-cli -rpcwallet=hot getdescriptorinfo "<desc>"
```

## Multipath descriptors (BIP389)

```
wpkh([fp/84h/0h/0h]xpub.../<0;1>/*)#chk
```

`<0;1>` = receive (0) and change (1). One descriptor handles both
chains. Modern wallets prefer this over two separate descriptors.

## MuSig2 descriptors (BIP390)

```
tr(musig([fp1/...]xpub1.../<0;1>/*,[fp2/...]xpub2.../<0;1>/*))
tr(musig([fp1/...]xpub1...,[fp2/...]xpub2...)/<0;1>/*)
```

`musig(KEY,...)` aggregates the participants into one BIP327 key, so
both the output and the spend look like ordinary single-sig Taproot.
Core allows `musig()` only inside `tr()` and `rawtr()`, never nested in
another `musig()`, and sorts the participant keys after derivation and
before aggregating, so the order they are written in does not change
the address — but the descriptor *string* keeps the written order, so
participants who write the keys in different orders hold descriptors
with different `#chk` checksums. Derivation steps written *after*
`musig(...)` run on the BIP328 synthetic xpub, must be unhardened, and
are only legal when no participant key is itself ranged. Multipath is
the same either/or: `<0;1>` may sit on the participant keys or after
`musig(...)`, never on both.

Never auto-generated: import it, each participant importing the same
descriptor with only their own key in private form.

Support, as of Bitcoin Core 31.1 (July 2026):
- 30.0 (October 2025): descriptor parsing and address derivation
  (`getdescriptorinfo`, `deriveaddresses`), plus the BIP373 PSBT fields
  `PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS` (`0x1a`),
  `PSBT_IN_MUSIG2_PUB_NONCE` (`0x1b`),
  `PSBT_IN_MUSIG2_PARTIAL_SIG` (`0x1c`) and
  `PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS` (`0x08`).
- 31.0 (April 2026): the wallet can receive *and spend* — nonce
  generation and partial signing inside `walletprocesspsbt`
  (PR #29675, merged 2025-10-14).

Neither the 30.0 nor the 31.0 release notes mention MuSig2 at all, so
check `doc/descriptors.md` and `test/functional/wallet_musig.py` in the
tag you actually run rather than the notes.

Signing takes two `walletprocesspsbt` passes per participant, one per
MuSig2 round:
```bash
# Round 1 - every participant wallet adds its public nonce
n1=$(bitcoin-cli -rpcwallet=p1 walletprocesspsbt "$psbt" | jq -r .psbt)
n2=$(bitcoin-cli -rpcwallet=p2 walletprocesspsbt "$psbt" | jq -r .psbt)
nonces=$(bitcoin-cli combinepsbt "[\"$n1\",\"$n2\"]")

# Round 2 - every participant adds its partial signature
s1=$(bitcoin-cli -rpcwallet=p1 walletprocesspsbt "$nonces" | jq -r .psbt)
s2=$(bitcoin-cli -rpcwallet=p2 walletprocesspsbt "$nonces" | jq -r .psbt)
sigs=$(bitcoin-cli combinepsbt "[\"$s1\",\"$s2\"]")
bitcoin-cli finalizepsbt "$sigs"
```

`decodepsbt` reports `musig2_participant_pubkeys`, `musig2_pubnonces`
and `musig2_partial_sigs`, which is how you tell which round is short.
Secnonces are held in memory only and never serialized to the wallet
file.

## Multisig

```bash
bitcoin-cli -rpcwallet=ms createwallet "ms" \
  disable_private_keys=true descriptors=true blank=true

bitcoin-cli -rpcwallet=ms importdescriptors '[
  {"desc":"wsh(sortedmulti(2,[fp1/48h/0h/0h/2h]xpub1.../<0;1>/*,[fp2/48h/0h/0h/2h]xpub2.../<0;1>/*,[fp3/48h/0h/0h/2h]xpub3.../<0;1>/*))#chk",
   "active":true,"internal":false,"range":[0,999],"timestamp":"now"},
  {"desc":"wsh(sortedmulti(2,[fp1/48h/0h/0h/2h]xpub1.../<0;1>/*,[fp2/48h/0h/0h/2h]xpub2.../<0;1>/*,[fp3/48h/0h/0h/2h]xpub3.../<0;1>/*))#chk",
   "active":true,"internal":true,"range":[0,999],"timestamp":"now"}
]'
```

Or use `<0;1>/*` and only one entry.

## Receiving addresses

```bash
bitcoin-cli -rpcwallet=hot getnewaddress         # next external
bitcoin-cli -rpcwallet=hot getrawchangeaddress   # next internal
bitcoin-cli -rpcwallet=hot deriveaddresses "<desc>" '[0,9]'   # range
```

## Building / signing PSBT

```bash
# Build PSBT funded with appropriate inputs and change
bitcoin-cli -rpcwallet=hot walletcreatefundedpsbt \
  '[]'  '[{"bc1q...": 0.001}]'  0  '{"feeRate": 0.00001}'

# Sign
bitcoin-cli -rpcwallet=hot walletprocesspsbt "<psbt>"

# Or fully build and broadcast in one go (signing wallet only)
bitcoin-cli -rpcwallet=hot sendtoaddress bc1q... 0.001
```

For multisig coordination across separate wallets:
```bash
# Wallet A signs partial
psbt_a=$(bitcoin-cli -rpcwallet=signA walletprocesspsbt "$psbt" | jq -r .psbt)
# Wallet B signs partial
psbt_ab=$(bitcoin-cli -rpcwallet=signB walletprocesspsbt "$psbt_a" | jq -r .psbt)
# Combine + finalize
final=$(bitcoin-cli combinepsbt "[\"$psbt_ab\"]")
hex=$(bitcoin-cli finalizepsbt "$final" | jq -r .hex)
bitcoin-cli sendrawtransaction "$hex"
```

## Migrating legacy → descriptors

Pass the wallet name (or address it through the `-rpcwallet=` endpoint);
the wallet does not need to be loaded — since 30.0 it cannot be:

```bash
bitcoin-cli migratewallet "legacywallet"
bitcoin-cli migratewallet "legacywallet" "<passphrase>"   # if encrypted
```

Core opens the BDB file read-only inside the call, writes a backup named
`<wallet name>-<timestamp>.legacy.bak` into that wallet's directory, and
replaces the wallet with a descriptor one. Restore that backup with
`restorewallet` if the migration went wrong.

Result fields: `wallet_name`, `backup_path`, plus `watchonly_name` and
`solvables_name` when the legacy wallet held watch-only or
solvable-but-unwatched scripts that get split into their own wallets.

## Legacy RPCs removed in 30.0

Gone, with no descriptor-wallet equivalent under the same name:
`addmultisigaddress`, `dumpprivkey`, `dumpwallet`, `importaddress`,
`importmulti`, `importprivkey`, `importpubkey`, `importwallet`,
`newkeypool`, `sethdseed`, `upgradewallet`. The `bitcoin-wallet` tool
also dropped its `-legacy`, `-descriptors` and `-withinternalbdb`
options.

Port old scripts to `importdescriptors` (import), `listdescriptors`
(export, `true` for private keys) and `createwalletdescriptor` (add a
script type to an existing wallet).

## Common bugs

- Forgetting `range` on import → only address index 0 generated.
- `timestamp: 0` → triggers full chain rescan (slow).
- Mixing `[fp/path]` formatting (e.g., missing `]`) → checksum
  mismatch on import.
- Importing both `wpkh` and `sh(wpkh)` for same xpub but treating as
  separate accounts → in fact same key, different script type.
- Multisig with `multi` (unsorted) but signers in different order
  across wallets → completely different addresses.
- Restarting bitcoind between the two MuSig2 rounds → the in-memory
  secnonces are gone and every participant must re-nonce.

## See also

- [../../protocol/descriptors/SKILL.md](../../protocol/descriptors/SKILL.md)
- [../../protocol/psbt/SKILL.md](../../protocol/psbt/SKILL.md)
- [../../wallets/hd/SKILL.md](../../wallets/hd/SKILL.md)
- [../../cryptography/musig2/SKILL.md](../../cryptography/musig2/SKILL.md)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…