Return-Path: Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id A403BC000D for ; Thu, 9 Sep 2021 15:54:42 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 8542A40143 for ; Thu, 9 Sep 2021 15:54:42 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org X-Spam-Flag: NO X-Spam-Score: -4.199 X-Spam-Level: X-Spam-Status: No, score=-4.199 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no 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 L8O565suhzc9 for ; Thu, 9 Sep 2021 15:54:41 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from outgoing.mit.edu (outgoing-auth-1.mit.edu [18.9.28.11]) by smtp2.osuosl.org (Postfix) with ESMTPS id A3E30400F3 for ; Thu, 9 Sep 2021 15:54:40 +0000 (UTC) Received: from mail-lf1-f43.google.com (mail-lf1-f43.google.com [209.85.167.43]) (authenticated bits=0) (User authenticated as jlrubin@ATHENA.MIT.EDU) by outgoing.mit.edu (8.14.7/8.12.4) with ESMTP id 189Fsbtw028096 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=NOT) for ; Thu, 9 Sep 2021 11:54:38 -0400 Received: by mail-lf1-f43.google.com with SMTP id t19so4594078lfe.13 for ; Thu, 09 Sep 2021 08:54:38 -0700 (PDT) X-Gm-Message-State: AOAM532Wj+fq3SqGK1zGx0SAM1qvRXx6nnM7YrxS0OH9OYjk3FOVAh4e /+Hi+TLf4TARgGfc03O0F0sml9GEEgdkjZPsKNc= X-Google-Smtp-Source: ABdhPJyyyGeb83Qmkxj1+NNaVPxXhoiirAcIKLmG/dj9ov+fGHoPkfSqB5zm5TxeOpmRgkhdnDpLO4oAg+sZ5wwy4mk= X-Received: by 2002:ac2:4645:: with SMTP id s5mr415611lfo.516.1631202877102; Thu, 09 Sep 2021 08:54:37 -0700 (PDT) MIME-Version: 1.0 References: <20210909064138.GA22496@erisian.com.au> <20210909065330.GB22496@erisian.com.au> In-Reply-To: <20210909065330.GB22496@erisian.com.au> From: Jeremy Date: Thu, 9 Sep 2021 08:54:25 -0700 X-Gmail-Original-Message-ID: Message-ID: To: Anthony Towns , Bitcoin Protocol Discussion Content-Type: multipart/alternative; boundary="0000000000000bdf1305cb920407" Subject: Re: [bitcoin-dev] TAPLEAF_UPDATE_VERIFY covenant opcode 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: Thu, 09 Sep 2021 15:54:42 -0000 --0000000000000bdf1305cb920407 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable I like this proposal, I think it has interesting use cases! I'm quick to charitably Matt's comment, "I=E2=80=99ve been saying we need more covenants research and proposals before we move forward with one", as before we move forward with *any.* I don't think that these efforts are rival -- different opcodes for different nodes as they say. I've previously done some analysis comparing Coin / Payment Pools with CTV to TapLeafUpdate which make CTV out favorably in terms of chain load and privacy. On the "anyone can withdraw themselves in O(1) transactions" front, is that if you contrast a CTV-style tree, the withdraws are O(log(n)) but E[O(1)] for all participants, e.g. summing over the entire tree as it splits to evict a bad actor ends up being O(N) total work over N participants, so you do have to look at the exact transactions that come out w.r.t. script size to determine which Payment Pool has overall less chain work to trustlessly withdraw. This is compounded by the fact that a Taproot for N participants uses a O(log N) witness. Let's do out that basic math. First, let's assume we have 30 participants. The basic script for each node would be: TLUV: Taproot(Tweaked Key, { DUP "" 1 TLUV CHECKSIGVERIFY IN_OUT_AMOUNT SUB GREATERTHANOREQUAL, ...}) Under this, the first withdraw for TLUV would require in witnesses stack: Assume average amount is 0.005BTC, so we have 4.2 B users =3D 18.9 bits =3D= 3 bytes 1 signature (1+64 bytes) + (1 Script =3D (+ 1 1 32 1 1 1 1 1 1 1 3 1 1) =3D= 46 bytes) + (1 taproot path =3D 2 + 33 + log2(N)*32) =3D 146+log2(N)*32. now, because we delete the key, we need to sum this from N=3D0 to N=3D30: >>> sum([65+46+35+math.log(N,2)*32 for N in range(1, 31)]) 7826.690154943152 bytes of witness data Each transaction should have 1 input (40 bytes), 2 outputs (2* (34+8) =3D 84), 4 bytes locktime, 4 bytes version, 2 byte witness flag, 1 byte in counter 1 byte out counter =3D 136 bytes (we already count witnesses above= ) 136 * 30 + 7827 =3D 11907 bytes to withdraw all trustlessly Now for CTV: -CTV: Taproot(MuSigKey(subparties), CTV) *sidebar: **why radix 4? A while ago, I did the math out and a radix of 4 or 5 was optimal for bare script... assuming this result holds with taproot.* balance holders: 0..30 you have a base set of transactions paying out: 0..4 4..8 8..12 12..16 16..20 20..24 24..27 27..30 interior nodes covering: 0..16 16..30 root node covering: 0..30 The witness for each of these looks like: (Taproot Script =3D 1+1+32+1) + (Taproot Control =3D 33) =3D 68 bytes A transaction with two outputs should have 1 input (40 bytes), 2 outputs (2* (34+8) =3D 84), 4 bytes locktime, 4 bytes version, 2 byte witness flag,= 1 byte in counter 1 byte out counter =3D 136 bytes + 68 bytes witness =3D 20= 4 A transaction with three outputs should have 1 input (40 bytes), 3 outputs (3* (34+8) =3D 126), 4 bytes locktime, 4 bytes version, 2 byte witness flag= , 1 byte in counter 1 byte out counter =3D 178 bytes + 68 bytes witness =3D = 246 A transaction with 4 outputs should have 1 input (40 bytes), 4 outputs (4* (34+8) =3D 126), 4 bytes locktime, 4 bytes version, 2 byte witness flag, 1 byte in counter 1 byte out counter =3D 220 bytes + 68 bytes witness =3D 28= 8 204 + 288*6 + 246*2 =3D 2424 bytes Therefore the CTV style pool is, in this example, about 5x more efficient in block space utilization as compared to TLUV at trustlessly withdrawing all participants. This extra space leaves lots of headroom to e.g. including things like OP_TRUE anchor outputs (12*10) =3D 120 bytes total fo= r CPFP; an optional script path with 2 inputs for a gas-paying input (cost is around 32 bytes for taproot?). The design also scales beyond 30 participants, where the advantage grows further (iirc, sum i =3D 0 to n log= i is relatively close to n log n). In the single withdrawal case, the cost to eject a single participant with CTV is 204+288 =3D 492 bytes, compared to 65+46+35+math.log(30,2)*32+136 = =3D 439 bytes. The cost to eject a second participant in CTV is much smaller as it amortizes -- worst case is 288, best case is 0 (already expanded), whereas in TLUV there is limited amortization so it would be about 438 bytes. The protocols are identical in the cooperative case. In terms of privacy, the CTV version is a little bit worse. At every splitting, radix of the root nodes total value gets broadcast. So to eject a participant, you end up leaking a bit more information. However, it might be a reasonable assumption that if one of your counterparties is uncooperative, they might dox you anyways. CTV trees are also superior during updates for privacy in the cooperative case. With the TLUV pool, you must know all tapleafs and the corresponding balances. Whereas in CTV trees, you only need to know the balances of the nodes above you. E.g., we can update the balances from: [[1 Alice, 2 Bob], [3 Carol, 4 Dave]] to: [[2.5 Alice, 0.5 Bob], [3 Carol, 4 Dave]] without informing Carol or Dave about the updates in our subtree, just that our slice of participants signed off on it. So even in the 1 party uncooperative case, 3/4 the pool has update privacy against them, and the subtrees that share a branch with the uncooperative also have this privacy recursively to an extent. CTV's TXID stability also lets you embed payment channels a bit easier, but perhaps TLUV would be in an ANYPREVOUT universe w.r.t. channel protocols so that might matter less across the designs. Nevertheless, I think it's exciting to think about these payment pools where every node is an updatable channel. --0000000000000bdf1305cb920407 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I like this proposal, = I think it has interesting use cases! I'm quick to charitably Matt'= s comment, "I=E2=80=99ve been saying we need more covenants research a= nd proposals before we move forward with one", as before we move forwa= rd with any.=C2=A0I don't think that these efforts are rival -- = different opcodes for different nodes as they say.

I've previously done some analysis comparing Coin / Payment Pools w= ith CTV to TapLeafUpdate which make CTV out favorably in terms of chain loa= d and privacy.

On the "anyone can withdraw themselves in O(1) trans= actions" front, is that if you contrast a CTV-style tree, the withdraw= s are O(log(n)) but E[O(1)] for all participants, e.g. summing over the ent= ire tree as it splits to evict a bad actor ends up being O(N) total work ov= er N participants, so you do have to look at the exact transactions that co= me out w.r.t. script size to determine which Payment Pool has overall less = chain work to trustlessly withdraw. This is compounded by the fact that a T= aproot for N participants uses a O(log N) witness.


Let's do o= ut that basic math. First, let's assume we have 30 participants. The ba= sic script for each node would be:

TLUV: Taproot(Tweaked Key, {<KEY> DUP "" 1 = TLUV
=C2=A0 =C2=A0 CHECKSIGVERIFY=C2=A0
=C2=A0 =C2=A0 IN_OUT_AMOUNT SUB <BAL&= gt; GREATERTHANOREQUAL, ...})=
Under this, the first withdraw fo= r TLUV would require in witnesses stack:
Assume av= erage amount is 0.005BTC, so we have 4.2 B users =3D 18.9 bits =3D3 bytes

1 signature (1+64 byt= es) + (1 Script =3D (+ 1 1 32 1 1 1 1=C2=A01=C2=A01=C2=A01 3 1= 1) =3D 46 bytes)=C2=A0+ (1 tap= root path =3D 2 + 33 + log2(N)*32)=C2=A0
= =3D 146+log2(N)*32.

= now, because we delete the key, we need to sum this from N=3D0 to N=3D30:

>>> sum([65+= 46+35+math.log(N,2)*32 for N in range(1, 31)])
7826.690154943152 bytes o= f witness data

E= ach transaction should have 1 input (40 bytes), 2 outputs (2* (34+8) =3D 84= ), 4 bytes locktime, 4 bytes version, 2 byte witness flag, 1 byte in counte= r 1 byte out counter =C2=A0=3D 136 bytes (we already count witnesses above)=


136 * 30=C2=A0+ 7827 =3D 11907 bytes to withdraw all trustlessly=

Now for CTV:
<= div class=3D"gmail_default" style=3D"font-family:arial,helvetica,sans-serif= ;color:rgb(0,0,0)">
-CTV: Taproot(MuSigKey(subp= arties), <H splits pool with radix 4> CTV)

sidebar: why radix 4? A while = ago, I did the math out and a radix of 4 or 5 was optimal for bare script..= . assuming this result holds with taproot.

balance holders: 0= ..30
you have a base set of transactions paying ou= t: 0..4 4..8 8..12 12..16 16..20 20..24 24..27 27..30
interior nodes covering: 0..16 16..30
root nod= e covering: 0..30

Th= e witness for each of these looks like:

=
(Taproot Script =3D 1+1+32+1)=C2=A0+ (Taproot Control = =3D 33) =3D 68 bytes

A transaction with two outputs should have 1 = input (40 bytes), 2 outputs (2* (34+8) =3D 84), 4 bytes locktime, 4 bytes v= ersion, 2 byte witness flag, 1 byte in counter 1 byte out counter =C2=A0=3D= 136 bytes=C2=A0+ 68 bytes witness =3D 204
A transaction with three outputs should have 1 input (40 bytes), 3 output= s (3* (34+8) =3D 126), 4 bytes locktime, 4 bytes version, 2 byte witness fl= ag, 1 byte in counter 1 byte out counter =C2=A0=3D 178 bytes=C2=A0+ 68 byte= s witness =3D 246
A transaction with 4 outputs should have 1 input= (40 bytes), 4 outputs (4* (34+8) =3D 126), 4 bytes locktime, 4 bytes versi= on, 2 byte witness flag, 1 byte in counter 1 byte out counter =C2=A0=3D 220= bytes +=C2=A068 bytes witness =3D 288

204=C2=A0+= 288*6=C2=A0+ 246*2 =3D=C2=A02424 bytes

=
Therefore the CTV style pool is, in this example, about= 5x more efficient in block space utilization as compared to TLUV at trustl= essly withdrawing all participants. This extra space leaves lots of headroo= m to e.g. including things like OP_TRUE anchor outputs (12*10) =3D 120 byte= s total for CPFP; an optional script path with 2 inputs for a gas-paying in= put (cost is around 32 bytes for taproot?). The design also scales beyond 3= 0 participants, where the advantage grows further (iirc, sum i =3D 0 to n l= og i is relatively close to n log n).=C2=A0

In the single withdrawal case, the cost to eject a = single participant with CTV is 204+288 =3D 492 bytes, compared to 65+46+35+= math.log(30,2)*32+136 =3D 439 bytes. The cost to eject a second participant= in CTV is much smaller as it amortizes -- worst case is 288, best case is = 0 (already expanded), whereas in TLUV there is limited amortization so it w= ould be about 438 bytes.

The protocols are identical in the cooperative case.

In terms of privacy, the CTV versio= n is a little bit worse. At every splitting, radix of the root nodes total = value gets broadcast. So to eject a participant, you end up leaking a bit m= ore information. However, it might be a reasonable assumption that if one o= f your counterparties is uncooperative, they might dox you anyways. CTV tre= es are also superior during updates for privacy in the cooperative case. Wi= th the TLUV pool, you must know all tapleafs and the corresponding balances= . Whereas in CTV trees, you only need to know the balances of the nodes abo= ve you. E.g., we can update the balances

from: [[1 Alice, 2 Bob], [3 Carol, 4 Dave]]
to: [[2.5 Alice, 0.5 Bob], [3 Carol, 4 Dave]]

without informing Carol or Dave ab= out the updates in our subtree, just that our slice of participants signed = off on it. So even in the 1 party uncooperative case, 3/4 the pool has upda= te privacy against them, and the subtrees that share a branch with the unco= operative also have this privacy recursively to an extent.

CTV's TXID sta= bility also lets you embed payment channels a bit easier, but perhaps TLUV = would be in an ANYPREVOUT universe w.r.t. channel protocols so that might m= atter less across the designs. Nevertheless, I think it's exciting to t= hink about these payment pools where every node is an updatable channel.


--0000000000000bdf1305cb920407--