summaryrefslogtreecommitdiff
path: root/7d/36d87d0891aa76626c1af2182a0ca4f22332f3
blob: f4d76c6f5bfdec811cc98d2e0bf196018ac0c5b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
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--