From 44798a2a9e19643040af61818a7acb4cded47e00 Mon Sep 17 00:00:00 2001 From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com> Date: Thu, 1 Sep 2022 15:30:54 +0200 Subject: [PATCH 1/7] New BIP: Wallet Policies --- bip-wallet-policies.mediawiki | 343 +++++++++++++++++++++++++ bip-wallet-policies/wallet_policies.py | 200 ++++++++++++++ 2 files changed, 543 insertions(+) create mode 100644 bip-wallet-policies.mediawiki create mode 100644 bip-wallet-policies/wallet_policies.py diff --git a/bip-wallet-policies.mediawiki b/bip-wallet-policies.mediawiki new file mode 100644 index 0000000000..e46df1c646 --- /dev/null +++ b/bip-wallet-policies.mediawiki @@ -0,0 +1,343 @@ +
+ BIP: wallet-policies + Layer: Applications + Title: Wallet Policies for Descriptor Wallets + Author: Salvatore Ingala+ +== Abstract == + +Wallet policies build on top of output descriptors to represent in a compact, easier to inspect way the types of descriptors that are typically used to represent "accounts" in a software wallet, or a hardware signing device. A wallet policy always represents exactly two descriptors, which produce the receive and change addresses that are logically part of the same account. + +Reducing the generality of descriptors to just the essential features, and separating the extended pubkeys and other key information from the descriptor, allows to simplify the language in a way that suits devices with limited memory, where even keeping the entire descriptor in memory could be a major hurdle. + +Moreover, together with the gain in compactness, this simplifies user's inspection of the policy. + +Finally, by keeping the language extremely close to that of output script descriptors, the compilation of wallet policies to the corresponding descriptor is extremely easy, and even the reverse process is not too difficult for supported descriptors. + +== Copyright == + +This BIP is licensed under the BSD 2-clause license. + +== Motivation == + +''[[bip-0380.mediawiki|Output Script Descriptors]]'' were introduced in bitcoin-core as a way to represent collections of output scripts. It is a very 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: + +* they are embedded devices with limited RAM, and computational power; +* they cannot import additional private keys (that is, they can only sign with keys derived from a single seed via [[bip-0032.mediawiki|BIP-32]]); +* they have limited storage, or they might not have persistent storage at all (''stateless design''). + +Moreover, other limitations like the limited size of the screen might affect what design choices are available in practice. Therefore, minimizing the size of the information shown on-screen is important for a good user experience; that is crucial since the ability for the user to completely validate on-screen the kind of script used (and each of the involved keys) is a prerequisite 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/change might also benefit the UX of software wallets using descriptors to represent software wallets using descriptors (possibly with miniscript) for 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. + +=== Security and UX concerns for hardware signing devices === + +For a hardware signing device, allowing the usage of complex scripts presents challenges in terms of both security and user experience. + +==== Security issues ==== + +One of the security properties that hardware signing devices strive to guarantee is the following: as long as the user correctly verifies the information that is shown on the device's screen before approving, no action can be performed without the user's consent. + +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. + +==== UX issues ==== + +With miniscript (and taproot trees) allowing substantially more complex spending policies to be used, it becomes more challenging to make sure that the user is practically able to verify the information on the screen. + +Therefore, there are two fundamental design goals to strive for: +* Minimize the amount of information that is shown on screen - so that the user can actually validate it. +* Minimize the number of times the user has to validate such information. + +Designing a secure protocol for the coordination of a descriptor wallet among distant parties is also a challenging problem that is out of scope in this document. See [[bip-00129.mediawiki|BIP-129 (Bitcoin Secure Multisig Setup)]] for an approach designed for multisignature wallets. Regardless 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. + +=== Policy registration as a solution === + +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 software wallet initiates a ''wallet policy registration'' on the hardware signing device; the information should include the wallet policy, but also a unique ''name'' that identifies the policy. +# The device shows the wallet policy to the user using the secure screen. +# After inspecting the policy and comparing it with a trusted source (for example a printed backup), the user approves the policy. +# If stateful, the hardware signing device persists the policy in its permanent memory; if stateless, it returns a "proof of registration". + +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: +* generating receive and change addresses; +* showing addresses on the secure screen; +* sign transactions spending from a wallet, while correctly identifying change addresses and computing the transaction fees. + +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. + +=== Avoiding blowup in descriptor size === + +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). + +For example, using Taproot, a 3-of-5 multisignature wallet could use: +* a key path with a 5-of-5 MuSig2 aggregated key +* a script tree with 11 leaves: +** 10 different script using a 3-of-3 MuSig2 aggregated key, plus +** a final leaf with a fallback 3-of-5 multisignature using OP_CHECKSIGADD (in case interactive signing is not available). + +This could look similar to: + ++ Comments-Summary: No comments yet. + Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-wallet-policies + Status: Draft + Type: Informational + Created: 2022-11-16 + License: BSD-2-Clause +
+tr(musig(xpubA,xpubB,xpubC,xpubD,xpubE)/<0;1>/*), { + { + { + pk(musig(xpubA,xpubB,xpubC)/<2;3>/*), + { + pk(musig(xpubA,xpubB,xpubD)/<4;5>/*) + pk(musig(xpubA,xpubB,xpubE)/<6;7>/*), + } + }, + { + pk(musig(xpubA,xpubC,xpubD)/<8;9>/*), + { + pk(musig(xpubA,xpubC,xpubE)/<10;11>/*), + pk(musig(xpubA,xpubD,xpubE)/<12;13>/*) + } + } + }, + { + { + pk(musig(xpubB,xpubC,xpubD)/<14;15>/*), + pk(musig(xpubB,xpubC,xpubE)/<16;17>/*) + }, + { + pk(musig(xpubB,xpubD,xpubE)/<18;19>/*), + { + pk(musig(xpubC,xpubD,xpubE)/<20;21>/*), + sortedmulti_a(3, + xpubA/<22;23>/*, + xpubB/<22;23>/*, + xpubC/<22;23>/*, + xpubD/<22;23>/*, + xpubE/<22;23>/*) + } + } + } +}) ++ +Notice how each root xpub appears 8 times. With xpubs being up to 118 bytes long, the length of the full descriptor can get extremely long (the problem rapidly gets worse with larger multisignature schemes). + +Replacing the common part of the key with a short key placeholder and moving the key expression separately helps to keep the size of the wallet policy small, which is crucial to allow human inspection during the registration flow. + +== Specification == + +This section formally defines wallet policies, and how they relate to output script descriptors. + +=== Formal definition === + +A ''wallet policy'' is composed by a ''wallet descriptor template'', together with a vector of ''key information items''. + +==== Wallet descriptor template ==== + +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(KP) (not inside tr): P2PKH output for the given public key. +* wpkh(KP) (top level or inside sh only): P2WPKH output for the given compressed pubkey. +* multi(k,KP_1,KP_2,...,KP_n) (inside sh or wsh only): ''k''-of-''n'' multisig script. +* sortedmulti(k,KP_1,KP_2,...,KP_n) (inside sh or wsh only): ''k''-of-''n'' multisig script with keys sorted lexicographically in the resulting script. +* tr(KP) or tr(KP,TREE) (top level only): P2TR output with the specified key as internal key, and optionally a tree of script paths. +* any valid miniscript template (inside wsh or tr only). + +TREE expressions: +* any SCRIPT expression +* An open brace {, a TREE expression, a comma ,, a TREE expression, and a closing brace } + + +KP expressions (key placeholders) consist of +* a single character @ +* followed by a non-negative decimal number, with no leading zeros (except for @0) +* ''always'' followed by either: +** the string /**, or +** a string of the form /
/* for the same index i, then the sets {M, N} and {P, Q} must be disjoint.
+
+The key information vector should be ordered so that placeholder @i never appear 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.
+
+=== Descriptor derivation ===
+
+From a wallet descriptor template (and the associated vector of key information items), one can therefore obtain the corresponding multipath descriptor by:
+
+* replacing each key placeholder with the corresponding key origin
+information;
+* replacing every /** 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>/*)
+
+=== Implementation guidelines ===
+
+Implementations must not necessarily implement all the possible wallet policies defined by this standard, but it is recommended to clearly document any limitation.
+
+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-0044.mediawiki|BIP-44]], [[bip-0049.mediawiki|BIP-49]], [[bip-0084.mediawiki|BIP-84]], [[bip-0086.mediawiki|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.
+
+Avoiding key reuse among different wallet accounts is also extremely important, but out of scope for this document.
+
+=== Optional derivation paths ===
+
+In order to allow supporting legacy derivation schemes (for example, using simply /* instead of the more common / /* for the same index i, then the sets {M, N} and {P, Q} must be disjoint.
-The key information vector should be ordered so that placeholder @i never appear 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.
+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.
=== Descriptor derivation ===
@@ -183,7 +183,7 @@ produces the following multipath descriptor:
=== Implementation guidelines ===
-Implementations must not necessarily implement all the possible wallet policies defined by this standard, but it is recommended to clearly document any limitation.
+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.
@@ -274,10 +274,10 @@ The following descriptor templates are invalid:
* 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
+* sh(multi(1,@0/**,xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/<0;1>/*)): Expression with a non-KP key present
* pkh(@0/<0;1;2>/*): Solved cardinality > 2
-Remark: some of the descriptor templates above might be valid if optional extensions allowing them are added in the implementation.
+Remark: some of the examples of invalid descriptor templates may be valid via optional extensions.
== Backwards Compatibility ==
From 7d0c08e38acac3ef14095d0e8664c7332b7be381 Mon Sep 17 00:00:00 2001
From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com>
Date: Tue, 7 May 2024 22:24:23 +0200
Subject: [PATCH 7/7] More nits from PR review
---
bip-0388.mediawiki | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/bip-0388.mediawiki b/bip-0388.mediawiki
index d7ed0bec49..4efc588106 100644
--- a/bip-0388.mediawiki
+++ b/bip-0388.mediawiki
@@ -14,7 +14,7 @@
== Abstract ==
-Wallet policies build on top of output descriptors to represent the types of descriptors that are typically used to represent "accounts" in a software wallet, or a hardware signing device, in a compact, reviewable way. A wallet policy always represents exactly two descriptors, which produce the receive and change addresses that are logically part of the same account.
+Wallet policies build on top of output script descriptors to represent the types of descriptors that are typically used to represent "accounts" in a software wallet, or a hardware signing device, in a compact, reviewable way. A wallet policy always represents exactly two descriptors, which produce the receive and change addresses that are logically part of the same account.
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.
@@ -101,7 +101,7 @@ In fact, there are many reasonable spending policies with a quadratic size in th
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 moving the key expression separately helps to keep the size of the wallet policy small, which is crucial to allow human inspection during the registration flow.
+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.
== Specification ==
@@ -146,7 +146,7 @@ The placeholder @i for some number ''i'' represents the ''i''-th key in
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.
-==== Keys information vector ====
+==== Key information vector ====
Each element of the key origin information vector is a KEY expression.
@@ -161,7 +161,7 @@ Each element of the key origin information vector is a KEY expression.
A wallet policy must have at least one key placeholder and the corresponding key.
-The public keys obtained by deserializing elements of the keys information vector must be pairwise distinct'''Why must public keys be distinct?''' Reusing pubkeys could be insecure in the context of wallet policies containing [https://bitcoin.sipa.be/miniscript/ miniscript]. Avoiding repeated public keys altogether avoids the problem at the source..
+The public keys obtained by deserializing elements of the key information vector must be pairwise distinct'''Why must public keys be distinct?''' Reusing pubkeys could be insecure in the context of wallet policies containing [https://bitcoin.sipa.be/miniscript/ miniscript]. Avoiding repeated public keys altogether avoids the problem at the source..
If two key placeholders are @i/ /* for the same index i, then the sets {M, N} and {P, Q} must be disjoint.
@@ -197,7 +197,7 @@ In order to allow supporting legacy derivation schemes (for example, using simpl
However, care needs to be taken in view of the following considerations:
-* Allowing derivation schemes with a different length or cardinality in the same wallet policy would make it difficult to guarantee that there are no repeated pubkeys for every possible address generated by the policy. For example, `@0/<0;1>/*` and `@1/*` would generate the same pubkeys if the second public key in the keys 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).
+* Allowing derivation schemes with a different length or cardinality in the same wallet policy would make it difficult to guarantee that there are no repeated pubkeys for every possible address generated by the policy. For example, `@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).
* Allowing naked pubkeys with no /* 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.
== Examples ==
@@ -281,7 +281,7 @@ Remark: some of the examples of invalid descriptor templates may be valid via op
== Backwards Compatibility ==
-The @ character used for key placeholders is not part of the syntax of output script descriptors, therefore any valid output 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.
+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.
+[[bip-0049.mediawiki|BIP-49]], second account
+ Descriptor template: sh(wpkh(@0/**))
+ Keys info: ["[6738736c/49'/0'/1']xpub6Bex1CHWGXNNwGVKHLqNC7kcV348FxkCxpZXyCWp1k27kin8sRPayjZUKDjyQeZzGUdyeAj2emoW5zStFFUAHRgd5w8iVVbLgZ7PmjAKAm9"]
+ Descriptor:sh(wpkh([6738736c/49'/0'/1']xpub6Bex1CHWGXNNwGVKHLqNC7kcV348FxkCxpZXyCWp1k27kin8sRPayjZUKDjyQeZzGUdyeAj2emoW5zStFFUAHRgd5w8iVVbLgZ7PmjAKAm9))
+
+[[bip-0084.mediawiki|BIP-84]], third account
+ Descriptor template: wpkh(@0/**)
+ Keys info: ["[6738736c/84'/0'/2']xpub6CRQzb8u9dmMcq5XAwwRn9gcoYCjndJkhKgD11WKzbVGd932UmrExWFxCAvRnDN3ez6ZujLmMvmLBaSWdfWVn75L83Qxu1qSX4fJNrJg2Gt"]
+ Descriptor:wpkh([6738736c/84'/0'/2']xpub6CRQzb8u9dmMcq5XAwwRn9gcoYCjndJkhKgD11WKzbVGd932UmrExWFxCAvRnDN3ez6ZujLmMvmLBaSWdfWVn75L83Qxu1qSX4fJNrJg2Gt)
+
+[[bip-0086.mediawiki|BIP-86]], first account
+ Descriptor template: tr(@0/**)
+ Keys info: ["[6738736c/86'/0'/0']xpub6CryUDWPS28eR2cDyojB8G354izmx294BdjeSvH469Ty3o2E6Tq5VjBJCn8rWBgesvTJnyXNAJ3QpLFGuNwqFXNt3gn612raffLWfdHNkYL"]
+ Descriptor:tr([6738736c/86'/0'/0']xpub6CryUDWPS28eR2cDyojB8G354izmx294BdjeSvH469Ty3o2E6Tq5VjBJCn8rWBgesvTJnyXNAJ3QpLFGuNwqFXNt3gn612raffLWfdHNkYL)
+
+[[bip-0048.mediawiki|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,[b2b1f0cf/48'/0'/0'/2']xpub6EWhjpPa6FqrcaPBuGBZRJVjzGJ1ZsMygRF26RwN932Vfkn1gyCiTbECVitBjRCkexEvetLdiqzTcYimmzYxyR1BZ79KNevgt61PDcukmC7))
+
+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),and_v(v:multi(2,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2,[bb641298/44'/0'/0'/100']xpub6Dz8PHFmXkYkykQ83ySkruky567XtJb9N69uXScJZqweYiQn6FyieajdiyjCvWzRZ2GoLHMRE1cwDfuJZ6461YvNRGVBJNnLA35cZrQKSRJ),older(65535))))
+
+
+TBD: add examples with taproot scripts and miniscript.
+
+=== Invalid policies ===
+
+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
+
+Remark: some of the descriptor templates above might be valid if optional extensions allowing them are added in the implementation.
+
+== Backwards Compatibility ==
+
+The @ character used for key placeholders is not part of the syntax of output script descriptors, therefore any valid output 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 harder 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.
+
+== Reference Implementation ==
+
+Wallet policies are implemented in
+* the [https://github.com/LedgerHQ/app-bitcoin-new Ledger bitcoin application] since version 2.1.0;
+* the [https://github.com/digitalbitbox/bitbox02-firmware BitBox02 firmware] since version v9.15.0;
+* [https://github.com/Blockstream/Jade Blockstream Jade] since version v1.0.24, via [https://github.com/ElementsProject/libwally-core libwally-core] v1.0.0.
+
+For development and testing purposes, we provide a [[bip-wallet-policies/wallet_policies.py|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.
+
+==Footnotes==
+
+
- BIP: wallet-policies
+ BIP: 388
Layer: Applications
Title: Wallet Policies for Descriptor Wallets
Author: Salvatore Ingala
diff --git a/bip-wallet-policies/wallet_policies.py b/bip-0388/wallet_policies.py
similarity index 100%
rename from bip-wallet-policies/wallet_policies.py
rename to bip-0388/wallet_policies.py
From 40c7760d781e760fe01bb6fe86a7731f506daa07 Mon Sep 17 00:00:00 2001
From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com>
Date: Sun, 5 May 2024 11:38:49 -0500
Subject: [PATCH 3/7] Apply suggestions from code review
Co-authored-by: Mark "Murch" Erhardt
-tr(musig(xpubA,xpubB,xpubC,xpubD,xpubE)/<0;1>/*), {
- {
- {
- pk(musig(xpubA,xpubB,xpubC)/<2;3>/*),
- {
- pk(musig(xpubA,xpubB,xpubD)/<4;5>/*)
- pk(musig(xpubA,xpubB,xpubE)/<6;7>/*),
- }
- },
- {
- pk(musig(xpubA,xpubC,xpubD)/<8;9>/*),
- {
- pk(musig(xpubA,xpubC,xpubE)/<10;11>/*),
- pk(musig(xpubA,xpubD,xpubE)/<12;13>/*)
- }
- }
- },
- {
- {
- pk(musig(xpubB,xpubC,xpubD)/<14;15>/*),
- pk(musig(xpubB,xpubC,xpubE)/<16;17>/*)
- },
- {
- pk(musig(xpubB,xpubD,xpubE)/<18;19>/*),
- {
- pk(musig(xpubC,xpubD,xpubE)/<20;21>/*),
- sortedmulti_a(3,
- xpubA/<22;23>/*,
- xpubB/<22;23>/*,
- xpubC/<22;23>/*,
- xpubD/<22;23>/*,
- xpubE/<22;23>/*)
- }
- }
- }
-})
-
-
-Notice how each root xpub appears 8 times. With xpubs being up to 118 bytes long, the length of the full descriptor can get extremely long (the problem rapidly gets worse with larger multisignature schemes).
+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 moving the key expression separately helps to keep the size of the wallet policy small, which is crucial to allow human inspection during the registration flow.
@@ -299,8 +258,11 @@ Miniscript: A singlesig wallet with automatic inheritance to a timelocked 2-of-3
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),and_v(v:multi(2,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2,[bb641298/44'/0'/0'/100']xpub6Dz8PHFmXkYkykQ83ySkruky567XtJb9N69uXScJZqweYiQn6FyieajdiyjCvWzRZ2GoLHMRE1cwDfuJZ6461YvNRGVBJNnLA35cZrQKSRJ),older(65535))))
-
-TBD: add examples with taproot scripts and miniscript.
+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,xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<2;3>/*,xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K/<0;1>/*),or_b(pk(xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM/<0;1>/*),s:pk(xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6/<0;1>/*))})
+
=== Invalid policies ===
From a0c8501f960ac29878e137c03dcd6d8a3b9096b5 Mon Sep 17 00:00:00 2001
From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com>
Date: Tue, 7 May 2024 10:55:55 +0200
Subject: [PATCH 5/7] Added BIP-388 to README
---
README.mediawiki | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/README.mediawiki b/README.mediawiki
index be18dcf859..0ce7b8e81f 100644
--- a/README.mediawiki
+++ b/README.mediawiki
@@ -1184,6 +1184,13 @@ Those proposing changes should consider that ultimately consent may rest with th
| Informational
| Draft
|-
+| [[bip-0388.mediawiki|388]]
+| Applications
+| Wallet Policies for Descriptor Wallets
+| Salvatore Ingala
+| Standard
+| Draft
+|-
| [[bip-0389.mediawiki|389]]
| Applications
| Multipath Descriptor Key Expressions
From cf2250e27cc682289facc27e1e2ff16e94d12aab Mon Sep 17 00:00:00 2001
From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com>
Date: Tue, 7 May 2024 22:10:44 +0200
Subject: [PATCH 6/7] Apply suggestions from code review
Co-authored-by: Mark "Murch" Erhardt