Rethinking Wallet Architecture using Wallet Descriptors
Introduction
Bitcoin Core’s wallet recently changed. At the Advancing Bitcoin Conference 2020, Andrew Chow spoke about Rethinking Wallet Architecture. Previously, the wallet inside of Bitcoin Core (the CWallet class) followed a ‘bunch of keys’ model. It was an unstructured collection of keys, scripts, watch scripts, keypools, HD chains, and metadata.
Explaining descriptors
If we delve a bit deeper, the bunch of keys model has a number of problems with it:
- Given a key, it is not clear what pieces of the puzzle are needed to access the funds ( solve the scriptsig)
- Using the current model, it is difficult to expand what is possible, both now and in the future when new features are introduced
- Multisig
- An unusual contract
- BIP 124 type contracts
This issue was first documented by Pieter Wuille in 2017 and it’s interesting to see the progression of this issue into the solution now implemented. An elegant solution to this problem was to redesign Bitcoin Core’s wallet architecture to use Descriptors. In simple terms, a descriptor is information that describes data. You can find descriptors in your favorite programming language and application frameworks, they are commonly used across computing.
Bitcoin Core’s wallet architecture previously focused on taking a key, applying a script to it, and presenting it to the user. The new architecture now focused on structuring everything around descriptors to describe all of the information needed to solve the scriptsig, and ultimately claim the funds.
Benefits of this solution
- Knowledge of what is needed to redeem funds attached to a transaction is clear and unambiguous so the structure and logic is more orderly
- When importing keys into a wallet:
- knowledge of which derivation path to use is unambiguous
- knowledge of what type of address to produce is also unambiguous
- Backups are extremely easy as they are simply terse strings
The benefits aren’t only for Core. Wallet developers can also take note and implement descriptors into their wallet. Benefits of this include:
- Interoperability between wallets is now easier to implement
- Wallets are easier to update when new scripting structures and features are released
- It is easier for wallets to be more expressive in the types of scripts they recognise
Descriptors in details
Native wallet descriptors (in Core) are ‘engineer readable’ so anyone with a good understanding of bitcoin code can understand at a high level what is needed. Below we discuss this in a bit more detail.
In Core, a descriptor is simply a function with the following attributes:
Function name | Input type | Output | Example |
---|---|---|---|
pk | Key | scriptPubKey | pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)#gn28ywm7 |
pkh | Key | scriptPubKey | pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)#8fhd9pwu |
wpkh | Key | scriptPubKey | wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)#8zl0zxma |
sh | Script | A script describing outputs with the input as embedded script | sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)#8mwyhs2t |
wsh | Script | A script describing outputs with the input as embedded script | wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))#en3tu306 |
Table-1
The rules describing inputs and outputs for a limited number of descriptor functions are summarised above. For full details, including multisig scripts please see https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md
Structure of a descriptor function
A descriptor returns a script.
Some have a checksum at the end after hash.
We can demonstrate the structure of a descriptor in pseudo code as:
descriptor_name(argument) => script
Example-1
pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)#8fhd9pwu
Let’s break this down
Section | Description |
---|---|
pkh | This is the function name. In this example this descriptor creates P2PKH bitcoin addresses. |
02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5 | This is the argument, which is the public key in hex format. |
8fhd9pwu | This is a checksum that bitcoin wallets can use to check whether the public key provided as an input is a valid public key. |
If we then encode the pubkey above 02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5
we get the bitcoin address: 1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP
Give it a try here: https://iancoleman.io/bitcoin-key-compression/
Example 2
pkh([d34db33f/44'/0'/0'] xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)#ml40v0wf
Section | Description |
---|---|
pkh | This is the function name. In this example this descriptor creates P2PKH bitcoin addresses. |
[d34db33f/44'/0'/0'] | This is key origin information, which is made up of the fingerprint and the derivation path. This tells us that this pubkey (xpub) is derived from the master key with fingerprint d34db33f and a derivation path of 44'/0'/0'. For more information on BIP-32 derivation paths please see:https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki |
xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL | The xpub. |
/1/* | Range descriptor. This tells the wallet where to find more keys at. This is a BIP 44 style derivation path. For more info please see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki |
#ml40v0wf | checksum |
Conclusion
Native wallet descriptors will be implemented into Core shortly and hopefully other bitcoin wallets will implement descriptors which provides us with better bitcoin wallet interoperability.
You can watch Andrew Chow speak about Rethinking Wallet Architecture here: https://www.youtube.com/watch?v=xC25NzIjzog&t=4s