Reading text-only. View on full site →

The Keys Alone Will Not Find the Vault

2026-10-14 · articles · en

A seed phrase restores keys and nothing else. Which addresses those keys produce is decided by a single line called an output descriptor. An experiment where the same three keys yield five different first addresses, how to read a descriptor and its eight-character checksum, the multisig case where a threshold of seeds still cannot open the vault, and how to back it all up.


Take three seed phrases. One is the phrase starting with abandon that every Bitcoin wallet uses as a test vector, and the other two are public phrases from the BIP-39 test vectors as well. From each, derive an extended public key for multisig and build a 2-of-3 vault that needs any two signatures. The keys are the same three. Yet the vault's first receiving address is not one address.

How it is assembledFirst address
Native SegWit, keys sortedbc1qm43n7nnev58aj3nrznz2xscgv98t7gxycq5pmp20a5vzfp5t0q2s7r6twa
Native SegWit, keys in order A, B, Cbc1qp38kn2cpud9u5z8px3ga9vtuwdp3n0cc6xly6td45u5g6nsgcl9sagut92
Native SegWit, keys in order C, B, Abc1qlklm7s3d48twp5rj4q39shf74ccfty96nyqc5gvm9y2fzxrdrdcs8t36am
Wrapped SegWit, keys sorted3HTVa2FKu5zq9e95qEoJX8Cy5vBo5y9Ljc
Legacy P2SH, keys sorted39tmmWUjbK9SeTK4L6vjZi7kEkPx4DjcJb

All five addresses were computed separately with Bitcoin Core 31.1 and with an independent library, and the results matched. What changed is not the keys but the assembly: which format wraps the script, and whether the keys are sorted or inserted in the order listed. Key order alone gives six possibilities.

Now suppose the person holding one of those keys loses their seed in a fire. Two seeds remain. It is 2-of-3, so there are enough signatures. But without the third person's public key, none of the addresses above can be recomputed, because an address is the hash of a script containing all three public keys. You hold signatures that could spend the coins, yet you do not know where the coins are, and even if you did, you could not build the script you must reveal to spend them.

The output descriptor fills that gap. It is a single line of text that states which keys, along which paths, go into which script to produce addresses.

Words restore keys, and only keys

A seed phrase holds the root of a tree of private keys. From that root you can regrow any number of keys, but nothing in the words records which addresses those keys were used to make.

The motivation section of BIP-380, which specifies descriptors, starts from exactly this problem. Wallet backups have traditionally consisted only of private keys, today mostly in the form of BIP-39 mnemonics, and since Segregated Witness added new output types that is no longer enough. Given only private keys, a restored wallet cannot know which kinds of scripts and addresses to produce, and this has caused incompatibilities between wallets when restoring backups or exporting watch-only data.

Derivation paths make it worse. BIPs 44, 49 and 84 define standard paths for each output type, but not every wallet supports or uses them. When the same twelve words show a zero balance in a different wallet, this is usually why. The keys are right, but the wallet looked along a different path, at a different address type.

Some wallets saw the gap from the start. Electrum puts a version number in its seed phrases: the leading bits of a hash of the phrase say which derivation to follow. Electrum's documentation criticizes BIP-39 on this point. BIP-39 seeds carry no version number, so software must always know how to generate keys and addresses, and trying various derivation schemes one by one is extremely inefficient and rests on the assumption that future wallets will support every past method.

Another workaround was to change the prefix of the extended public key: write ypub or zpub instead of xpub to signal wrapped or native SegWit. BIP-380 calls this a layer violation. Key derivation should be separate from the meaning of a script type, yet the two were fused, and the fix only works for one particular path and one particular script type.

Reading one descriptor

A descriptor writes this information in a short language that people can read. The BIP-84 account of the abandon test phrase looks like this.

wpkh([73c5da0a/84h/0h/0h]xpub6CatWdiZiodmUeTDp8LT5or8nmbKNcuyvz7WyksVFkKB4RHwCD3XyuvPEbvqAQY3rAPshWcMLoP2fMFMKHPJ4ZeZXYVUhLv1VMrjPC7PW6V/0/*)#afwvtk2s

The outer wpkh(...) is a script expression: produce native SegWit outputs locked to the hash of a single public key. Inside the brackets, 73c5da0a is the eight-character fingerprint of the master key this key comes from, and 84h/0h/0h is the path from there down to this xpub. h marks hardened derivation; older notation uses an apostrophe. The /0/* after the xpub means use every child on branch 0 of this xpub in turn, and the trailing #afwvtk2s is the checksum.

The first address of this descriptor is bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu, the same as the test vector in BIP-84. Change only the outer function and the same xpub produces entirely different addresses.

Script expressionFirst address
pkh(...)1JaUQDVNRdhfNsVncGkXedaPSM5Gc54Hso
sh(wpkh(...))3GtVZYzsKF6Feikdjd4bDyPdAiyeHANY9b
wpkh(...)bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
tr(...)bc1p8knh0enfv47gmpuf66528zd4jtkgjq4sv5w5l2gqwgk8exu2ynns9g8c9m

It is plain at a glance that the difference between address formats is not a difference of keys but of this outer function. A descriptor describes not one address but the whole list of them, and it carries everything needed to produce that list. Import a descriptor that contains only public information, with no private keys, and you get a watch-only wallet that sees the balance and history.

Wallets make receiving and change addresses on separate branches; by convention branch 0 is for receiving and branch 1 for change. That used to mean writing two descriptors, but BIP-389 proposes a notation like /<0;1>/* that puts both branches on one line. The document is still a draft, but Bitcoin Core accepts the notation from 29.0 and expands it into two stored descriptors, one for receiving and one for change.

What a multisig cannot do without

A multisig descriptor is the same structure with several keys in it. The 2-of-3 vault from the opening looks like this.

wsh(sortedmulti(2,[73c5da0a/48h/0h/0h/2h]xpub6DkF.../<0;1>/*,[b8688df1/48h/0h/0h/2h]xpub6FQy.../<0;1>/*,[28645006/48h/0h/0h/2h]xpub6DnE.../<0;1>/*))

Each shortened part is a 111-character xpub. wsh is a native SegWit script hash, 2 is the number of signatures required, and the three signers' keys follow. Under BIP-383, when several xpubs appear, they are derived in lockstep at the same child index. Address number 5 is built from child key 5 of each of the three xpubs.

The difference between sortedmulti and multi is what split the addresses in the first table. multi puts keys into the script in the order the descriptor lists them; sortedmulti sorts the derived public keys lexicographically first. The sorting rule comes from BIP-67, written in 2015 to make P2SH multisig addresses deterministic. With sorting, nobody has to remember the order in which keys were entered; without it, the order itself must be backed up. The script expression also limits how many keys fit. Inside sh, the 520-byte limit on a single script element allows at most 15 compressed public keys; inside wsh, the limit is 20.

Back to the opening question. Even if someone loses their seed, as long as the descriptor survives the other two can open the vault. The descriptor contains the third xpub, so every address can be rebuilt, and the two remaining seeds supply the signatures. Without the descriptor there is no way to recover the third public key. If coins have already been spent from some address, that address's script is on the blockchain and shows all three public keys, but those are the child keys for that one address, not for any other.

Conversely, if all three seeds survive, you can usually recover without the descriptor by trying combinations of standard paths such as BIP-48, common script types and sorted or unsorted keys. It is inefficient but possible. The moment the descriptor becomes decisive is when only a threshold of seeds remains, which is exactly the situation multisig was meant to survive.

An eight-character checksum

The eight characters after the # are an error-detecting code that uses the same character set as bech32 addresses. BIP-380 states its properties. Any single symbol error is always detected. Any two or three symbol errors are always detected in descriptors up to 49,154 characters long. A random corruption slips through with a chance of 1 in 2 to the 40th power.

The checksum protects the numbers in keys and paths too. Change a single 0h in the account path above to 1h and feed it to Bitcoin Core, and it is rejected.

Provided checksum 'afwvtk2s' does not match computed checksum 'mu5cevdr'

A typo in a hand-copied descriptor is caught before the wallet accepts it. The flip side is that the checksum sees characters literally. Write the same path as 84'/0'/0' and the checksum becomes wc3n3van; write it as 84h/0h/0h and it is afwvtk2s. When Bitcoin Core 26.0 switched its output from apostrophes to h, the release notes said this changes the descriptor checksum. Do not reinterpret a descriptor when you copy it; keep the exact string the wallet exported.

From 0.17 to 30.0

Descriptors were implemented before they were standardized. In Bitcoin Core 0.17.0, released on October 3, 2018, descriptors written by Pieter Wuille first appeared as arguments to the scantxoutset command, along with a reference document for the language. Version 0.21.0, on January 14, 2021, added a new wallet type that manages outputs through descriptors as an experimental feature. BIP numbers 380 through 386 were assigned under the names of Wuille and Ava Chow on June 27 of that year, nearly three years after the first implementation.

From 23.0, on April 25, 2022, newly created wallets became descriptor wallets by default, with a tr() descriptor added automatically for single-key Taproot addresses. Version 30.0, on October 10, 2025, took the final step: legacy wallets can no longer be created or loaded, and the only path left is migrating them to descriptor wallets with migratewallet. Inside Bitcoin Core, the era when a wallet was a bag of keys is over. A wallet is now a list of descriptors.

What a hardware wallet checks

What happens if a descriptor is forged? BIP-129, which standardizes multisig setup, divides the danger in two. An attacker who can modify the configuration can trick the user into sending funds to the wrong address and steal them or hold them for ransom. An attacker who cannot modify it but can read it can watch the wallet's transactions, which destroys privacy.

The first danger is a hardware wallet problem. If the computer is compromised, there is no reason to trust the multisig address it displays. A descriptor whose third key has been swapped for the attacker's still looks like a 2-of-3 vault, but the attacker now needs to fool only one other signer. BIP-388 answers this with wallet policy registration. On first use, the hardware wallet shows the policy and every key on its own screen, and the user approves after comparing it against a trusted source such as a printed backup. From then on the device makes addresses and identifies change only through the registered policy. For a device to confirm a change output as its own in a PSBT multisig spend, that registration has to come first.

BIP-388 shortens what the screen must show by replacing xpubs with placeholders such as @0 and @1. The implementations it lists are Ledger's Bitcoin app from 2.1.0, BitBox02 firmware from 9.15.0 and Blockstream Jade from 1.0.24.

Descriptors with time in them

The descriptor language was designed to be extended, and the largest extension is Miniscript. Being written up as BIP-379, it expresses a subset of Bitcoin Script in a structured way so that wallets can analyze under what conditions coins can be spent, and sign for them. Bitcoin Core let wallets spend P2WSH Miniscript outputs in 25.0 and extended this into Taproot in 26.0.

Together they put a time condition into a single descriptor line: spend with my one key in normal times, and if the coins sit untouched for a set period, a family member's key can spend them too. The Liana wallet, first released by Wizardsardine in late 2022, works this way. It locks the recovery key with Miniscript's older(), a relative timelock under BIP-112, and the clock starts for each coin at the block where that coin was received. So each coin has to be moved at least once within the set number of blocks to reset its clock.

In a wallet like this, the descriptor is not an accessory to the backup but its core, because the time condition and the heir's key are written nowhere else. Liana's usage guide says you must back up both the script template, in the form of a descriptor, and the private keys, and that without the descriptor you will not be able to recover from your backup.

How to back it up

A descriptor is information that is not secret and yet should not be public. It contains no private keys, so it cannot spend anything on its own. But it contains every xpub, so anyone who sees it sees every address, balance and transaction in the vault. It does not need to be hidden as carefully as a seed, but it is not something to post anywhere.

Sparrow Wallet shows a warning when you save a multisig whose threshold is smaller than its total number of keys. It says that restoring the wallet needs at least the threshold number of seeds and all of the xpubs, and recommends backing up the wallet file or the output descriptor for the xpubs. From that dialog you can save the descriptor as a PDF with a QR code.

For a multisig, keep a copy of the descriptor with each key. If a descriptor sits wherever a seed phrase is stored, then whichever key is lost, the map is still next to the keys that remain. Keep the descriptor in only one place and that place becomes a new single point of failure, which defeats the reason for using multisig at all.

Verify the stored descriptor once. Import it as watch-only into a different wallet program and check that the first receiving address matches the first address of the wallet you use. The checksum catches typos made while copying, but it cannot catch a descriptor that was wrong from the start. If the addresses match, the map is right. This is also why the multisig setup record defined in BIP-129 puts the wallet's first address on its fourth line.

The same holds for an inheritance plan. If all you leave an heir is a seed phrase, the heir receives the keys and still has to work out which wallet, which path and which format to search. For a single-signature wallet a descriptor line removes that work; for a multisig the vault depends on whether that line exists.

Keys and a map

A seed phrase is a key. A key opens a lock but does not say where the lock is. In single-signature wallets this difference was easy to miss, because feeling along a few standard paths usually turns up the lock. In multisig wallets and wallets with time conditions, there are too many paths to feel along, or none at all.

A descriptor is a map that writes the path down in one line. The five addresses at the start are only a few of the vaults that the same three keys can build. Which vault holds the coins, only the descriptor knows.

Read on the full site: https://learn.txid.uk/en/articles/bitcoin-output-descriptors/