Wallet Architecture
Output Descriptors
The problem a descriptor solves
A wallet needs to know two things to be useful: the keys it controls, and the scripts those keys are meant to satisfy. The first is supplied by the seed. The second is a description of the spending conditions — a single key, a multisig arrangement, a timelocked path — and it is not contained in the seed. Before descriptors, that second piece of information was conveyed by a set of import formats that each encoded it differently and incompletely.
The practical failure this produced is well known. A user restores a wallet from a seed, the software derives keys, and the balance shows as zero because the software guessed the wrong script type or the wrong branch. The keys are correct and the funds are recoverable, but the wallet does not know what to look for. The import formats could not express the full range of scripts a wallet might use, so the information needed to reconstruct the wallet was simply absent.
A descriptor closes that gap by describing the script directly. It names the function — a single public key, a multisig threshold, a timelock — and supplies the key material or the derivation path for each participant. Because it is a complete statement rather than a partial hint, a wallet that is given the descriptor knows exactly which outputs to watch and how to spend them.
What a descriptor contains
A descriptor is written as a function applied to its arguments. A single-key native SegWit descriptor takes the form wpkh(...), where the argument is either a public key or a derivation path that produces one. A multisig descriptor wraps a threshold and a list of keys in sortedmulti(...) or multi(...), and the difference between the two is whether the keys are sorted before the script is built, which decides whether the descriptor is portable between wallets.
The function name also states the script type. pkh is legacy P2PKH, sh(wpkh(...)) is P2SH-wrapped SegWit, wpkh is native SegWit, and tr is Taproot. Because the script type is explicit, a wallet reading a descriptor does not have to guess which addresses to derive, which is precisely the guess that produced the empty-balance restore.
Descriptors can also carry a range. A descriptor that ends with a wildcard such as /0/* describes not one script but a family of them, one for each address index. That is how a descriptor covers a whole receiving branch rather than a single address, and it is what makes a descriptor a complete description of a wallet rather than of one output.
The checksum
A descriptor ends with an eight-character checksum, separated from the body by a hash. The checksum is not a security measure; it is a transcription guard. A descriptor is long enough that a single mistyped character is easy to miss, and a descriptor with a wrong character would derive a different set of scripts and produce a wallet that watches the wrong outputs. The checksum makes that failure immediate and visible rather than silent.
The checksum is computed from the descriptor body, so any change to the body changes the checksum. A wallet that validates the checksum before using a descriptor will reject a mistyped one outright. That is a small thing, but it is the difference between a restore that fails loudly and one that appears to succeed while showing an empty wallet — and the second is far more dangerous, because the user may conclude the funds are gone.
The checksum does not protect against a descriptor that is internally consistent but wrong — a correct checksum over the wrong derivation path, for instance. It guards transcription, not intention. The path convention covered on the previous page is still the thing that decides whether the descriptor describes the wallet the user actually had.
Sources and references
The description of the descriptor grammar, the function names and the checksum is taken from the Bitcoin Core documentation and the BIPs that define the script types a descriptor can name.
- The descriptor grammar and checksum. Bitcoin Core, Support for Output Descriptors in Bitcoin Core: documents the function names, the key expressions, the wildcard range and the checksum algorithm.
- The native SegWit script type. Bitcoin, BIP 141: Segregated Witness: defines the witness program that the wpkh descriptor names.
- The Taproot script type. Bitcoin, BIP 341: Taproot: defines the output type that the tr descriptor names.
- The multisig script type. Bitcoin, BIP 11: M-of-N Standard Transactions: defines the multisig output that the multi and sortedmulti descriptors describe.
Related reading
- Wallet ArchitectureHow a wallet is built: key derivation, descriptors, signing models and recovery.
- HD Wallets & DerivationHow one seed produces a tree of keys, and what a derivation path actually selects.
- Wallet Backup & RecoveryWhat a backup must contain to restore a wallet, and the failure modes that make one insufficient.
- Watch-Only WalletsHow a wallet can track balances and build transactions while holding no spending key.
- HomeThe state of Bitcoin, in reference form.
- MarketThe largest assets by market capitalisation, with Bitcoin given the lead.