Return-Path: Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id EAC69C002D for ; Mon, 21 Nov 2022 11:27:42 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id B036A415D7 for ; Mon, 21 Nov 2022 11:27:42 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org B036A415D7 Authentication-Results: smtp4.osuosl.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=MGpOa/G9 X-Virus-Scanned: amavisd-new at osuosl.org X-Spam-Flag: NO X-Spam-Score: -2.098 X-Spam-Level: X-Spam-Status: No, score=-2.098 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id iDOHD_J4x3VY for ; Mon, 21 Nov 2022 11:27:40 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 6CBB3415A9 Received: from mail-lj1-x22c.google.com (mail-lj1-x22c.google.com [IPv6:2a00:1450:4864:20::22c]) by smtp4.osuosl.org (Postfix) with ESMTPS id 6CBB3415A9 for ; Mon, 21 Nov 2022 11:27:39 +0000 (UTC) Received: by mail-lj1-x22c.google.com with SMTP id d20so14105965ljc.12 for ; Mon, 21 Nov 2022 03:27:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=QTWK+lAwIi2zcGIBOHb5z9n780GKk5vdAYcK/0a00h4=; b=MGpOa/G96IjmIidDgWxsOhOacwKCeCqCnnQKguI/Qtl0D4a36sE3EGfXV4pZQKH6mM kdtEYCIrruVySZyYkMUeKIfhy5mkd9myXVlo0gRDOuYixBaNeyPrwGA8b4mu47ej01fa 4+LebbU7TaWQtF1Jwuzo6+yzSSog9cE3X10in5MzPShti1B2DrhRsHXvKH/q/o6oEVgi ozwD0msjU0ckcKoC4jRLbUJ2a/23rl7r11sW7c142sBg1cjaMeNGwf5WDZCD2r7x8zMm W0zEossyNRlPrCMNgY9ppfH6x/fNVnJjQw+WutHUnzLU7f1o4rQXtFtYdcIeKn7jkq/z EdeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=QTWK+lAwIi2zcGIBOHb5z9n780GKk5vdAYcK/0a00h4=; b=UY48Wl2kVEjuOOiDb9sgf7LbcZFIrHsujcPpUNImwx7qYf9QQZWLcqDZBx2D67sQvi FMKrs5fbomu+R2HSDgg8pO7rPEnjDpfW40J1j8FdMCnO4qJ5+dhnPix7cqXCcoL449HQ 1zVgWuUFixYXrSHjEZHRqGTo1tAcOmkz3jUBJljujyFfIVDryJHbf+lTeQdqCpSkmQhv OJfxv+VqGvXw+GA5iTE8WSgS1YlfbBrRLbpHry7/YZbMzwAaOjCd6wRaMd+sV2IsyUfZ WAfNnDL+liphPe1cM/0kokSK5e1rkIxlOIHvN4xuNOTCDsDTyhk0N+5CPWHB5uHKMpQS khnw== X-Gm-Message-State: ANoB5pmd8SZcdhEjJ2r9X0bNpO7naU1YddngwXjxQBuNCU9cjkTr7nwZ hq9D6IP9j35DJzZAyvQvZylZKG3QXmTi8j9GfIOXpLYFW5o= X-Google-Smtp-Source: AA0mqf4zU/93m6lkzRLTJOeinOjhVQaUBFdVL1i5/54PMTfv8wmQI22FSeWgJVxbqOuzx1t4xCkMzJhhk0TJhDhYEOk= X-Received: by 2002:a05:651c:1592:b0:277:b9f:cdbd with SMTP id h18-20020a05651c159200b002770b9fcdbdmr6138658ljq.0.1669030056436; Mon, 21 Nov 2022 03:27:36 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Salvatore Ingala Date: Mon, 21 Nov 2022 12:27:25 +0100 Message-ID: To: Bitcoin Protocol Discussion Content-Type: multipart/alternative; boundary="000000000000a226df05edf957ba" X-Mailman-Approved-At: Mon, 21 Nov 2022 13:03:59 +0000 Subject: Re: [bitcoin-dev] Wallet policies for descriptor wallets X-BeenThere: bitcoin-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Bitcoin Protocol Discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Nov 2022 11:27:43 -0000 --000000000000a226df05edf957ba Content-Type: text/plain; charset="UTF-8" Hi list, Following up on this topic, I now opened a pull request with the BIP proposal: https://github.com/bitcoin/bips/pull/1389 I also attempted a proof-of-concept of how an integration of wallet policies to HWI might look like: https://github.com/bitcoin-core/HWI/pull/647 which might help to provide context, and also serves as a demo of the possible UX flows with hardware signers (as currently implemented in the Ledger bitcoin app). There are no substantial changes to the initial version proposed to the list: - some additional restrictions to the allowed descriptors were added as further simplifications; - added test vectors and observations on backwards compatibility; - general improvements to the text. I look forward to your comments and improvements. Salvatore Ingala On Thu, 5 May 2022 at 16:32, Salvatore Ingala wrote: > In the implementation work to implement descriptors and miniscript support > in hardware wallets [a][b], I encountered a number of challenges. Some of > them are technical in nature (e.g. due to constraints of embedded > development). Others are related to the attempts of shaping a good user > experience; with bitcoin reaching more people who are not tech-savvy, > self-custody is only as secure as what those newcomers can use easily > enough. > > The main tool that I am using to address some of these challenges is a > layer that sits _on top_ of descriptors/miniscript, while staying very > close to it. Since there is nothing that is vendor-specific in the vast > majority of the approach I'm currently using, I tried to distill it here > for your comments, and will propose a BIP if this is deemed valuable. > > I called the language "wallet policies" (suggestions for a better name are > welcome). I believe an approach based on wallet policies can benefit all > hardware wallets (stateless or not) that want to securely support complex > scripts; moreover, wallet policies are close enough to descriptors that > their integration should be extremely easy for any software wallet that is > currently using descriptors. > > [a]: https://blog.ledger.com/bitcoin-2 - early demo > [b]: https://blog.ledger.com/miniscript-is-coming - miniscript example > > > Salvatore Ingala > > > ====================================================== > > This document starts with a discussion on the motivation for wallet > policies, followed by their formal definition, and some recommendations for > implementations. > > == Rationale == > > Output script descriptors [1] 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 bitcoin-core's descriptor-based > wallets). > > Unfortunately, descriptors are not a perfect match for the typical usage > of hardware wallets. Most hardware wallets have the following limitations > compared to a general-purpose machine running bitcoin-core: > > - they are embedded devices with limited RAM and computational power; > - they might not be able to import additional private keys (all the keys > are generated from a single seed via [BIP-32]( > https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)); > - they might not have permanent storage (*stateless* hardware wallet > 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. > > A more native, compact representation of the wallet receive/change would > also benefit the UX of software wallets using descriptors to represent > software wallets using descriptors/miniscript for multisignature or other > complex locking conditions. > > === Security and UX concerns of scripts in hardware wallets === > > For a hardware wallet, 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 wallets strive to guarantee > is the following: **as long as the user correctly verifies the information > that is shown on the hardware wallet'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 hardware wallet, 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 wallet must guarantee that the user knows precisely *what* > "policy" is being used to spend the funds, and that the "unspent" funds (if > any) will be protected by the same policy. This makes it impossible for an > attacker to surreptitiously modify the policy, therefore stealing or > burning 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 able _in practice_ 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-129 [2] for an approach designed for multisignature > wallets. > > === 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 > wallet. The "wallet policy" must contain enough information to generate all > the relevant addresses/scripts, and for the hardware wallet 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 wallet. While the details of the process > are out of scope in this document, the flow should be something similar to > the following: > > 1) The software wallet initiates a _wallet policy registration_ on the > hardware wallet; the information should include the wallet policy, but also > a unique *name* that identifies the policy. > 2) The hardware wallet shows the wallet policy to the user using the > secure screen. > 3) After inspecting the policy and comparing it with a trusted source (for > example a printed backup), the user approves the policy. > 4) If stateful, the hardware wallet persists the policy in its permanent > memory; if stateless, it returns a "proof of registration". > > The details of how to create a proof of registration are out of scope for > this document; using a *message authentication codes* 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 wallet can perform the usual > 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 wallet 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 its name), and *as long as the policy > registration was executed securely*, hardware wallets 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 MuSig > - a script tree with a tree of 10 different 3-of-3 MuSig2 scripts, that > are generated, plus a leaf with a fallback *3*-of-*5* multisignature using > plain multisignature (with `OP_CHECKSIGADD`). > > This could look similar to: > > ``` > tr(musig2(xpubA,xpubB,xpubC,xpubD,xpubE)/<0;1>/*), { > { > { > pk(musig2(xpubA,xpubB,xpubC)/<2;3>/*), > { > pk(musig2(xpubA,xpubB,xpubD)/<4;5>/*) > pk(musig2(xpubA,xpubB,xpubE)/<6;7>/*), > } > }, > { > pk(musig2(xpubA,xpubC,xpubD)/<8;9>/*), > { > pk(musig2(xpubA,xpubC,xpubE)/<10;11>/*), > pk(musig2(xpubA,xpubD,xpubE)/<12;13>/*) > } > } > }, > { > { > pk(musig2(xpubB,xpubC,xpubD)/<14;15>/*), > pk(musig2(xpubB,xpubC,xpubE)/<16;17>/*) > }, > { > pk(musig2(xpubB,xpubD,xpubE)/<18;19>/*), > { > pk(musig2(xpubC,xpubD,xpubE)/<20;21>/*), > sortedmulti_a(3, > xpubA/<22;23>/*, > xpubB/<22;23>/*, > xpubC/<22;23>/*, > xpubD/<22;23>/*, > xpubE/<22;23>/*) > } > } > } > }) > ``` > > Note that 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 > gets *exponentially* 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 in the > registration flow. > > === Restrictions on the supported descriptors ==== > > The policy language proposed in this document purposely targets only a > stricter subset of the output descriptors language, and it attempts to > generalize in the most natural way the approach that is already used for > single-signature *accounts* (as described in BIP-44 [3], BIP-49 [4], BIP-84 > [5], or BIP-86 [6]), or in multisignature setups (see for example BIP-48 > [7] and BIP-87 [8]). > > Unlike the BIPs mentioned above, it is not tied to any specific script > template, as it applies to arbitrary scripts that can be represented with > descriptors and miniscript. > > Supporting only a reduced feature set when compared to output descriptors > helps in implementations (especially on hardware wallets), while attempting > to capture all the common use cases. More features can be added in the > future if motivated by real world necessity. > > By keeping the structure of the wallet policy language very close to that > of descriptors, it should be straightforward to: > - write wallet policy parsers; > - extract the descriptors defined by a wallet policy; > - convert a pair of descriptors describing a wallet "account" used in > current implementations into the corresponding wallet policy. > > > == Wallet policies == > > 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 (use > `addr` if you only know the pubkey hash). > - `wpkh(KP)` (top level or inside `sh` only): P2WPKH output for the given > compressed pubkey. > - `multi(k,KP_1,KP_2,...,KP_n)`: k-of-n multisig script. > - `sortedmulti(k,KP_1,KP_2,...,KP_n)`: 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 `}` > > Note: "miniscript templates" are not formally defined in this version of > the document, but it is straightforward to adapt this approach. > > `KP` expressions (key placeholders) consist of > - a single character `@` > - followed by a non-negative decimal number, with no leading zeros (except > for `@0`). > - possibly followed by either: > - the string `/**`, or > - a string of the form `//*`, for two distinct decimal numbers > `NUM` representing unhardened derivations > > The `/**` in the placeholder template represents commonly used paths for > receive/change addresses, and is equivalent to `<0;1>`. > > The placeholder `@i` for some number *i* represents the *i*-th key in the > vector of key origin information (which must be of size at least *i* + 1, > or the wallet policy is invalid). > > ==== Key informations vector ==== > > Each element of the key origin information vector is a `KEY` expression. > > - Optionally, key origin information, consisting of: > - An open bracket `[` > - Exactly 8 hex characters for the fingerprint of the master key from > which this key is derived from (see [BIP32]( > https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) for > details) > - Followed by zero or more `/NUM'` path elements to indicate hardened > derivation steps between the fingerprint and the xpub that follows > - A closing bracket `]` > - Followed by the actual key, which is either > - a hex-encoded pubkey, which is either > - inside `wpkh` and `wsh`, only compressed public keys are permitted > (exactly 66 hex characters starting with `02` or `03`. > - inside `tr`, x-only pubkeys are also permitted (exactly 64 hex > characters). > - a serialized extended public key (`xpub`) (as defined in [BIP 32]( > https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)) > > The placeholder `@i` for some number *i* represents the *i*-th key in the > vector of key orIgin information (which must be of size at least *i* + 1, > or the wallet policy is invalid). > > The policy template is invalid if any placeholder `@i` has derivation > steps while the corresponding `(i+1)`-th element of the keys vector is not > an xpub. > > ==== Additional rules ==== > > The wallet policy is invalid if any placeholder expression with additional > derivation steps is used when the corresponding key information is not an > xpub. > > 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 > informations), one can therefore obtain the 1-dimensional descriptor for > receive and change addresses by: > > - replacing each key placeholder with the corresponding key origin > information; > - replacing every `/**` with `/0/*` for the receive descriptor, and > `/1/*` for the change descriptor; > - replacing every `/` with `/M` for the receive descriptor, and `/N` > for the change descriptor. > > For example, the wallet descriptor `pkh(@0/**)` with key information > `["[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"]` > produces the following two descriptors: > > - Receive descriptor: > `pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*)` > > - Change descriptor: > `pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)` > > === Implementation guidelines === > > Implementations must not necessarily implement all of 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 general-purpose software wallet allowing arbitrary > scripts (or any scripts that involve external cosigners) should put great > care into a process for backing up a wallet policy. In fact, unlike typical > 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. > > Avoiding key reuse among different wallet accounts is also extremely > important, but out of scope for this document. > > == Examples == > > Some examples of wallet descriptor templates (vectors of keys omitted for > simplicity): > - Template for a native segwit account: > wpkh(@0/**) > - Template for a taproot BIP86 account: > tr(@0/**) > - Template for a native segwit 2-of-3: > wsh(sortedmulti(2,@0/**,@1/**,@2/**)) > - Template with miniscript for "1 of 2 equally likely keys": > wsh(or_b(pk(@0/**),s:pk(@1/**))) > > More examples (esp. targeting miniscript on taproot) will be added in the > future. > > == References == > > * [1] - Output Script Descriptors: > https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md > * [2] - BIP-129 (Bitcoin Secure Multisig Setup): > https://github.com/bitcoin/bips/blob/master/bip-0129.mediawiki > * [3] - BIP-44: > https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki > * [4] - BIP-49: > https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki > * [5] - BIP-84: > https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki > * [6] - BIP-86: > https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki > * [7] - BIP-48: > https://github.com/bitcoin/bips/blob/master/bip-0048.mediawiki > * [8] - BIP-87: > https://github.com/bitcoin/bips/blob/master/bip-0087.mediawiki > > --000000000000a226df05edf957ba Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi list,

Following up on this topic, I now ope= ned a pull request with the BIP proposal:

I also attempted a= proof-of-concept of how an integration of wallet policies to HWI might loo= k like:

=C2=A0 =C2=A0 =C2=A0https://github.com/bitcoin-core/HWI/pull/647
which might help to provide context, and also serves as a demo of the = possible UX flows with hardware signers (as currently implemented in the Le= dger bitcoin app).

There are no substantial changes to the initial v= ersion proposed to the list:
- some additional restrictions to th= e allowed descriptors were added=C2=A0as further simplifications;
- added test vectors and observations on backwards compatibility;
- general improvements to the text.

I look forwa= rd to your comments and improvements.
Salvatore Ingala

On Th= u, 5 May 2022 at 16:32, Salvatore Ingala <salvatore.ingala@gmail.com> wrote:
In the implement= ation work to implement descriptors and miniscript support in hardware wall= ets [a][b], I encountered a number of challenges. Some of them are technica= l in nature (e.g. due to constraints of embedded development). Others are r= elated to the attempts of shaping a good user experience; with bitcoin reac= hing more people who are not tech-savvy, self-custody is only as secure as = what those newcomers can use easily enough.

The main tool that I am = using to address some of these challenges is a layer that sits _on top_ of = descriptors/miniscript, while staying very close to it. Since there is noth= ing that is vendor-specific in the vast majority of the approach I'm cu= rrently using, I tried to distill it here for your comments, and will propo= se a BIP if this is deemed valuable.

I called the language "wal= let policies" (suggestions for a better name are welcome). I believe a= n approach based on wallet policies can benefit all hardware wallets (state= less or not) that want to securely support complex scripts; moreover, walle= t policies are close enough to descriptors that their integration should be= extremely easy for any software wallet that=C2=A0is currently using descri= ptors.

[a]: https://blog.ledger.com/bitcoin-2 - early demo
[b]:=C2=A0ht= tps://blog.ledger.com/miniscript-is-coming - miniscript example

=
Salvatore Ingala


=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D

This document starts with a discussion on the motivation for wal= let policies, followed by their formal definition, and some recommendations= for implementations.

=3D=3D Rationale =3D=3D

Output script descriptors [1] were introduced in bitcoin-core as a way t= o represent collections of output scripts. It is a very general and flexibl= e 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 bitcoin-core's descriptor-based w= allets).

Unfortunately, descriptors are not a perfect match for the = typical usage of hardware wallets. Most hardware wallets have the following= limitations compared to a general-purpose machine running bitcoin-core:
- they are embedded devices with limited RAM and computational power;<= br>- they might not be able to import additional private keys (all the keys= are generated from a single seed via [BIP-32](https://g= ithub.com/bitcoin/bips/blob/master/bip-0032.mediawiki));
- they migh= t not have permanent storage (*stateless* hardware wallet design).

M= oreover, other limitations like the limited size of the screen might affect= what design choices are available in practice. Therefore, minimizing the s= ize of the information shown on-screen is important for a good user experie= nce.

A more native, compact representation of the wallet receive/cha= nge would also benefit the UX of software wallets using descriptors to repr= esent software wallets using descriptors/miniscript for multisignature or o= ther complex locking conditions.

=3D=3D=3D Security and UX concerns = of scripts in hardware wallets =3D=3D=3D

For a hardware w= allet, allowing the usage of complex scripts presents challenges in terms o= f both security and user experience.

=3D=3D=3D=3D Security issues = =3D=3D=3D=3D

One of the security properties that hardware wal= lets strive to guarantee is the following: **as long as the user correctly = verifies the information that is shown on the hardware wallet'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 hardware wallet, and can execute a= rbitrary requests or tamper with the legitimate user's requests.
Therefore, it is not at all trivial to allow complex scripts, especially i= f they contain keys that belong to third parties.
The hardware wallet mu= st guarantee that the user knows precisely *what* "policy" is bei= ng used to spend the funds, and that the "unspent" funds (if any)= will be protected by the same policy. This makes it impossible for an atta= cker to surreptitiously modify the policy, therefore stealing or burning us= er's funds.

=3D=3D=3D=3D UX issues =3D=3D=3D=3D

Wi= th miniscript (and taproot trees) allowing substantially more complex spend= ing policies to be used, it becomes more challenging to make sure that the = user is able _in practice_ to verify the information on the screen. Therefo= re, there are two fundamental design goals to strive for:
- Minimize the= amount of information that is shown on screen - so that the user can actua= lly 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-129 [2] for an approach desi= gned for multisignature wallets.

=3D=3D=3D Policy registration as a = solution =3D=3D=3D

A solution to address the security concern= s, and part of the UX concerns, is to have a *registration* flow for the wa= llet policy in the hardware wallet. The "wallet policy" must cont= ain enough information to generate all the relevant addresses/scripts, and = for the hardware wallet to identify the keys that it controls and that are = needed to spend the funds sent to those addresses.

Before a new poli= cy is used for the first time, the user will register a `wallet policy` int= o the hardware wallet. While the details of the process are out of scope in= this document, the flow should be something similar to the following:
<= br>1) The software wallet initiates a _wallet policy registration_ on the h= ardware wallet; the information should include the wallet policy, but also = a unique *name* that identifies the policy.
2) The hardware wallet shows= the wallet policy to the user using the secure screen.
3) After inspect= ing the policy and comparing it with a trusted source (for example a printe= d backup), the user approves the policy.
4) If stateful, the hardware wa= llet persists the policy in its permanent memory; if stateless, it returns = a "proof of registration".

The details of how to create a = proof of registration are out of scope for this document; using a *message = authentication codes* on a hash committing to the wallet policy, its name a= nd any additional metadata is an effective solution if correctly executed.<= br>
Once a policy is registered, the hardware wallet can perform the usu= al operations securely:
- generating receive and change addresses;
- = showing addresses on the secure screen;
- sign transactions spending fro= m a wallet, while correctly identifying change addresses and computing the = transaction fees.

Before any of the actions mentioned above, the har= dware wallet will retrieve the policy from its permanent storage if statefu= l; if stateless it will validate the _proof of registration_ before using t= he wallet policy provided by the client.
Once the previously registered = policy is correctly identified and approved by the user (for example by its= name), and *as long as the policy registration was executed securely*, har= dware wallets can provide a user experience similar to the usual one for si= ngle-signature transactions.

=3D=3D=3D Avoiding blowup in descriptor= size =3D=3D=3D

While reusing a pubkey in different branches = of a miniscript is explicitly forbidden by miniscript (as it has certain ne= gative security implications), it is still reasonable to reuse the same *xp= ub* 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 coul= d use:
- a key path with a 5-of-5 MuSig
- a script tree with a tree o= f 10 different 3-of-3 MuSig2 scripts, that are generated, plus a leaf with = a fallback *3*-of-*5* multisignature using plain multisignature (with `OP_C= HECKSIGADD`).

This could look similar to:

```
tr(musig2(xp= ubA,xpubB,xpubC,xpubD,xpubE)/<0;1>/*), {
=C2=A0 {
=C2=A0 =C2=A0= {
=C2=A0 =C2=A0 =C2=A0 pk(musig2(xpubA,xpubB,xpubC)/<2;3>/*),
= =C2=A0 =C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 pk(musig2(xpubA,xpubB= ,xpubD)/<4;5>/*)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 pk(musig2(xpubA,xpubB= ,xpubE)/<6;7>/*),
=C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 },
= =C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 pk(musig2(xpubA,xpubC,xpubD)/<8;= 9>/*),
=C2=A0 =C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 pk(musig= 2(xpubA,xpubC,xpubE)/<10;11>/*),
=C2=A0 =C2=A0 =C2=A0 =C2=A0 pk(mu= sig2(xpubA,xpubD,xpubE)/<12;13>/*)
=C2=A0 =C2=A0 =C2=A0 }
=C2= =A0 =C2=A0 }
=C2=A0 },
=C2=A0 {
=C2=A0 =C2=A0 {
=C2=A0 =C2=A0 = =C2=A0 pk(musig2(xpubB,xpubC,xpubD)/<14;15>/*),
=C2=A0 =C2=A0 =C2= =A0 pk(musig2(xpubB,xpubC,xpubE)/<16;17>/*)
=C2=A0 =C2=A0 },
= =C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 pk(musig2(xpubB,xpubD,xpubE)/<18= ;19>/*),
=C2=A0 =C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 pk(mus= ig2(xpubC,xpubD,xpubE)/<20;21>/*),
=C2=A0 =C2=A0 =C2=A0 =C2=A0 sor= tedmulti_a(3,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 xpubA/<22;23>/*,<= br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 xpubB/<22;23>/*,
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 xpubC/<22;23>/*,
=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 xpubD/<22;23>/*,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 xpu= bE/<22;23>/*)
=C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 }
=C2=A0 = }
})
```

Note that each root xpub appears 8 times. With xpubs = being up to 118 bytes long, the length of the full descriptor can get extre= mely long (the problem gets *exponentially* worse with larger multisignatur= e schemes).

Replacing the common part of the key with a short key pl= aceholder and moving the key expression separately helps to keep the size o= f the wallet policy small, which is crucial to allow human inspection in th= e registration flow.

=3D=3D=3D Restrictions on the supported descrip= tors =3D=3D=3D=3D

The policy language proposed in this document purp= osely targets only a stricter subset of the output descriptors language, an= d it attempts to generalize in the most natural way the approach that is al= ready used for single-signature *accounts* (as described in BIP-44 [3], BIP= -49 [4], BIP-84 [5], or BIP-86 [6]), or in multisignature setups (see for e= xample BIP-48 [7] and BIP-87 [8]).

Unlike the BIPs mentioned = above, it is not tied to any specific script template, as it applies to arb= itrary scripts that can be represented with descriptors and miniscript.
=
Supporting only a reduced feature set when compared to output descripto= rs helps in implementations (especially on hardware wallets), while attempt= ing to capture all the common use cases. More features can be added in the = future if motivated by real world necessity.

By keeping the structur= e of the wallet policy language very close to that of descriptors, it shoul= d be straightforward to:
- write wallet policy parsers;
- extract the= descriptors defined by a wallet policy;
- convert a pair of descriptors= describing a wallet "account" used in current implementations in= to the corresponding wallet policy.


=3D=3D Wallet policies =3D= =3D

This section formally defines wallet policies, and how th= ey relate to output script descriptors.

=3D=3D=3D Formal defi= nition =3D=3D=3D

A wallet policy is composed by a = wallet descriptor template, together with a vector of key information items= .

=3D=3D=3D=3D Wallet descriptor template =3D=3D=3D=3D

A wall= et descriptor template is a `SCRIPT` expression.

`SCRIPT` expression= s:
- `sh(SCRIPT)` (top level only): P2SH embed the argument.
- `wsh(S= CRIPT)` (top level or inside `sh` only): P2WSH embed the argument.
- `pk= h(KP)` (not inside `tr`): P2PKH output for the given public key (use `addr`= if you only know the pubkey hash).
- `wpkh(KP)` (top level or inside `s= h` only): P2WPKH output for the given compressed pubkey.
- `multi(k,KP_1= ,KP_2,...,KP_n)`: k-of-n multisig script.
- `sortedmulti(k,KP_1,KP_2,...= ,KP_n)`: k-of-n multisig script with keys sorted lexicographically in the r= esulting script.
- `tr(KP)` or `tr(KP,TREE)` (top level only): P2TR outp= ut 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 o= pen brace `{`, a `TREE` expression, a comma `,`, a `TREE` expression, and a= closing brace `}`

Note: "miniscript templates" are n= ot formally=C2=A0defined in this version of the document, but it is straigh= tforward=C2=A0to adapt this approach.

`KP` expressions (key pl= aceholders) consist of
- a single character `@`
- followed by a non-n= egative decimal number, with no leading zeros (except for `@0`).
- possi= bly followed by either:
=C2=A0 - the string =C2=A0`/**`, or
=C2=A0 - = a string of the form `/<NUM;NUM>/*`, for two distinct decimal numbers= `NUM` representing unhardened derivations

The `/**` in the placehol= der template represents commonly used paths for receive/change addresses, a= nd is equivalent to `<0;1>`.

The placeholder `@i` for some num= ber *i* represents the *i*-th key in the vector of key origin information (= which must be of size at least *i* + 1, or the wallet policy is invalid).

=3D=3D=3D=3D Key informations vector =3D=3D=3D= =3D

Each element of the key origin information vector is a `KEY` exp= ression.

- Optionally, key origin information, consisting of:
=C2= =A0 - An open bracket `[`
=C2=A0 - Exactly 8 hex characters for the fing= erprint of the master key from which this key is derived from (see [BIP32](= https://github.com/bitcoin/bips/blob/master/bip-0032.medi= awiki) for details)
=C2=A0 - Followed by zero or more `/NUM'` pa= th elements to indicate hardened derivation steps between the fingerprint a= nd the xpub that follows
=C2=A0 - A closing bracket `]`
- Followed by= the actual key, which is either
=C2=A0 - a hex-encoded pubkey, which is= either
=C2=A0 =C2=A0 - inside=C2=A0`wpkh`=C2=A0and=C2=A0`wsh`, only com= pressed public keys are permitted (exactly 66 hex characters starting with = `02` or `03`.
=C2=A0 =C2=A0 - inside `tr`, x-only pubkeys are also permi= tted (exactly 64 hex characters).
=C2=A0 - a serialized extended public = key (`xpub`) (as defined in [BIP 32](https://github.com/b= itcoin/bips/blob/master/bip-0032.mediawiki))

The placeholder `@i= ` for some number *i* represents the *i*-th key in the vector of key orIgin= information=C2=A0(which must be of size at least *i* + 1, or the wallet po= licy is invalid).

The policy template is invalid if any placeholder = `@i` has derivation steps while the corresponding `(i+1)`-th element of the= keys vector is not an xpub.

=3D=3D=3D=3D Additional rules =3D=3D=3D= =3D

The wallet policy is invalid if any placeholder expressio= n with additional derivation steps is used when the corresponding key infor= mation is not an xpub.

The key information vector *should* be ordere= d so that placeholder `@i` never appear for the first time before an occurr= ence of `@j` =C2=A0for some `j < i`; for example, the first placeholder = is always `@0`, the next one is `@1`, etc.

=3D=3D=3D Descriptor deri= vation =3D=3D=3D

From a wallet descriptor template (and the a= ssociated vector of key informations), one can therefore obtain the 1-dimen= sional descriptor for receive and change addresses by:

- replacing e= ach key placeholder with the corresponding key origin information;
- rep= lacing every `/**` =C2=A0with `/0/*` for the receive descriptor, and `/1/*`= for the change descriptor;
- replacing every `/<M,N>` with =C2=A0= `/M` for the receive descriptor, and `/N` for the change descriptor.
For example, the wallet descriptor `pkh(@0/**)` with key information `[&qu= ot;[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1= LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL&quo= t;]` produces the following two descriptors:

- Receive descriptor: `= pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G= 1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/= *)`

- Change descriptor: `pkh([d34db33f/44'/0'/0']xpub6E= RApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4A= LHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)`

=3D=3D=3D Implementation guidel= ines =3D=3D=3D

Implementations must not necessarily implement= all of the possible wallet policies defined by this standard, but it is re= commended to clearly document any limitation. =C2=A0

Implementations= can add additional metadata that is stored together with the wallet policy= for the purpose of wallet policy registration and later usage. Metadata ca= n be vendor-specific and is out of the scope of this document.

Any i= mplementation in a general-purpose software wallet allowing arbitrary scrip= ts (or any scripts that involve external cosigners) should put great care i= nto a process for backing up a wallet policy. In fact, unlike typical singl= e-signature scenarios, the seed alone is no longer enough to discover walle= t policies with existing funds, and the loss of the backup is likely to lea= d to permanent loss of funds.

Avoiding key reuse among different wal= let accounts is also extremely important, but out of scope for this documen= t.

=3D=3D Examples =3D=3D

Some examples of wallet descriptor = templates (vectors of keys omitted for simplicity):
- Template fo= r a native segwit account:
=C2=A0 wpkh(@0/**)
- Template = for a taproot BIP86 account:
=C2=A0 tr(@0/**)
- Template for a native segwit 2-of-3:
=C2=A0 wsh(sortedmulti(2,= @0/**,@1/**,@2/**))
- Template with miniscript for "1 of 2 e= qually likely keys":
=C2=A0 wsh(or_b(pk(@0/**),s:pk(@1/**)))=

More examples (esp. targeting miniscript on tapro= ot) will be added in the future.

=3D=3D References= =3D=3D

* [2] - BIP-129 (Bitcoin Secure Multisig Setup): https://github.com/bitcoin/bips/blob/master/bip-0129.mediawi= ki
* [4] - BIP-49: https://github.com/bitcoin/bips/blob/master/bip-0049.mediaw= iki

--000000000000a226df05edf957ba--