([http://fc16.ifca.ai/bitcoin/papers/MES16.pdf MES16]) and informally since [https://web.archive.org/web/20160220215151/https://bitcointalk.org/index.php?topic=511881.0 2014]. The value of
* amounts and withdrawal patterns must be precommitted to,
* 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,
* the particular fee management technique or wallet must be decided upon vault creation,
This proposal intends to address the problems outlined above by
providing a delay period/recovery path use with minimal transactional and
operational overhead using a specialized covenant.
The design goals of the proposal are:
* '''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.
* '''batched operations''' for recovery and withdrawal to allow managing multiple vault coins efficiently.
* '''unbounded partial withdrawals''', which allows users to withdraw partial vault balances without having to perform the setup ceremony for a new vault.
* '''dynamic unvault targets''', which allow the proposed withdrawal target for a vault to be specified at withdrawal time rather than when the vault is first created. This would remove the need for a prespecified, intermediate wallet that only exists to route unvaulted funds to their desired destination.
* '''dynamic fee management''' that, like dynamic targets, defers the specification of fee rates and source to unvault time rather than vault creation time.
These goals are accompanied by basic safety considerations (e.g. not being
vulnerable to pinning) and a desire for concision, both in terms of the number
This proposal is designed to be compatible with any future sighash modes (e.g. <code>SIGHASH_GROUP</code>) or fee management strategies (e.g. [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-September/018168.html transaction sponsors]) that may be introduced. Use of these opcodes will benefit from, but do not strictly rely on, [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-September/020937.html v3 transaction relay] and [https://github.com/instagibbs/bips/blob/ephemeral_anchor/bip-ephemeralanchors.mediawiki ephemeral anchors].
These tapleaf replacement rules, described more precisely below, ensure a
timelocked withdrawal, where the timelock is fixed by the original
<code>OP_VAULT</code> parameters, to a fixed set of outputs (via
<code>OP_CHECKTEMPLATEVERIFY</code><ref>'''Why is <code>OP_CHECKTEMPLATEVERIFY</code> (BIP-119) relied upon for this proposal?''' During the withdrawal process, the proposed final destination for value being withdrawn must be committed to. <code>OP_CTV</code> is the simplest, safest way to commit the spend of some coins to a particular set of outputs. An earlier version of this proposal attempted to use a simpler, but similar method, of locking the spend of coins to a set of outputs, but this method introduced txid malleability.<br />Note that if some other method of locking spends to a particular set of outputs should be deployed, that method can be used in the <code>OP_VAULT</code> <code><leaf-update-script-body></code> with no changes.</ref>) which is chosen when the withdrawal
* '''vault transaction''': encumbers some coins into a Taproot structure that includes at least one <code>OP_VAULT</code> leaf and one <code>OP_VAULT_RECOVER</code> leaf.
* '''trigger transaction''': spends one or more <code>OP_VAULT</code>-tapleaf inputs into an output which is encumbered by a timelocked withdrawal to a fixed set of outputs, chosen at trigger time. This publicly broadcasts the intent to withdraw to some specific set of outputs.<br /><br />The trigger 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> as the <code>OP_VAULT</code>-containing input(s) being spent.
* '''withdrawal transaction''': spends the timelocked <code>OP_CHECKTEMPLATEVERIFY</code> trigger inputs into a compatible set of final withdrawal outputs per the CTV hash, after the trigger inputs have matured per the spend delay. Timelocked CTV transactions are the motivating usage of OP_VAULT, but any script template can be specified during the creation of the vault.
* '''recovery transaction''': spends one or more <code>OP_VAULT_RECOVER</code>-tapleaf inputs to the prespecified recovery path, which can be done at any point before the withdrawal transaction confirms. Each input can optionally require a witness satisfying a specified ''recovery authorization'' script, an optional script prefixing the <code>OP_VAULT_RECOVER</code> fragment. The use of recovery authorization has certain trade-offs discussed later.
* <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>
** Otherwise, script execution MUST fail and terminate immediately.
* <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>
* 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.
** 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.
* <code><trigger-vout-idx></code> is an up to 4-byte CScriptNum-encoded number indicating the index of the output which, in conjunction with an optional revault output, carries forward the value of this input, and has an identical taptree aside from the currently executing leaf.
** If this value does not decode to a valid CScriptNum, script execution when spending this output MUST fail and terminate immediately.
** 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.
* <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.
* Let the output designated by <code><trigger-vout-idx></code> be called ''triggerOut''.
* If the scriptPubKey of ''triggerOut'' is not a witness program of the same version as the currently executing script, script execution MUST fail and terminate immediately.
* 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''.
** The leaf-update data items will be in the same order in the ''leaf-update-script'' as they appeared on the stack.
* 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.
** Note: the parity bit of the resulting taproot output is allowed to vary, so both values for the new output must be checked.
* Let the output designated by <code><revault-vout-idx></code> (if the index value is non-negative) be called ''revaultOut''.
* 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.
* 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 MUST fail and terminate immediately.
* (Deferred<ref>'''What is a deferred check and why does this proposal require them for correct script evaluation?''' A deferred check is a validation check that is executed only after all input scripts have been validated, and is based on aggregate information collected during each input's EvalScript run.<br /><br />Currently, the validity of each input is (usually) checked concurrently across all inputs in a transaction. Because this proposal allows batching the spend of multiple vault inputs into a single recovery or withdrawal output, we need a mechanism to ensure that all expected values per output can be summed and then checked. This necessitates the introduction of an "aggregating" set of checks which can only be executed after each input's script is evaluated. Note that similar functionality would be required for batch input validation or cross-input signature aggregation.</ref>) the <code>nValue</code> of ''triggerOut'', plus the <code>nValue</code> of ''revaultOut'' if one exists, must equal the sum of all vault inputs which cite it as their corresponding trigger output. If these values are not equal, the script MUST fail and terminate immediately.
** If this value does not decode to a valid CScriptNum, script execution when spending this output MUST fail and terminate immediately.
** 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.
* Let the output at index <code><recovery-vout-idx></code> be called ''recoveryOut''.
* 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.
* 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.
* (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>
* When validating an <code>OP_VAULT_RECOVER</code> input being spent, 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 griefed in the process.</ref>
*# 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
*# the version of the recovery transaction has an nVersion other than 3.
If the script containing <code>OP_VAULT_RECOVER</code> is 34 bytes or less<ref>34 bytes is the length of a recovery script that consists solely of <code><recovery-sPK-hash> OP_VAULT_RECOVER</code>.</ref>, let
it be called "unauthorized," because there is no script guarding the recovery
process. In order to prevent pinning attacks in the case of unauthorized
recovery - since the spend of the input (and the structure of the
transaction) is not authorized by a signed signature message - the output structure of
** If the spending transaction has two outputs, and the output which is not ''recoveryOut'' is not an [https://github.com/instagibbs/bips/blob/ephemeral_anchor/bip-ephemeralanchors.mediawiki 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>
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].
The specification above, perhaps surprisingly, does not cover how a relative timelocked withdrawal process with a fixed target is implemented. The tapleaf update semantics specified in <code>OP_VAULT</code> as well as the output-based authorization enabled by <code>OP_VAULT_RECOVER</code> can be used to implement a vault, but they are incomplete without two other pieces:
In order to vault coins, they must be spent into a witness v1<ref>'''Can <code>OP_VAULT</code> be used with a future witness version (greater than 1)?''' Yes, however use of yet to be defined witness versions is discouraged, since such a usage makes the coins spendable by anyone.</ref> <code>scriptPubKey</code>
* <code>$leaf-update-script-body</code> is, for example, <code>OP_CHECKSEQUENCEVERIFY OP_DROP OP_CHECKTEMPLATEVERIFY</code>.
** This is one example of a trigger script, but ''any'' script fragment can be used, allowing the creation of different types of vaults. For example, you could use <code>OP_CHECKSEQUENCEVERIFY OP_DROP OP_CHECKSIG</code> to do a time-delayed transfer of the coins to another key. This also future-proofs <code>OP_VAULT</code> for future scripting capabilities.
* The script fragment in <code>(i)</code> is called the "trigger authorization," because it gates triggering the withdrawal. This can be done in whatever manner the wallet designer would like.
* The script fragment in <code>(ii)</code> is the incomplete <code>OP_VAULT</code> invocation - it will be completed once the rest of the parameters (the CTV target hash, trigger vout index, and revault vout index) are provided by the trigger transaction witness.
Unauthorized recovery simplifies vault use in that recovery never requires additional information aside from the location of the vault outpoints and the recovery path - the "authorization" is simply the reveal of the recovery path, i.e. the preimage of <code><recovery-sPK-hash></code>.
But because this reveal is the only authorization necessary to spend the vault coins to recovery, the user must expect to recover all such vaults at once, since an observer can replay this recovery (provided they know the outpoints).
With authorized recovery, the user must keep track of an additional piece of information: how to solve the recovery authorization script fragment when recovery is required.
If this key is lost, the user will be unable to initiate the recovery process for their coins. If an attacker obtains the recovery key, they may grief the user during the recovery process by constructing a low fee rate recovery transaction and broadcasting it (though they will not be able to pin because of the replaceability requirement on recovery transactions).
However, authorized recovery configurations have significant benefits. Batched recoveries are possible for vaults with otherwise incompatible recovery parameters. Fee management is much more flexible, since authorized recovery transactions are "free form" and unrelated inputs and outputs can be added, potentially to handle fees.
==== Recommendation: use a simple, offline recovery authorization key seed ====
The benefits of batching and fee management that authorized recovery provides are significant. If the recovery authorization key falls into the hands of an attacker, the outcome is not catastrophic, whereas if the user loses their recovery authorization key as well as their trigger key, the result is likely coin loss. Consequently, the author's recommendation is to use a simple seed for the recovery authorization key that can be written down offline and replicated.
Note that the recovery authorization key '''is not''' the recovery path key, and
this is '''much different''' than any recommendation on how to generate the
recovery path key itself.
=== Address reuse and recovery ===
When creating a vault, four factors affect the resulting P2TR address: