Return-Path: <gsanders87@gmail.com> Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 20103C0032 for <bitcoin-dev@lists.linuxfoundation.org>; Thu, 9 Mar 2023 18:45:31 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id EE70A41A51 for <bitcoin-dev@lists.linuxfoundation.org>; Thu, 9 Mar 2023 18:45:30 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org EE70A41A51 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=Cgf/Y9aX X-Virus-Scanned: amavisd-new at osuosl.org X-Spam-Flag: NO X-Spam-Score: -1.848 X-Spam-Level: X-Spam-Status: No, score=-1.848 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_ENVFROM_END_DIGIT=0.25, 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 Kp8uM3LrRnfZ for <bitcoin-dev@lists.linuxfoundation.org>; Thu, 9 Mar 2023 18:45:29 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org D68BE41A50 Received: from mail-ed1-x534.google.com (mail-ed1-x534.google.com [IPv6:2a00:1450:4864:20::534]) by smtp4.osuosl.org (Postfix) with ESMTPS id D68BE41A50 for <bitcoin-dev@lists.linuxfoundation.org>; Thu, 9 Mar 2023 18:45:28 +0000 (UTC) Received: by mail-ed1-x534.google.com with SMTP id ec29so10912848edb.6 for <bitcoin-dev@lists.linuxfoundation.org>; Thu, 09 Mar 2023 10:45:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1678387527; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=yUVCdWY0bMxwtVKnOvlkvaDDdCLGSjbLn2s1c0VbEuY=; b=Cgf/Y9aXZl1XjzA2N5CXf3GSIV4sNACTZ2KJ2bWZRKv8ymABh16j54l908jBAuKHDg vH17gC2LvTSsNxKqxBZ2C7Xb/gGrC+Ogc353aJABvOEgf5Z2OrSgmW2xf7dJvQXtlY7w Z1JAwZmAd0BPZXxSTVr4+MT0Snw72ZFcFLTUHSeKtsLVAgL6idG5OjTbQtLqIenqmG1B 1JzncetxPb13BCbB8q1tu5UmL1wghigYbq84rvrxJP659q7LMl1Jor6sUnf8n8xH2G8+ 1th26urdG1Tvn9gxbEumPFEInY34gs6N7VRUnRRt7D9ZSLkUAoMSnxt57mjm4H30Pv4C FDpQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678387527; h=cc: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=yUVCdWY0bMxwtVKnOvlkvaDDdCLGSjbLn2s1c0VbEuY=; b=w/Kb02syiWZkb50ghma9IZwDdDkXx1tNH1X4FPG/rYX1FPQZd4VF2qmm84zg+FJZYV Pwsv5pefxEjEdN1a1pca7cYHoJwkXRb9QW8JuN1CE4529iFELdbOa14xyMzpIjE3Glpt mf8B+sGISDRhjeIZtQKf3evSfWEkEx/9ceDyevJEHYCrwDx6wyfyQzoex30j31cWPk5B b7CEXXPPb9CszsCt8j6F3cxz6+ws9HoSJCX4EEC+6P/lL4QEUFqGOqIlZw7MtaCc/5LC TelH8hhLlomCTaNKVZ1VoPqhoeQvjlpZZG0U2FEsLQ3UHRdGmn2X3s+3SsmiVvFk1jjg zDQQ== X-Gm-Message-State: AO0yUKUo/LoutLaW7zeupUaOsP0NNQrpERxfeXcSNtU2AkOWzkH2vZ4M 3uVXgZSwc8oyObvtzPe3qGZpoy9L3EnoXLfex98= X-Google-Smtp-Source: AK7set/c8IOfPJJnenbdJawEi+ib3tcWBWCoiyXZzZ5aVA3tLGvHUNAk1zh2zKLG08Z7jgu1YBziEytO3SE2kjmDrb0= X-Received: by 2002:a17:906:cc8c:b0:88d:ba79:4317 with SMTP id oq12-20020a170906cc8c00b0088dba794317mr15449986ejb.7.1678387526776; Thu, 09 Mar 2023 10:45:26 -0800 (PST) MIME-Version: 1.0 References: <CAPfvXfJQKb7i8GBvTEvTTz-3dU_5mH8jOv8Nm4Q8gPt=KxrqLQ@mail.gmail.com> <CAB3F3DveCDz6yy-rd3ttV8+4sMufsvB+9J-qVK95yh9aLYX+Mw@mail.gmail.com> <ZAAqIZZO1KK32Th9@erisian.com.au> <CAB3F3DtGpVHkyT_=KLS42rvdP=dgnMvChhR1Rs0BHO5yOEabmw@mail.gmail.com> <CAPfvXf+4iX0h-nSuyTai_VvJHkDvAyKtgSk6DsaEwE8N3wnYEg@mail.gmail.com> <ZAcx7oEZxC9BiWvg@erisian.com.au> In-Reply-To: <ZAcx7oEZxC9BiWvg@erisian.com.au> From: Greg Sanders <gsanders87@gmail.com> Date: Thu, 9 Mar 2023 13:45:15 -0500 Message-ID: <CAB3F3Dt_0JB1W-JEEFs5j3HHNGfmXd9uU6civ7Ue8go=+z79Eg@mail.gmail.com> To: Anthony Towns <aj@erisian.com.au> Content-Type: multipart/alternative; boundary="000000000000543c3e05f67c0cdf" Cc: Bitcoin Protocol Discussion <bitcoin-dev@lists.linuxfoundation.org> Subject: Re: [bitcoin-dev] BIP for OP_VAULT X-BeenThere: bitcoin-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Bitcoin Protocol Discussion <bitcoin-dev.lists.linuxfoundation.org> List-Unsubscribe: <https://lists.linuxfoundation.org/mailman/options/bitcoin-dev>, <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=unsubscribe> List-Archive: <http://lists.linuxfoundation.org/pipermail/bitcoin-dev/> List-Post: <mailto:bitcoin-dev@lists.linuxfoundation.org> List-Help: <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=help> List-Subscribe: <https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev>, <mailto:bitcoin-dev-request@lists.linuxfoundation.org?subject=subscribe> X-List-Received-Date: Thu, 09 Mar 2023 18:45:31 -0000 --000000000000543c3e05f67c0cdf Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable AJ, Interesting stuff! Just a couple thoughts on these proposed opcodes, at least one we discussed elsewhere: 1) OP_FORWARD_SELF is a JET of OP_FLU in the revaulting common case. Maybe obvious but I missed this initially and thought it was useful to be pointed out. 2) Using these extended primitives, you can do rate-limiting of the two step unvaulting, or just a single step vault by committing to the partial values. For the single stage case it's something like: $recovery =3D Same As Before $withdrawal =3D <deposit-delay> OP_CSV OP_DROP <pubkey> OP_CHECKSIG OP_DUP <V> OP_LESSTHANOREQUAL OP_VERIFY OP_FORWARD_PARTIAL OP_FORWARD_TARGET OP_FORWARD_SELF $withdrawal is spent by: <self-idx> <target-idx> <spk> <0<=3Dv<=3DV> <signature> where "V" is the max allowed withdrawal value, and "deposit-delay" the required gap in withdrawals Due to the OP_LEQ, you are bound to ~21 BTC in value for this operation, but for larger vaults it's pretty trivial to add larder fixed denominations to "peel out" value until you get to your final ~21 BTC. This rate-limiting(in either the two-stage or one-stage scheme) can limit the risk of theft during a watchtower outage to a constant value per utxo per time period of watchtower failure. As we've seen in the past with LN infrastructure, software risks are often correlated, so it's a good idea to build in belt and suspenders where we can or at least have them available when possible. Cheers, Greg On Tue, Mar 7, 2023 at 7:45=E2=80=AFAM Anthony Towns <aj@erisian.com.au> wr= ote: > On Mon, Mar 06, 2023 at 10:25:38AM -0500, James O'Beirne via bitcoin-dev > wrote: > > What Greg is proposing above is to in essence TLUV-ify this proposal. > > FWIW, the way I'm thinking about this is that the "OP_VAULT" concept is > introducing two things: > > a) the concept of "forwarding" the input amount to specified > outputs in a way that elegantly allows merging/splitting > > b) various restrictions on the form of the output scripts > > These concepts go together well, because restricting an output script is > only an interesting thing to do if you're moving value from this input > into it. And then it's just a matter of figuring out a nice way to pick > opcodes that combine those two concepts in interesting ways. > > This is different from TLUV, in that TLUV only did part (b), and > assumed you'd do part (a) manually somehow, eg via "OP_IN_OUT_AMOUNT" > and arithmetic opcodes. The advantage of this new approach over that > one is that it makes it really easy to get the logic right (I often > forgot to include the IN_OUT_AMOUNT checks at all, for instance), and > also makes spending multiple inputs to a single output really simple, > something that would otherwise require kind-of gnarly logic. > > I think there are perhaps four opcodes that are interesting in this class= : > > idx sPK OP_FORWARD_TARGET > -- sends the value to a particular output (given by idx), and > requires that output have a particular scriptPubKey (given > by sPK). > > idx [...] n script OP_FORWARD_LEAF_UPDATE > -- sends the value to a particular output (given by idx), and > requires that output to have almost the same scriptPubKey as this > input, _except_ that the current leaf is replaced by "script", > with that script prefixed by "n" pushes (of values given by [...]= ) > > idx OP_FORWARD_SELF > -- sends the value to a particular output (given by idx), and > requires that output to have the same scriptPubKey as this input > > amt OP_FORWARD_PARTIAL > -- modifies the next OP_FORWARD_* opcode to only affect "amt", > rather than the entire balance. opcodes after that affect the > remaining balance, after "amt" has been subtracted. if "amt" is > 0, the next OP_FORWARD_* becomes a no-op. > > Then each time you see OP_FORWARD_TARGET or OP_FORWARD_LEAF_UPDATE, you > accumulate the value that's expected to be forwarded to the output by > each input, and verify that the amount for that output is greater-or-equa= l > to the accumulated value. > > > ## Required opcodes > > - OP_VAULT: spent to trigger withdrawal > > - OP_VAULT_RECOVER: spent to recover > > Naming here is OP_VAULT ~=3D OP_FORWARD_LEAF_UPDATE; OP_VAULT_RECOVER ~= =3D > OP_FORWARD_TARGET. > > > For each vault, vaulted coins are spent to an output with the taproot > > structure > > > > taproot(internal_key, {$recovery_leaf, $trigger_leaf, ...}) > > > > where > > > > $trigger_leaf =3D > > <trigger> <auth> <script> <spend-delay> OP_VAULT > > With the opcodes above, the trigger_leaf (for spending some of the > vaulted funds via your hot wallet) script might look like: > > OP_FORWARD_PARTIAL OP_FORWARD_SELF > 1 "288 OP_CSV OP_DROP OP_CTV" OP_FORWARD_LEAF_UPDATE > key CHECKSIG > > So if you have 2.0 BTC in a vault utxo, you might spend 0.4 BTC by > supplying the witness data: > > 160000000 (1.6BTC in sats) > 0 (output 0 puts 1.6BTC goes back into the vault) > <ctvhash> (the outputs where you want the remaining 0.4 BTC to end up) > 1 (the output idx that will be spend via the CTV path once the CSV > delay is done) > <sig> (a signature of this transaction via the hot wallet "key") > > That is, the script becomes: > > 160000000 FORWARD_PARTIAL > 0 FORWARD_SELF > 1 <ctvhash> 1 "288 CSV DROP CTV" FORWARD_LEAF_UPDATE > sig key CHECKSIG > > Output 1 would then have a tapscript of "<ctvhash> 288 OP_CSV OP_DROP > OP_CTV", satisfied with an empty witness stack (along with the recovery > path, etc). > > Output 0 is just 1.6BTC back in your vault, and immediately available > for use. > > Other inputs/outputs (for fees etc) would still be committed to by <sig>, > so nothing here is malleable. The script here is about 45 bytes (compared > to 34 for a simple "key CHECKSIG") and the witness data is about 105 byte= s > (compared to 65 bytes for just a signature), which seems pretty nice. > > > ... =3D > > other (optional) leaves in the taptree > > This would allow you to have multiple hot wallets (if any of them are > compromised you can still use the recovery path to avoid loss of funds; > but if some hot wallet becomes temporarily *inaccessible* you can still > easily spend the funds via one of the alternative hot wallets), or, > if you have multiple watchtowers validating your spends and recovering > funds to your cold wallet on a violation, you could have multiple recover= y > paths to provide some auditability for who triggered the recovery. > > > Happens via script-path spend to $expr_withdraw, i.e. a timelocked > > OP_CTV. > > Note that if you calculated the OP_CTV incorrectly (eg, you don't set a > correct nSequence timelock, so that any tx that passes OP_CTV won't pass > the OP_CSV check, and vice-versa) then this spend path becomes invalid, > and the funds can only be reclaimed via some other path (key path spend, > recovery tapscript, potentially an alternative hotwallet script path). > > OP_FORWARD_LEAF_UPDATE is equivalent to a very specific form of TLUV, > namely "FALSE <h> 2 TLUV", where "<h>" is calculated by building the > script, prefixing the pushes, then doing the Hash_TapLeaf calculation. > > Not being able to tweak the internal public key ("FALSE" rather than > "<x>") means this can't be used to build a coinpool with unilateral > exit -- you can't remove your key from the key path, which screws over > everyone who's still in the coinpool. > > On the other hand, not tweaking the internal public key avoids introducin= g > all the x-only pubkey complications, and keeps it relatively simple, > which is nice, and keeping things simple and targeted now means there's > still plenty of OP_SUCCESS opcodes available later for something more > general, if that turns out to be desirable. > > Cheers, > aj > --000000000000543c3e05f67c0cdf Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr"><div>AJ,</div><div><br></div><div>Interesting stuff! Just = a couple thoughts on these proposed opcodes, at least one we discussed else= where:</div><div><br></div><div>1) OP_FORWARD_SELF is a JET of OP_FLU in th= e revaulting common case. Maybe obvious but I missed this initially and tho= ught it was useful to be pointed out.<br></div><div><br></div><div>2)=C2=A0= Using these extended primitives, you can do rate-limiting of the two step = unvaulting, or just a single step vault by committing to the partial values= . For the single stage case it's something like:</div><div>=C2=A0</div>= <div>$recovery =3D Same As Before<br></div><div><br></div><div>$withdrawal = =3D <deposit-delay> OP_CSV OP_DROP <pubkey> OP_CHECKSIG OP_DUP = <V> OP_LESSTHANOREQUAL OP_VERIFY OP_FORWARD_PARTIAL OP_FORWARD_TARGET= OP_FORWARD_SELF</div><br>$withdrawal is spent by:<br><br><self-idx> = <target-idx> <spk> <0<=3Dv<=3DV> <signature><= div><br></div><div>where "V"=C2=A0 is the max allowed withdrawal = value, and "deposit-delay" the required gap in withdrawals</div><= div><br></div><div>Due to the OP_LEQ, you are bound to ~21 BTC in value for= this operation, but for larger vaults it's pretty trivial to add larde= r fixed denominations to "peel out" value until you get to your f= inal ~21 BTC.</div><div><br></div><div>This rate-limiting(in either the two= -stage or one-stage scheme) can limit the risk of theft during a watchtower= outage to a constant value per utxo per time period of watchtower failure.= As we've seen in the past with LN infrastructure, software risks are o= ften correlated, so it's a good idea to build in belt and suspenders wh= ere we can or at least have them available when possible.</div><div><br></d= iv><div>Cheers,</div><div>Greg<br><div><br></div></div></div><br><div class= =3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Tue, Mar 7, 2023 = at 7:45=E2=80=AFAM Anthony Towns <<a href=3D"mailto:aj@erisian.com.au">a= j@erisian.com.au</a>> wrote:<br></div><blockquote class=3D"gmail_quote" = style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);pa= dding-left:1ex">On Mon, Mar 06, 2023 at 10:25:38AM -0500, James O'Beirn= e via bitcoin-dev wrote:<br> > What Greg is proposing above is to in essence TLUV-ify this proposal.<= br> <br> FWIW, the way I'm thinking about this is that the "OP_VAULT" = concept is<br> introducing two things:<br> <br> =C2=A0a) the concept of "forwarding" the input amount to specifie= d<br> =C2=A0 =C2=A0 outputs in a way that elegantly allows merging/splitting<br> <br> =C2=A0b) various restrictions on the form of the output scripts<br> <br> These concepts go together well, because restricting an output script is<br= > only an interesting thing to do if you're moving value from this input<= br> into it. And then it's just a matter of figuring out a nice way to pick= <br> opcodes that combine those two concepts in interesting ways.<br> <br> This is different from TLUV, in that TLUV only did part (b), and<br> assumed you'd do part (a) manually somehow, eg via "OP_IN_OUT_AMOU= NT"<br> and arithmetic opcodes. The advantage of this new approach over that<br> one is that it makes it really easy to get the logic right (I often<br> forgot to include the IN_OUT_AMOUNT checks at all, for instance), and<br> also makes spending multiple inputs to a single output really simple,<br> something that would otherwise require kind-of gnarly logic.<br> <br> I think there are perhaps four opcodes that are interesting in this class:<= br> <br> =C2=A0 =C2=A0idx sPK OP_FORWARD_TARGET<br> =C2=A0 =C2=A0 =C2=A0-- sends the value to a particular output (given by idx= ), and<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 requires that output have a particular scriptPu= bKey (given<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 by sPK).<br> <br> =C2=A0 =C2=A0idx [...] n script OP_FORWARD_LEAF_UPDATE<br> =C2=A0 =C2=A0 =C2=A0-- sends the value to a particular output (given by idx= ), and<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 requires that output to have almost the same sc= riptPubKey as this<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 input, _except_ that the current leaf is replac= ed by "script",<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 with that script prefixed by "n" push= es (of values given by [...])<br> <br> =C2=A0 =C2=A0idx OP_FORWARD_SELF<br> =C2=A0 =C2=A0 =C2=A0-- sends the value to a particular output (given by idx= ), and<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 requires that output to have the same scriptPub= Key as this input<br> <br> =C2=A0 =C2=A0amt OP_FORWARD_PARTIAL<br> =C2=A0 =C2=A0 =C2=A0-- modifies the next OP_FORWARD_* opcode to only affect= "amt",<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 rather than the entire balance. opcodes after t= hat affect the<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 remaining balance, after "amt" has be= en subtracted. if "amt" is<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 0, the next OP_FORWARD_* becomes a no-op.<br> <br> Then each time you see OP_FORWARD_TARGET or OP_FORWARD_LEAF_UPDATE, you<br> accumulate the value that's expected to be forwarded to the output by<b= r> each input, and verify that the amount for that output is greater-or-equal<= br> to the accumulated value.<br> <br> > ## Required opcodes<br> > - OP_VAULT: spent to trigger withdrawal<br> > - OP_VAULT_RECOVER: spent to recover<br> <br> Naming here is OP_VAULT ~=3D OP_FORWARD_LEAF_UPDATE; OP_VAULT_RECOVER ~=3D<= br> OP_FORWARD_TARGET.<br> <br> > For each vault, vaulted coins are spent to an output with the taproot<= br> > structure<br> > <br> >=C2=A0 =C2=A0taproot(internal_key, {$recovery_leaf, $trigger_leaf, ...}= )<br> > <br> > where<br> > <br> >=C2=A0 =C2=A0$trigger_leaf =3D<br> >=C2=A0 =C2=A0 =C2=A0<trigger> <auth> <script> <spe= nd-delay> OP_VAULT<br> <br> With the opcodes above, the trigger_leaf (for spending some of the<br> vaulted funds via your hot wallet) script might look like:<br> <br> =C2=A0 =C2=A0OP_FORWARD_PARTIAL OP_FORWARD_SELF<br> =C2=A0 =C2=A01 "288 OP_CSV OP_DROP OP_CTV" OP_FORWARD_LEAF_UPDATE= <br> =C2=A0 =C2=A0key CHECKSIG<br> <br> So if you have 2.0 BTC in a vault utxo, you might spend 0.4 BTC by<br> supplying the witness data:<br> <br> =C2=A0 160000000=C2=A0 (1.6BTC in sats)<br> =C2=A0 0 (output 0 puts 1.6BTC goes back into the vault)<br> =C2=A0 <ctvhash> (the outputs where you want the remaining 0.4 BTC to= end up)<br> =C2=A0 1 (the output idx that will be spend via the CTV path once the CSV<b= r> =C2=A0 =C2=A0 =C2=A0delay is done)<br> =C2=A0 <sig> (a signature of this transaction via the hot wallet &quo= t;key")<br> <br> That is, the script becomes:<br> <br> =C2=A0 160000000 FORWARD_PARTIAL<br> =C2=A0 0 FORWARD_SELF<br> =C2=A0 1 <ctvhash> 1 "288 CSV DROP CTV" FORWARD_LEAF_UPDATE= <br> =C2=A0 sig key CHECKSIG<br> <br> Output 1 would then have a tapscript of "<ctvhash> 288 OP_CSV OP= _DROP<br> OP_CTV", satisfied with an empty witness stack (along with the recover= y<br> path, etc).<br> <br> Output 0 is just 1.6BTC back in your vault, and immediately available <br> for use.<br> <br> Other inputs/outputs (for fees etc) would still be committed to by <sig&= gt;,<br> so nothing here is malleable. The script here is about 45 bytes (compared<b= r> to 34 for a simple "key CHECKSIG") and the witness data is about = 105 bytes<br> (compared to 65 bytes for just a signature), which seems pretty nice.<br> <br> >=C2=A0 =C2=A0... =3D<br> >=C2=A0 =C2=A0 =C2=A0other (optional) leaves in the taptree<br> <br> This would allow you to have multiple hot wallets (if any of them are<br> compromised you can still use the recovery path to avoid loss of funds;<br> but if some hot wallet becomes temporarily *inaccessible* you can still<br> easily spend the funds via one of the alternative hot wallets), or,<br> if you have multiple watchtowers validating your spends and recovering<br> funds to your cold wallet on a violation, you could have multiple recovery<= br> paths to provide some auditability for who triggered the recovery.<br> <br> > Happens via script-path spend to $expr_withdraw, i.e. a timelocked<br> > OP_CTV.<br> <br> Note that if you calculated the OP_CTV incorrectly (eg, you don't set a= <br> correct nSequence timelock, so that any tx that passes OP_CTV won't pas= s<br> the OP_CSV check, and vice-versa) then this spend path becomes invalid,<br> and the funds can only be reclaimed via some other path (key path spend,<br= > recovery tapscript, potentially an alternative hotwallet script path).<br> <br> OP_FORWARD_LEAF_UPDATE is equivalent to a very specific form of TLUV,<br> namely "FALSE <h> 2 TLUV", where "<h>" is c= alculated by building the<br> script, prefixing the pushes, then doing the Hash_TapLeaf calculation.<br> <br> Not being able to tweak the internal public key ("FALSE" rather t= han<br> "<x>") means this can't be used to build a coinpool wit= h unilateral<br> exit -- you can't remove your key from the key path, which screws over<= br> everyone who's still in the coinpool.<br> <br> On the other hand, not tweaking the internal public key avoids introducing<= br> all the x-only pubkey complications, and keeps it relatively simple,<br> which is nice, and keeping things simple and targeted now means there's= <br> still plenty of OP_SUCCESS opcodes available later for something more<br> general, if that turns out to be desirable.<br> <br> Cheers,<br> aj<br> </blockquote></div> --000000000000543c3e05f67c0cdf--