Return-Path: Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 97279C002D for ; Tue, 17 May 2022 08:45:07 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 852DA40C0F for ; Tue, 17 May 2022 08:45:07 +0000 (UTC) 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 Authentication-Results: smtp2.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.com Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xxNGMcgHpQhK for ; Tue, 17 May 2022 08:45:06 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 Received: from mail-pj1-x102f.google.com (mail-pj1-x102f.google.com [IPv6:2607:f8b0:4864:20::102f]) by smtp2.osuosl.org (Postfix) with ESMTPS id 1512E40C9D for ; Tue, 17 May 2022 08:45:05 +0000 (UTC) Received: by mail-pj1-x102f.google.com with SMTP id l20-20020a17090a409400b001dd2a9d555bso1697822pjg.0 for ; Tue, 17 May 2022 01:45:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:references:in-reply-to:from:date:message-id:subject:to; bh=raSHQ0JdXv28WuQRuquW5BCN0XaYG6+VJDTKvcMLUy8=; b=R/q95tIWtkeoEQzwuWVmSUwYIG7XpC7gRY8R750Y8DmcR09aiCjGc+h9R1CEuJvmZL s8fzAst8pVKGiyYm4LP0laMXJNThY41n0sF6xBTPFRC27ZeY85IW+eRX02Vy78tqtzHI iXHUe4jGZLv+7jWaLB77fzjSFwfFSvqVr6q6NAmHVQ1s8q3giTc9fvQjrBvwr6fiNrMT yV3nMqA7K+sMjLbMNxkAsB3S6rCPYHZ7TGhORUEQE/+YvU8SY/HxglproR3kO7rgV3UF 3sEAozRIOOnymw0RkU0K+9OEPN1INTj/Mi29pi3fRoF/uBt8IfuJEzL4lybvELxrpQ9w NURA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to; bh=raSHQ0JdXv28WuQRuquW5BCN0XaYG6+VJDTKvcMLUy8=; b=tGYh9XN2xRxIwIAWwQeACzld1XvOo0OuKiWQei1tEX62QIUXWt/xxZEYtuoA/GUANQ ZxfP2N2+aw7CzT1Uch/Us6DL2T8tWQXwnPxbSXqVpkTGTCXc2Joi0QXLdONRjGAGT7a5 Aw/LsurDFuKXvvUFfFDQdAMLTlMElE4GF+EPo4kE8eLoc9bn33j4duuRFXNOXe4Qgq3A LdizwMNzfs69/856xZ3QqPVH9p203n86S6pD5Fv8IdHBau/tFywHnTOW6m0Bdz/Kcg1W IVkNISm+zJi5FEcWagLrwoUEXhg13nB2JhCsgFQHYBQiPxQVeuOMjTZ4Nfji8/gn1+nY 1o1w== X-Gm-Message-State: AOAM531FQVkA+qqMhP5A42KQByGCbWLbK3/fnYp29QgkFwmeTyvm3yfG /Pk7Q+PwrBVbctrJtzE4j45E3oqDtUKx+tCicqR1jAl2qhvynw== X-Google-Smtp-Source: ABdhPJyib9IM0R8k4bQYcqoOexZYG5KV+Mh8Vi8hFYaUJmsPOpBzsNBtkTZmfKjPRZqooEEEPP/kh+dxal58QRmWIpA= X-Received: by 2002:a17:902:ccc2:b0:15f:4acc:f202 with SMTP id z2-20020a170902ccc200b0015f4accf202mr20982085ple.3.1652777104809; Tue, 17 May 2022 01:45:04 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Salvatore Ingala Date: Tue, 17 May 2022 10:44:53 +0200 Message-ID: To: Bitcoin Protocol Discussion Content-Type: multipart/alternative; boundary="00000000000039a07605df31288f" X-Mailman-Approved-At: Tue, 17 May 2022 09:25:14 +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: Tue, 17 May 2022 08:45:07 -0000 --00000000000039a07605df31288f Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi all, TL;DR: It is easy to convert from wallet policy to descriptors and back; imho aliases are better left out of descriptors in real world usage; some more examples given. I received some very useful feedback on the wallet policy proposal (in this list and outside); that also led me to realize that my initial post lacked some clarity and more practical examples. This post wants to: - clarify that extracting descriptors from the wallet policy is trivial; - argue that figuring out the wallet policy (template and list of keys information) from the descriptor is reasonably easy =E2=88=92 automatable f= or sane descriptors currently in use, and much more general ones as well; - give an idea of what the information shown on a hardware wallet screen would look like (emphasizing compactness); - explain my point of view on "descriptors with aliases". This gist demoes conversions from wallet policies to descriptors, and back: https://gist.github.com/bigspider/10df51401be3aa6120217c03c2836ffa Note that I would expect/hope software wallets to prefer working directly with wallet policies =E2=88=92 but it might help to have automated tools fo= r the conversion, for interoperability with tools that do not adopt wallet policies. (All the following examples use the `/**` notation as a shortcut for `/<0,1>/*`; this notation might be dropped without consequences on the rest of the proposal.) All the keys in the example I'm proposing are followed by /**. It is unclear to me if hardware wallets should allow *registration* of wallet policies with static keys (that is, without any range operator), as that would incentivize key reuse. The specs still support it as there might be other use cases. The policy for miniscript examples not using taproot was generated with the online compiler: https://bitcoin.sipa.be/miniscript. Many examples are also borrowed from there. (To the best of my knowledge, there is no publicly released compiler for miniscript on taproot, yet) Note on aliases: it has been pointed out that many miniscript implementations internally use aliases to refer to the keys. In my opinion, aliases: - should be external to the descriptor language, as they bear no significance for the actual script(s) that the descriptor can produce - fail to distinguish which part of the KEY expression is part of the "wallet description", and which part is not By clearly separating the key information in the vector (typically, an xpub with key origin information) from the key placeholder expression (which typically will have the `/**` or `/<0,1>/*` derivation step), wallet policies semantically represent keys in a way that should be convenient to both software wallets and hardware signers. Associating recognizable names to the xpubs (and registering them on the device) is a good idea for future developments and can greatly improve the UX, both during wallet setup, or in recognizing outputs for repeated payments; it should be easy to build this feature on top of wallet policies= . =3D=3D Examples =3D=3D All the examples show: - Miniscript policy: semantic spending rules, and optimization hints (can be compiled to miniscript automatically) - Miniscript: the actual miniscript descriptor, compiles 1-to-1 to Bitcoin Script - Wallet template: the "wallet descriptor template" - Vector of keys: the list of key information (with key origin information) Together, the wallet template and the vector of keys are the complet "wallet policy". =3D=3D=3D Example 1: Either of two keys (equally likely) =3D=3D=3D Miniscript policy: or(pk(key_0),pk(key_1)) Miniscript: wsh(or_b([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUH= QVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<0;1>/*)= ,s:pk([12345678/44'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8= syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/<0;1>/*))) Descriptor template: wsh(or_b(pk(@0/**),s:pk(@1/**))) Vector of keys: [ "[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMk= hgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", "[12345678/44'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmR= UapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" ] In all the following examples, I will replace the xpubs with aliases in the miniscript for brevity, and omit the corresponding vector of keys in the wallet policy. Of course, in comparing the "information density" (especially for UX purposes), it is important to take the full descriptor into account. It is always to be assumed that the keys are xpubs, complete with key origin information if internal (that is, controlled by the software or hardware signer that the wallet policy is being with). =3D=3D=3D Example 2: Either of two keys, but one is more likely =3D=3D=3D Miniscript policy: or(99@pk(key_likely),pk(key_unlikely)) Miniscript: wsh(or_d(pk(key_likely),pkh(key_unlikely))) Descriptor template: wsh(or_d(pk(@0/**),pkh(@1/**))) Vector of keys: =3D=3D=3D Example 3: A 3-of-3 that turns into a 2-of-3 after 90 days =3D=3D= =3D Miniscript policy: thresh(3,pk(key_0),pk(key_1),pk(key_2),older(12960)) Miniscript: wsh(thresh(3,pk(key_0),s:pk(key_1),s:pk(key_2),sln:older(12960))) Descriptor template: wsh(thresh(3,pk(@0/**),s:pk(@1/**),s:pk(@2/**),sln:older(12960))))) Vector of keys: =3D=3D=3D Example 4: The BOLT #3 received HTLC policy =3D=3D=3D Miniscript policy: andor(pk(key_remote),or_i(and_v(v:pkh(key_local),hash160(395e368b267d64945f= 30e4b71de1054f364c9473)),older(1008)),pk(key_revocation)) Miniscript: wsh(andor(pk(key_remote),or_i(and_v(v:pkh(key_local),hash160(395e368b267d6= 4945f30e4b71de1054f364c9473)),older(1008)),pk(key_revocation))) Descriptor template: wsh(andor(pk(@0/**),or_i(and_v(v:pkh(@1/**),hash160(395e368b267d64945f30e4b= 71de1054f364c9473)),older(1008)),pk(@2/**))) Vector of keys: =3D=3D=3D Example 5: Taproot complex script (2-of-2 with cold backup and timelocked inheritance) =3D=3D=3D The likely path is a 2-of-2 of a hot_key and a cosigner_key (2FA-like service). At any time, a cold_key can be used for signing, and after about a year, a separate timelocked_key becomes active (for example, to a notary for inheritance purposes). The timelock is reset every time UTXOs are spent. Miniscript policy: or(99@thresh(2,pk(hot_key),pk(cosigner_key)),1@or(99@pk (cold_key),1@and(pk(timelocked_key),older(52596)))) Miniscript: tr(cold_key,{and_v(v:pk(timelocked_key),older(52596)),multi_a(2,hot_key,co= signer_key)}) Descriptor template: tr(@0/**,{and_v(v:pk(@1/**),older(52596)),multi_a(2,@2/**,@3/**)}) Vector of keys: =3D=3D=3D Example 6: Taproot complex script with MuSig2 =3D=3D=3D The same policy as above, but we assume that the hot wallet and the cosigner are able to engage in the MuSig2 protocol. This greatly exemplifies the practical advantage of MuSig2 with taproot in terms of both transaction cost and privacy. Miniscript policy: or(99@musig2(hot_key,cosigner_key),1@or(99@pk (cold_key),1@and(pk(timelocked_key),older(52596)))) Miniscript: tr(musig2(hot_key,cosigner_key),{and_v(v:pk(timelocked_key),older(52596)),= pk(cold_key)}) Descriptor template: tr(musig2(@0,@1)/**,{and_v(v:pk(@2/**),older(52596)),pk(@3/**)}) Vector of keys: . Note: the order of keys differs from the previous example. Salvatore Ingala --00000000000039a07605df31288f Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi all,

TL;DR: It is eas= y to convert from wallet policy to descriptors and back; imho aliases are b= etter left out of descriptors in real world usage; some more examples given= .=C2=A0

I received some very useful feedback o= n the wallet policy proposal (in this list and outside); that also led me t= o realize that my initial post lacked some clarity and more practical examp= les.

This post wants to:- clarify that extracting descriptors from the wallet policy is trivial;<= /div>
- argue that figuring out the wallet policy (template= and list of keys information) from the descriptor is reasonably easy =E2= =88=92 automatable for sane descriptors currently in use, and much more gen= eral ones as well;
- give an idea of what the informa= tion shown on a hardware wallet screen would look like (emphasizing compact= ness);
- explain my point of view on "descriptors with alias= es".

This gist demoes conversions= from wallet policies to descriptors, and back:=C2=A0https://gist.githu= b.com/bigspider/10df51401be3aa6120217c03c2836ffa
=
Note that I would expect/hope software wallets to prefer wor= king directly with wallet policies =E2=88=92 but it might help to have auto= mated tools for the conversion, for interoperability with tools that do not= adopt wallet policies.

(All the following exampl= es use the `/**` notation as a shortcut for `/<0,1>/*`; this notation= might be dropped without consequences on the rest of the proposal.)
All the keys in the example I'm proposing are f= ollowed by /**. It is unclear to me if hardware wallets should allow=C2=A0<= i>registration=C2=A0of wallet policies with static keys (that is, witho= ut any range operator), as that would incentivize key reuse. The specs stil= l support it as there might be other use cases.

<= /div>
The policy for miniscript examples not using taproot was generate= d with the online compiler:=C2=A0https://bitcoin.sipa.be/miniscript. Many examples are also borrow= ed from there.
(To the best of my knowledge, there is no publicly= released compiler for miniscript on taproot, yet)
Note on aliases: it has been pointed out that many miniscript = implementations internally use aliases to refer to the keys. In my opinion,= aliases:
- should be external to the descriptor language, as the= y bear no significance for the actual script(s) that the descriptor can pro= duce
- fail to distinguish which part of the KEY expression is pa= rt of the "wallet description", and which part is not
<= br>
By clearly separating the key information in the vector (typi= cally, an xpub with key origin information) from the key placeholder expres= sion (which typically will have the `/**` or `/<0,1>/*` derivation st= ep), wallet policies semantically represent keys in a way that should be co= nvenient to both software wallets and hardware signers.

Associating recognizable names to the xpubs (and registering them on = the device) is a good idea for future developments and can greatly improve = the UX,=C2=A0both during wallet setup,=C2=A0or in recognizing outputs for r= epeated payments; it should be easy to build this feature on top of wallet = policies.

=3D=3D Examples =3D=3D
All the examples show:
- Miniscript policy: semanti= c spending rules, and optimization hints (can be compiled to miniscript aut= omatically)
- Miniscript: the actual miniscript descriptor, compi= les 1-to-1 to Bitcoin Script
- Wallet template: the "wallet = descriptor template"=C2=A0
- Vector of keys: the list of key= information (with key origin information)

=
Together, the wallet template and the vector of keys are the complet &= quot;wallet policy".

=3D=3D=3D Example 1: Ei= ther of two keys (equally likely) =3D=3D=3D

Miniscript policy: or(pk= (key_0),pk(key_1))
Miniscript: =C2=A0 =C2=A0 =C2=A0 =C2=A0wsh(or_b([d34d= b33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVH= QKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<0;1>= /*),s:pk([12345678/44'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2P= St5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGud= uB/<0;1>/*)))

Descriptor template: =C2=A0 wsh(or_b(pk(@0/**),s= :pk(@1/**)))
Vector of keys: [
=C2=A0 =C2=A0 "[d34db33f/44'/= 0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZ= RkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
=C2=A0 =C2=A0 = "[12345678/44'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5= bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB&= quot;
]

In all the following examples, I will replace the xpubs w= ith aliases in the miniscript for brevity, and omit the corresponding vecto= r of keys in the wallet policy.

Of course, in= comparing the "information density" (especially for UX purposes)= , it is important to take the full descriptor into account.
It is always to be assumed that the keys are xpubs, complete wit= h key origin information if internal (that is, controlled by the software o= r hardware signer that the wallet policy is being with).

=3D=3D=3D E= xample 2: Either of two keys, but one is more likely =3D=3D=3D

Minis= cript policy: or(99@pk(key_likely),pk(key_unlikely))
Miniscript:=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0wsh(or_d(pk(key_likely),pkh(key_unlikely)= ))

Descriptor template: wsh(or_d(pk(@0/**),pkh(@1/**)))
Vector of= keys:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0<omitted>

=3D=3D=3D Ex= ample 3: A 3-of-3 that turns into a 2-of-3 after 90 days =3D=3D=3D

M= iniscript policy: thresh(3,pk(key_0),pk(key_1),pk(key_2),older(12960))
M= iniscript:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0wsh(thresh(3,pk(key_0),s= :pk(key_1),s:pk(key_2),sln:older(12960)))

Descriptor template: wsh(t= hresh(3,pk(@0/**),s:pk(@1/**),s:pk(@2/**),sln:older(12960)))))
Vector of= keys:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0<omitted>

=3D=3D=3D Ex= ample 4: The BOLT #3 received HTLC policy =3D=3D=3D

Miniscript polic= y: andor(pk(key_remote),or_i(and_v(v:pkh(key_local),hash160(395e368b267d649= 45f30e4b71de1054f364c9473)),older(1008)),pk(key_revocation))
Miniscript:= =C2=A0 =C2=A0 =C2=A0 =C2=A0wsh(andor(pk(key_remote),or_i(and_v(v:pkh(key_l= ocal),hash160(395e368b267d64945f30e4b71de1054f364c9473)),older(1008)),pk(ke= y_revocation)))

Descriptor template: wsh(andor(pk(@0/**),or_i(and_v(= v:pkh(@1/**),hash160(395e368b267d64945f30e4b71de1054f364c9473)),older(1008)= ),pk(@2/**)))
Vector of keys:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 <omit= ted>

=3D=3D=3D Example 5: Taproot complex script (2-of-2 with col= d backup and timelocked inheritance) =3D=3D=3D

The likely path is a 2-of-2 of a hot_key and a cosigner_key (2FA-lik= e service). At any time, a cold_key can be used for signing, and after abou= t a year, a separate timelocked_key becomes active (for example, to a notar= y for inheritance purposes).
The timelock is reset every time UTX= Os are spent.

Miniscript policy: or(99@thre= sh(2,pk(hot_key),pk(cosigner_key)),1@or(99@pk(cold_key),1@and(pk(timelocked= _key),older(52596))))
Miniscript:=C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0tr(cold_key,{and_v(v:pk(timelocked_key),older(52596)),= multi_a(2,hot_key,cosigner_key)})

Descriptor template: tr(@0/**,{and_v(v:pk(@1/*= *),older(52596)),multi_a(2,@2/**,@3/**)})
Vecto= r of keys:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 <omitted>

=3D=3D=3D Example 6: Taproot complex= script with MuSig2 =3D=3D=3D

The = same policy as above, but we assume that the hot wallet and the cosigner ar= e able to engage in the MuSig2 protocol.
This greatly exemplifies= the practical advantage of MuSig2 with taproot in terms of both transactio= n cost and privacy.

Miniscript policy:= or(99@musig2(hot_key,cosigner_key),1@or(99@pk(cold_key),1@and(pk(timelocke= d_key),older(52596))))
Miniscript:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0tr(musig2(hot_key,cosigner_key),{and_v(v:pk(timelocked_key),older= (52596)),pk(cold_key)})

Descriptor templa= te: tr(musig2(@0,@1)/**,{and_v(v:pk(@2/**),older(52596)),pk(@3/**)})
<= div>
Vector of keys:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0<o= mitted>. Note: the order of keys differs from the previous example.
<= /div>



Salvatore Ingala
--00000000000039a07605df31288f--