0388 - Proposed - Standards - Nov. 16, 2022 (3 years, 8 months ago)
16
2022
BIP: 388
Layer: Applications
Title: Wallet Policies for Descriptor Wallets
Author: Salvatore Ingala <[email protected]>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP 388
Status: Proposed
Type: Standards Track
Created: 2022-11-16
License: BSD-2-Clause
Post-History: 2022-05-10: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-May/020423.html
Software wallets and hardware signing devices typically partition funds into separate "accounts". When signing or visualizing a transaction, aggregate flows of funds of all accounts affected by the transaction may (and should) be displayed to the user.
Wallet policies build on top of output script descriptors to represent such accounts in a compact, reviewable way. An account encompasses a logical group of receive and change addresses, and each wallet policy represents all descriptors necessary to describe an account in its entirety.
We simplify the language to suit devices with limited memory, where even keeping the entire descriptor in memory could be a major hurdle, by reducing the generality of descriptors to just the essential features and by separating the extended pubkeys and other key information from the descriptor.
This results in a more compact representation and simplifies the inspection of the policy by the user.
The compilation of wallet policies to the corresponding descriptor is trivial, and the reverse process is easy for supported descriptors, because the language is kept similar to that of output script descriptors.
This BIP is licensed under the BSD 2-clause license.
Output Script Descriptors were introduced in Bitcoin Core as a way to represent collections of output scripts. It is a general and flexible language, designed to catch all the possible use-cases of bitcoin wallets (that is, if you know the script and you have the necessary keys, it will be possible to sign transactions with any descriptor-based software wallet).
Unfortunately, descriptors are not a perfect match for the typical usage of hardware signing devices (often also called hardware wallets ). Most of them have some of the following limitations when compared to a general-purpose machine running Bitcoin Core:
Moreover, other limitations like the limited size of the screen might affect what design choices are available in practice. Therefore, minimizing the amount of information shown on-screen is important for a good user experience. The ability for the user to completely validate on-screen the kind of script used (and each of the involved keys) is crucial for secure usage, as the machine that is interacting with the hardware signer (and running the software wallet) is considered untrusted.
A more native, compact representation of the wallet receive and change addresses might also benefit the UX of software wallets when they use descriptors (possibly with miniscript) for representing complex locking conditions.
We remark that wallet policies are not related to the policy language, a higher level language that can be compiled to miniscript .
The usage of complex scripts presents challenges in terms of both security and user experience for a hardware signing device.
Hardware signing devices strive to guarantee that no action can be performed without the user’s consent as long as the user correctly verifies the information that is shown on the device’s screen before approving.
This must hold even in scenarios where the attacker has full control of the machine that is connected to the signing device, and can execute arbitrary requests, or tamper with the legitimate user's requests.
Therefore, it is not at all trivial to allow complex scripts, especially if they contain keys that belong to third parties. The hardware signing device must guarantee that the user knows precisely what "policy" is being used to spend the funds, and that any "unspent" funds (if any) that is sent to a change address will be protected by the same policy.
This makes it impossible for an attacker to surreptitiously modify the policy, therefore stealing or burning the user's funds.
Reusing public keys within a script is a source of malleability when using miniscript policies, which has potential security implications.
Reusing keys across different UTXOs harms user privacy by allowing external parties to link these UTXOs to the same entity once they are spent.
By constraining the derivation path patterns to have a uniform structure, wallet policies prevent key reuse among the same or different UTXOs of the same account.
It is strongly recommended to avoid key reuse across accounts. Distinct public keys per account can be guaranteed by using distinct hardened derivation paths. This specification does not mandate hardened derivation in order to maintain compatibility with existing deployments that do not adhere to this recommendation.
It is out of scope for this document to guarantee that users do not reuse extended public keys among different wallet accounts. This is still very important, but the responsibility is left to the users and their software wallet.
Miniscript (and taproot trees) allow substantially more complex spending policies. It is a challenge to ensure that the user can practically verify such spending policies per the screen.
We set two fundamental design goals:
Designing a secure protocol for the coordination of a descriptor wallet among distant parties is also a challenging problem that is out of the scope of this document. See BIP 129 (Bitcoin Secure Multisig Setup) for an approach designed for multisig wallets. Regardless of the approach, the ability for the user to carefully verify all the details of the spending policies using the hardware signer's screen is a prerequisite for security in adversarial environments.
A solution to address the security concerns, and part of the UX concerns, is to have a registration flow for the wallet policy in the hardware signing device. The wallet policy must contain enough information to generate all the relevant addresses/scripts, and for the hardware signing device to identify the keys that it controls and that are needed to spend the funds sent to those addresses.
Before a new policy is used for the first time, the user will register a wallet policy into the hardware device. While the details of the process are out of scope in this document, the flow should be something similar to the following:
The proof of registration will allow the hardware signer to verify that a certain policy was indeed previously approved by the user, and is therefore safe to use without repeating the expensive user verification procedure. The details of how to create a proof of registration are out of scope for this document; using a Message Authentication Code on a hash committing to the wallet policy, its name and any additional metadata is an effective solution if correctly executed.
Once a policy is registered, the hardware signing device can perform the typical operations securely:
Before any of the actions mentioned above, the hardware signing device will retrieve the policy from its permanent storage if stateful; if stateless it will validate the proof of registration before using the wallet policy provided by the client.
Once the previously registered policy is correctly identified and approved by the user (for example by showing its name), and as long as the policy registration was executed securely, hardware signing devices can provide a user experience similar to the usual one for single-signature transactions.
While reusing a pubkey in different branches of a miniscript is explicitly forbidden by miniscript (as it has certain negative security implications), it is still reasonable to reuse the same xpub in multiple places, albeit with different final steps of derivation (so that the actual pubkeys that are used in the script are indeed different).
In fact, there are many reasonable spending policies with a quadratic size in the number of participants. For example, using Taproot, a 3-of-5 threshold wallet could use:
multi_a
(in case interactive signing is not
available).
With each xpub being 118 bytes long, the repetition of xpubs makes the descriptor become extremely large.
Replacing the common part of the key with a short key placeholder and organizing all the key expressions in a separate list helps to keep the size of the wallet policy small, which is crucial to allow human inspection during the registration flow.
This section formally defines wallet policies, and how they relate to output script descriptors.
A wallet policy is composed by a wallet descriptor template , together with a vector of key information items .
A
wallet descriptor template
is a
SCRIPT
expression.
SCRIPT
expressions:
sh(SCRIPT)
(top level only): P2SH embed the
argument.
wsh(SCRIPT)
(top level or inside
sh
only):
P2WSH embed the argument.
pkh(KEY)
(not inside
tr
): P2PKH output for
the given public key.
wpkh(KEY)
(top level or inside
sh
only):
P2WPKH output for the given compressed pubkey.
multi(k,KEY_1,KEY_2,...,KEY_n)
(inside
sh
or
wsh
only):
k
-of-
n
multisig
script.
sortedmulti(k,KEY_1,KEY_2,...,KEY_n)
(inside
sh
or
wsh
only):
k
-of-
n
multisig script with keys sorted lexicographically in the resulting
script.
tr(KEY)
or
tr(KEY,TREE)
(top level only):
P2TR output with the specified key as internal key, and optionally a
tree of script paths.
wsh
or
tr
only).
See
BIP
379
for a precise specification of all the valid miniscript
SCRIPT
expressions.
TREE
expressions:
SCRIPT
expression
{
, a
TREE
expression, a
comma
,
, a
TREE
expression, and a closing
brace
}
KEY
expressions consist of
KP
expression
/**
, or
/<NUM;NUM>/*
, for two
distinct decimal numbers
NUM
representing unhardened
derivations, or
KP
expressions (key placeholders) consist of either:
KI
(key index) expression, or
tr
):
musig(KI_1,KI_2,...,KI_n)
A
KI
(key index) expression consists of:
@
@0
)
The
/**
in the placeholder template represents commonly
used paths for receive/change addresses, and is equivalent to
/<0;1>/*
.
Note that while
BIP 389
allows multipath
/<NUM;NUM;...;NUM>
expressions with an arbitrary
number of options, this specification restricts it to exactly 2 choices
(with the typical meaning of receive/change addresses).
SCRIPT
,
TREE
and
KEY
expressions map directly to the corresponding concepts defined in
BIP 380
for output script descriptors.
Each
KEY
expression always corresponds to a precise
public key in the final bitcoin Script. Therefore, all the derivation
steps in the BIP 32 hierarchy are included in a
KEY
expression.
Each
KP
(key placeholder) expression, on the other hand,
maps to the root of all the corresponding public keys for all the
possible UTXOs that belong to the account represented in the wallet
policy. Therefore, no derivation steps are allowed in a
KP
expression.
A
KI
(key index)
@i
for some number
i
represents the
i
-th key in the vector of key
information items (which must be of size at least
i + 1
, or the
wallet policy is invalid).
Note: while descriptor templates for miniscript are not formally
defined in this version of the document (pending standardization), it is
straightforward to adapt this approach by adding additional
SCRIPT
expressions.
Each element of the key origin information vector is a
KEY_INFO
expression, containing an extended public key, and
(optionally) its key origin.
[
/NUM'
or
/NUM
path elements to indicate hardened or unhardened derivation steps
between the fingerprint and the xpub that follows
]
A wallet policy must have at least one key placeholder and the corresponding key.
The public keys obtained by deserializing elements of the key information vector must be pairwise distinct 1 .
If two
KEY
are
KP/<M;N>/*
and
KP/<P;Q>/*
for the same key placeholder
KP
, then the sets
{M, N}
and
{P, Q}
must be disjoint. Two
musig
key
placeholders are the same if they have exactly the same set of key
indexes (regardless of the order).
Repeated
KI
expressions are not allowed inside a
musig
placeholder.
The key information vector should be ordered so that placeholder
@i
never appears for the first time before an occurrence of
@j
for some
j < i
; for example, the first
placeholder is always
@0
, the next one is
@1
,
etc.
From a wallet descriptor template (and the associated vector of key information items), one can therefore obtain the corresponding multipath descriptor by:
/**
with
/<0;1>/*
.
For example, the wallet descriptor
pkh(@0/**)
with key
information
["[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"]
produces the following multipath descriptor:
pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<0;1>/*)
It is acceptable to implement only a subset of the possible wallet policies defined by this standard. It is recommended that any limitations are clearly documented.
Implementations can add additional metadata that is stored together with the wallet policy for the purpose of wallet policy registration and later usage. Metadata can be vendor-specific and is out of the scope of this document.
Any implementation in a software wallet that allows wallet policies not matching any of the specifications in BIP 44 , BIP 49 , BIP 84 , BIP 86 (especially if involving external cosigners) should put great care into a process for backing up the wallet policy that represents the account. In fact, unlike standard single-signature scenarios, the seed alone is no longer enough to discover wallet policies with existing funds, and the loss of the backup is likely to lead to permanent loss of funds. Unlike the seed, leaking such backups only affects the privacy of the user, but it does not allow the attacker to steal funds.
In order to allow supporting legacy derivation schemes (for example,
using simply
/*
instead of the more common
/<M;N>/*
scheme most software wallets use today), or
other schemes that are not covered in this document, implementations
might choose to permit additional derivation patterns for the key
placeholder (
KP
) expressions.
However, care needs to be taken in view of the following considerations:
@0/<0;1>/*
and
@1/*
would generate the same pubkeys if the second public
key in the key information vector is one of the first two unhardened
children of the first public key. This could cause malleability with
potential security implications (for example, in policies containing
miniscript).
/*
suffix (for example a
descriptor template like
wsh(multi(2,@0,@1/<0;1>/*))
)
would cause a pubkey to be repeated in every output generated from the
policy, which would result in a total loss of privacy.
In the examples in this section, the vector of key information items is omitted. See the test vectors below for complete examples.
Common single-signature account patterns:
pkh(@0/**)
(legacy).
wpkh(@0/**)
(native segwit).
sh(wpkh(@0/**))
(nested segwit).
tr(@0/**)
(taproot single-signature account).
Common multisig schemes:
wsh(multi(2,@0/**,@1/**))
- SegWit 2-of-2 multisig,
keys in order.
sh(sortedmulti(2,@0/**,@1/**,@2/**))
- Legacy 2-of-3
multisig, sorted keys.
tr(musig(@0,@1)/**)
- MuSig2 2-of-2 in the taproot
keypath
Some miniscript policies in
wsh
:
wsh(and_v(v:pk(@0/**),or_d(pk(@1/**),older(12960))))
-
Trust-minimized second factor, degrading to a single signature after
about 90 days.
wsh(thresh(3,pk(@0/**),s:pk(@1/**),s:pk(@2/**),sln:older(12960)))
- A 3-of-3 wallet that becomes a 2-of-3 if coins are not spent for about
90 days.
wsh(or_d(pk(@0/**),and_v(v:multi(2,@1/**,@2/**,@3/**),older(65535))))
- A singlesig wallet with automatic inheritance to a timelocked 2-of-3
multisig of family members.
BIP 44 , first account
Descriptor template: pkh(@0/**)
Keys info: ["[6738736c/44'/0'/0']xpub6Br37sWxruYfT8ASpCjVHKGwgdnYFEn98DwiN76i2oyY6fgH1LAPmmDcF46xjxJr22gw4jmVjTE2E3URMnRPEPYyo1zoPSUba563ESMXCeb"]
Descriptor: pkh([6738736c/44'/0'/0']xpub6Br37sWxruYfT8ASpCjVHKGwgdnYFEn98DwiN76i2oyY6fgH1LAPmmDcF46xjxJr22gw4jmVjTE2E3URMnRPEPYyo1zoPSUba563ESMXCeb/<0;1>/*)
BIP 49
,
second account
Descriptor template: sh(wpkh(@0/**))
Keys info: ["[6738736c/49'/0'/1']xpub6Bex1CHWGXNNwGVKHLqNC7kcV348FxkCxpZXyCWp1k27kin8sRPayjZUKDjyQeZzGUdyeAj2emoW5zStFFUAHRgd5w8iVVbLgZ7PmjAKAm9"]
Descriptor: sh(wpkh([6738736c/49'/0'/1']xpub6Bex1CHWGXNNwGVKHLqNC7kcV348FxkCxpZXyCWp1k27kin8sRPayjZUKDjyQeZzGUdyeAj2emoW5zStFFUAHRgd5w8iVVbLgZ7PmjAKAm9/<0;1>/*))
BIP 84
,
third account
Descriptor template: wpkh(@0/**)
Keys info: ["[6738736c/84'/0'/2']xpub6CRQzb8u9dmMcq5XAwwRn9gcoYCjndJkhKgD11WKzbVGd932UmrExWFxCAvRnDN3ez6ZujLmMvmLBaSWdfWVn75L83Qxu1qSX4fJNrJg2Gt"]
Descriptor: wpkh([6738736c/84'/0'/2']xpub6CRQzb8u9dmMcq5XAwwRn9gcoYCjndJkhKgD11WKzbVGd932UmrExWFxCAvRnDN3ez6ZujLmMvmLBaSWdfWVn75L83Qxu1qSX4fJNrJg2Gt/<0;1>/*)
BIP 86
,
first account
Descriptor template: tr(@0/**)
Keys info: ["[6738736c/86'/0'/0']xpub6CryUDWPS28eR2cDyojB8G354izmx294BdjeSvH469Ty3o2E6Tq5VjBJCn8rWBgesvTJnyXNAJ3QpLFGuNwqFXNt3gn612raffLWfdHNkYL"]
Descriptor: tr([6738736c/86'/0'/0']xpub6CryUDWPS28eR2cDyojB8G354izmx294BdjeSvH469Ty3o2E6Tq5VjBJCn8rWBgesvTJnyXNAJ3QpLFGuNwqFXNt3gn612raffLWfdHNkYL/<0;1>/*)
BIP 48
P2WSH multisig
Descriptor template: wsh(sortedmulti(2,@0/**,@1/**))
Keys info: ["[6738736c/48'/0'/0'/2']xpub6FC1fXFP1GXLX5TKtcjHGT4q89SDRehkQLtbKJ2PzWcvbBHtyDsJPLtpLtkGqYNYZdVVAjRQ5kug9CsapegmmeRutpP7PW4u4wVF9JfkDhw", "[b2b1f0cf/48'/0'/0'/2']xpub6EWhjpPa6FqrcaPBuGBZRJVjzGJ1ZsMygRF26RwN932Vfkn1gyCiTbECVitBjRCkexEvetLdiqzTcYimmzYxyR1BZ79KNevgt61PDcukmC7"]
Descriptor: wsh(sortedmulti(2,[6738736c/48'/0'/0'/2']xpub6FC1fXFP1GXLX5TKtcjHGT4q89SDRehkQLtbKJ2PzWcvbBHtyDsJPLtpLtkGqYNYZdVVAjRQ5kug9CsapegmmeRutpP7PW4u4wVF9JfkDhw/<0;1>/*,[b2b1f0cf/48'/0'/0'/2']xpub6EWhjpPa6FqrcaPBuGBZRJVjzGJ1ZsMygRF26RwN932Vfkn1gyCiTbECVitBjRCkexEvetLdiqzTcYimmzYxyR1BZ79KNevgt61PDcukmC7/<0;1>/*))
Miniscript: A 3-of-3 that becomes a 2-of-3 after 90 days
Descriptor template: wsh(thresh(3,pk(@0/**),s:pk(@1/**),s:pk(@2/**),sln:older(12960)))
Keys info: ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", "[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js", "[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2"]
Descriptor: wsh(thresh(3,pk([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*),s:pk([b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js/<0;1>/*),s:pk([a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2/<0;1>/*),sln:older(12960)))
Miniscript: A singlesig wallet with automatic inheritance to a
timelocked 2-of-3 multisig
Descriptor template: wsh(or_d(pk(@0/**),and_v(v:multi(2,@1/**,@2/**,@3/**),older(65535))))
Keys info: ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", "[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js", "[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2", "[bb641298/44'/0'/0'/100']xpub6Dz8PHFmXkYkykQ83ySkruky567XtJb9N69uXScJZqweYiQn6FyieajdiyjCvWzRZ2GoLHMRE1cwDfuJZ6461YvNRGVBJNnLA35cZrQKSRJ"]
Descriptor: wsh(or_d(pk([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*),and_v(v:multi(2,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js/<0;1>/*,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2/<0;1>/*,[bb641298/44'/0'/0'/100']xpub6Dz8PHFmXkYkykQ83ySkruky567XtJb9N69uXScJZqweYiQn6FyieajdiyjCvWzRZ2GoLHMRE1cwDfuJZ6461YvNRGVBJNnLA35cZrQKSRJ/<0;1>/*),older(65535))))
Taproot wallet policy with sortedmulti_a and a miniscript leaf
Descriptor template: tr(@0/**,{sortedmulti_a(1,@0/<2;3>/*,@1/**),or_b(pk(@2/**),s:pk(@3/**))})
Keys info: ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", "xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K", "xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM", "xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6"]
Descriptor: tr([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*,{sortedmulti_a(1,[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<2;3>/*,xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K/<0;1>/*),or_b(pk(xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM/<0;1>/*),s:pk(xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6/<0;1>/*))})
Taproot MuSig2 3-of-3 in the key path, with three 2-of-2 MuSig2 recovery
paths after 90 days in the script paths
Descriptor template: tr(musig(@0,@1,@2)/**,{and_v(v:pk(musig(@0,@1)/**),older(12960)),{and_v(v:pk(musig(@0,@2)/**),older(12960)),and_v(v:pk(musig(@1,@2)/**),older(12960))}})
Keys info: ["[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa", "[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js", "[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2"]
Descriptor: tr(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*,{and_v(v:pk(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js)/<0;1>/*),older(12960)),{and_v(v:pk(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*),older(12960)),and_v(v:pk(musig([b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*),older(12960))}})
The following descriptor templates are invalid:
pkh(@0)
: Key placeholder with no path following it
pkh(@0/0/**)
: Key placeholder with an explicit path
present
sh(multi(1,@1/**,@0/**))
: Key placeholders out of
order
sh(multi(1,@0/**,@2/**))
: Skipped key placeholder
@1
sh(multi(1,@0/**,@0/**))
: Repeated keys with the same
path expression
sh(multi(1,@0/<0;1>/*,@0/<1;2>/*))
:
Non-disjoint multipath expressions (
@0/1/*
appears
twice)
sh(multi(1,@0/**,xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/<0;1>/*))
:
Expression with a non-KP key present
pkh(@0/<0;1;2>/*)
: Solved cardinality > 2
tr(musig(@0/**,@1/**))
: Derivation before aggregation
is not allowed in wallet policies (despite being allowed in
BIP
390
)
Remark: some of the examples of invalid descriptor templates may be valid via optional extensions.
The
@
character used for key placeholders is not part of
the syntax of output script descriptors, therefore any valid descriptor
with at least one
KEY
expression is not a valid descriptor
template. Vice versa, any descriptor template with at least one key
placeholder is not a valid output script descriptor.
Adoption of wallet policies in software and hardware wallets is opt-in. Conversion from wallet policies to the corresponding descriptors is programmatically extremely easy, and conversion from descriptors to wallet policies (when respecting the required patterns) can be automated. See the reference implementation below for some examples of conversion.
Software wallets are recommended to allow exporting plain descriptors for the purposes of interoperability with software not using wallet policies.
Wallet policies are implemented in
Miniscript.Parse
.
For development and testing purposes, we provide a Python 3.7 reference implementation of simple classes to handle wallet policies, and the conversion to/from output script descriptors. The reference implementation is for demonstration purposes only and not to be used in production environments.
musig
key placeholders in descriptor
templates.
The authors would like to thank the people who provided feedback in the bitcoin-dev list, and in person.