A Bitcoin 2FA Wallet with Presigned Recovery and Inheritance
This is an alternative construction for Robin Linus’s 2FA wallet proposal. It preserves the central idea: an emergency withdrawal must consume a token created by a separate kickoff transaction, and the withdrawal delay runs from that token’s creation. A deposit can remain protected indefinitely until someone starts an emergency exit.
The construction uses existing Taproot signature rules. It needs neither OP_TEMPLATEHASH2 nor OP_TEMPLATEHASH, and it does not require deleting private keys. Instead, the user and the 2FA server jointly presign two restricted spends for each deposit. A Taproot SIGHASH_NONE signature commits to the complete input set, including the emergency token and its relative timelock, while leaving outputs unspecified. At withdrawal time, the token holder supplies another signature that authorizes the destination and fee.
The tradeoff is per-UTXO interaction and durable signature backups. A receiving address alone cannot provide the unilateral exits: the actual deposit outpoint and amount must be known before they can be armed.
There are three participants: the user, holding signing keys U; the server, holding independent cosigning keys S and checking a separate authentication factor; and the heir, holding independent keys H. Use distinct derived keys for different deposits, roots, tokens, and fee outputs. The role symbols below abbreviate those keys.
Every deposit D_i is a standard Taproot output whose internal key is the MuSig2 aggregate of U_i and S_i, with the usual Taproot tweak and no spendable script leaves. Both participants verify the resulting output key. Normal payments require a fresh joint signature. The emergency signatures are completed joint signatures produced earlier under that same output key. MuSig2 supports this aggregation and the necessary Taproot tweaking. BIP327
Retaining the ordinary signing keys creates no additional emergency bypass: producing a new unrestricted deposit signature still requires both parties, exactly as for a normal payment. The server never receives the user’s secret key. A completed emergency signature gives its holder only the transaction authorization encoded by that signature.
For each D_i, assign one recovery token R_i and one inheritance token I_i. A canonical recovery kickoff K_R creates the R_i outputs; a separate canonical inheritance kickoff K_I creates the I_i outputs. Each kickoff can provision a finite segment of slots for multiple deposits. The two token outpoints for a deposit are therefore known before either kickoff is broadcast.
The user and server produce the following two deposit-input authorizations:
| Field | Recovery skeleton T_R,i |
Inheritance skeleton T_I,i |
|---|---|---|
| Transaction version | 2 |
2 |
nLockTime |
0 |
0 |
| Input 0 | R_i |
I_i |
| Input 0 sequence | Encoded recovery delay d_R |
Encoded inheritance delay d_I |
| Input 1 | D_i, sequence 0xffffffff |
D_i, sequence 0xffffffff |
| Deposit signature | Joint U_i + S_i, SIGHASH_NONE |
Joint U_i + S_i, SIGHASH_NONE |
| Outputs | Chosen when withdrawing | Chosen when withdrawing |
| Live token authorization | User | Heir |
The saved deposit signatures, sigma_R,i and sigma_I,i, are 65 bytes: a completed 64-byte Schnorr signature followed by 0x02. They are not unfinished MuSig2 sessions or server partial signatures. Each is verified against the deposit’s tweaked Taproot output key before being accepted as a backup.
For Taproot, SIGHASH_NONE without ANYONECANPAY commits to the transaction version, locktime, all input outpoints, all input amounts and scriptPubKeys, all input sequences, the signing input’s index, and that input’s annex state. It omits the outputs. This all-sequences commitment is specifically part of Taproot’s signature hashing; do not substitute legacy or SegWit-v0 SIGHASH_NONE semantics. BIP341 signature message
Consequently, neither token substitution, token removal, input insertion, nor a shorter token sequence preserves the deposit signature. Changing the transaction to version 1 or setting the token sequence’s disable bit also invalidates it. Adding ANYONECANPAY would remove the required commitment to the other input and must never be used for these authorizations.
For example, a recovery sequence can encode 4,725 units of 512 seconds, approximately four weeks. An inheritance sequence can encode 30,375 units, corresponding to a nominal 180-day interval. In both cases set the time-type flag, leave the disable flag clear, and interpret maturity using BIP68’s median-time-past rules. These are chain-time delays, not exact civil-calendar deadlines. The sequence on D_i disables its own relative lock; the relevant age is that of the token produced by the kickoff. BIP68
No CSV opcode is required in the deposit script or the token script. BIP68 enforces the sequence already committed by the deposit signature. Leaving the token’s own script immediately spendable is intentional: it permits cancellation before the delay expires.
Each token has a Taproot internal key constructed so that its discrete logarithm is unknown, preventing key-path spending. All public keys used in the leaves are 32-byte BIP340 keys. Its leaves are:
R_i, user leaf:
OP_SIZE 64 OP_EQUALVERIFY
<U_token_i> OP_CHECKSIG
I_i, heir leaf:
OP_SIZE 64 OP_EQUALVERIFY
<H_token_i> OP_CHECKSIG
I_i, user cancellation leaf:
OP_SIZE 64 OP_EQUALVERIFY
<U_cancel_i> OP_CHECKSIG
The 64-byte length requirement forces SIGHASH_DEFAULT. The token signature therefore commits to the withdrawal’s outputs as well as its inputs. The saved deposit signature can be distributed without giving arbitrary holders a way to choose a destination: a valid token signature is still necessary. The same deposit signature may accompany different valid withdrawals, but each needs the appropriate token holder’s fresh authorization. These are ordinary Tapscript signature checks. BIP341 signature validation, BIP342
The kickoff roots can also be built without covenants. The recovery root is a user-controlled Taproot output. The user can sign the canonical K_R when needed, or spend the root elsewhere to disable that segment’s recovery authorizations. Any alternative root spend creates different outpoints and cannot supply the tokens required by the saved deposit signatures.
The inheritance root has a user-controlled key path and this script leaf:
OP_SIZE 64 OP_EQUALVERIFY
<U_kickoff_auth> OP_CHECKSIGVERIFY
OP_SIZE 64 OP_EQUALVERIFY
<H_kickoff> OP_CHECKSIG
During setup, the user signs the root input of the exact canonical K_I through this leaf using SIGHASH_DEFAULT. The heir stores that signature, the transaction, the leaf, and its control block. To initiate inheritance later, the heir supplies their own fresh SIGHASH_DEFAULT signature. The initial script stack, bottom to top, is [sig_H, presig_U].
The user’s saved signature fixes all kickoff inputs, sequences, and outputs. The heir cannot redirect the root or substitute a different kickoff using that authorization. The user’s key path remains available for destroying the root or broadcasting the same canonical transaction. A copied inheritance package alone cannot start the clock because it lacks the heir’s live signature.
Each canonical kickoff uses native SegWit inputs, fixed transaction fields, fixed token outputs, and a dedicated fee-bump output spendable by the relevant broadcaster. It should include an appropriate base fee and outputs meeting the applicable relay policy. Use an ordinary funded fee-bump output for the baseline construction; ephemeral anchors are not a requirement.
Later signatures change the witness without changing the canonical kickoff’s txid. That is what allows deposit signatures to refer to its future token outpoints. The user and heir must preserve the non-witness transaction exactly. BIP141
The baseline uses independent finite segments rather than a recursive chain. When more slots are needed, establish another pair of roots and canonical kickoffs. Existing deposits remain assigned to their original segments. Future roots and signatures cannot be inferred merely by assuming an indefinitely extensible presigned transaction graph.
Setup proceeds as follows:
- Establish and confirm the two kickoff roots. Reserve them against ordinary wallet coin selection. Construct the canonical kickoffs, their token scripts, and the inheritance-root presignature. Distribute the inheritance kickoff material.
- Establish the exact deposit transaction and identify
D_i: txid, output index, amount, and scriptPubKey. Assign freshR_iandI_islots from segments whose kickoffs have not begun. - Independently verify both skeletons and all spent-output data. The server must check the approved token keys, kickoff ancestry, delays, input order, sighash, and deposit amount. Both participants must reject a token with a bypass key path, an alternative claim script, or an already-running delay.
- Run two separate MuSig2 signing sessions, with fresh nonces, to produce and verify the complete deposit signatures. The server authorizes this operation under the wallet’s authentication and policy rules. It must not expose unrestricted signing of caller-supplied
SIGHASH_NONEmessages. - Durably back up each exit package and make the inheritance package accessible independently of the user’s device and the cosigning server. Only then label the deposit as having armed unilateral exits.
- Continue monitoring the roots and their canonical kickoffs during setup. If a kickoff begins while enrollment is in progress, stop assigning its slots and resolve the active exit before treating a deposit as dormant.
For a self-funded deposit, the transaction can be fixed and the exits armed before releasing its funding signatures, provided the funding transaction has stable non-witness serialization. The same ordering can protect change created by a normal payment: finalize the parent transaction, arm the new change output, and then release the parent signatures. Any later parent replacement requires new exit signatures for the replacement outpoint.
An unsolicited external payment is different. The wallet can receive it at a normal MuSig2 address, but it cannot presign exits for an unknown outpoint. Until the server participates in arming the actual UTXO, that UTXO is ordinary 2-of-2 custody with no guaranteed unilateral recovery. Waiting for confirmation resolves outpoint uncertainty but leaves this setup interval. An interactive payer can supply a stable transaction in advance; a plain address cannot promise that property.
Once armed, normal payments use a fresh user–server MuSig2 signature after authentication. Spending D_i invalidates all saved emergency signatures for it. The wallet may combine deposits freely for normal payments because both signers are online. Any resulting change needs its own completed emergency setup.
To recover unilaterally, the user signs and broadcasts canonical K_R, waits for R_i to mature under the committed BIP68 sequence, and reconstructs T_R,i. The user chooses the outputs and fee, supplies a fresh 64-byte signature for R_i, and attaches sigma_R,i as the deposit input’s key-path witness. The server does not participate.
Inheritance works similarly. The heir completes and broadcasts canonical K_I using the saved user root signature, waits for I_i to mature, chooses a destination and fee, and spends I_i through its heir leaf alongside D_i carrying sigma_I,i. This works after loss of the user’s keys and disappearance of the server only if the complete inheritance material remains available.
Kickoff transactions have fixed txids because the saved exits reference their outputs. Fee-bump them through a child spending the dedicated fee output; the child can contribute external fee inputs. Changing a kickoff’s outputs, inputs, or base fee changes its txid and invalidates the associated exits. CPFP still needs a workable relay path, sufficient fee funds, and eventual confirmation; it does not guarantee inclusion.
The final emergency withdrawal is more flexible. Its outputs were not committed by the saved deposit signature, so the user or heir can choose a higher fee by reducing the payout and signing the token again. If a previously broadcast withdrawal needs replacement, applicability depends on relay policy; the old token signature will not authorize the new outputs. An extra input cannot be appended to this withdrawal because it would invalidate the deposit signature.
Each saved authorization covers exactly its own input list. Two independently presigned withdrawals cannot simply be merged into a batch. The baseline therefore uses one final withdrawal per deposit. An optional batch must be jointly presigned in advance with every deposit and token fixed; it becomes unusable if any required input is spent elsewhere. Independent exits should remain available even when optional batches are prepared.
Monitoring watches the kickoff roots, deposits, and token spends. A canonical kickoff should trigger alerts promptly, while the cancellation deadline is calculated from its confirmed chain position. Broadcast alone is not proof that either a kickoff or a cancellation has taken effect.
Before a kickoff confirms, the user can attempt to cancel by spending its root to a different transaction. This is a conflicting spend; it must win confirmation to invalidate the canonical token outpoints. Once a kickoff confirms, the user cancels by spending the relevant tokens in a separate transaction that does not include the deposits. Its sequences disable relative locks, and its token signatures use SIGHASH_DEFAULT.
The absence of CSV in the token leaves makes that cancellation immediately valid. The delayed deposit signatures do not constrain a standalone token spend. After cancellation confirms, a saved emergency transaction fails because its required token has already been consumed. To terminate an entire affected segment, consume all of that segment’s unspent tokens, including unused slots, or confirm a spend destroying its still-unspent root. The baseline has no successor root to destroy.
Cancellation should confirm comfortably before token maturity, allowing for fees, delayed alerts, and reorgs. Once the delay expires, the user and claimant are competing spenders. A server or watchtower may hold a separately presigned cancellation directing token value to a predetermined destination. Such a service can disable the covered exits; it cannot choose a different payout or move the deposits. That denial-of-service authority is real and should be explicit.
Cancellation does not require a deposit to move. The deposit script contains no token outpoint. If the user and server remain available, they can establish fresh roots and jointly sign replacement exits for the same unspent D_i. This restores the canceled path without a redeposit transaction. Creating new signatures alone does not revoke old ones: the old root or token must first be consumed with sufficient confirmation confidence.
If the user’s signing key was compromised, restoring an exit to the same deposit does not rotate that key. Move funds through the normal path to fresh user–server keys when that is the required remedy. If the server is unavailable, new joint authorizations cannot be created; preserve an independent surviving exit rather than assuming a canceled path can always be repaired.
The same revocation rule applies to changing heirs or delays. A former heir’s package remains valid while its canonical root or token can still support the saved spend. Policy changes and replacement metadata cannot revoke an on-chain spending capability.
A recovery or inheritance backup must include the following information for every armed deposit:
| Record | Required contents |
|---|---|
| Deposit | Outpoint, amount, scriptPubKey, key-derivation information, and verified output-key construction |
| Emergency authorization | Complete aggregate signature, exact input ordering, version, locktime, sequences, and annex convention |
| Token | Canonical outpoint, amount, scriptPubKey, applicable leaf and control block, and authorized key derivation |
| Kickoff | Exact canonical transaction, root spent-output data, and information needed to sign and fee-bump it |
| Inheritance kickoff | User’s completed root presignature, its leaf and control block, and the heir’s signing instructions |
| Lifecycle | Slot assignment, segment identifier, cancellation history, and replacement-package records |
Keep the heir’s private keys separately under the heir’s control. The package needs sufficient public derivation information to use those keys correctly. Independent redundant copies can contain the presignatures without granting arbitrary deposit spending, although they expose wallet relationships and should be handled accordingly.
Two xpubs may allow an heir to discover deposit addresses, but they cannot regenerate missing aggregate signatures without the server’s participation. A small static envelope can contain keys and instructions for retrieving an authenticated, replicated archive; the per-deposit archive itself must grow. Lost presignatures are lost unilateral capabilities even when every seed is intact.
Address reuse is discouraged for privacy and bookkeeping. Unlike a script permanently tied to a slot, two UTXOs received at the same address can each be armed here using separate token pairs and separate signatures. Automatically treating an old package as covering a second UTXO is invalid: it commits to the first outpoint and amount.
Server-enforced spending limits and policy-change delays remain possible on normal payments and on the creation of new emergency authorizations. They are service guarantees, not additional consensus conditions. A user-signed policy authenticates the requested policy but does not force a malicious server to enforce it. Existing valid emergency signatures also cannot be withdrawn merely by changing server policy. A TEE, if used, introduces its own assurance assumptions.
The resulting failure behavior is:
| Event | Result and remaining requirement |
|---|---|
| Server disappears after both exits are armed | User recovery and heir inheritance remain usable from their backups. |
| Server disappears before a deposit is armed | That deposit has no assured unilateral exit; ordinary spending still needs the server. |
| User loses signing keys | Heir can claim armed deposits with their key and complete package. |
| User loses only the authentication factor | User can initiate the already-armed recovery path. |
| Attacker obtains only the server key | Cannot create unrestricted deposit signatures or satisfy user/heir token scripts. |
| Attacker obtains the user key | Can attempt delayed recovery and can sabotage roots or tokens; timely defensive action is necessary. Destruction of every available exit while the server is absent can strand funds. |
| Attacker obtains both deposit signing keys | Can spend immediately through the normal key path. |
| Heir attempts an unwanted claim | User must confirm cancellation or spend the deposit before the claim confirms. |
| Presignatures are lost | Chain scanning and xpubs cannot restore unilateral authorizations. |
| Old token has already matured | Its saved exit has no fresh waiting period; never reuse that slot for new enrollment. |
| Cancellation confirms | The covered exit is disabled; another exit may survive, or both signers can arm a replacement. |
| Fees or censorship prevent confirmation | Kickoff can be delayed and cancellation can fail to arrive in time; the protocol provides no inclusion guarantee. |
The central security argument is short. Without both deposit signing keys, an attacker must use an existing deposit signature. That signature requires the exact token and its committed sequence in a version-2 transaction. The token’s age therefore gates withdrawal, and its script requires the authorized user or heir to approve the outputs. Spending the token separately removes that authorization’s required input. Security against newly forged unrestricted spends rests on the ordinary user–server multisignature; no ephemeral-key erasure claim is involved.
The wallet must preserve several invariants: both exits are completed and backed up before a deposit is marked armed; every token is exclusively assigned and has no bypass spend path; no emergency deposit signature uses ANYONECANPAY; all MuSig2 sessions use fresh nonces; all old capabilities remain tracked until their required inputs are spent; and activated or canceled segments are closed to further enrollment.
Compared with the proposed opcode approach, this construction changes the following properties:
| Property | Presigned construction |
|---|---|
| New consensus rules | None |
| Secure deletion of setup keys | Not required |
| Dormant deposit’s timelock | Does not age toward unilateral spendability |
| Emergency destination | Selected when withdrawing |
| Extra emergency inputs or arbitrary batching | Unavailable without additional advance signatures |
| New deposit setup | Requires the actual outpoint and user–server interaction |
| Backup model | Growing per-deposit signature archive |
| Cancellation repair | Can rearm the same deposit with both signers after invalidating the old capability |
| Additional deposit tapleaves | None |
The signature-message construction was checked against all seven published BIP341 key-path spending vectors, followed by thirteen targeted checks of input binding, sequence and version changes, output flexibility, and annex handling. Those checks cover signature-message commitments, not a complete wallet, MuSig2 implementation, script execution, or mempool behavior. A reference implementation should additionally exercise inheritance-root witnesses, relative-lock maturity, conflicting cancellations, fee-bumping, parent replacement, and recovery from archived packages. BIP341 test vectors