Skip to content

Commit 58cbc4e

Browse files
committed
vaults: various feedback
Thanks to Vojtěch Strnad for most of this.
1 parent c589490 commit 58cbc4e

File tree

1 file changed

+20
-18
lines changed

1 file changed

+20
-18
lines changed

bip-vaults.mediawiki

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ key winds up being ''too'' highly secure.
6868

6969
Institutional custodians of Bitcoin would likely use vaults in similar fashion.
7070

71-
===== Avoiding the $5 wrench attack =====
71+
===== Provable timelocks =====
7272

73-
This proposal uniquely provides a solution to the
73+
This proposal provides a solution to the
7474
[https://web.archive.org/web/20230210123933/https://xkcd.com/538/ "$5 wrench attack."] By
7575
setting the spend delay to, say, a week, and using as the recovery path a
7676
script that enforces a longer relative timelock, the owner of the vault can
@@ -122,8 +122,8 @@ Having a "general" covenant mechanism that can encode arbitrary transactional
122122
state machines would allow us to solve these issues, but at the cost of complex
123123
and large scripts that would probably be duplicated many times over in the
124124
blockchain. The particular design and deployment timeline of such a general
125-
framework is also uncertain. There are no sample vault implementations using
126-
these means known to the author.
125+
framework is also uncertain. This approach was demonstrated
126+
[https://blog.blockstream.com/en-covenants-in-elements-alpha/ in 2016].
127127

128128
This proposal intends to address the problems outlined above by
129129
providing a delay period/recovery path use with minimal transactional and
@@ -135,7 +135,7 @@ The design goals of the proposal are:
135135
136136
* '''batched operations''' for recovery and withdrawal to allow managing multiple vault coins efficiently.
137137
138-
* '''unbounded partial withdrawals''', which allows users to withdrawal partial vault balances without having to perform the setup ceremony for a new vault.
138+
* '''unbounded partial withdrawals''', which allows users to withdraw partial vault balances without having to perform the setup ceremony for a new vault.
139139
140140
* '''dynamic unvault targets''', which allow the proposed withdrawal target for a vault to be specified at withdrawal time rather than when the vault is first created. This would remove the need for a prespecified, intermediate wallet that only exists to route unvaulted funds to their desired destination.
141141
@@ -145,7 +145,7 @@ These goals are accompanied by basic safety considerations (e.g. not being
145145
vulnerable to pinning) and a desire for concision, both in terms of the number
146146
of outputs created as well as script sizes.
147147

148-
This proposal is designed to be compatible with any future sighash modes (e.g. <code>SIGHASH_GROUP</code>) or fee management strategies (e.g. [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-September/018168.html transaction sponsors]) that may be introduced. Use of these opcodes will benefit from, but do not strictly rely on, future transaction versions (e.g. [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-September/020937.html v3]) and [https://github.com/instagibbs/bips/blob/ephemeral_anchor/bip-ephemeralanchors.mediawik ephemeral anchors].
148+
This proposal is designed to be compatible with any future sighash modes (e.g. <code>SIGHASH_GROUP</code>) or fee management strategies (e.g. [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-September/018168.html transaction sponsors]) that may be introduced. Use of these opcodes will benefit from, but do not strictly rely on, [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-September/020937.html v3 transaction relay] and [https://github.com/instagibbs/bips/blob/ephemeral_anchor/bip-ephemeralanchors.mediawiki ephemeral anchors].
149149

150150
== Design ==
151151

@@ -249,11 +249,9 @@ The tapscript opcodes <code>OP_SUCCESS187</code> (<code>0xbb</code>) and
249249
==== Witness program ====
250250

251251
When evaluating <code>OP_VAULT</code> (<code>OP_SUCCESS187</code>,
252-
<code>0xbb</code>), the witness program is pushed onto the stack for the
253-
following result (stack shown top to bottom):
252+
<code>0xbb</code>), the expected format of the stack, shown top to bottom, is:
254253

255254
<source>
256-
OP_VAULT (*) being evaluated
257255
<trigger-sPK-hash>
258256
<spend-delay>
259257
<recovery-params>
@@ -262,7 +260,7 @@ OP_VAULT (*) being evaluated
262260
where
263261

264262
* <code><trigger-sPK-hash></code> is a 32 byte tagged hash of the scriptPubKey used to authorize the spend of this output into an <code>OP_UNVAULT</code> trigger output
265-
** <code>tagged_hash("VaultTriggerSPK", spk)</code>, per BIP-0340.
263+
** <code>tagged_hash("VaultTriggerSPK", <trigger-sPK>)</code>, per BIP-0340.
266264
** If this value is not 32 bytes, script execution when spending this output MUST fail and terminate immediately.
267265
** Because this parameter's scriptPubKey is committed to using a hash, witness version upgradeability for the trigger key is preserved.
268266
@@ -301,12 +299,8 @@ where
301299
302300
==== <code>OP_VAULT</code> evaluation for recovery spend ====
303301

304-
* If the recovery output does not have an <code>nValue</code> greater than this input's amount, the script MUST fail and terminate immediately.
305-
* (Deferred) if the recovery output does not have an <code>nValue</code> equal to the sum of all <code>OP_VAULT</code>/<code>OP_UNVAULT</code> inputs with a corresponding recovery sPK hash, the transaction validation MUST fail.<ref>'''How do recovery transactions pay for fees?''' If the recovery is unauthorized, fees are attached either via CPFP with an ephemeral anchor or as inputs which are solely spent to fees (i.e. no change output). If the recovery is authorized, fees can be attached in any manner, e.g. unrelated inputs and outputs or CPFP via anchor.</ref>
306-
** Note that in the draft implementation, this is facilitated by a "deferred check" which is queued by the script interpreter, but executed after the script interpreter has finished, in other validation code.<ref>'''Why does this proposal require a "deferred checks" framework for correct script evaluation?''' The deferred checks framework is an augmentation to execution of the Bitcoin script interpreter. Currently, the validity of each input is checked in an order-indepdendent manner across all inputs in a transaction. Because this proposal allows batching the spend of multiple vault inputs into a single recovery or withdrawal output, we need a mechanism to ensure that all expected values per output can be summed and then checked. This necessitates the introduction of an "aggregating" set of checks which can only be executed after each input's script is evaluated. Note that similar functionality would be required for batch input validation or cross-input signature aggregation.</ref>
307-
* The script must FAIL (by policy, not consensus) and terminate immediately if neither<ref>'''Why are recovery transactions required to be replaceable?''' In the case of unauthorized recoveries, an attacker may attempt to pin recovery transactions by broadcasting a "rebundled" version with a low fee rate. Vault owners must be able to overcome this with replacement. In the case of authorized recovery, if an attacker steals the recovery authorization key, the attacker may try to pin the recovery transaction during theft. Requiring replaceability ensures that the owner can always raise the fee rate of the recovery transaction, even if they are RBF rule #3 griefed in the process.</ref>
308-
*# the input is marked as opt-in replaceable by having an nSequence number less than <code>0xffffffff - 1</code>, per [https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki BIP-0125], nor
309-
*# the version of the recovery transaction has an nVersion equal to 3.
302+
* If the recovery output does not have an <code>nValue</code> greater than or equal to this input's amount, the script MUST fail and terminate immediately.
303+
* (Deferred<ref>'''What is a deferred check and why does this proposal require them for correct script evaluation?''' A deferred check is a validation check that is executed only after all input scripts have been validated, and is based on aggregate information collected during each input's EvalScript run.<br /><br />Currently, the validity of each input is (usually) checked concurrently across all inputs in a transaction. Because this proposal allows batching the spend of multiple vault inputs into a single recovery or withdrawal output, we need a mechanism to ensure that all expected values per output can be summed and then checked. This necessitates the introduction of an "aggregating" set of checks which can only be executed after each input's script is evaluated. Note that similar functionality would be required for batch input validation or cross-input signature aggregation.</ref>) if the recovery output does not have an <code>nValue</code> equal to the sum of all <code>OP_VAULT</code>/<code>OP_UNVAULT</code> inputs with a corresponding recovery sPK hash, the transaction validation MUST fail.<ref>'''How do recovery transactions pay for fees?''' If the recovery is unauthorized, fees are attached either via CPFP with an ephemeral anchor or as inputs which are solely spent to fees (i.e. no change output). If the recovery is authorized, fees can be attached in any manner, e.g. unrelated inputs and outputs or CPFP via anchor.</ref>
310304
311305
The stack may now have 0 or more elements. Any items on the stack will be used to verify the recovery authorization witness program, if any.
312306

@@ -378,9 +372,10 @@ spent. Its presence is optional.
378372
For each vault input citing a particular <code><trigger-vout-idx></code>, the output
379373
located at <code>vout[<trigger-vout-idx>]</code> (the "trigger output") must:
380374

381-
* have as its scriptPubKey a witness program version 1 with a single <code>OP_UNVAULT</code> tapscript, having the internal key <code>lift_x(0x0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)</code>, per the NUMS point mentioned in [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#user-content-Design BIP-0341].<ref>'''Why must the OP_UNVAULT taproot use a NUMS point as its internal key?''' This ensures that an OP_UNVAULT trigger output is verifiable as expected. It also ensures that it is spendable only by the conditions of the vault.</ref>
375+
* have as its scriptPubKey a witness program with a single <code>OP_UNVAULT</code> tapscript, having the internal x-only key <code>0x0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0</code>, per the NUMS point mentioned in [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs BIP-0341].<ref>'''Why must the OP_UNVAULT taproot use a predefined NUMS point as its internal key?''' This ensures that an OP_UNVAULT trigger output is verifiable as expected. It also ensures that it is spendable only by the conditions of the vault.</ref>
382376
** If the witness program has a version less than 1, the script MUST fail and terminate immediately.
383-
** If the scriptPubKey of the output does not match the expected scriptPubKey, as computed by creating a taproot output using the cited NUMS point and a single tapscript spend condition of the form<br /><code><recovery-params-from-stack> <spend-delay-from-stack> <target-outputs-hash-from-stack> OP_UNVAULT</code>,<br />the script MUST fail and terminate immediately.
377+
** If the witness program has a version greater than 1, the script MUST succeed to enable upgradeability.
378+
** If the witness program has a version of 1 and the scriptPubKey of the output does not match the expected scriptPubKey, as computed by creating a taproot output using the cited NUMS point and a single tapscript spend condition of the form<br /><code><recovery-params-from-stack> <spend-delay-from-stack> <target-outputs-hash-from-stack> OP_UNVAULT</code>,<br />the script MUST fail and terminate immediately.
384379
** Witness versions greater than 1 are allowed for upgradeability.
385380
386381
* If there does not exist a revault output in the transaction for this input:
@@ -486,6 +481,13 @@ def serialize_txout(txo: CTxOut) -> bytes:
486481

487482
If the above conditions do not fail, a single true value (<code>0x01</code>) is pushed to the stack.
488483

484+
== Policy changes ==
485+
486+
In order to prevent possible pinning attacks, recovery transactions must be replaceable.
487+
488+
* When validating an <code>OP_VAULT</code>/<code>OP_UNVAULT</code> input being spent towards a recovery, the script must FAIL (by policy, not consensus) and terminate immediately if neither<ref>'''Why are recovery transactions required to be replaceable?''' In the case of unauthorized recoveries, an attacker may attempt to pin recovery transactions by broadcasting a "rebundled" version with a low fee rate. Vault owners must be able to overcome this with replacement. In the case of authorized recovery, if an attacker steals the recovery authorization key, the attacker may try to pin the recovery transaction during theft. Requiring replaceability ensures that the owner can always raise the fee rate of the recovery transaction, even if they are RBF rule #3 griefed in the process.</ref>
489+
*# the input is marked as opt-in replaceable by having an nSequence number less than <code>0xffffffff - 1</code>, per [https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki BIP-0125], nor
490+
*# the version of the recovery transaction has an nVersion equal to 3.
489491
490492
== Implementation ==
491493

0 commit comments

Comments
 (0)