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
|
Return-Path: <aj@erisian.com.au>
Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137])
by lists.linuxfoundation.org (Postfix) with ESMTP id 813E2C0032
for <bitcoin-dev@lists.linuxfoundation.org>;
Tue, 7 Mar 2023 12:45:46 +0000 (UTC)
Received: from localhost (localhost [127.0.0.1])
by smtp4.osuosl.org (Postfix) with ESMTP id 50034416FF
for <bitcoin-dev@lists.linuxfoundation.org>;
Tue, 7 Mar 2023 12:45:46 +0000 (UTC)
DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 50034416FF
X-Virus-Scanned: amavisd-new at osuosl.org
X-Spam-Flag: NO
X-Spam-Score: -1.902
X-Spam-Level:
X-Spam-Status: No, score=-1.902 tagged_above=-999 required=5
tests=[BAYES_00=-1.9, SPF_HELO_PASS=-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 aP7rePEEUTeB
for <bitcoin-dev@lists.linuxfoundation.org>;
Tue, 7 Mar 2023 12:45:44 +0000 (UTC)
X-Greylist: from auto-whitelisted by SQLgrey-1.8.0
DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org A4F384086A
Received: from cerulean.erisian.com.au (azure.erisian.com.au [172.104.61.193])
by smtp4.osuosl.org (Postfix) with ESMTPS id A4F384086A
for <bitcoin-dev@lists.linuxfoundation.org>;
Tue, 7 Mar 2023 12:45:44 +0000 (UTC)
Received: from [124.181.34.14] (helo=sapphire.erisian.com.au)
by cerulean.erisian.com.au with esmtpsa (TLS1.3) tls
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2)
(envelope-from <aj@erisian.com.au>)
id 1pZWhG-0005TK-Ri; Tue, 07 Mar 2023 22:45:41 +1000
Received: by sapphire.erisian.com.au (sSMTP sendmail emulation);
Tue, 07 Mar 2023 22:45:35 +1000
Date: Tue, 7 Mar 2023 22:45:34 +1000
From: Anthony Towns <aj@erisian.com.au>
To: James O'Beirne <james.obeirne@gmail.com>,
Bitcoin Protocol Discussion <bitcoin-dev@lists.linuxfoundation.org>
Message-ID: <ZAcx7oEZxC9BiWvg@erisian.com.au>
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>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
In-Reply-To: <CAPfvXf+4iX0h-nSuyTai_VvJHkDvAyKtgSk6DsaEwE8N3wnYEg@mail.gmail.com>
X-Spam_score: -1.0
X-Spam_bar: -
Cc: Greg Sanders <gsanders87@gmail.com>
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: Tue, 07 Mar 2023 12:45:46 -0000
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-equal
to the accumulated value.
> ## Required opcodes
> - OP_VAULT: spent to trigger withdrawal
> - OP_VAULT_RECOVER: spent to recover
Naming here is OP_VAULT ~= OP_FORWARD_LEAF_UPDATE; OP_VAULT_RECOVER ~=
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 =
> <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 bytes
(compared to 65 bytes for just a signature), which seems pretty nice.
> ... =
> 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 recovery
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 introducing
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
|