Tools/Explorers

Bitcoin Output Descriptors Reference Guide

Reference guide to Bitcoin output descriptors: syntax, types (pk, pkh, wpkh, tr, multi), checksum, and key expressions. Learn how descriptors replace xpubs.

Spark TeamInvalid Date

What Are Output Descriptors?

Bitcoin output descriptors are a standardized language for describing which scripts (and therefore which addresses) a wallet should track. Defined across BIP 380 through BIP 386, descriptors package script type, key material, and derivation metadata into a single, unambiguous string. Instead of handing someone an xpub and hoping they guess the correct address format, you hand them a descriptor that encodes everything needed to reproduce your wallet's address set.

Descriptors were introduced by Pieter Wuille in Bitcoin Core v0.17 (October 2018) and have since become the default wallet representation. As of Bitcoin Core v23.0 (April 2022), all newly created wallets are descriptor wallets by default. The descriptor format also serves as the foundation for miniscript, enabling wallets to reason about arbitrary spending policies without custom code per script template.

Why Descriptors Replace Raw xpubs

An extended public key alone carries no information about how it should be used. When SegWit introduced multiple new address types, the same xpub could produce P2PKH, P2SH-P2WPKH, or native P2WPKH addresses depending on interpretation. This ambiguity caused real interoperability failures: users who exported an xpub from one wallet and imported it into another would see the wrong addresses, miss UTXOs, or report incorrect balances.

Descriptors solve this by making the script type explicit. A descriptor like wpkh([fingerprint/84'/0'/0']xpub...) tells any compliant wallet exactly which derivation path was used, which script type to construct, and which master key the xpub originated from. This eliminates guesswork and enables reliable wallet migration.

For a deeper look at how Bitcoin address types evolved and why this ambiguity emerged, see our research on Bitcoin address types from P2PKH to Taproot.

BIP Specification Map

The descriptor standard is split across seven BIPs, each covering a specific set of descriptor functions. All were authored by Pieter Wuille and Ava Chow, all carry "Deployed" status.

BIPTitleDescriptor Functions
380General OperationFramework: key expressions, checksum, syntax rules
381Non-SegWit Descriptorspk(), pkh(), sh()
382SegWit Descriptorswpkh(), wsh()
383Multisig Descriptorsmulti(), sortedmulti()
384combo() Descriptorcombo()
385raw() and addr() Descriptorsraw(), addr()
386Taproot Descriptortr()

Additional BIPs extend the system: BIP 387 adds multi_a() and sortedmulti_a() for tapscript multisig, BIP 389 defines the multipath /<0;1>/ syntax, and BIP 390 introduces musig() for MuSig2 key aggregation.

Descriptor Type Reference

Each descriptor function maps to a specific Bitcoin script template. The following table covers every standardized descriptor type, its output script, and where it can appear.

FunctionScript TypeContextBIP
pk(KEY)P2PK: bare pubkeyTop-level, inside sh/wsh381
pkh(KEY)P2PKH: pubkey hashTop-level, inside sh381
wpkh(KEY)P2WPKH: native SegWitTop-level, inside sh382
sh(SCRIPT)P2SH: script hash wrapperTop-level only381
wsh(SCRIPT)P2WSH: witness script hashTop-level, inside sh382
tr(KEY) / tr(KEY, TREE)P2TR: TaprootTop-level only386
multi(k, KEY...)k-of-n multisig, specified orderInside sh/wsh383
sortedmulti(k, KEY...)k-of-n multisig, keys sortedInside sh/wsh383
multi_a(k, KEY...)Tapscript k-of-n multisigInside tr() script tree387
sortedmulti_a(k, KEY...)Tapscript k-of-n, keys sortedInside tr() script tree387
combo(KEY)P2PK + P2PKH (+ P2WPKH + P2SH-P2WPKH if compressed)Top-level only384
addr(ADDR)Script for the given addressTop-level only385
raw(HEX)Exact hex scriptTop-level only385

Key Expression Syntax

Key expressions define how public keys are represented inside descriptors. BIP 380 supports several key formats, each suited to different use cases.

Hex Public Keys

Raw public keys can be specified as hex strings. Compressed keys (33 bytes, prefix 02 or 03) are required for SegWit and Taproot contexts. X-only keys (32 bytes, no prefix) are used inside tr() descriptors. Uncompressed keys (65 bytes, prefix 04) are only valid in legacy pk() and pkh() contexts.

Extended Keys with Derivation Paths

HD wallet xpubs and xprvs (as defined in BIP 32) can include derivation path suffixes. For example, xpub6ERApfZ.../0/2 means "derive child 0, then child 2 from this extended key." Hardened derivation steps use h, ', or H as the suffix (e.g., /44h/0h/0h). Since hardened derivation requires the private key, descriptors with hardened steps after the xpub require an xprv.

Key Origin Information

The optional origin prefix records the master key fingerprint and full derivation path: [d34db33f/84'/0'/0']xpub6ERApfZ.... The fingerprint is 4 bytes (8 hex characters) of the master public key's Hash160. This metadata lets signing devices identify which master key produced the xpub and verify they hold the corresponding private key. It is essential for PSBT workflows, where multiple signers need to determine which inputs they can sign.

Ranged Descriptors and Wildcards

The /* wildcard makes a descriptor ranged: it represents a family of scripts, one per child index. For example, wpkh([d34db33f/84'/0'/0']xpub.../0/*) generates a new P2WPKH address for each index (0, 1, 2, ...). BIP 389 extends this with multipath syntax: /<0;1>/* encodes both the receive chain (index 0) and change chain (index 1) in a single descriptor. For more on how derivation paths work, see the key derivation paths reference.

Descriptor Examples

The following examples demonstrate common wallet configurations. Each shows the descriptor syntax alongside the address type it produces.

Legacy P2PKH (BIP 44):

pkh([d34db33f/44'/0'/0']xpub6ERApfZ.../0/*)#checksum

Wrapped SegWit P2SH-P2WPKH (BIP 49):

sh(wpkh([d34db33f/49'/0'/0']xpub6ERApfZ.../0/*))#checksum

Native SegWit P2WPKH (BIP 84):

wpkh([d34db33f/84'/0'/0']xpub6ERApfZ.../0/*)#checksum

Taproot P2TR single-key (BIP 86):

tr([d34db33f/86'/0'/0']xpub6ERApfZ.../0/*)#checksum

2-of-3 multisig in P2WSH:

wsh(sortedmulti(2,[aabbccdd/48'/0'/0'/2']xpubA.../0/*,[11223344/48'/0'/0'/2']xpubB.../0/*,[55667788/48'/0'/0'/2']xpubC.../0/*))#checksum

Using sortedmulti instead of multi is standard practice for multisig wallets because it ensures the same script regardless of the order keys are provided. This is critical for wallet interoperability: two coordinators given the same three xpubs will always produce identical addresses.

Multipath descriptor with receive and change (BIP 389):

wpkh([d34db33f/84'/0'/0']xpub6ERApfZ.../<0;1>/*)#checksum

Checksum Format

Every descriptor can include an 8-character checksum appended after a # separator. The checksum uses the bech32 character set (qpzry9x8gf2tvdw0s3jn54khce6mua7l) and a BCH error-detecting code. It guarantees detection of any single-character error and all errors of up to 3 characters in descriptors up to 49,154 characters long.

Bitcoin Core's getdescriptorinfo RPC computes and appends the correct checksum. Several RPCs including importdescriptors and deriveaddresses require the checksum to be present. When sharing descriptors between wallets, always include the checksum to prevent transcription errors.

Nesting Rules

Not every descriptor can appear inside every other. The nesting rules enforce valid Bitcoin script combinations:

  • sh() can wrap: wpkh(), wsh(), multi(), sortedmulti()
  • wsh() can wrap: pk(), pkh(), multi(), sortedmulti(), miniscript expressions
  • tr() script tree can contain: pk(), multi_a(), sortedmulti_a(), miniscript expressions
  • combo(), addr(), raw() are top-level only and cannot be nested
  • wpkh() and wsh() require compressed public keys
  • tr() uses x-only public keys (compressed keys are auto-converted by dropping the prefix byte)

Wallet Support

Descriptor wallet support has expanded steadily since 2018. The following wallets and libraries handle descriptor import, export, or native operation.

Wallet / LibraryTypeDescriptor Support
Bitcoin CoreFull nodeFull native (default since v23)
Sparrow WalletDesktopFull import/export
LianaDesktopFull native with miniscript
Bitcoin Dev Kit (BDK)LibraryFull native (primary abstraction)
NunchukMobile/DesktopFull import/export
Specter DesktopDesktop coordinatorFull support
COLDCARDHardwareDescriptor export
Bitcoin KeeperMobileImport with miniscript

The Bitcoin Dev Kit (BDK), used by many modern wallet SDKs, treats descriptors as its core wallet primitive. When building on BDK, you define a wallet entirely through its descriptors rather than through separate key and script-type configuration. This makes BDK-based wallets inherently portable: self-custodial applications built with Spark or similar infrastructure can export a user's descriptor for recovery in any compatible wallet.

Bitcoin Core Version History

Descriptor support in Bitcoin Core has evolved across multiple releases:

VersionYearDescriptor Feature
v0.172018Initial descriptor support: scantxoutset, getdescriptorinfo, deriveaddresses
v0.182019Checksum support added; importmulti accepts descriptors
v0.212021Descriptor wallets introduced (SQLite); importdescriptors RPC
v22.02021tr() Taproot descriptors; listdescriptors RPC
v23.02022Descriptor wallets become the default; BIP 86 Taproot derivation
v25.02023Miniscript signing for wsh() descriptors
v26.02023Full miniscript in tr() Taproot descriptors

Descriptors and Miniscript

Miniscript (BIP 379) is a structured subset of Bitcoin Script that enables wallets to analyze, compose, and generically sign for arbitrary spending policies. Descriptors and miniscript are complementary: a descriptor specifies the keys and derivation, while a miniscript expression inside the descriptor defines the spending conditions.

Miniscript expressions are valid inside wsh() (for SegWit) and inside tr() script tree leaves (for Taproot). For example, a decaying multisig that starts as 3-of-3 and relaxes to 2-of-3 after a certain block height can be expressed as a single miniscript descriptor. For more on how miniscript extends Bitcoin's programmability, see our research on miniscript spending policies and the Bitcoin Script programmability overview.

Practical Usage

Exporting a Wallet Descriptor

In Bitcoin Core, use listdescriptors to export all descriptors for a wallet. The output includes the descriptor string with checksum, the derivation range, and the timestamp. For cold storage recovery planning, store these descriptors alongside your seed phrase: the seed recovers the keys, and the descriptor tells the recovery wallet how to use them.

Importing Descriptors

Use importdescriptors in Bitcoin Core to import one or more descriptors into a wallet. You can specify the derivation range (range parameter) and the rescan timestamp. For watch-only wallets, import the xpub-based descriptor without private keys. For signing wallets, import with xprvs or use PSBT workflows where the signing device holds the keys. See the PSBT workflow guide for details.

Deriving Addresses

The deriveaddresses RPC takes a descriptor and an optional range, returning the corresponding addresses. This is useful for verifying that an xpub viewer generates the same addresses as your signing device. For ranged descriptors, you can generate addresses 0 through 999 to check for any address gap issues.

Frequently Asked Questions

What is a Bitcoin output descriptor?

A Bitcoin output descriptor is a string that fully describes a set of output scripts (addresses) a wallet should track. It encodes the script type (P2PKH, P2WPKH, P2TR, multisig, etc.), the public keys or extended keys, the derivation path, and the master key fingerprint. Descriptors were standardized in BIP 380 through BIP 386 and are the default wallet format in Bitcoin Core since version 23.0.

How do output descriptors differ from xpubs?

An xpub is a raw extended public key with no information about script type or derivation context. A descriptor wraps the xpub with explicit script semantics: it specifies whether the key produces P2PKH, P2WPKH, P2TR, or multisig addresses, records the full derivation path via key origin info, and includes a checksum for error detection. Two wallets given the same descriptor will always produce identical addresses, which is not guaranteed with a bare xpub.

What is the descriptor checksum and how is it computed?

The descriptor checksum is an 8-character suffix appended after a # character. It uses a BCH error-detecting code over the bech32 character set. The algorithm maps each character in the descriptor to a symbol, processes them through a polynomial modular arithmetic function, and outputs 8 characters that detect any single-character error. Bitcoin Core's getdescriptorinfo RPC computes the checksum automatically.

Which wallets support descriptor import and export?

Bitcoin Core has full native support and uses descriptor wallets by default since v23.0 (2022). Sparrow Wallet, Liana, Nunchuk, and Specter Desktop support descriptor import and export. The Bitcoin Dev Kit (BDK) library uses descriptors as its primary wallet abstraction. Hardware wallets like COLDCARD can export descriptors. The ecosystem continues to adopt the format as older xpub-only workflows are phased out.

What is the difference between multi() and sortedmulti()?

Both create k-of-n multisig scripts, but multi() places keys in the exact order specified, while sortedmulti() sorts keys lexicographically before constructing the script. Use sortedmulti() for multisig wallets to ensure that all participants produce identical addresses regardless of the order they provide their keys. This is the standard for interoperable multisig setups using tools like the multisig planner.

How do descriptors work with Taproot?

The tr() descriptor (BIP 386) handles Taproot outputs. A simple key-path spend uses tr(KEY). Script-path spends add a tree of scripts: tr(KEY, {SCRIPT_A, SCRIPT_B}). Inside the script tree, tapscript-specific functions like multi_a() replace legacy multi() because Taproot uses Schnorr signatures with OP_CHECKSIGADD instead of OP_CHECKMULTISIG. For background on Taproot's design, see our Taproot and Schnorr signatures explainer.

Can I use descriptors for watch-only wallets?

Yes. Replace xprvs with xpubs in the descriptor and import it as watch-only. The wallet will track balances and generate addresses but cannot sign transactions. This is the standard pattern for coordinating with hardware signers: the watch-only wallet builds unsigned transactions or PSBTs, and the signing device (which holds the private keys) signs them offline.

This reference is for informational purposes only and does not constitute financial advice. Descriptor syntax and wallet support may change as BIPs are updated. Always verify descriptor compatibility with your specific wallet software before relying on it for fund recovery.

Build with Spark

Integrate bitcoin, Lightning, and stablecoins into your app with a few lines of code.

Read the docs →