Add OP_VAULT (BIP 345) #1421
pull jamesob wants to merge 31 commits into bitcoin:master from jamesob:jamesob-23-02-opvault changing 5 files +1801 −0-
jamesob commented at 8:55 pm on February 13, 2023: memberPairs with the draft implementation in https://github.com/bitcoin-inquisition/bitcoin/pull/21.
-
jamesob force-pushed on Feb 13, 2023
-
jamesob force-pushed on Feb 13, 2023
-
Add OP_VAULT BIP b30e37c8a2
-
in bip-vaults.mediawiki:73 in b30e37c8a2 outdated
68+ 69+Institutional custodians of Bitcoin would likely use vaults in similar fashion. 70+ 71+===== Avoiding the hostage situation ===== 72+ 73+This proposal uniquely provides a solution to the "hostage situation;" by
naumenkogs commented at 9:38 am on February 14, 2023:- typo
- it’s unclear what “hostage situation” means.
jamesob commented at 5:51 pm on February 14, 2023:Fixed. I’m calling this the $5 wrench attack instead, and linking to relevant source material. ;)
19861826 commented at 5:23 am on March 29, 2023:- 錯別字
- 目前還不清楚“人質情況”是什麼意思。
in bip-vaults.mediawiki:103 in b30e37c8a2 outdated
98+* amounts and withdrawal patterns must be precommitted to, 99+* there is a necessity to precommit to an address that the funds must pass through on their way to the final withdrawal target, which is likely only known at unvault time, 100+* the particular fee management technique or wallet must be decided upon vault creation, 101+* coin loss follows if a vault address is reused, 102+* the transaction data that represents the "bearer asset" of the vault must be stored for perpetuity else value is lost, and 103+* the vault creation ceremony must be performed each time a new balance is to be deposited.
naumenkogs commented at 9:42 am on February 14, 2023:duplicate ofcoin loss follows if a vault address is reused,
?
jamesob commented at 5:53 pm on February 14, 2023:I don’t think so; these are separate problems. Requiring an expensive set-up ceremony for each deposit is a different problem than the unaddressable risk of burning coins by reusing a presigned vault address.in bip-vaults.mediawiki:119 in b30e37c8a2 outdated
114+However, the limitations of precomputation still apply: amounts, 115+destinations, and fee management are all fixed. Funds must flow through a fixed 116+intermediary to their final destination. Batch operations, which may be vital 117+for successful recovery during fee spikes or short spend delay, are not possible. 118+ 119+[[File:bip-VAULT/withdrawal-comparison.drawio.png|frame|center]]
naumenkogs commented at 9:45 am on February 14, 2023:hard to tell what the circles are supposed to mean :)
jamesob commented at 6:10 pm on February 14, 2023:I’ve clarified the images a bit; let me know if still unclear.in bip-vaults.mediawiki:154 in b30e37c8a2 outdated
149+ 150+== Design == 151+ 152+=== State machine === 153+ 154+[[File:bip-VAULT/opvault-flow.drawio.png|frame|center]]
naumenkogs commented at 9:58 am on February 14, 2023:- It would help adding more arrow pointers to the dashed lines, because it’s hard to tell in which directions some of them go (e.g., the one from the trigger tx to the right)
- [outputs controlled by recovery keys] should emphasize when they are chosen.
in bip-vaults.mediawiki:160 in b30e37c8a2 outdated
155+ 156+The vault has a number of stages, some of them optional: 157+ 158+* '''vault transaction''': encumbers some coins with an <code>OP_VAULT</code> script invocation. 159+ 160+* '''trigger transaction''': spends one or more <code>OP_VAULT</code> outputs into one compatible <code>OP_UNVAULT</code> output, which broadcasts the intent to withdrawal to some specific set of outputs. This transaction may have an additional output which allocates some of the vault balance into a partial revault, which simply encumbers the revaulted portion of the value into the same <code>scriptPubKey</code> of the originating <code>OP_VAULT</code> output(s).
naumenkogs commented at 9:59 am on February 14, 2023:what does “compatible” mean?in bip-vaults.mediawiki:162 in b30e37c8a2 outdated
157+ 158+* '''vault transaction''': encumbers some coins with an <code>OP_VAULT</code> script invocation. 159+ 160+* '''trigger transaction''': spends one or more <code>OP_VAULT</code> outputs into one compatible <code>OP_UNVAULT</code> output, which broadcasts the intent to withdrawal to some specific set of outputs. This transaction may have an additional output which allocates some of the vault balance into a partial revault, which simply encumbers the revaulted portion of the value into the same <code>scriptPubKey</code> of the originating <code>OP_VAULT</code> output(s). 161+ 162+* '''withdrawal transaction''': spends <code>OP_UNVAULT</code> inputs into a compatible set of final withdrawal outputs per the target hash. The only authorization for this spend is the content hash of the withdrawal outputs.
naumenkogs commented at 10:02 am on February 14, 2023:should mention a delay?in bip-vaults.mediawiki:189 in b30e37c8a2 outdated
184+ 185+The second component, the recovery authorization scriptPubKey, is optional. It 186+is a raw scriptPubKey that, if specified, must be satisfied to allow the input 187+to be recovered. The benefit of using this parameter will be discussed later. 188+If this component is not given, the de facto "authorization" is the reveal of 189+the <code><recovery sPK tagged hash></code> preimage.
naumenkogs commented at 10:08 am on February 14, 2023:Are you implying an additional preimage/hash pair here, or maybe it is sufficient to use the recovery destination alone as a secret (it’s already hashed in taproot fashion)?
jamesob commented at 5:03 pm on February 14, 2023:Yes, it’s the latter - in the case of unauthorized recovery, knowledge of the recovery path itself serves as authorization.in bip-vaults.mediawiki:197 in b30e37c8a2 outdated
192+which is an important practical aspect of managing large numbers of vaults. 193+ 194+<code><spend-delay></code> 195+ 196+The spend delay dictates the duration of blocks or time which must 197+elapse for the trigger <code>OP_UNVAULT</code> output to be claimable into the
naumenkogs commented at 10:08 am on February 14, 2023:since theOP_VAULT
was mined, i assume?
jamesob commented at 6:11 pm on February 14, 2023:Yes, since it was spent into an OP_UNVAULT, which is the output that has to mature forspend-delay
.in bip-vaults.mediawiki:310 in b30e37c8a2 outdated
305+</source> 306+ 307+==== <code>OP_VAULT</code> evaluation for recovery spend ==== 308+ 309+* If the recovery output does not have an <code>nValue</code> greater than this input's amount, the script MUST fail and terminate immediately. 310+* (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.
naumenkogs commented at 10:25 am on February 14, 2023:I’m confused, how this transaction is supposed to pay the fee?
jamesob commented at 5:47 pm on February 14, 2023:Adding a rationale footnote for this.in bip-vaults.mediawiki:323 in b30e37c8a2 outdated
318+* If the ''recovery authorization sPK'' is not null: 319+** If <code>VerifyWitnessProgram(<stack elements>, <recovery-auth-sPK>, ...)</code> fails, the script MUST fail and terminate immediately. 320+** (This validates that the recovery has been authorized.) 321+* else (if the recovery is allowed to be unauthorized): 322+** If the spending transaction has more than two outputs, the script MUST fail and terminate immediately. 323+** If the spending transaction has two outputs, and the output not the recovery output is not an ephemeral anchor, the script MUST fail and terminate immediately.<ref>'''Why can unauthorized recoveries only process a single recovery path?''' Because there is no signature required for unauthorized recoveries, if additional outputs were allowed, someone observing a recovery in the mempool would be able to rebundle and broadcast the recovery with a lower fee rate.</ref>
naumenkogs commented at 10:28 am on February 14, 2023:ephemeral anchors come up very unexpectedly here…. so this is how the fee is paid?
Ruffledfeatherz commented at 0:05 am on August 18, 2023:looks like this is itin bip-vaults.mediawiki:359 in b30e37c8a2 outdated
354+ 355+* <code><trigger-vout-idx></code> is a <code>CScriptNum</code> of up to 4 bytes. 356+** It indicates the vout index of this input's corresponding <code>OP_UNVAULT</code> output. 357+*** Validation rules for this output are described below. 358+** If this value does not decode as a valid CScriptNum value, the script MUST fail and terminate immediately. 359+** If this value does not correspond to a valid output in the spending transaction, the script MUST fail and terminate immediately.
naumenkogs commented at 10:32 am on February 14, 2023:Do you simply mean there is fewer outputs than idx?
jamesob commented at 5:04 pm on February 14, 2023:Or if the given CScriptNum is negative.in bip-vaults.mediawiki:362 in b30e37c8a2 outdated
357+*** Validation rules for this output are described below. 358+** If this value does not decode as a valid CScriptNum value, the script MUST fail and terminate immediately. 359+** If this value does not correspond to a valid output in the spending transaction, the script MUST fail and terminate immediately. 360+ 361+* <code><target-outputs-hash></code> is a 32 byte data push. 362+** It is the hash of the proposed withdrawal target output set, defined by <code>target_outputs_hash(outputs)</code> below. Note that this value is duplicated here.<ref>'''Why, when spending an OP_VAULT output into a trigger, does the target hash need to be duplicated on the witness stack?''' The target hash exists in the <code>OP_UNVAULT</code> output script, likely behind a taproot pubkey. Its presence is required on the witness stack also so that the expected taproot pubkey for the <code>OP_UNVAULT</code> output can be constructed for comparison.</ref>
naumenkogs commented at 10:43 am on February 14, 2023:here
doesn’t point to a location, but rather to a description of the location. The rationale is hard to comprehend too.naumenkogs commented at 11:03 am on February 14, 2023: memberConcept ACK! Looking forward to the merge into inquisition and prototyping with these opcodes.in bip-vaults.mediawiki:93 in b30e37c8a2 outdated
88+ 89+The only way to implement vaults given the existing consensus rules, aside from 90+[https://github.com/revault emulating vaults with large multisig 91+configurations], is to use presigned transactions created with a one-time-use 92+key. This approach was first demonstrated 93+[https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-April/017755.html
joostjager commented at 11:22 am on February 14, 2023:Link incorrect
jamesob commented at 5:50 pm on February 14, 2023:Fixed, thanks.fixup! typos and clarification
from feedback by Gleb and Joost.
jamesob force-pushed on Feb 14, 2023fixup! image clarifications 9124f2940evaults: make recovery transaction explicit
Instead of implicitly detecting whether or not an OP_VAULT/OP_UNVAULT spend is a recovery spend by scanning outputs for matching scriptPubKeys, explicitly indicate recoveries by requiring a witness stack element that is either -1 in the case of no recovery OR corresponds to an output index that is the recovery output.
ajtowns commented at 1:09 pm on February 16, 2023: contributorCan this get a number assigned?in bip-vaults.mediawiki:381 in c589490f98 outdated
376+spent. Its presence is optional. 377+ 378+For each vault input citing a particular <code><trigger-vout-idx></code>, the output 379+located at <code>vout[<trigger-vout-idx>]</code> (the "trigger output") must: 380+ 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>
vostrnad commented at 6:53 am on February 18, 2023:Suggested edits:
- Remove
lift_x
because the internal taproot key is a 32-byte array (x-only key), not a point. - Change BIP341 link to point to the actual section where this NUMS point is mentioned.
- Change “a NUMS point” to “a predefined NUMS point”. The former would only need to be justified by the second footnote sentence (and wouldn’t really need to be required by consensus as implementations could freely supply this or any other NUMS point). The verification requirement means the point must be predefined, hence the clarification.
0* have as its scriptPubKey a witness program version 1 with a single <code>OP_UNVAULT</code> tapscript, having the internal 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>
jamesob commented at 4:42 pm on February 21, 2023:Good feedback; fixed in ca12c526ab11b0e7bf2b2da693405cf8ac350bfa, thanks.in bip-vaults.mediawiki:71 in c589490f98 outdated
66+time-delayed fallback to an "easier" recovery method, in case the highly secure 67+key winds up being ''too'' highly secure. 68+ 69+Institutional custodians of Bitcoin would likely use vaults in similar fashion. 70+ 71+===== Avoiding the $5 wrench attack =====
vostrnad commented at 7:05 am on February 18, 2023:Since the section already mentions the $5 wrench attack, the title itself could be something more descriptive, such as “Provably non-bypassable timelocks”.in bip-vaults.mediawiki:138 in c589490f98 outdated
133+ 134+* '''efficient reuse of an existing vault configuration.'''<ref>'''Why does this support address reuse?''' The proposal doesn't rely on or encourage address reuse, but certain uses are unsafe if address reuse cannot be handled - for example, if a custodian gives its users a vault address to deposit to, it cannot enforce that those users make a single deposit for each address.</ref> A single vault configuration, whether the same literal <code>scriptPubKey</code> or not, should be able to “receive” multiple deposits. 135+ 136+* '''batched operations''' for recovery and withdrawal to allow managing multiple vault coins efficiently. 137+ 138+* '''unbounded partial withdrawals''', which allows users to withdrawal partial vault balances without having to perform the setup ceremony for a new vault.
vostrnad commented at 7:07 am on February 18, 2023:0* '''unbounded partial withdrawals''', which allows users to withdraw partial vault balances without having to perform the setup ceremony for a new vault.
in bip-vaults.mediawiki:148 in c589490f98 outdated
143+ 144+These goals are accompanied by basic safety considerations (e.g. not being 145+vulnerable to pinning) and a desire for concision, both in terms of the number 146+of outputs created as well as script sizes. 147+ 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].
vostrnad commented at 7:15 am on February 18, 2023:“Future transaction versions” is unnecessarily broad. Also fixing the ephemeral anchors link.
0This 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].
in bip-vaults.mediawiki:307 in c589490f98 outdated
302+==== <code>OP_VAULT</code> evaluation for recovery spend ==== 303+ 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>
vostrnad commented at 7:37 am on February 18, 2023:The core (consensus-critical) specification should probably only include consensus rules and not recommended standardness/policy rules (which should perhaps not be included at all, just like they aren’t in BIP341).
harding commented at 3:49 am on February 20, 2023:I also didn’t like the mixing of consensus and policy rules in the spec. I think work on different layers has previously been dealt with by using different BIPs, e.g. BIPs 13 vs 16, or BIPs 141/143 vs 144.
That said, if separate BIPs is just too annoying, maybe the rules for policy could just be moved to a different section.
jamesob commented at 4:44 pm on February 21, 2023:I’ve moved this provision into a policy section (ca12c526ab11b0e7bf2b2da693405cf8ac350bfa), and will likely make recovery transaction structure (for unauthorized vs. authorized) a matter of policy as well, since the distinction only exists there to prevent pinning.
luke-jr commented at 4:56 pm on February 21, 2023:Node policy is an individual decision and generally not a subject for BIPs at all.
michaelfolkson commented at 9:40 pm on February 21, 2023:@luke-jr: Agreed but there are going to be future discussions on changing the default policy in Bitcoin Core and the recommended default policy in other implementations (which those implementations are free to ignore as are all users given it is only a policy default and not a consensus rule). It is inevitable with proposals for Lightning, vaults etc and the associated pinning vectors. Personally I would much rather any proposed changes to default policy were spec’ed and discussed in the open in the BIP process than just say merged into Core.
I do agree that mixing consensus and policy in the same proposal isn’t ideal. Perhaps there can be a note to the reader on what the BIP author recommends for default policy changes associated with this proposal but making it clear that this BIP stands alone as a proposed consensus change and not a default policy change.
in bip-vaults.mediawiki:384 in c589490f98 outdated
379+located at <code>vout[<trigger-vout-idx>]</code> (the "trigger output") must: 380+ 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> 382+** 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. 384+** Witness versions greater than 1 are allowed for upgradeability.
vostrnad commented at 7:39 am on February 18, 2023:The parent bullet point requires version 1, so which is it?
jamesob commented at 4:44 pm on February 21, 2023:I’ve clarified this in ca12c526ab11b0e7bf2b2da693405cf8ac350bfa.in bip-vaults.mediawiki:126 in c589490f98 outdated
121+Having a "general" covenant mechanism that can encode arbitrary transactional 122+state machines would allow us to solve these issues, but at the cost of complex 123+and large scripts that would probably be duplicated many times over in the 124+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.
apoelstra commented at 2:40 pm on February 18, 2023:FWIW we did a demo in 2016: https://blog.blockstream.com/en-covenants-in-elements-alpha/
We would definitely not take this approach today unless we really had to. The script is really complex and I had to write custom ad-hoc analysis code to convince myself that the construction was okay.
We have a more modern covenant design in elements-miniscirpt which we believe would be significantly simpler, and be possible to analyze without unholy means, but so far we’ve only implemented non-recursive covenants, not vaults.
jamesob commented at 4:38 pm on February 21, 2023:Thanks! Will include.jamesob force-pushed on Feb 21, 2023jamesob force-pushed on Feb 21, 2023jamesob force-pushed on Feb 21, 2023vaults: various feedback
Thanks to Vojtěch Strnad for most of this.
vaults: make recovery output structure a matter of policy
Since constraints on unauthorized recovery transaction structure exist only to avoid pinning, make them a matter of policy and not consensus.
in bip-vaults.mediawiki:636 in 0204c9a1f9 outdated
631+Script descriptors for vault-related outputs will be covered in a subsequent BIP. 632+ 633+ 634+== Deployment == 635+ 636+It is anticipated that deployment would happen in the same way [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#user-content-Deployment that BIP-0341 was deployed]. Parameters to be determined.
luke-jr commented at 5:02 pm on February 21, 2023:Taproot was activated by BIP 343, not the linked section of 341 which lacked community support at the time, and negative sentiment toward has only grown since. Attempting to use “speedy trial” or BIP 9 is an attack on Bitcoin.
harding commented at 8:37 pm on February 21, 2023:Would you refuse to merge this PR because you think it is in error and proposes an attack on Bitcoin?
michaelfolkson commented at 9:01 pm on February 21, 2023:Can you leave this deployment section blank for now @jamesob? There’s no reason to spend time discussing deployment prior to getting a BIP number. Still lots of discussion, merging on bitcoin-inquisition, prototype vault designs on signet, building community consensus on the actual proposal etc before deployment becomes relevant.
jamesob commented at 9:06 pm on February 21, 2023:@michaelfolkson yeah that’s fine with me. I’ll replace this section’s content with TBD.luke-jr changes_requestedluke-jr commented at 5:03 pm on February 21, 2023: memberMissing section on backward compatibilityvaults: blank deployment 6ff8efd3d1typos / gramma cleanup 24241ee26bkallewoof commented at 2:42 am on February 24, 2023: memberIn my book, it is perfectly fine (if it applies) to say that there are no backwards compatibility issues, especially if it is stated why that is the case (e.g. “because the op-codes in question are OP_SUCCESSes”). Not having a backwards compatibility section means the reader has to determine whether there are any, by themselves.jamesob commented at 6:11 pm on February 27, 2023: memberCan this get a number assigned please? Is there anything blocking?kallewoof commented at 10:04 am on March 1, 2023: memberLooking at this over the next few days. Please hold on.in bip-vaults.mediawiki:481 in 61dd727556 outdated
476+ 477+In order to prevent possible pinning attacks, recovery transactions must be replaceable. 478+ 479+* 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> 480+*# 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 481+*# the version of the recovery transaction has an nVersion equal to 3.
kallewoof commented at 7:33 am on March 2, 2023:This is going to be misinterpreted in its current form. Perhaps this would be easier to human-parse:
0* 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 both<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](/bitcoin-bips/3/) griefed in the process.</ref> 1*# the input is not 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], and 2*# the version of the recovery transaction has an nVersion other than 3.
kallewoof commented at 7:44 am on March 2, 2023: memberOne question: when I discussed a similar idea some years back – https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-February/015793.html – I was told that it was not going to work, because it essentially incentivizes the attacker to kill the victim, in order to guarantee that the victim won’t “revoke” or otherwise take back their coins within the “grace” (?) period. I admit I’m honestly not sure how this proposal addresses that particular concern.jamesob commented at 2:39 pm on March 2, 2023: memberOne question: when I discussed a similar idea some years back – https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-February/015793.html
Didn’t realize you had proposed something along these lines - will add to references.
I was told that it was not going to work, because it essentially incentivizes the attacker to kill the victim, in order to guarantee that the victim won’t “revoke” or otherwise take back their coins within the “grace” (?) period. I admit I’m honestly not sure how this proposal addresses that particular concern.
I want to be clear that I think most of the benefit of using vaults comes in addressing remote attacks, where someone compromises a key remotely from live infrastructure or has backdoored some hardware that you would otherwise be relying on. I think the potential benefit for individual users is plain, and if you ask basically any industrial custodian, they would be likely to tell you of the potential value of this capability.
I thought the provable time delay was novel and interesting, but I can remove that section if it muddles the use of this proposal.
jamesob force-pushed on Mar 2, 2023vostrnad commented at 5:17 pm on March 2, 2023: contributoressentially incentivizes the attacker to kill the victim
Maybe I misunderstand, but I believe this problem can be mitigated by setting up some form of dead man’s switch using a watchtower service that would either initiate the recovery process itself (this involves some degree of trust) or notify a group of close individuals who possess the recovery data necessary (and may be better positioned to verify the legitimacy of the unvault). I imagine these setups will be quite ubiquitous for people using OP_VAULT or similar if we get it deployed.
harding commented at 6:19 pm on March 2, 2023: contributorit essentially incentivizes the attacker to kill the victim
A discussion about that subject between sipa, kanzure, gmaxwell, maaku, and myself can be found at:
https://gnusha.org/taproot-bip-review/2020-02-10.log
The details box below has a few quotes I thought were relevant (lightly edited):
13:54 < gmaxwell> If someone kidnaps you and is going to hit you with a $5 wrench until you give them the coins, telling them you control no coins that you can immediately move is just going to get you killed or seriously injured.
13:57 < gmaxwell> kanzure: I think privacy is a much better protection via technical means, in spite of its limitations.
13:57 < kanzure> privacy is not robust… once you lose your privacy, you can’t get it back.
13:58 < gmaxwell> kanzure: I’m saying that no matter what you do most everyone is already lying about their cold storage stratgy (including lying that they have one), so a prospective kidnapper would probably just ignore those claims and instead use absent privacy to choose targets they are confident own enough coins to make their risks worthwhile.
14:00 < kanzure> yes cage-until-expiration is something that requires you to setup tripwires and deadman-whatevers before you get kidnapped
14:05 < gmaxwell> The challenge with coersion is that if you’re already assuming attackers who will kidnap– you’re better off yielding, and precommiting to not yeald just carries a lot of risk of martyring yourself over some mere money. particularly because it’s fundimentally impossible to prove that beating you harder won’t get you to disclose something valuable, if not the keys for some specific outputs the keys for some other unencoumbered outputs.
14:08 < gmaxwell> one should also consider that a lot of kidnappers are not the brightest builbs. There are cases of educated metntally stable highly socipathic kidnappers, but there are a lot of cases of drugged out idiots. The former might be dissuaded by protective coloration… the latter not so much. Both are probably better defended against by traditional security trappings… locked fences, lights, cameras, guard dogs, security, etc.
ajtowns commented at 0:29 am on March 3, 2023: contributorI was told that it was not going to work, because it essentially incentivizes the attacker to kill the victim, in order to guarantee that the victim won’t “revoke” or otherwise take back their coins within the “grace” (?) period. I admit I’m honestly not sure how this proposal addresses that particular concern.
a) The same argument applies to many things – if you’ve sent a large lightning payment and received your receipt off-chain, instead of resolving the htlc off-chain you should kill/DoS your counterparty, wait for the timeout to complete, and claim a refund on-chain, getting both the goods and services and your money back. If you’re dealing with amounts large enough to make kidnapping/murder worthwhile, you need to invest in personal security, not script hacks?
b) If someone is aware of your utxos and able to kill you they could just demand you reveal your cold wallet/recovery key to them; then they can verify you gave them the correct private key, and claim all your funds immediately with no delay. That differs from the “delay only” paths in that here you can provably give them your bitcoin immediately in order to buy your freedom/life, so there’s no incentive for them to kill you to avoid a race. I think this is a big difference between this and “time lock and throw away the keys for any alterntive ways of spending” approaches as far as this argument goes.
c) You could increase the security of your bitcoin by having a third party sweep your funds to the recovery address if they see a withdrawal attempt and you don’t do some out of band authentication, in which case killing you doesn’t get them the bitcoins either.
(I would have thought that sort of question would be better discussed on the list, not in the PR?)
kallewoof commented at 1:37 am on March 3, 2023: memberI thought the provable time delay was novel and interesting, but I can remove that section if it muddles the use of this proposal.
No, I think it’s fine personally. As others have chimed in already, there are plenty of existing cases where this is the case. @ajtowns
(I would have thought that sort of question would be better discussed on the list, not in the PR?)
Yeah. I casually mentioned it thinking there was a “D’oh, this is why” answer in there that I didn’t see.
luke-jr commented at 1:43 am on March 3, 2023: memberAssigned BIP 345jamesob renamed this:
Add OP_VAULT BIP
Add OP_VAULT (BIP 345)
on Mar 3, 2023in bip-vaults.mediawiki:50 in 00eadde0fb outdated
45+well as physical access to the hardware, so they generate a new key that is 46+highly secure, but would be impractical for daily use. For example the key 47+could be generated in some analog fashion, or on an old computer (with added 48+entropy) that is then destroyed, with the private key replicated only in paper 49+form. Or the key could be a 2-of-3 multisig using devices from different 50+manufacturers. Perhaps the key is geographically distributed.
ariard commented at 1:18 am on March 8, 2023:I think there is an operational complexity in authenticating the “recovery path” to the individual/group of stakeholders locking up the funds in the initial OP_VAULT UTXO, without re-introducing one of the security risk aforementioned, e.g the origin hardware wallet could have been compromised to display the “honest” recovery path, while at the script-level a malicious “recovery path” has been substituted for. I think you might need at least 2 “logical” trusted systems, the air-gapped/cold storage where the OP_VAULT funding transaction is generated, and the initial hardware where are relying the non-covenanted secret key (though it could be one “physical” system).in bip-vaults.mediawiki:55 in 00eadde0fb outdated
50+manufacturers. Perhaps the key is geographically distributed. 51+ 52+This individual can use <code>OP_VAULT</code> to make use of the highly secure 53+key as the unlikely recovery path, while using their existing signing procedure 54+as the withdrawal trigger key, with a configured spend delay of 1 day. They can 55+run software on their mobile device that monitors the blockchain for spends of
ariard commented at 1:19 am on March 8, 2023:A side-note, the block-relay stack of most of lightweight clients is a level or two of hardening under the Bitcoin Core p2p stack, so building a vault mobile client is probably going to be passive to eclipse/time-dilation attacks and other niceties.in bip-vaults.mediawiki:79 in 00eadde0fb outdated
74+[https://web.archive.org/web/20230210123933/https://xkcd.com/538/ "$5 wrench attack."] By 75+setting the spend delay to, say, a week, and using as the recovery path a 76+script that enforces a longer relative timelock, the owner of the vault can 77+prove that he is unable to access its value immediately. To the author's 78+knowledge, this is the only way to configure this defense without rolling 79+timelocked coins for perpetuity or relying on a trusted third party.
ariard commented at 1:30 am on March 8, 2023:I think there is still the trigger key path that a victim could be coerced to use to unlock the funds to the trigger transaction. This is nullified only if you have a set of watchtowers to correct the “misbehaving” unlock with the recovery transaction. I think the presence of watchtowers is what grants vaults a proactive security model and your “$5 wrench attack” is proportional to your number of watchtowers.in bip-vaults.mediawiki:134 in 00eadde0fb outdated
129+providing a delay period/recovery path use with minimal transactional and 130+operational overhead using a specialized covenant. 131+ 132+The design goals of the proposal are: 133+ 134+* '''efficient reuse of an existing vault configuration.'''<ref>'''Why does this support address reuse?''' The proposal doesn't rely on or encourage address reuse, but certain uses are unsafe if address reuse cannot be handled - for example, if a custodian gives its users a vault address to deposit to, it cannot enforce that those users make a single deposit for each address.</ref> A single vault configuration, whether the same literal <code>scriptPubKey</code> or not, should be able to “receive” multiple deposits.
ariard commented at 1:36 am on March 8, 2023:I think if you use key blinding, you should be able to prevent address reuse, e.g your user gives you a set of blinding points to be used one-time with the same basescriptPubKey
, a solution to address reuse would be valuable for coins confidentiality.
luke-jr commented at 7:42 pm on March 11, 2023:Someone sending to the same address multiple times is undefined behaviour. Users should not expect it to work, and enforcement can be done by simply setting an expectation that additional sends may be lost. This is inherently also true for edge cases such as changing your wallet or being compromised. Address reuse cannot be made safe.
luke-jr commented at 7:43 pm on March 11, 2023:Making it efficient encourages reuse. Better not to.
michaelfolkson commented at 12:57 pm on March 12, 2023:“undefined behaviour”? I get it is discouraged in a single signer use case because why not just generate a new key? But in multiparty and vault like use cases getting all participants and all spending paths to generate a new key any time there is movement of funds might be at best unwieldy and at worst impact security practices (e.g. needing to access a key in cold storage when you’d rather not). Seems to me that we are firmly in privacy, security/UX trade-off territory here when we (arguably) weren’t in a single signer use case.
michaelfolkson commented at 1:23 pm on March 12, 2023:I put “arguably” in brackets because even in the single signer use case there are additional computational and tracking costs and so we are already in trade-off territory. It is just many would value privacy benefits higher than these relatively tiny costs hence a universal attitude against address reuse makes much more sense in a single signer use case. In a more complex setting (more keys, more spending paths) it is much, much harder to justify any kind of universal approach.in bip-vaults.mediawiki:136 in 00eadde0fb outdated
131+ 132+The design goals of the proposal are: 133+ 134+* '''efficient reuse of an existing vault configuration.'''<ref>'''Why does this support address reuse?''' The proposal doesn't rely on or encourage address reuse, but certain uses are unsafe if address reuse cannot be handled - for example, if a custodian gives its users a vault address to deposit to, it cannot enforce that those users make a single deposit for each address.</ref> A single vault configuration, whether the same literal <code>scriptPubKey</code> or not, should be able to “receive” multiple deposits. 135+ 136+* '''batched operations''' for recovery and withdrawal to allow managing multiple vault coins efficiently.
ariard commented at 1:47 am on March 8, 2023:Recovery batching is unsafe if you assume an adversary that can generate trigger transactions for all your swept vault outputs, even if you use nversion=3 I think. Your batching recovery transaction spending the OP_UNVAULT outputs can be rejected on the ground of bip125 rule #5 (i.e max 100 replacements), so you might to limit the size of your batch.
instagibbs commented at 6:58 pm on March 9, 2023:Batching here means additional confirmed inputs, not unconfirmed batching.
in other words: You have N deposited utxos. If they share the same recovery path, they all can be moved into the same new output in a single bitcoin transaction,
luke-jr commented at 8:12 pm on March 11, 2023:This could reintroduce serious privacy issues if not combined with some kind of coinjoin/CISA
instagibbs commented at 2:39 pm on March 13, 2023:Fundamental issue here is that you cannot, sans ZKP magic, get rid of the association.
The funds must go from the vault to the specific covenant derivation. If that link wasn’t enforced by bitcoin script, it would be useless.
in bip-vaults.mediawiki:191 in 00eadde0fb outdated
186+is a raw scriptPubKey that, if specified, must be satisfied to allow the input 187+to be recovered. The benefit of using this parameter will be discussed later. 188+If this component is not given, the de facto "authorization" is the reveal of 189+the <code><recovery sPK tagged hash></code> preimage, i.e. the recovery path. 190+ 191+Vaults which share the same recovery path can always be swept in batch operations,
ariard commented at 1:55 am on March 8, 2023:I think one flexibility increase could be to allow the output index to be included as an element in the witness data, therefore you might do mixed batching, where inputs [0..2] goes to output N and inputs [3..7] goes to output M, with a single anchor output/pair of inputs/outputs and fee-bumping reserve coin is consumed for dynamic fee adjustment.in bip-vaults.mediawiki:209 in 00eadde0fb outdated
204+The trigger key, committed to with <code><trigger-sPK-hash></code>, is used to 205+authorize the ''trigger transaction'' - an on-chain declaration to attempt a 206+withdrawal to a certain set of target outputs. 207+ 208+This functions as the "normal" spending key, but if an attacker obtains access 209+to this key, the outcome is not catastrophic: any withdrawal attempt can be
ariard commented at 2:06 am on March 8, 2023:I don’t know if this statement is correct in the presence of unsolved pinnings vectors. E.g a bip125 rule #3 pinning where a malicious recovery transaction (without a secondary authorisationscriptPubkey
) iiuc is absolute fee inflated to prevent confirmation of a recovery transaction. Though this one should be solved by nversion=3 I believe.ariard commented at 2:08 am on March 8, 2023: noneReviewed so far until “Specification” excluded.in bip-vaults.mediawiki:61 in 00eadde0fb outdated
56+the vault outpoints. 57+ 58+If the vaulted coins move in an unexpected way, the user can immediately sweep 59+them to the highly secure recovery path, but spending the coins on a daily 60+basis works in the same way it did prior to vaulting - aside from the spend 61+delay.
luke-jr commented at 7:27 pm on March 11, 2023:…and additional transactions. Seems like the ideal solution would allow sending locked UTXOs to their final destination in a single transaction, yet allow a “claw back” transaction as well. Locktimes+RBF basically do this already, but without an on-chain commitment to the intermediate transaction which can be enforced by the UTXO itself.
Perhaps a better approach would be to have a covenant requiring the recipient to have a locktime N days later when spending, and an escape hatch for the clawback scenario. The drawback to this would be that we might need receiving wallets to understand the process (and yet another address format upgrade?).
(I suppose this is too different from OP_VAULT to be a change to your BIP, but it might be something to consider before moving too far along with OP_VAULT)
instagibbs commented at 2:27 pm on March 13, 2023:Completely handwaving here, but I think you’d need something like recursive taproot(or better bitcoin script) to do this properly. The “forwarded” script would at spend time get a
<pubkey>
that is inserted to a timelocked sub-expression in a larger expression that has otherwise been committed to at deposit time.You can’t just insert random bitcoin script at spend time, as we need to ensure it’s purely restricting spends, i.e. cannot be OP_SUCCESSX or something that modifies the stack in an unexpected way.
Recipients would have to scan on the input side to decipher outputs have hit their wallet, and they would still have to sweep that output to call it a proper deposit, ala HTLC resolution.
I’m not sure that’s a compelling use-case, since wallets will likely sweep outputs promptly for this very reason regardless.
in bip-vaults.mediawiki:77 in 00eadde0fb outdated
72+ 73+This proposal provides a solution to the 74+[https://web.archive.org/web/20230210123933/https://xkcd.com/538/ "$5 wrench attack."] By 75+setting the spend delay to, say, a week, and using as the recovery path a 76+script that enforces a longer relative timelock, the owner of the vault can 77+prove that he is unable to access its value immediately. To the author's
luke-jr commented at 7:34 pm on March 11, 2023:I’m not sure it actually solves the $5 wrench attack. The attacker can just demand all the multisig keys. Or even accept the delay, but deprive the owner of clawback key(s) to intervene later.in bip-vaults.mediawiki:97 in 00eadde0fb outdated
92+configurations], is to use presigned transactions created with a one-time-use 93+key. This approach was first demonstrated 94+[https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-April/017755.html in 2020]. 95+ 96+Unfortunately, this approach has a number of practical shortcomings: 97+* generating and securely deleting ephemeral keys, which are used to emulate the vault covenant, is required,
luke-jr commented at 7:36 pm on March 11, 2023:Isn’t it inherently always required in one form or another?in bip-vaults.mediawiki:98 in 00eadde0fb outdated
93+key. This approach was first demonstrated 94+[https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-April/017755.html in 2020]. 95+ 96+Unfortunately, this approach has a number of practical shortcomings: 97+* generating and securely deleting ephemeral keys, which are used to emulate the vault covenant, is required, 98+* amounts and withdrawal patterns must be precommitted to,
luke-jr commented at 7:37 pm on March 11, 2023:But multiple patterns can be precommitted to, so is this an issue in practice?in bip-vaults.mediawiki:359 in 00eadde0fb outdated
354+ 355+===== Transaction outputs validation ===== 356+ 357+Once the contents of the witness stack have been parsed and validated, the transaction outputs must be checked. 358+ 359+First, we must check for a ''revault output'': an output in the trigger transaction whose
luke-jr commented at 8:27 pm on March 11, 2023:Even this kind of address reuse has serious privacy implications. There should be a way to make it unknowable which output is change.
instagibbs commented at 2:33 pm on March 13, 2023:You can derive a new address via key derivation and send change there. This means the user has to wait for the timelock to run out before re-spending, and at spend time it becomes somewhat obvious which is change due to timelock configuration, at a minimum.jamesob commented at 7:01 pm on March 22, 2023: memberOops; hadn’t meant to push to this branch yet. Content here is currently in flux.bitcoin deleted a comment on Mar 23, 2023jamesob force-pushed on Mar 23, 2023jamesob force-pushed on Mar 23, 2023vaults: add backwards compatibility 4f03aaea2cvaults: add Corey Haddad reference 6dc766d937date fix
copypasta FTW
Update bip-vaults.mediawiki
Co-authored-by: kallewoof <kalle.alm@gmail.com>
fixup! add Kalle reference 915ede327afixup! FLUification
Adds AJ and Greg as co-authors
fixup! rename vaults BIP a0b433471dfixup! add TLUV references a6452eaf1ajamesob force-pushed on Mar 27, 2023few word changes e08f6ad4dfinstagibbs commented at 4:18 pm on March 28, 2023: memberTo allow for batch validation of ec mults when computing the spk that needs to match
triggerOut
, I think the most natural place to smuggle in a parity bit istrigger-vout-idx
, having the number’s sign-ness the that parity bit. This would interfere with uses were the forwarded leaf is being entirely predetermined except for therevault-vout-idx
, but I suppose in that extreme case one could put some conditionals in it to work around that issue, committing to both versions and choosing which one at spend time to use.If we want to allow “no trigger output” for a revault-only action, maybe we can also allow
trigger-vout-idx
to be out of bounds(exactly equal to number of outputs in tx?) and still pass validation. Currently I don’t think that’s allowed might disallow quick consolidations when many outputs with the same spk exist.why n-pushes 8bad703ed8remove vestigial reference in applications section 8bf5b869e5Be explicit about tapleaf version forwarding 29345a10f0few fixups 0b0674c54619861826 approved19861826 approvedin bip-0345.mediawiki:160 in 0b0674c546 outdated
155+== Design == 156+ 157+In typical usage, a vault is created by encumbering coins under a 158+taptree [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki (BIP-341)] 159+containing at least two leaves: one with an <code>OP_VAULT</code>-containing script that 160+facilitates the expected withdrawal process, and leaf another with
pinheadmz commented at 2:13 pm on March 29, 2023:nit …?
0facilitates the expected withdrawal process, and another leaf with
jamesob commented at 8:18 pm on April 19, 2023:Fixed, thanks.minor wording updates 7112f308b3in bip-0345.mediawiki:298 in 0b0674c546 outdated
293+After the stack is parsed, the following validation checks are performed: 294+ 295+* Let the output at index <code><recovery-vout-idx></code> be called ''recoveryOut''. 296+* If the scriptPubKey of ''recoveryOut'' does not have a tagged hash equal to <code><recovery-sPK-hash></code> (<code>tagged_hash("VaultRecoverySPK", recoveryOut.scriptPubKey) == recovery-sPK-hash</code>, where <code>tagged_hash()</code> is from the [https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py BIP-0340 reference code]), script execution when spending this output MUST fail and terminate immediately. 297+* If ''recoveryOut'' does not have an <code>nValue</code> greater than or equal to this input's amount, the script MUST fail and terminate immediately. 298+* (Deferred) if ''recoveryOut'' does not have an <code>nValue</code> equal to the sum of all <code>OP_VAULT_RECOVER</code>-spent inputs with a corresponding <code>recovery-sPK-hash</code>, 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>
instagibbs commented at 1:46 am on April 15, 2023:Not sure this matches the implementation. In the implementation if an input executes multiple OP_VAULT opcodes, I think it will expect multiple times the value of that particular input?jamesob commented at 8:28 pm on April 19, 2023: memberContent here no longer in flux. Spec review is welcome!darosior commented at 1:12 pm on April 21, 2023: memberInteresting proposal. The writeup is very clear, thanks for that.
You recommend to use
CTV
in order to commit to the withdrawal transaction that should eventually be used. But the transaction hash is unauthenticated since it is passed in the witness. This means anyone on the network (most likely a miner) can change the committed withdrawal transaction at trigger time. Now, sure, you still got the recovery mechanism. But:- I think the value of this proposal is that it can be thought of as defense in depth, as in it’s only “additional security”: if your watchtower(s) fail you your security level falls back to what it was without using the vault. But since the withdrawal transaction commitment can be replaced by anyone, it’s actually not: if your watchtower(s) fail you and a miner put their own withdrawal transaction when mining your trigger, it’s game over.
- Even if you assume your watchtower won’t fail you (i don’t think it is reasonable), it’s just a very trivial DOS anybody can perform at no cost. And it gets worse since you probably want to recover all your coins if you trigger recovery on one of them?
Maybe this could be fixed by using an
ANYPREVOUT | ANYSCRIPT
signature to commit to the withdrawal instead? It’s a bit more malleable, but at least it’s authenticated. It does not get rid of all the complexity though. You want the private key that will sign usingAPOAS
the withdrawal transaction to live on the signing device (and have the user verify its outputs there), otherwise it’s again a degradation in security: access to the hot laptop + failing watchtowers allows to steal.jamesob commented at 1:23 pm on April 21, 2023: memberYou recommend to use CTV in order to commit to the withdrawal transaction that should eventually be used. But the transaction hash is unauthenticated since it is passed in the witness. This means anyone on the network (most likely a miner) can change the committed withdrawal transaction at trigger time.
This is not the case. Recall that the CTV hash is committed to in the trigger output tapleaf, so if an observer changes the CTV hash given in the witness, making it mismatch the one given in the trigger output, the spend will be invalid.
instagibbs commented at 1:28 pm on April 21, 2023: memberRight, note that you could construct a vault with no trigger authentication easily, it just seems unwise to me. But that’s up to the end wallet developer.michaelfolkson commented at 12:19 pm on April 23, 2023: contributorI still think @vostrnad’s comment hasn’t fully been addressed on keeping proposed changes to default policy out of this BIP (a consensus change proposal) and referencing separate BIPs/draft BIPs on proposed default policy changes where necessary. If they don’t yet exist then perhaps a temporary statement that you’re waiting on new BIP(s) outlining these changes. In the extreme case that other default policy BIPs don’t cover the changes needed for this proposal a new BIP can be drafted in parallel with this one. This might seem overly finicky but I’d like to see policy prerequisites for this proposal hammered out in the V3 discussion and ideally implemented and active prior to considering any consensus change activation attempt on mainnet for this. I’m not sure what others’ views on this are but I think the consensus change activation attempt should be the very last step.
Originally, this proposal had a hard dependency on reformed transaction nVersion=3 policies, including ephemeral anchors, but it has since been revised to simply benefit from these changes in policy as well as other potential fee management mechanisms.
I’m not sure what this means. There is no longer a hard dependency on V3? Or there is still a hard dependency on V3 but the relevant parts of V3 haven’t been included in this BIP? Would you recommend OP_VAULT be used if V3 was never implemented or does it require V3 before you would give that recommendation?
Bigdoe41 approvedjamesob force-pushed on Jun 28, 2023fixup! add <revault-amount> and clarify deferred checks
This change makes the amount being revaulted (if any) explicit to avoid issues surfaced by AJ Towns (e.g. multiple compatible vault inputs duplicating triggers and revaults to confuse the old deferred check logic). Pseudocode is also provided for the deferred checks, and their inline validation description has been changed to be more faithful to the implementation - we make mention of queueing deferred checks, and then later describe the algorithm used to aggregate and perform them.
fiuxp! allow larger trigger/recovery output amounts
Allow trigger/recovery output nValues to exceed the amounts supplied by constituent vault inputs. This allows future compatibility for e.g. trigger collateral.
jamesob commented at 1:46 pm on July 28, 2023: memberI think this might be ready for fixup/merge. Is there any outstanding feedback to address?jamesob commented at 3:52 pm on July 28, 2023: memberIf they don’t yet exist then perhaps a temporary statement that you’re waiting on new BIP(s) outlining these changes.
Unless we have a hard and fast rule about consensus and policy changes living in disparate BIPs, I actually don’t think a separate BIP necessary to outline the proposed policy changes here. The policy changes outlined in this BIP are fairly minimal, and fragmenting them over to a separate BIP seems unnecessary.
I’ve always found the timelock BIPs (BIP-65, -68, -112, -113) cumbersome to reread because of what feels like unnecessary fragmentation - I can never remember what is in which BIP.
There is no longer a hard dependency on V3?
There is not.
Would you recommend OP_VAULT be used if V3 was never implemented or does it require V3 before you would give that recommendation?
OP_VAULT is practically usable today without V3 transactions provided it’s used with authenticated recovery, which allows unrelated inputs and outputs to be mixed into vault operations for fee management. I’ll remove mention of V3 in the BIP text for clarity.
instagibbs commented at 3:58 pm on July 28, 2023: memberI lean towards just removing all policy carveouts, since it’s secure as-is with authentication at each step, and future mempool updates make make unauthenticated recovery paths safe as well.in bip-0345.mediawiki:252 in e2ff23b3f0 outdated
247+** If this value does not decode to a valid CScriptNum, script execution when spending this output MUST fail and terminate immediately. 248+** If this value is less than 0, script execution when spending this output MUST fail and terminate immediately. 249+** If fewer than <code><n-pushes> + 2</code> items are on the stack, script execution when spending this output MUST fail and terminate immediately. 250+ 251+* The following <code><n-pushes></code> stack items are popped off the stack and prefixed as minimally-encoded push-data arguments to the <code><leaf-update-script-body></code> to construct the expected tapleaf replacement script. 252+** If there are fewer than <code><n-pushes> + 2</code> items on the stack, script execution when spending this output MUST fail and terminate immediately.
sanket1729 commented at 11:57 pm on August 31, 2023:I think this should ben_pushes + 3
according to above diagram?
jamesob commented at 9:33 am on September 1, 2023:Good catch! I clearly missed updating this when I made<revault-amt>
explicit (https://github.com/bitcoin/bips/pull/1421/commits/cb50446a65cb8504499e1ebded22539188612bb3).in bip-0345.mediawiki:260 in e2ff23b3f0 outdated
255+** If this value does not decode to a valid CScriptNum, script execution when spending this output MUST fail and terminate immediately. 256+** If this value is less than 0 or is greater than or equal to the number of outputs, script execution when spending this output MUST fail and terminate immediately. 257+ 258+* <code><revault-vout-idx></code> is an up to 4-byte CScriptNum-encoded number optionally indicating the index of an output which, in conjunction with the trigger output, carries forward the value of this input, and has an identical scriptPubKey to the current input. 259+** If this value does not decode to a valid CScriptNum, script execution when spending this output MUST fail and terminate immediately. 260+** If this value is greater than or equal to the number of outputs, script execution when spending this output MUST fail and terminate immediately.
sanket1729 commented at 1:56 am on September 1, 2023:I think we should fix to CScritpNum(-1) to avoid potential malleability vectors.
jamesob commented at 10:32 am on September 1, 2023:Yep, good point. I’ll update this.Axxel2323 approvedAxxel2323 commented at 2:06 am on September 1, 2023: noneThank you. Apparently I can’t multi task when codingfixup! fix off-by-one and revault-idx malleability
Co-authored-by: sanket1729 <sanket1729@gmail.com>
instagibbs commented at 2:21 pm on September 5, 2023: memberGiven that this spec requires an ec mult, I think each OP_VAULT invocation should be counted in the sigops budget in taproot? Lack of batch validation possibility is also a bit of a downer, maybe recost it in light of that?ChrisMartl commented at 3:33 pm on September 5, 2023: none@jamesob Could you please provide an analysis statement, which effects or how this proposal could increment the exploit exposure for the Bitcoin system due to the loose flexibility of Bitcoin’s script (predicative processing)?
Bitcoin has this predicate issue (since genesis) and it is necessary to perform a risk analysis about this for every proposed change.
sanket1729 commented at 11:21 pm on September 11, 2023: contributor@instagibbs, agreed. The costing seems to be an important missing component in the BIP. Rereading BIP341, footnote 12, I don’t think that batch validation was considered while costing the 50 execution budget for CHECKSIG.
I think that having a fixed (50 ?) execution budget for this also makes sense. Note that this operation also consists hashing potentially 127*32 bytes (maximum length of control block).
- One option is to cost this dynamically we can cost this operation dynamically based on control block size. I don’t like this because we introduce more complexity with this.
- Another option is to argue that this is similar to checksig operator which already does a similar computation for sighash value. And this is no different than it. However, sighash values are cached, but we cannot thing being hashed here. If we argue it this way, we can keep a simple 50 cost rule. For most (useful) cases, The stack would consist of
0 // Stack: Example for CTV vault 1 // - <flu-script> ------- 3 bytes 2 // - <n-pushes> ------- 1 byte 3 // [ n items ... ] ------- 2 items: 1 + 32 4 // - <trigger-vout-idx> --- min 1 byte 5 // - <revault-vout-idx> --- min 1 byte 6 // - <revault-amount> ---- min 1 byte
total cost per OP_VAULT = 3 + 1 + (1 + 32) + 1 + 1 + 1 + = 41 bytes + 7 (one per each element for varint len prefix) + 1(op_vault itself) = 49 bytes. Or 50 bytes if people want to withdraw more than 127 sats(1 byte CScriptNum).
Allocating 50 budget is also good because people don’t need any sort of witness stuffing of any sort to make sure of multiple op_vault in a same transaction input. And this is also consistent with signature costing model. Normal sighash hashes computation takes about 300 bytes (assuming pre-computed data), but a tree with depth 128 would do a hash of 4kb and a ec tweak operation.
I don’t think this is a big cost as compared to schnorr signature verification, but this should be benchmarked to make sure that this is within the same ballpark.
- Regular checksig operation that calls schnorrsigverify.
- A max length control block with CTV vault operation.
Another point to note is that this operation is similar to signing, not verification. I would expect this is benchmark to be faster than checksig verification because we have access to precomputed G values. So, there is even an argument to cost this at a smaller budget(25?) instead of 50.
Summary
To recap my opinion,
a) There should be some cost. We should avoid a potential dos vector where an user can dup all input elements and call op_vault multiple times. b) Having a fixed cost is easy to implement and reason about instead of a dynamic cost based on control block size. The cost is dominated by formula
cost_hashing(control_block_len) + cost_x_only_add
. c) Verify that the cost is dominated by cost_x_only_add for max control block size(128 * 32) and compute a cost number by comparing this cost with checksigschnorr in tapscript. d) Verify that common use-cases don’t have to any sort of witness stuffing to make them useful.instagibbs commented at 12:39 pm on September 12, 2023: memberd) Verify that common use-cases don’t have to any sort of witness stuffing to make them useful.
The only use-cases I’ve seen involve 1 or 2 invocations, with the second invocation often being the same destination which would allow caching
jamesob commented at 1:55 pm on September 12, 2023: memberThe costing seems to be an important missing component in the BIP.
Thanks for the analysis, @sanket1729. Indeed this is an important part of the proposal that I’d missed.
I think that having a fixed (50 ?) execution budget for this also makes sense.
I agree. As Greg noted, most uses of OP_VAULT should be a single evaluation per input - there’s really no reason to do more aside from a speculative use (collateral lockup) that would require other changes (i.e. reworking OP_VAULT to leave residual revault CAmount on the stack).
So I am comfortable “overcosting” OP_VAULT, since I think any use beyond a single invocation per input is worthless, at least as written right now. My only concern would be (as you mention) providing enough headroom so that the common use (OP_VAULT gated by a CHECKSIG) doesn’t require witness stuffing (i.e. inflating the witness unnaturally to add the necessary sigops budget, since budget is determined by input witness length in BIP-342).
I agree with everyone else that a static cost is the way to go for simplicity’s sake.
But if as you say the marginal witness data for the OP_VAULT stack in this usage is at or above 50 bytes, then I think we should be okay costing OP_VAULT at 50 and still leaving enough headroom for a CHECKSIG beforehand.
I don’t think this is a big cost as compared to schnorr signature verification, but this should be benchmarked to make sure that this is within the same ballpark.
I suspect that the EC mult to verify the output taptweak will dominate all the hashing (and be less expensive than a Schnorr verification) but I will work on writing this benchmark.
It is also worth noting that there cannot be different valid OP_VAULT invocations (in terms of hash/taptweak check) for the same output, so I think that limits the DoSability a good deal. In other words, a lot is cachable or limited by output size.
You could maybe play games by varying the revault parameters, but the hashing/taptweaking will be the same, since varying revault only changes the deferred amount checks that are queued in the case of successful input evaluation.
in bip-0345.mediawiki:277 in 4aae726be9 outdated
272+** Note: the leaf-update data items will be in the same order in the ''leaf-update-script'' as they appeared on the stack. 273+* If the scriptPubKey of ''triggerOut'' does not match that of a taptree that is identical to that of the currently evaluated input, but with the leaf script substituted for ''leaf-update-script'', script execution when spending this output MUST fail and terminate immediately. 274+** Note: the parity bit of the resulting taproot output is allowed to vary, so both values for the new output must be checked. 275+* Let the output designated by <code><revault-vout-idx></code> (if the index value is non-negative) be called ''revaultOut''. 276+* If the scriptPubKey of ''revaultOut'' is not equal to the scriptPubKey of the input being spent, script execution when spending this output MUST fail and terminate immediately. 277+* Implemetation recommendation: if the sum of the amounts of ''triggerOut'' and ''revaultOut'' (if any) are not greater than or equal to the value of this input, script execution when spending this output SHOULD fail and terminate immediately.
twhit223 commented at 8:56 pm on September 13, 2023:I think this could use additional clarification. On initial read, I was confused as to why the ‘’triggerOut’’ amount should be greater than or equal to the input amount. To this point, the BIP discusses how once vaulted, an input can be triggered for withdrawal. Fees were glanced over in the beginning. So my mental model was that an input in a vault would be spent through a trigger tx as follows:
- Specify input to be spent (input_idx) which contains an amount (A)
- Specify trigger amt (B)
- Specify revault amt (C)
- Spend input_idx with amt A such that: fee = A - (B + C)
In this case, the logic would be that the sum of ‘’triggerOut’’ and ‘‘revaultOut’’ must be less than or equal to the value of the input for this tx to be valid. While some clarification is made later in the BIP, I think a discussion of the fee structure needs to happen before walking through the Python code of trigger checks.
instagibbs commented at 9:04 pm on September 13, 2023:fees aren’t a part of the specification, “protecting input value” is. It’s an accumulator of value-to-outputs over the various OP_VAULT invocations.
that said, if you can think of a way to make the text clearer, would be great.
ajtowns commented at 6:23 am on October 13, 2023:“implementation” has two n’s in it
jamesob commented at 8:56 pm on January 3, 2024:Fixed the spelling issue and added an explanatory note to the recommendation, which hopefully makes it more easily understood.in bip-0345.mediawiki:393 in 4aae726be9 outdated
388+== Implementation == 389+ 390+A sample implementation is available on bitcoin-inquisition [https://github.com/jamesob/bitcoin/tree/2023-01-opvault-inq here], with an associated [https://github.com/bitcoin-inquisition/bitcoin/pull/21 pull request]. 391+ 392+ 393+== Applications ==
twhit223 commented at 9:00 pm on September 13, 2023:For completeness, it would be nice to have a quick mention of the actual “withdrawal transaction” mentioned in this section. Even if all it does is state that the withdrawal tx is trivially easy once the trigger timelock has elapsed.
ajtowns commented at 6:27 am on October 13, 2023:Might be better to have this as a separate BIP, separate the consensus spec from the application advice – presumably once the consensus change is made, application devs will be more interesting in reading about the application advice than the consensus details?
jamesob commented at 9:07 pm on January 3, 2024:If many others also feel the applications section should be separated out, I can do that. However I should note that I intentionally bundled them together to avoid, e.g. the bad usability seen in the CLTV/CSV/nLockTime BIPs, which require having three separate BIPs open to fully understand the behavior. I think there is high utility in bundling the most common use of OP_VAULT along with its specification.in bip-0345.mediawiki:580 in 4aae726be9 outdated
575+ 576+For vaults using unauthorized recovery, the recovery 577+transaction relies on the use of either fully-spent fee inputs or an ephemeral 578+anchor output. This means that vaults which do not use recovery authorization 579+are essentially dependent on v3 transaction relay policy being deployed. 580+
twhit223 commented at 9:22 pm on September 13, 2023:Is the interpretation of this that vaults using unauthorized recovery could “pre-load” themselves with varying size UTXOs to pay for fees based on the fee rate at the time of trigger (with the understanding that those UTXOs will have to be fully spent towards fees)? The last sentence makes it sound like unauthorized recovery is unusable without v3 transaction relay being deployed. A bit more clarification here would be helpful.
jamesob commented at 9:11 pm on January 3, 2024:Correct - “fully spent fee inputs” there confirms your question.jamesob commented at 4:58 pm on September 22, 2023: memberReporting back on costing.
I’ve written a benchmark (https://github.com/bitcoin-inquisition/bitcoin/pull/21/commits/dcf14426971908f7fbdcd06b3f884d4232fac8da) that simulates the relative cost of an OP_VAULT invocation vs. a successful Schnorr verification, and I was surprised to find that the taptweak check (including the necessary hashing) with a max-length control block is actually about as expensive as a good Schnorr verification; in fact, it’s about 5% slower!
relative ns/verify verify/s err% ins/verify bra/verify miss% total benchmark 100.0% 39,301.34 25,444.43 0.1% 419,357.03 13,067.01 0.0% 0.54 opvault-tweak
883.6% 4,447.92 224,824.24 0.0% 41,841.00 242.00 0.0% 0.54 schnorr-bad-verify
106.4% 36,938.11 27,072.31 0.1% 405,222.03 3,905.01 0.0% 0.53 schnorr-good-verify
I did some profiling and generated a flamegraph (full interactive graph here); perhaps as expected, most time is spent on the secp256k tweak_add operation, but I was surprised that a substantial amount of time is spent on
ComputeTapbranchHash
.As mentioned earlier, some of the midstate can probably be cached there. But to be conservative, I’ve costed OP_VAULT at 60 (vs. 50 for successful Schnorr verification) (https://github.com/bitcoin-inquisition/bitcoin/pull/21/commits/207b33e7912a19175b943f570efba8b837ae9eb7). I’ve verified that all existing testcases pass, and (given requisite witness sizes) I have to bump the cost much higher to encounter any issues running out of budget during expected use.
If 60 sounds like a fine cost to everyone, I’ll update the BIP. And then I think we’re in okay shape to merge?
sanket1729 commented at 9:48 pm on September 23, 2023: contributor@jamesob, interesting. I spend some time digesting the numbers that you wrote. I have a small piece of rust code also produces the similar result. The performance is worse for me because I don’t have AVX/sha-ni intrinsic.
I am not informed enough to make this choice across different architectures with various CPU features, but at least for my somewhat old system it puts the number closer to 90/95. Given that
- prior budgeting of hash values was probably done with sha2 intrinsic machines,
- and most modern machines have sha-ni support, I am inclined to set a 60 limit to be consistent with the precedent.
0| relative | ns/verify | verify/s | err% | total | benchmark 1|---------:|--------------------:|--------------------:|--------:|----------:|:---------- 2| 100.0% | 93,845.36 | 10,655.83 | 4.4% | 0.01 | `opvault-tweak` 3| 1,711.3% | 5,483.97 | 182,349.53 | 1.8% | 0.01 | `schnorr-bad-verify` 4| 195.2% | 48,076.90 | 20,800.01 | 6.7% | 0.01 | :wavy_dash: `schnorr-good-verify` (Unstable with ~19.7 iters. Increase `minEpochIterations` to e.g. 197)
I also reproduced with similar results using rust-bitcoin software. https://github.com/sanket1729/rust-bitcoin/tree/vault_bench
If 60 sounds like a fine cost to everyone, I’ll update the BIP. And then I think we’re in okay shape to merge?
It sounds fine to me. I would be great if one of BIP342 authors provide more input on specific number.
I think this is in okay shape to merge. Excited to play with this after this is merged in inquisition and might have more feedback based on it.
jamesob commented at 1:37 pm on September 24, 2023: member@sanket1729 for what it’s worth, you can experiment with OP_VAULT pretty conveniently right now on regtest using this repo: https://github.com/jamesob/opvault-demo. I’m hoping it’s very easy to set up if you’ve already got Docker installed on your machine.ajtowns commented at 5:40 pm on September 24, 2023: contributorIt sounds fine to me. I would be great if one of BIP342 authors provide more input on specific number.
The 50 figure mostly just matches the ratio between MAX_BLOCK_SIGOPS_COST and MAX_BLOCK_WEIGHT. When testing, it seemed that filling a block with the various possible slow opcodes all ended up with similar performance: 3,000,000 “1000 OP_ROLL” took about 4.3s, a block full of “OP_3DUP OP_HASH256 OP_DROP OP_HASH256 OP_DROP OP_HASH256” took about 3.6s, and those were roughly the same as 80k checksigs. Obviously this varies across different hardware.
instagibbs commented at 7:04 pm on September 24, 2023: memberAssuming the client caches each
OP_VAULT
’s computed p2tr outputs, I think this means that the worst would be that each invocation results in a different output script, which means they must pay for 34*4=136WU per invocation. An attack seems pretty self-limiting, as a single block can only contain ~23,250 p2tr outputs, excluding everything else.Not sure what number this results in exactly, but perhaps this is useful for analysis.
jamesob force-pushed on Sep 29, 2023BIP-345: add sigops cost of 60 014b832e07jamesob commented at 1:22 pm on September 29, 2023: memberI’ve updated the BIP to giveOP_VAULT
a sigops cost of 60. Is there anything else we should wait for here before merging?in bip-0345.mediawiki:49 in 014b832e07 outdated
44+ 45+==== Example uses ==== 46+ 47+A common configuration for an individual custodying Bitcoin is "single 48+signature and passphrase" using a hardware wallet. A user with such a 49+configuration might concerned about the risk associated with relying on a
darosior commented at 10:55 am on October 4, 2023:nit
0configuration might be concerned about the risk associated with relying on a
in bip-0345.mediawiki:271 in 014b832e07 outdated
266+ 267+After the stack is parsed, the following validation checks are performed: 268+ 269+* Decrement the per-script sigops budget (see [https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#user-content-Resource_limits BIP-0342]) by 60<ref>'''Why is the sigops cost for OP_VAULT set to 60?''' To determine the validity of a trigger output, OP_VAULT must perform an EC multiplication and hashing proportional to the length of the control block in order to generate the output's expected TapTweak. This has been measured to have a cost in the worst case (max length control block) of roughly twice a Schnorr verification. Because the hashing cost could be mitigated by caching midstate, the cost is 60 and not 100.</ref>; if the budget is brought below zero, script execution MUST fail and terminate immediately. 270+* Let the output designated by <code><trigger-vout-idx></code> be called ''triggerOut''. 271+* If the scriptPubKey of ''triggerOut'' is not a witness program of the same version and same tapleaf version as the currently executing script, script execution MUST fail and terminate immediately.
darosior commented at 1:01 pm on October 4, 2023:The scriptPubKey doesn’t have a tapleaf version, i think you should only keep the witness program version check here?
0* If the scriptPubKey of ''triggerOut'' is not a witness program of the same version, script execution MUST fail and terminate immediately.
In fact, since this is only defined for segwit v1 this may as well check explicitly for it?
0* If the scriptPubKey of ''triggerOut'' is not a version 1 witness program, script execution MUST fail and terminate immediately.
jamesob commented at 6:07 pm on October 9, 2023:Good observation, fixed. cc @instagibbsjamesob force-pushed on Oct 9, 2023BIP-0345: restrict trigger output to v1 witness
Co-authored-by: Antoine Poinsot <darosior@protonmail.com>
in bip-0345.mediawiki:227 in eb3fb727c3 outdated
222+The tapscript opcodes <code>OP_SUCCESS187</code> (<code>0xbb</code>) and 223+<code>OP_SUCCESS188</code> (<code>0xbc</code>) are constrained with new rules 224+to implement <code>OP_VAULT</code> and <code>OP_VAULT_RECOVER</code>, 225+respectively. 226+ 227+=== <code>OP_VAULT</code> evaluation ===
ajtowns commented at 5:41 am on October 13, 2023:I’d like to review the discussion from bitcoin-dev 2023-03, with a slightly different opcode split.
What we currently have is:
[revault_amount revault_idx] [unvault_idx unvault_pushes unvault_n unvault_script] OP_VAULT
[idx hash] OP_VAULT_RECOVER
where
OP_VAULT
does both unvaulting and revaulting, and both doscriptPubKey
checks and deferred amount checks.What I’d like to propose for consideration is:
[idx amount] OP_REVAULT
[idx amount pushes... n script] OP_UNVAULT
[idx amount sPK] OP_VAULT_RECOVER
(~OP_VAULT_FREEZE
~)
with the behaviour that
idx amount
specifies the given amount from this input is aggregated into the output at idx (via the deferred checks), and thescriptPubKey
at idx matches (respectively) the scriptPubKey being spent by this input, the scriptPubKey you get by replacing the current tapleaf with the script prefixed by n-pushes of the given n stack items, or the given sPK. If the amount is -1 that represents the remaining value from this input. If the amount is 0, or the amount is -1 but the remaining amount is 0, the operation succeeds. If the operation succeeds,1
is left on the stack.I think that’s a slightly better split, in that it separates out the “spend/revault” operations into separate opcodes, and also make the “claim the entire remainder” vs “claim and explicit remount” logic available to each vault operation.
It’s possible to go further in breaking down the logic:
- you could split out the amount verification and the scriptPubKey verification into different opcodes, but then you need to manually link the
idx
between the two, but that seems to just gets awkward for no real benefit (there’s no point restricting an output amount or an output scriptPubKey on their own, after all) - you could split out the “build up a script by prefixing pushes” operation from the “verify a scriptPubKey is made up by replacing the current script with a given script”; but that seems slightly exploitable (as you could be building up a script much larger than 520 bytes)
You can reimplement OP_VAULT as:
[unvault_idx unvault_amt unvault_pushes... unvault_n unvault_script] [revault_idx revault_amount]
(input)OVER TOALT REVAULT DROP UNVAULT DROP -1 FROMALT REVAULT
(script)
I think this enables plausible new behaviour compared to the current spec; eg limiting the amount you can withdraw:
[unvault_idx unvault_amt unvault_pushes... unvault_n unvault_script] [revault_idx revault_amount]
(input)OVER TOALT REVAULT DROP
TOALT DUP 1ADD PICK DUP 0 GT VERIFY <limit> LT VERIFY FROMALT UNVAULT
DROP -1 FROMALT REVAULT
Or a “hodl until day D, but allow consolidations” script:
[sigA]
to spend afterD
, or[idx sigA]
to consolidate to outputidx
A CHECKSIGVERIFY DEPTH IF -1 REVAULT ELSE <D> CLTV ENDIF
instagibbs commented at 1:39 pm on October 13, 2023:OP_VAULT_FREEZE
I assume this is the ranamed “RECOVERY”
If the amount is -1 that represents the remaining value from this input.
Drilling down into this idea as before, it probably conceptually helps to drill down precisely what this case means.
e.g., per input script execution, the remaining value starts at total input value, and simply decrements for every
OP_*VAULT
invocation, flooring at 0? I think that still allows most realistic use-cases where certain amounts are locked in at destinations, with the remaining being put somewhere else. Collateral could be done by specifying the value explicitly after whatever-1
vault operation was done.Here was my attempt at a rework which avoids additional interpreter state, in case you hadn’t seen it https://github.com/jamesob/bips/pull/4
I think functionally these are very similar?
ajtowns commented at 4:30 am on October 14, 2023:OP_VAULT_FREEZE I assume this is the ranamed “RECOVERY”
Err, yeah; I started writing that comment making more changes to the behaviour and wanted to be able to redefine VAULT_RECOVER in terms of my suggestion, so gave it a new name to make it clearer which is old and which is new. Then removed those changes, but didn’t change the name back.
If the amount is -1 that represents the remaining value from this input.
Drilling down into this idea as before, it probably conceptually helps to drill down precisely what this case means.
e.g., per input script execution, the remaining value starts at total input value, and simply decrements for every
OP_*VAULT
invocation, flooring at 0? I think that still allows most realistic use-cases where certain amounts are locked in at destinations, with the remaining being put somewhere else. Collateral could be done by specifying the value explicitly after whatever-1
vault operation was done.Yes. At start of script execution, define
uint64_t vault_balance = input_amt;
. Every time a vault operation occurs:- if the amount is
-1
, increment the expected balance for the output at idx byvault_balance
; setvault_balance=0
- otherwise, increment the expected balance for the output at idx by
amount
; setvault_balance = vault_balance - min(amount, vault_balance)
Here was my attempt at a rework which avoids additional interpreter state, in case you hadn’t seen it jamesob#4
I think I’d forgotten it so reinvented it from scratch; but yeah, pretty much the same. I guess I think the
-1
approach is probably better given you can’t do maths in script on amounts more 21.475 BTC; so “10 BTC to A, 20 BTC to B, the rest to C” would be hard to specify, even when all the explicit amounts are representable as a 4BCScriptNum
.I suppose one other behaviour that could be allowed is “-1 1000 OP_FALSE OP_VAULT_FREEZE” (that is idx=-1, sPK=empty, amount>0) to allow some of the funds to be spent arbitrarily, eg to miner/watchtower fees.
in bip-0345.mediawiki:235 in eb3fb727c3 outdated
230+<code>0xbb</code>), the expected format of the stack, shown top to bottom, is: 231+ 232+<source> 233+<leaf-update-script-body> 234+<n-pushes> 235+[ n leaf-update script data items ... ]
ajtowns commented at 5:51 am on October 13, 2023:Should use eithern-pushes
orn
consistently.
ajtowns commented at 6:43 pm on November 6, 2023:(orpush_count
might be a clearer name?)
jamesob commented at 8:32 pm on January 3, 2024:Fixed.in bip-0345.mediawiki:246 in eb3fb727c3 outdated
241+where 242+ 243+* <code><leaf-update-script-body></code> is a minimally-encoded data push of a serialized script. <ref>In conjunction with the leaf-update data items, it dictates the tapleaf script in the output taptree that will replace the one currently executing.</ref> 244+** Otherwise, script execution MUST fail and terminate immediately. 245+ 246+* <code><n-pushes></code> is an up to 4-byte <code>CScriptNum</code>-encoded number indicating how many leaf-update script items should be popped off the stack. <ref>'''Why only prepending with data pushes?''' Prepending the <code>leaf-update-script-body</code> with opcodes opens up the door to prepending OP_SUCCESSX opcodes, to name a single issue only, side-stepping the validation that was meant to be run by the committed script.</ref>
ajtowns commented at 6:03 am on October 13, 2023:“minimally-encoded
CScriptNum
” ?s/prepend/prefix/g :smile:
jamesob commented at 8:36 pm on January 3, 2024:Fixed.in bip-0345.mediawiki:263 in eb3fb727c3 outdated
258+** If this value does not decode to a valid CScriptNum, script execution when spending this output MUST fail and terminate immediately. 259+** If this value is greater than or equal to the number of outputs, script execution when spending this output MUST fail and terminate immediately. 260+** If this value is negative and not equal to -1, script execution when spending this output MUST fail and terminate immediately.<ref>'''Why is -1 the only allowable negative value for revault-vout-idx?''' A negative revault index indicates that no revault output exists; if this value were allowed to be any negative number, the witness could be malleated (and bloated) while a transaction is waiting for confirmation.</ref> 261+ 262+* <code><revault-amount></code> is an up to 7-byte CScriptNum-encoded number indicating the number of satoshis being revaulted. 263+** If this value does not decode to a valid CScriptNum, script execution when spending this output MUST fail and terminate immediately.
ajtowns commented at 6:08 am on October 13, 2023:“when spending this output” seems redundant and slightly confusing (we’re looking at an input not an output; the entire tx fails not just this output; and other attempts to spend this output could succeed) – why not just “script execution MUST fail and terminate immediately” here and elsewhere?
jamesob commented at 9:21 pm on January 3, 2024:Fixedin bip-0345.mediawiki:265 in eb3fb727c3 outdated
260+** If this value is negative and not equal to -1, script execution when spending this output MUST fail and terminate immediately.<ref>'''Why is -1 the only allowable negative value for revault-vout-idx?''' A negative revault index indicates that no revault output exists; if this value were allowed to be any negative number, the witness could be malleated (and bloated) while a transaction is waiting for confirmation.</ref> 261+ 262+* <code><revault-amount></code> is an up to 7-byte CScriptNum-encoded number indicating the number of satoshis being revaulted. 263+** If this value does not decode to a valid CScriptNum, script execution when spending this output MUST fail and terminate immediately. 264+** If this value is not greater than or equal to 0, script execution when spending this output MUST fail and terminate immediately. 265+** If this value is non-zero but <code><revault-vout-idx></code> is negative, script execution when spending this output MUST fail and terminate immediately.
ajtowns commented at 6:09 am on October 13, 2023:If this value is zero,<revault-vout-idx>
must be-1
? Otherwise you still have malleability?
jamesob commented at 8:40 pm on January 3, 2024:Added, and the single allowable negative value (-1) for<revault-vout-idx>
is fixed above.in bip-0345.mediawiki:283 in eb3fb727c3 outdated
278+* Implemetation recommendation: if the sum of the amounts of ''triggerOut'' and ''revaultOut'' (if any) are not greater than or equal to the value of this input, script execution when spending this output SHOULD fail and terminate immediately. 279+** Amount checks are ultimately done with deferred checks, but this check can help short-circuit obviously invalid spends. 280+* Queue a deferred check<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> that ensures the satoshis for this input's <code>nValue</code> minus <code><revault-amount></code> are included within the output <code>nValue</code> found at <code><trigger-vout-idx></code>. 281+* Queue a deferred check that ensures <code><revault-amount></code> satoshis, if non-zero, are included within the output's <code>nValue</code> found at <code><revault-vout-idx></code>. 282+** These deferred checks could be characterized in terms of the pseudocode below (in ''Deferred checks'') as<br /><code>TriggerCheck(input_amount, <revault-amount>, <trigger-vout-idx>, <revault-vout-idx>)</code>. 283+
ajtowns commented at 6:17 am on October 13, 2023:In so far as rehashing the tapleaf path with a replaced script can be expensive, perhaps an implementation recommendation should be made to store theH_TapLeaf()
value after the first successfulOP_VAULT
, and afterwards simply compare if the hashed script matches the cached value, without recalculating the merkle root and taproot tweak.in bip-0345.mediawiki:284 in eb3fb727c3 outdated
279+** Amount checks are ultimately done with deferred checks, but this check can help short-circuit obviously invalid spends. 280+* Queue a deferred check<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> that ensures the satoshis for this input's <code>nValue</code> minus <code><revault-amount></code> are included within the output <code>nValue</code> found at <code><trigger-vout-idx></code>. 281+* Queue a deferred check that ensures <code><revault-amount></code> satoshis, if non-zero, are included within the output's <code>nValue</code> found at <code><revault-vout-idx></code>. 282+** These deferred checks could be characterized in terms of the pseudocode below (in ''Deferred checks'') as<br /><code>TriggerCheck(input_amount, <revault-amount>, <trigger-vout-idx>, <revault-vout-idx>)</code>. 283+ 284+If none of the conditions fail, a single true value (<code>0x01</code>) is left on the stack.
ajtowns commented at 6:18 am on October 13, 2023:These are all effectively*VERIFY
operations that either succeed or abort execution; seems slightly strange to leave a true value on the stack.
jamesob commented at 9:02 pm on January 3, 2024:Otherwise to abid by cleanstack (in the common case) we’d have to append anOP_TRUE
to each script following theOP_VAULT*
invocation. I figure why not save the byte?
ajtowns commented at 4:42 pm on January 5, 2024:You’ll normally have a signature check as well, so can just useCHECKSIG
to get a 1 and save the byte that way? I think the only case where it could make sense to not have a signature would be withVAULT_RECOVER
, which is the (hopefully) extremely rare case where you’re probably not worried about a witness byte anyway?in bip-0345.mediawiki:273 in eb3fb727c3 outdated
268+ 269+* Decrement the per-script sigops budget (see [https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#user-content-Resource_limits BIP-0342]) by 60<ref>'''Why is the sigops cost for OP_VAULT set to 60?''' To determine the validity of a trigger output, OP_VAULT must perform an EC multiplication and hashing proportional to the length of the control block in order to generate the output's expected TapTweak. This has been measured to have a cost in the worst case (max length control block) of roughly twice a Schnorr verification. Because the hashing cost could be mitigated by caching midstate, the cost is 60 and not 100.</ref>; if the budget is brought below zero, script execution MUST fail and terminate immediately. 270+* Let the output designated by <code><trigger-vout-idx></code> be called ''triggerOut''. 271+* If the scriptPubKey of ''triggerOut'' is not a version 1 witness program, script execution MUST fail and terminate immediately. 272+* Let the script constructed by taking the <code><leaf-update-script-body></code> and prefixing it with minimally-encoded data pushes of the <code><n-pushes></code> leaf-update script data items be called the ''leaf-update-script''. 273+** Note: the leaf-update data items will be in the same order in the ''leaf-update-script'' as they appeared on the stack.
ajtowns commented at 6:22 am on October 13, 2023:This still seems ambiguous? If the top of the stack is A and the next element is B; having the script push A then B will mean that B is on the top of the stack when the remainder of the script is executed.
jamesob commented at 8:53 pm on January 3, 2024:Just removing this line as it probably creates more confusion than it resolves.in bip-0345.mediawiki:370 in eb3fb727c3 outdated
365+ 366+This ensures that all compatible vault inputs can be batched into shared 367+corresponding trigger or recovery outputs while preserving their entire input value. 368+ 369+ 370+== Policy changes ==
ajtowns commented at 6:25 am on October 13, 2023:Not sure the policy changes thoughts really make sense/add very much in this document.
jamesob commented at 9:06 pm on January 3, 2024:I disagree - I think this piece of policy is an important shim in this proposal because it forces awareness of a pinning vector. If we remove this policy requirement, we can add a recommendation somewhere, but I’m not sure of any benefit or possible use of not following that policy constraint.
This section is relatively short and the associated code is too, so I think it would be kind of silly to remove it for no good reason other than BIPs apparently shouldn’t talk about policy.
If there is some practical reason not to enforce this policy, I would be very curious to hear it.
ajtowns commented at 4:45 pm on January 5, 2024:I would have suggested just having a separate BIP for the policy approach, but apparently policy is now out of scope for BIPs so… The point of separating would have been that the consensus changes could continue to work even as policy evolves over time, so locking in a description of a policy could become confusing when that policy becomes outdated.in bip-0345.mediawiki:422 in eb3fb727c3 outdated
417+ <spend-delay> 2 $leaf-update-script-body OP_VAULT, (ii) 418+ 419+ ... [ possibly other leaves ] 420+ } 421+) 422+</source>
ajtowns commented at 6:26 am on October 13, 2023:Might be asking too much, but these seem like they would be better as diagrams?
jamesob commented at 9:08 pm on January 3, 2024:If someone wants to contribute I’ll be happy to incorporate them.in bip-0345/vaults.drawio:262 in eb3fb727c3 outdated
257+ <mxGeometry x="233" y="430" width="100" height="120" as="geometry" /> 258+ </mxCell> 259+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-53" value="" style="group" parent="1" vertex="1" connectable="0"> 260+ <mxGeometry x="465" y="307.5" width="120" height="95" as="geometry" /> 261+ </mxCell> 262+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-15" value="<p style="line-height: 10%;"><b>scriptPubKey</b></p><p style="border-color: var(--border-color); line-height: 4.8px;"><i style="border-color: var(--border-color);"><b style="border-color: var(--border-color);"><font style="border-color: var(--border-color);" color="#0066cc">recov-hash&nbsp;</font><font style="border-color: var(--border-color);" color="#00060d">...</font></b>&nbsp;</i></p><p style="border-color: var(--border-color); line-height: 4.8px;"><span style="border-color: var(--border-color); background-color: initial;">&nbsp; OP_UNVAULT</span></p>" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="IMLKYxiTQTyD-2dyPs5i-53" vertex="1">
ajtowns commented at 4:39 pm on October 26, 2023:OP_UNVAULT doesn’t exist anymore, and batch-sweep doesn’t seem to be included anywhere; perhaps this should be dropped from the source file?
jamesob commented at 9:13 pm on January 3, 2024:Dropped the unused diagram.fixup! rename `n-pushes` -> `push-count` c2cec65937fixup! misc. feedback from AJ and twhit223 327025b369fixup! remove unused diagram de9ef59307jamesob commented at 9:12 pm on January 3, 2024: memberI think I’ve at least responded to all outstanding feedback.illesdavid approvedjamesob commented at 8:38 pm on January 16, 2024: memberIs there anything else required for merge here?sanket1729 approvedajtowns commented at 2:47 am on February 6, 2024: contributorIs there anything else required for merge here?
I presume saying “please merge this” is required, if nothing else?
BIP-0345: add copyright eccf3db139luke-jr merged this on Feb 23, 2024luke-jr closed this on Feb 23, 2024
github-metadata-mirror
This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-10-30 03:10 UTC
This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me