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&#39;s something like:</div><div>=C2=A0</div>=
<div>$recovery =3D Same As Before<br></div><div><br></div><div>$withdrawal =
=3D &lt;deposit-delay&gt; OP_CSV OP_DROP &lt;pubkey&gt; OP_CHECKSIG OP_DUP =
&lt;V&gt; OP_LESSTHANOREQUAL OP_VERIFY OP_FORWARD_PARTIAL OP_FORWARD_TARGET=
 OP_FORWARD_SELF</div><br>$withdrawal is spent by:<br><br>&lt;self-idx&gt; =
&lt;target-idx&gt; &lt;spk&gt; &lt;0&lt;=3Dv&lt;=3DV&gt; &lt;signature&gt;<=
div><br></div><div>where &quot;V&quot;=C2=A0 is the max allowed withdrawal =
value, and &quot;deposit-delay&quot; 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&#39;s pretty trivial to add larde=
r fixed denominations to &quot;peel out&quot; 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&#39;ve seen in the past with LN infrastructure, software risks are o=
ften correlated, so it&#39;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 &lt;<a href=3D"mailto:aj@erisian.com.au">a=
j@erisian.com.au</a>&gt; 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&#39;Beirn=
e via bitcoin-dev wrote:<br>
&gt; What Greg is proposing above is to in essence TLUV-ify this proposal.<=
br>
<br>
FWIW, the way I&#39;m thinking about this is that the &quot;OP_VAULT&quot; =
concept is<br>
introducing two things:<br>
<br>
=C2=A0a) the concept of &quot;forwarding&quot; 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&#39;re moving value from this input<=
br>
into it. And then it&#39;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&#39;d do part (a) manually somehow, eg via &quot;OP_IN_OUT_AMOU=
NT&quot;<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 &quot;script&quot;,<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 with that script prefixed by &quot;n&quot; 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=
 &quot;amt&quot;,<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 &quot;amt&quot; has be=
en subtracted. if &quot;amt&quot; 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&#39;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>
&gt; ## Required opcodes<br>
&gt; - OP_VAULT: spent to trigger withdrawal<br>
&gt; - 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>
&gt; For each vault, vaulted coins are spent to an output with the taproot<=
br>
&gt; structure<br>
&gt; <br>
&gt;=C2=A0 =C2=A0taproot(internal_key, {$recovery_leaf, $trigger_leaf, ...}=
)<br>
&gt; <br>
&gt; where<br>
&gt; <br>
&gt;=C2=A0 =C2=A0$trigger_leaf =3D<br>
&gt;=C2=A0 =C2=A0 =C2=A0&lt;trigger&gt; &lt;auth&gt; &lt;script&gt; &lt;spe=
nd-delay&gt; 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 &quot;288 OP_CSV OP_DROP OP_CTV&quot; 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 &lt;ctvhash&gt; (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 &lt;sig&gt; (a signature of this transaction via the hot wallet &quo=
t;key&quot;)<br>
<br>
That is, the script becomes:<br>
<br>
=C2=A0 160000000 FORWARD_PARTIAL<br>
=C2=A0 0 FORWARD_SELF<br>
=C2=A0 1 &lt;ctvhash&gt; 1 &quot;288 CSV DROP CTV&quot; FORWARD_LEAF_UPDATE=
<br>
=C2=A0 sig key CHECKSIG<br>
<br>
Output 1 would then have a tapscript of &quot;&lt;ctvhash&gt; 288 OP_CSV OP=
_DROP<br>
OP_CTV&quot;, 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 &lt;sig&=
gt;,<br>
so nothing here is malleable. The script here is about 45 bytes (compared<b=
r>
to 34 for a simple &quot;key CHECKSIG&quot;) and the witness data is about =
105 bytes<br>
(compared to 65 bytes for just a signature), which seems pretty nice.<br>
<br>
&gt;=C2=A0 =C2=A0... =3D<br>
&gt;=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>
&gt; Happens via script-path spend to $expr_withdraw, i.e. a timelocked<br>
&gt; OP_CTV.<br>
<br>
Note that if you calculated the OP_CTV incorrectly (eg, you don&#39;t set a=
<br>
correct nSequence timelock, so that any tx that passes OP_CTV won&#39;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 &quot;FALSE &lt;h&gt; 2 TLUV&quot;, where &quot;&lt;h&gt;&quot; 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 (&quot;FALSE&quot; rather t=
han<br>
&quot;&lt;x&gt;&quot;) means this can&#39;t be used to build a coinpool wit=
h unilateral<br>
exit -- you can&#39;t remove your key from the key path, which screws over<=
br>
everyone who&#39;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&#39;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--