summaryrefslogtreecommitdiff
path: root/6a/e3ca7f3e5a172585fe734a4535e40a5535b8a9
blob: de237ba6a23729b32e88621c5eaf957646dbc8b4 (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
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
Return-Path: <antoine.riard@gmail.com>
Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136])
 by lists.linuxfoundation.org (Postfix) with ESMTP id 8F81DC000D
 for <bitcoin-dev@lists.linuxfoundation.org>;
 Sun, 12 Sep 2021 23:38:12 +0000 (UTC)
Received: from localhost (localhost [127.0.0.1])
 by smtp3.osuosl.org (Postfix) with ESMTP id 6B75660779
 for <bitcoin-dev@lists.linuxfoundation.org>;
 Sun, 12 Sep 2021 23:38:12 +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: smtp3.osuosl.org (amavisd-new);
 dkim=pass (2048-bit key) header.d=gmail.com
Received: from smtp3.osuosl.org ([127.0.0.1])
 by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)
 with ESMTP id eZB1wYmPPTTs
 for <bitcoin-dev@lists.linuxfoundation.org>;
 Sun, 12 Sep 2021 23:38:10 +0000 (UTC)
X-Greylist: whitelisted by SQLgrey-1.8.0
Received: from mail-wr1-x429.google.com (mail-wr1-x429.google.com
 [IPv6:2a00:1450:4864:20::429])
 by smtp3.osuosl.org (Postfix) with ESMTPS id 10284606B6
 for <bitcoin-dev@lists.linuxfoundation.org>;
 Sun, 12 Sep 2021 23:38:09 +0000 (UTC)
Received: by mail-wr1-x429.google.com with SMTP id q11so11767073wrr.9
 for <bitcoin-dev@lists.linuxfoundation.org>;
 Sun, 12 Sep 2021 16:38:09 -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
 :cc; bh=rZWCrZw4etAheTiQwl/+zVVkBjYbMvvaaz6gGOpDt6I=;
 b=OHV2PZx1EFBd9mfr5QUSaGkPNjWmrjoLyRu34Yw77N8mzE+KC2aDhPImTKabVd1F3A
 MAOQ5zNU6KBMQSA7Ax/EtcRlHc/Hk/w2tPrDm754KjlnnqSzjGlEegV7tZNCGhxCQ87C
 x5A4eZ8pVnQTJ+few+prc6KZIpJztXtZxOiNlqarp1+MqWKnw1yNEPRMWqFpzH5iTXkL
 65gwo1CXp9LplcKT3EvmqvRm61uz5AHttG1NZiUMP5OmO+xY2dGZPkpwg+yII5k/aKJZ
 51ZKorLcwkyGnWdl3vskEOXa+JJhZgPsC3vmsHe9NRxqGOkK+MX4+EmrOrbIY5gH2ZJr
 2j+w==
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:cc;
 bh=rZWCrZw4etAheTiQwl/+zVVkBjYbMvvaaz6gGOpDt6I=;
 b=kAHBWC4HXixNmIKrDF3PfY1wj4gAJIj0YQ8suI+rItsKARjTnPz1zpc1jLJ6ZH5YQI
 3YwP9nobIHa7m3A5VFJQnpCaHNX7RXgQdFV0LQ4X+TFCaSUggoqqhUVS8e/m1u5Fh1LC
 1khHTuoAA6hMlM37kLToNxUSXUFh6vgRzQxxK8RXkIP12g+/pE+kLzKapGYKxZhMGuxV
 hxhva9e2wu4+aOALzF98FvweFm2RAk3PgtAhtgMf5F168M7hQjyR3FG8p3sh4tq93d/N
 ctAnMBJ7ZlWUI8qi/f5HA5mDCfqzJJTAl3/ZKf8jozHFwCQy4SIcsf67nV/4cgxbfDV6
 BCAA==
X-Gm-Message-State: AOAM530E44l8Dfa9/2vOOO9B/EWtnMdJjGlWpa3SaU6je2mufUrRwYvT
 zMU5KnyqzqrgHm/Jf6SrWKhG9JtQ6HnTXTPBg1ucIzHFB4o=
X-Google-Smtp-Source: ABdhPJxRuY3hpNhh5alEAz26xTXz3tc9brIkcqmInt6IdrefUbVzASLQA+QIQLHNwG4xMSF28JoETdz4Afo2TOZCBhQ=
X-Received: by 2002:adf:9f0d:: with SMTP id l13mr9561467wrf.328.1631489888231; 
 Sun, 12 Sep 2021 16:38:08 -0700 (PDT)
MIME-Version: 1.0
References: <20210909064138.GA22496@erisian.com.au>
 <CALZpt+FnnbGJC4=KO_OPiKxt0Ey9Bzh1gxP1dQSDz2aBi9WyOA@mail.gmail.com>
 <20210911032644.GB23578@erisian.com.au>
In-Reply-To: <20210911032644.GB23578@erisian.com.au>
From: Antoine Riard <antoine.riard@gmail.com>
Date: Sun, 12 Sep 2021 19:37:56 -0400
Message-ID: <CALZpt+HzM__OJntegOhDqkg5zU=PQXtKgQoB518A2qP9=foovw@mail.gmail.com>
To: Anthony Towns <aj@erisian.com.au>
Content-Type: multipart/alternative; boundary="0000000000003e23ba05cbd4d7be"
X-Mailman-Approved-At: Sun, 12 Sep 2021 23:50:00 +0000
Cc: Bitcoin Protocol Discussion <bitcoin-dev@lists.linuxfoundation.org>
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 <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: Sun, 12 Sep 2021 23:38:12 -0000

--0000000000003e23ba05cbd4d7be
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Sorry for the lack of clarity, sometimes it sounds easier to explain ideas
with code.

While MERKLESUB is still WIP, here the semantic. If the input spent is a
SegWit v1 Taproot output, and the script path spending is used, the top
stack item is interpreted as an output position of the spending
transaction. The second top stack item is interpreted as a 32-byte x-only
pubkey to be negated and added to the spent internal pubkey.

The spent tapscript is removed from the merkle tree of tapscripts and a new
merkle root is recomputed with the first node element of the spending
control block as the tapleaf hash. From then, this new merkle root is added
as the taproot tweak to the updated internal pubkey, while correcting for
parity. This new tweaked pubkey is interpreted as a v1 witness program and
must match the scriptPubKey of the spending transaction output as the
passed position. Otherwise, MERKLESUB returns a failure.

I believe this is matching your description and the main difference
compared to your TLUV proposal is the lack of merkle tree extension, where
a new merkle path is added in place of the removed tapscript. Motivation is
saving up the one byte of the new merkle path step, which is not necessary
for our CoinPool use-case.

> That would mean anyone who could do a valid spend of the tx could
> violate the covenant by spending to an unencumbered witness v2 output
> and (by collaborating with a miner) steal the funds. I don't think
> there's a reasonable way to have existing covenants be forward
> compatible with future destination addresses (beyond something like CTV
> that strictly hardcodes them).

That's a good catch, thanks for raising it :)

Depends how you define reasonable, but I think one straightforward fix is
to extend the signature digest algorithm to encompass the segwit version
(and maybe program-size ?) of the spending transaction outputs.

Then you add a "contract" aggregated-key in every tapscript where a
TLUV/MERKLESUB covenant is present. The off-chain contract participant can
exchange signatures at initial setup committing to the segwit version. I
think this addresses the sent-to-unknown-witness-output point ?

When future destination addresses are deployed, assuming a new round of
interactivity, the participants can send the fund to a v1+ by exchanging
signatures with SIGHASH_ALL, that way authorizing the bypass of
TLUV/MERKLESUB.

Of course, in case of v1+ deployment, the key path could be used. Though
this path could have been "burnt" by picking up an internal point with an
unknown scalar following the off-chain contract/use-case semantic ?

> Having the output position parameter might be an interesting way to
> merge/split a vault/pool, but it's not clear to me how much sense it
> makes sense to optimise for that, rather than just doing that via the key
> path. For pools, you want the key path to be common anyway (for privacy
> and efficiency), so it shouldn't be a problem; but even for vaults,
> you want the cold wallet accessible enough to be useful for the case
> where theft is attempted, and maybe that's also accessible enough for
> the ocassional merge/split to keep your utxo count/sizes reasonable.

I think you can come up with interesting contract policies. Let's say you
want to authorize the emergency path of your pool/vault balances if X
happens (e.g a massive drop in USDT price signed by DLC oracles). You have
(A+B+C+D) forking into (A+B) and (C+D) pooled funds. To conserve the
contracts pre-negotiated economic equilibrium, all the participants would
like the emergency path to be inherited on both forks. Without relying on
the key path interactivity, which is ultimately a trust on the post-fork
cooperation of your counterparty ?

> Saving a byte of witness data at the cost of specifying additional
> opcodes seems like optimising the wrong thing to me.

I think we should keep in mind that any overhead cost in the usage of a
script primitive is echoed to the user of off-chain contract/payment
channels. If the tapscripts are bigger, your average on-chain spends in
case of non-cooperative scenarios are increased in consequence, and as such
your fee-bumping reserve. Thus making those systems less economically
accessible.

If we really envision having billions of Bitcoin users owning a utxo or
shards of them, we should also think that those users might have limited
means to pay on-chain fees. Where should be the line between resource
optimizations and protocol/implementation complexity ? Hard to tell.

> I don't think that works, because different scripts in the same merkle
> tree can have different script versions, which would here indicate
> different parities for the same internal pub key.

Let me make it clearer. We introduce a new tapscript version 0x20, forcing
a new bit in the first byte of the control block to be interpreted as the
parity bit of the spent internal pubkey. To ensure this parity bit is
faithful and won't break the updated key path, it's committed in the spent
taptweak. A malicious counterparty while having malleability on the control
block, by setting the parity bit to the wrong value will break the taptweak
and fail the taproot commitment verification ?

I think the correct commitment of different script versions in the merkle
tree can be verified by tree participants at setup ?

> The IN_OUT_AMOUNT opcode lets you do maths on the values, so you can
> specify "hot wallets can withdraw up to X" rather than "hot wallets
> must withdraw exactly X". I don't think there's a way of doing that with
> SIGHASH_GROUP, even with a modifier like ANYPUBKEY?

You can exchange signatures for withdraw outputs with multiples `nValue`
covering the authorized range, assuming the ANYAMOUNT modifier ? One
advantage of leveraging sighash is the ability to update a withdraw policy
in real-time. Vaults participants might be willing to bump the withdraw
policy beyond X, assuming you have N-of-M consents.

> If you want to tweak all the scripts, I think you should be using the
> key path.
>
> One way you could do somthing like that without changing the scripts
> though, is have the timelock on most of the scripts be something like
> "[3 months] CSV", and have a "delay" script that doesn't require a CSV,
> does require a signature from someone able to authorise the delay,
> and requires the output to have the same scriptPubKey and amount. Then
> you can use that path to delay resolution by 3 months however often,
> even if you can't coordinate a key path spend

I think I would like to express the following contract policy. Let's say
you have 1) a one-time conditional script path to withdraw fund ("a put on
strike price X"), 2) a conditional script path to tweak by 3 months all the
usual withdraw path and 3) those remaining withdraw paths. Once played out,
you would like the one-time path to be removed from your merkle tree. And
this removal to be inherited on the tweaked tree if 2) plays out.

I agree that's advanced Bitcoin contracting and we might not require from
one script primitive to cover the whole expressivity we're aiming to.

> that's a constant product market maker without a profit margin. There's
> lots of research in the ethereum world about doing these things, and
> bitmatrix is trying to do it on liquid. It's not clear to me if there's
> anywhere in bitcoin per se that it would make sense.

Good with the more detailed explanation. Yeah I know it's widely deployed
on the ethereum-side, still late on catching up with literature/resources
on that. Assuming we have a widely-deployed token protocol on the
bitcoin-side, you could couple it with a DLC-style of security model and
that might be enough to bootstrap a fruitful token trading ecosystem ?
Though I agree, expressing an AMM in bitcoin primitives is an interesting
design challenge!

> So maybe it would make more sense to introduce an opcode
> that builds a merkle root from tagged hashes directly, rather than one
> that lets you compare to 32B strings so that you can do the TapBranch
> logic manually.

IIUC, you would like an opcode to edit the spent merkle root or build a new
one from stack elements ? E.g adding new withdraw tapleaf if the input
amount is over X. I think that the design description gives more
flexibility but I'm worried you will need more than one opcode. Like
OP_TWEAKADD, to add the tweak on the updated internal key and
OP_SCRIPTPUBKEY_VERIFY (or at least OP_CSFS though more expensive) ?

Le ven. 10 sept. 2021 =C3=A0 23:26, Anthony Towns <aj@erisian.com.au> a =C3=
=A9crit :

> On Fri, Sep 10, 2021 at 12:12:24AM -0400, Antoine Riard wrote:
> > "Talk is cheap. Show me the code" :p
> >     case OP_MERKLESUB:
>
> I'm not entirely clear on what your opcode there is trying to do. I
> think it's taking
>
>    <N> <P> MERKLESUB
>
> and checking that output N has the same scripts as the current input
> except with the current script removed, and with its internal pubkey as
> the current input's internal pubkey plus P.
>
> >         txTo->vout[out_pos].scriptPubKey.IsWitnessProgram(witnessversio=
n,
> > witnessprogram);
> >         //! The committed to output must be a witness v1 program at lea=
st
>
> That would mean anyone who could do a valid spend of the tx could
> violate the covenant by spending to an unencumbered witness v2 output
> and (by collaborating with a miner) steal the funds. I don't think
> there's a reasonable way to have existing covenants be forward
> compatible with future destination addresses (beyond something like CTV
> that strictly hardcodes them).
>
> > One could also imagine a list of output positions to force the taproot
> update
> > on multiple outputs ("OP_MULTIMERKLESUB").
>
> Having the output position parameter might be an interesting way to
> merge/split a vault/pool, but it's not clear to me how much sense it
> makes sense to optimise for that, rather than just doing that via the key
> path. For pools, you want the key path to be common anyway (for privacy
> and efficiency), so it shouldn't be a problem; but even for vaults,
> you want the cold wallet accessible enough to be useful for the case
> where theft is attempted, and maybe that's also accessible enough for
> the ocassional merge/split to keep your utxo count/sizes reasonable.
>
> > For the merkle branches extension, I was thinking of introducing a
> separate
> > OP_MERKLEADD, maybe to *add* a point to the internal pubkey group
> signer. If
> > you're only interested in leaf pruning, using OP_MERKLESUB only should
> save you
> > one byte of empty vector ?
>
> Saving a byte of witness data at the cost of specifying additional
> opcodes seems like optimising the wrong thing to me.
>
> > One solution I was thinking about was introducing a new tapscript versi=
on
> > (`TAPROOT_INTERNAL_TAPSCRIPT`) signaling that VerifyTaprootCommitment
> must
> > compute the TapTweak with a new TapTweak=3D(internal_pubkey || merkle_r=
oot
> ||
> > parity_bit). A malicious participant wouldn't be able to interfere with
> the
> > updated internal key as it would break its own spending taproot
> commitment
> > verification ?
>
> I don't think that works, because different scripts in the same merkle
> tree can have different script versions, which would here indicate
> different parities for the same internal pub key.
>
> > > That's useless without some way of verifying that the new utxo retain=
s
> > > the bitcoin that was in the old utxo, so also include a new opcode
> > > IN_OUT_AMOUNT that pushes two items onto the stack: the amount from
> this
> > > input's utxo, and the amount in the corresponding output, and then
> expect
> > > anyone using TLUV to use maths operators to verify that funds are bei=
ng
> > > appropriately retained in the updated scriptPubKey.
> > Credit to you for the SIGHASH_GROUP design, here the code, with
> > SIGHASH_ANYPUBKEY/ANYAMOUNT extensions.
> >
> > I think it's achieving the same effect as IN_OUT_AMOUNT, at least for
> CoinPool
> > use-case.
>
> The IN_OUT_AMOUNT opcode lets you do maths on the values, so you can
> specify "hot wallets can withdraw up to X" rather than "hot wallets
> must withdraw exactly X". I don't think there's a way of doing that with
> SIGHASH_GROUP, even with a modifier like ANYPUBKEY?
>
> > (I think I could come with some use-case from lex mercatoria where if
> you play
> > out a hardship provision you want to tweak all the other provisions by =
a
> CSV
> > delay while conserving the rest of their policy)
>
> If you want to tweak all the scripts, I think you should be using the
> key path.
>
> One way you could do somthing like that without changing the scripts
> though, is have the timelock on most of the scripts be something like
> "[3 months] CSV", and have a "delay" script that doesn't require a CSV,
> does require a signature from someone able to authorise the delay,
> and requires the output to have the same scriptPubKey and amount. Then
> you can use that path to delay resolution by 3 months however often,
> even if you can't coordinate a key path spend.
>
> > > And second, it doesn't provide a way for utxos to "interact", which i=
s
> > > something that is interesting for automated market makers [5], but
> perhaps
> > > only interesting for chains aiming to support multiple asset types,
> > > and not bitcoin directly. On the other hand, perhaps combining it wit=
h
> > > CTV might be enough to solve that, particularly if the hash passed to
> > > CTV is constructed via script/CAT/etc.
> > That's where SIGHASH_GROUP might be more interesting as you could
> generate
> > transaction "puzzles".
> > IIUC, the problem is how to have a set of ratios between x/f(x).
>
> Normal way to do it is specify a formula, eg
>
>    outBTC * outUSDT >=3D inBTC * inUSDT
>
> that's a constant product market maker without a profit margin. There's
> lots of research in the ethereum world about doing these things, and
> bitmatrix is trying to do it on liquid. It's not clear to me if there's
> anywhere in bitcoin per se that it would make sense.
>
> Then your relative balances of each token imply a price, and traders will
> rebalance anytime that price is out of whack with the rest of the market.
>
> You can tweak the formula so that you make a profit, which also ends up
> meaning the fund pool becomes more liquid overtime. But that means that
> you want to cope with 100 BTC and 5M USDT at $50k, but also 200 BTC and
> 10M USDT at $50k, and many values in between. So I don't think:
>
> > The maker generates a Taproot tree where each leaf is committing to a
> different
> > "strike price".
>
> really works that well.
>
> One irritating thing I realised while reading Jeremy's mail is that
>
>   CAT "TapBranch" SHA256 DUP CAT SWAP CAT SHA256
>
> doesn't actually work -- the first CAT needs to sort the two branches
> first, and "LESSTHAN" etc want to compare values numerically rather
> than lexically. So maybe it would make more sense to introduce an opcode
> that builds a merkle root from tagged hashes directly, rather than one
> that lets you compare to 32B strings so that you can do the TapBranch
> logic manually.
>
> Cheers,
> aj
>
>

--0000000000003e23ba05cbd4d7be
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">Sorry for the lack of clarity, sometimes it sounds easier =
to explain ideas with code.<br><br>While MERKLESUB is still WIP, here the s=
emantic. If the input spent is a SegWit v1 Taproot output, and the script p=
ath spending is used, the top stack item is interpreted as an output positi=
on of the spending transaction. The second top stack item is interpreted as=
 a 32-byte x-only pubkey to be negated and added to the spent internal pubk=
ey.<br><br>The spent tapscript is removed from the merkle tree of tapscript=
s and a new merkle root is recomputed with the first node element of the sp=
ending control block as the tapleaf hash. From then, this new merkle root i=
s added as the taproot tweak to the updated internal pubkey, while correcti=
ng for parity. This new tweaked pubkey is interpreted as a v1 witness progr=
am and must match the scriptPubKey of the spending transaction output as th=
e passed position. Otherwise, MERKLESUB returns a failure.<br><br>I believe=
 this is matching your description and the main difference compared to your=
 TLUV proposal is the lack of merkle tree extension, where a new merkle pat=
h is added in place of the removed tapscript. Motivation is saving up the o=
ne byte of the new merkle path step, which is not necessary for our CoinPoo=
l use-case.<br><br>&gt; That would mean anyone who could do a valid spend o=
f the tx could<br>&gt; violate the covenant by spending to an unencumbered =
witness v2 output<br>&gt; and (by collaborating with a miner) steal the fun=
ds. I don&#39;t think<br>&gt; there&#39;s a reasonable way to have existing=
 covenants be forward<br>&gt; compatible with future destination addresses =
(beyond something like CTV<br>&gt; that strictly hardcodes them).<br><br>Th=
at&#39;s a good catch, thanks for raising it :)<br><br>Depends how you defi=
ne reasonable, but I think one straightforward fix is to extend the signatu=
re digest algorithm to encompass the segwit version (and maybe program-size=
 ?) of the spending transaction outputs.<br><br>Then you add a &quot;contra=
ct&quot; aggregated-key in every tapscript where a TLUV/MERKLESUB covenant =
is present. The off-chain contract participant can exchange signatures at i=
nitial setup committing to the segwit version. I think this addresses the s=
ent-to-unknown-witness-output point ?<br><br>When future destination addres=
ses are deployed, assuming a new round of interactivity, the participants c=
an send the fund to a v1+ by exchanging signatures with SIGHASH_ALL, that w=
ay authorizing the bypass of TLUV/MERKLESUB.<br><br>Of course, in case of v=
1+ deployment, the key path could be used. Though this path could have been=
 &quot;burnt&quot; by picking up an internal point with an unknown scalar f=
ollowing the off-chain contract/use-case semantic ?<br><br>&gt; Having the =
output position parameter might be an interesting way to<br>&gt; merge/spli=
t a vault/pool, but it&#39;s not clear to me how much sense it<br>&gt; make=
s sense to optimise for that, rather than just doing that via the key<br>&g=
t; path. For pools, you want the key path to be common anyway (for privacy<=
br>&gt; and efficiency), so it shouldn&#39;t be a problem; but even for vau=
lts,<br>&gt; you want the cold wallet accessible enough to be useful for th=
e case<br>&gt; where theft is attempted, and maybe that&#39;s also accessib=
le enough for<br>&gt; the ocassional merge/split to keep your utxo count/si=
zes reasonable.<br><br>I think you can come up with interesting contract po=
licies. Let&#39;s say you want to authorize the emergency path of your pool=
/vault balances if X happens (e.g a massive drop in USDT price signed by DL=
C oracles). You have (A+B+C+D) forking into (A+B) and (C+D) pooled funds. T=
o conserve the contracts pre-negotiated economic equilibrium, all the parti=
cipants would like the emergency path to be inherited on both forks. Withou=
t relying on the key path interactivity, which is ultimately a trust on the=
 post-fork cooperation of your counterparty ?<br><br>&gt; Saving a byte of =
witness data at the cost of specifying additional<br>&gt; opcodes seems lik=
e optimising the wrong thing to me.<br><br>I think we should keep in mind t=
hat any overhead cost in the usage of a script primitive is echoed to the u=
ser of off-chain contract/payment channels. If the tapscripts are bigger, y=
our average on-chain spends in case of non-cooperative scenarios are increa=
sed in consequence, and as such your fee-bumping reserve. Thus making those=
 systems less economically accessible.<br><br>If we really envision having =
billions of Bitcoin users owning a utxo or shards of them, we should also t=
hink that those users might have limited means to pay on-chain fees. Where =
should be the line between resource optimizations and protocol/implementati=
on complexity ? Hard to tell.<br><br>&gt; I don&#39;t think that works, bec=
ause different scripts in the same merkle<br>&gt; tree can have different s=
cript versions, which would here indicate<br>&gt; different parities for th=
e same internal pub key.<br><br>Let me make it clearer. We introduce a new =
tapscript version 0x20, forcing a new bit in the first byte of the control =
block to be interpreted as the parity bit of the spent internal pubkey. To =
ensure this parity bit is faithful and won&#39;t break the updated key path=
, it&#39;s committed in the spent taptweak. A malicious counterparty while =
having malleability on the control block, by setting the parity bit to the =
wrong value will break the taptweak and fail the taproot commitment verific=
ation ?<br><br>I think the correct commitment of different script versions =
in the merkle tree can be verified by tree participants at setup ?<br><br>&=
gt; The IN_OUT_AMOUNT opcode lets you do maths on the values, so you can<br=
>&gt; specify &quot;hot wallets can withdraw up to X&quot; rather than &quo=
t;hot wallets<br>&gt; must withdraw exactly X&quot;. I don&#39;t think ther=
e&#39;s a way of doing that with<br>&gt; SIGHASH_GROUP, even with a modifie=
r like ANYPUBKEY?<br><br>You can exchange signatures for withdraw outputs w=
ith multiples `nValue` covering the authorized range, assuming the ANYAMOUN=
T modifier ? One advantage of leveraging sighash is the ability to update a=
 withdraw policy in real-time. Vaults participants might be willing to bump=
 the withdraw policy beyond X, assuming you have N-of-M consents.<br><br>&g=
t; If you want to tweak all the scripts, I think you should be using the<br=
>&gt; key path.<br>&gt; <br>&gt; One way you could do somthing like that wi=
thout changing the scripts<br>&gt; though, is have the timelock on most of =
the scripts be something like<br>&gt; &quot;[3 months] CSV&quot;, and have =
a &quot;delay&quot; script that doesn&#39;t require a CSV,<br>&gt; does req=
uire a signature from someone able to authorise the delay,<br>&gt; and requ=
ires the output to have the same scriptPubKey and amount. Then<br>&gt; you =
can use that path to delay resolution by 3 months however often,<br>&gt; ev=
en if you can&#39;t coordinate a key path spend<br><br>I think I would like=
 to express the following contract policy. Let&#39;s say you have 1) a one-=
time conditional script path to withdraw fund (&quot;a put on strike price =
X&quot;), 2) a conditional script path to tweak by 3 months all the usual w=
ithdraw path and 3) those remaining withdraw paths. Once played out, you wo=
uld like the one-time path to be removed from your merkle tree. And this re=
moval to be inherited on the tweaked tree if 2) plays out.<br><br>I agree t=
hat&#39;s advanced Bitcoin contracting and we might not require from one sc=
ript primitive to cover the whole expressivity we&#39;re aiming to.<br><br>=
&gt; that&#39;s a constant product market maker without a profit margin. Th=
ere&#39;s<br>&gt; lots of research in the ethereum world about doing these =
things, and<br>&gt; bitmatrix is trying to do it on liquid. It&#39;s not cl=
ear to me if there&#39;s<br>&gt; anywhere in bitcoin per se that it would m=
ake sense.<br><br>Good with the more detailed explanation. Yeah I know it&#=
39;s widely deployed on the ethereum-side, still late on catching up with l=
iterature/resources on that. Assuming we have a widely-deployed token proto=
col on the bitcoin-side, you could couple it with a DLC-style of security m=
odel and that might be enough to bootstrap a fruitful token trading ecosyst=
em ? Though I agree, expressing an AMM in bitcoin primitives is an interest=
ing design challenge!<br><br>&gt; So maybe it would make more sense to intr=
oduce an opcode<br>&gt; that builds a merkle root from tagged hashes direct=
ly, rather than one<br>&gt; that lets you compare to 32B strings so that yo=
u can do the TapBranch<br>&gt; logic manually.<br><br>IIUC, you would like =
an opcode to edit the spent merkle root or build a new one from stack eleme=
nts ? E.g adding new withdraw tapleaf if the input amount is over X. I thin=
k that the design description gives more flexibility but I&#39;m worried yo=
u will need more than one opcode. Like OP_TWEAKADD, to add the tweak on the=
 updated internal key and OP_SCRIPTPUBKEY_VERIFY (or at least OP_CSFS thoug=
h more expensive) ?<br></div><br><div class=3D"gmail_quote"><div dir=3D"ltr=
" class=3D"gmail_attr">Le=C2=A0ven. 10 sept. 2021 =C3=A0=C2=A023:26, Anthon=
y Towns &lt;<a href=3D"mailto:aj@erisian.com.au">aj@erisian.com.au</a>&gt; =
a =C3=A9crit=C2=A0:<br></div><blockquote class=3D"gmail_quote" style=3D"mar=
gin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1=
ex">On Fri, Sep 10, 2021 at 12:12:24AM -0400, Antoine Riard wrote:<br>
&gt; &quot;Talk is cheap. Show me the code&quot; :p<br>
&gt; =C2=A0 =C2=A0 case OP_MERKLESUB:<br>
<br>
I&#39;m not entirely clear on what your opcode there is trying to do. I<br>
think it&#39;s taking<br>
<br>
=C2=A0 =C2=A0&lt;N&gt; &lt;P&gt; MERKLESUB<br>
<br>
and checking that output N has the same scripts as the current input<br>
except with the current script removed, and with its internal pubkey as<br>
the current input&#39;s internal pubkey plus P.<br>
<br>
&gt; =C2=A0 =C2=A0 =C2=A0 =C2=A0 txTo-&gt;vout[out_pos].scriptPubKey.IsWitn=
essProgram(witnessversion,<br>
&gt; witnessprogram);<br>
&gt; =C2=A0 =C2=A0 =C2=A0 =C2=A0 //! The committed to output must be a witn=
ess v1 program at least<br>
<br>
That would mean anyone who could do a valid spend of the tx could<br>
violate the covenant by spending to an unencumbered witness v2 output<br>
and (by collaborating with a miner) steal the funds. I don&#39;t think<br>
there&#39;s a reasonable way to have existing covenants be forward<br>
compatible with future destination addresses (beyond something like CTV<br>
that strictly hardcodes them).<br>
<br>
&gt; One could also imagine a list of output positions to force the taproot=
 update<br>
&gt; on multiple outputs (&quot;OP_MULTIMERKLESUB&quot;).<br>
<br>
Having the output position parameter might be an interesting way to<br>
merge/split a vault/pool, but it&#39;s not clear to me how much sense it<br=
>
makes sense to optimise for that, rather than just doing that via the key<b=
r>
path. For pools, you want the key path to be common anyway (for privacy<br>
and efficiency), so it shouldn&#39;t be a problem; but even for vaults,<br>
you want the cold wallet accessible enough to be useful for the case<br>
where theft is attempted, and maybe that&#39;s also accessible enough for<b=
r>
the ocassional merge/split to keep your utxo count/sizes reasonable.<br>
<br>
&gt; For the merkle branches extension, I was thinking of introducing a sep=
arate<br>
&gt; OP_MERKLEADD, maybe to *add* a point to the internal pubkey group sign=
er. If<br>
&gt; you&#39;re only interested in leaf pruning, using OP_MERKLESUB only sh=
ould save you<br>
&gt; one byte of empty vector ?<br>
<br>
Saving a byte of witness data at the cost of specifying additional<br>
opcodes seems like optimising the wrong thing to me.<br>
<br>
&gt; One solution I was thinking about was introducing a new tapscript vers=
ion<br>
&gt; (`TAPROOT_INTERNAL_TAPSCRIPT`) signaling that VerifyTaprootCommitment =
must<br>
&gt; compute the TapTweak with a new TapTweak=3D(internal_pubkey || merkle_=
root ||<br>
&gt; parity_bit). A malicious participant wouldn&#39;t be able to interfere=
 with the<br>
&gt; updated internal key as it would break its own spending taproot commit=
ment<br>
&gt; verification ?<br>
<br>
I don&#39;t think that works, because different scripts in the same merkle<=
br>
tree can have different script versions, which would here indicate<br>
different parities for the same internal pub key.<br>
<br>
&gt; &gt; That&#39;s useless without some way of verifying that the new utx=
o retains<br>
&gt; &gt; the bitcoin that was in the old utxo, so also include a new opcod=
e<br>
&gt; &gt; IN_OUT_AMOUNT that pushes two items onto the stack: the amount fr=
om this<br>
&gt; &gt; input&#39;s utxo, and the amount in the corresponding output, and=
 then expect<br>
&gt; &gt; anyone using TLUV to use maths operators to verify that funds are=
 being<br>
&gt; &gt; appropriately retained in the updated scriptPubKey.<br>
&gt; Credit to you for the SIGHASH_GROUP design, here the code, with<br>
&gt; SIGHASH_ANYPUBKEY/ANYAMOUNT extensions.<br>
&gt; <br>
&gt; I think it&#39;s achieving the same effect as IN_OUT_AMOUNT, at least =
for CoinPool<br>
&gt; use-case.<br>
<br>
The IN_OUT_AMOUNT opcode lets you do maths on the values, so you can<br>
specify &quot;hot wallets can withdraw up to X&quot; rather than &quot;hot =
wallets<br>
must withdraw exactly X&quot;. I don&#39;t think there&#39;s a way of doing=
 that with<br>
SIGHASH_GROUP, even with a modifier like ANYPUBKEY?<br>
<br>
&gt; (I think I could come with some use-case from lex mercatoria where if =
you play<br>
&gt; out a hardship provision you want to tweak all the other provisions by=
 a CSV<br>
&gt; delay while conserving the rest of their policy)<br>
<br>
If you want to tweak all the scripts, I think you should be using the<br>
key path.<br>
<br>
One way you could do somthing like that without changing the scripts<br>
though, is have the timelock on most of the scripts be something like<br>
&quot;[3 months] CSV&quot;, and have a &quot;delay&quot; script that doesn&=
#39;t require a CSV,<br>
does require a signature from someone able to authorise the delay,<br>
and requires the output to have the same scriptPubKey and amount. Then<br>
you can use that path to delay resolution by 3 months however often,<br>
even if you can&#39;t coordinate a key path spend.<br>
<br>
&gt; &gt; And second, it doesn&#39;t provide a way for utxos to &quot;inter=
act&quot;, which is<br>
&gt; &gt; something that is interesting for automated market makers [5], bu=
t perhaps<br>
&gt; &gt; only interesting for chains aiming to support multiple asset type=
s,<br>
&gt; &gt; and not bitcoin directly. On the other hand, perhaps combining it=
 with<br>
&gt; &gt; CTV might be enough to solve that, particularly if the hash passe=
d to<br>
&gt; &gt; CTV is constructed via script/CAT/etc.<br>
&gt; That&#39;s where SIGHASH_GROUP might be more interesting as you could =
generate<br>
&gt; transaction &quot;puzzles&quot;.<br>
&gt; IIUC, the problem is how to have a set of ratios between x/f(x).<br>
<br>
Normal way to do it is specify a formula, eg<br>
<br>
=C2=A0 =C2=A0outBTC * outUSDT &gt;=3D inBTC * inUSDT<br>
<br>
that&#39;s a constant product market maker without a profit margin. There&#=
39;s<br>
lots of research in the ethereum world about doing these things, and<br>
bitmatrix is trying to do it on liquid. It&#39;s not clear to me if there&#=
39;s<br>
anywhere in bitcoin per se that it would make sense.<br>
<br>
Then your relative balances of each token imply a price, and traders will<b=
r>
rebalance anytime that price is out of whack with the rest of the market.<b=
r>
<br>
You can tweak the formula so that you make a profit, which also ends up<br>
meaning the fund pool becomes more liquid overtime. But that means that<br>
you want to cope with 100 BTC and 5M USDT at $50k, but also 200 BTC and<br>
10M USDT at $50k, and many values in between. So I don&#39;t think:<br>
<br>
&gt; The maker generates a Taproot tree where each leaf is committing to a =
different<br>
&gt; &quot;strike price&quot;.<br>
<br>
really works that well.<br>
<br>
One irritating thing I realised while reading Jeremy&#39;s mail is that<br>
<br>
=C2=A0 CAT &quot;TapBranch&quot; SHA256 DUP CAT SWAP CAT SHA256<br>
<br>
doesn&#39;t actually work -- the first CAT needs to sort the two branches<b=
r>
first, and &quot;LESSTHAN&quot; etc want to compare values numerically rath=
er<br>
than lexically. So maybe it would make more sense to introduce an opcode<br=
>
that builds a merkle root from tagged hashes directly, rather than one<br>
that lets you compare to 32B strings so that you can do the TapBranch<br>
logic manually.<br>
<br>
Cheers,<br>
aj<br>
<br>
</blockquote></div>

--0000000000003e23ba05cbd4d7be--