Glossary

BIP 349 (OP_INTERNALKEY)

A proposed Bitcoin opcode that pushes the Taproot internal key onto the script stack for use in script-path spending.

Key Takeaways

  • BIP 349 defines OP_INTERNALKEY, a proposed tapscript opcode that pushes the 32-byte x-only taproot internal key onto the script stack, eliminating the need to embed it as literal data.
  • The proposal replaces the reserved OP_SUCCESS203 slot (byte 0xcb), making it deployable as a backward-compatible soft fork under the existing tapscript upgrade framework defined in BIP 342.
  • BIP 349 is a core component of the LNHANCE soft-fork bundle, where it combines with CTV and CSFS to enable recursive covenants, delegation patterns, and more efficient Lightning constructions.

What Is BIP 349?

BIP 349 is a Bitcoin Improvement Proposal that specifies a new opcode called OP_INTERNALKEY for use within tapscript. Authored by Brandon Black and Jeremy Rubin, the proposal assigns opcode byte 0xcb to a single operation: pushing the taproot internal key onto the script execution stack during a script-path spend.

In taproot's design (BIP 341), every P2TR output commits to an "internal key" and optionally a merkle tree of scripts. When spending via the script path, Bitcoin's consensus code already extracts this internal key from the control block to verify the merkle proof. However, the script itself has no way to access that key during execution. BIP 349 closes this gap by making a value the consensus layer already possesses directly available to the script stack.

The proposal remains in Draft status, with a reference implementation available as Bitcoin Core PR #29269. It is testable on Bitcoin Inquisition, a signet-based testing environment for proposed consensus changes.

How It Works

BIP 349 is intentionally minimal. The opcode has no inputs and produces a single output: the 32-byte x-only representation of the taproot internal key (referred to as p in BIP 341). It operates only within tapscript with leaf version 0xc0, as defined in BIP 342.

Technical Specification

The complete execution semantics fit in a few lines:

Opcode:       0xcb (replaces OP_SUCCESS203)
Leaf version: 0xc0 (standard tapscript, BIP 342)
Stack input:  (none)
Stack output: <32-byte x-only internal key>
Failure:      (none — the push always succeeds if the opcode is reached)

The opcode replaces an OP_SUCCESS slot. Under current tapscript rules, OP_SUCCESS203 causes any script containing it to succeed unconditionally. BIP 349 restricts this behavior: after activation, scripts containing byte 0xcb are no longer automatically valid. Instead, the byte triggers a stack push and script execution continues normally. This strictly narrows the set of valid scripts, which is what makes the change a soft fork.

Weight Savings

Without OP_INTERNALKEY, a script that needs the internal key must embed it as a 32-byte literal push. With the opcode, the same operation requires just 1 byte:

# Without OP_INTERNALKEY (34 bytes: 1 push opcode + 32 key + 1 CHECKSIG)
<32-byte-internal-key> OP_CHECKSIG

# With OP_INTERNALKEY (2 bytes: 1 INTERNALKEY + 1 CHECKSIG)
OP_INTERNALKEY OP_CHECKSIG

This saves 32 bytes of witness data, translating to approximately 8 vBytes under the SegWit witness discount. While modest for a single script, the savings compound in constructions like channel factories and timeout trees where hundreds of script leaves reference the same key.

Design Rationale

The BIP identifies three core motivations for exposing the internal key to scripts:

Key-Spend with Script Conditions

Many spending policies require the internal key holder to sign, but only under additional conditions (a timelock, a hash preimage, or a second co-signer). Without OP_INTERNALKEY, the internal key must be duplicated as literal data in every script leaf that references it. The opcode eliminates this redundancy, saving weight and simplifying script construction.

NUMS Point for Hash-Locked Outputs

When key-path spending is intentionally disabled (for example, in a hash-locked covenant), the internal key is set to a Nothing-Up-My-Sleeve (NUMS) point: a valid secp256k1 x-coordinate with no known private key. BIP 349 notes that finding a valid NUMS point typically requires approximately 2 attempts, since roughly half of 32-byte values are valid x-coordinates on the curve.

Automatic Re-Keying

This is the most powerful motivation. In multi-party protocols where participants change (for example, updating a MuSig2 aggregate key when a signer rotates), scripts that use OP_INTERNALKEY automatically reference the new key without reconstruction. Scripts that embed the key as literal data break when the key changes, requiring the entire taptree to be rebuilt.

The BIP illustrates this with a concrete example:

# With OP_INTERNALKEY (auto-updates when key changes):
TR(X, {CTV OP_INTERNALKEY CSFS <S+1> CLTV})

# Without OP_INTERNALKEY (static, breaks on key change):
TR(X, {CTV <X> CSFS <S+1> CLTV})

In the first form, when the output is recreated with a new internal key X', the script automatically picks up the new key. In the second form, the script still references the old key X, invalidating the construction.

The LNHANCE Bundle

BIP 349 is part of the LNHANCE soft-fork proposal, which bundles it with three other opcodes to unlock new protocol constructions:

BIPOpcodeFunction
119OP_CHECKTEMPLATEVERIFYConstrains spending to a pre-committed transaction template
348OP_CHECKSIGFROMSTACKVerifies a Schnorr signature over arbitrary stack data
349OP_INTERNALKEYPushes the taproot internal key onto the stack
442OP_PAIRCOMMITProduces a tagged hash commitment of two stack elements

These opcodes compose in ways that none achieves alone. For example, OP_INTERNALKEY OP_CHECKSIGFROMSTACK verifies that the internal key holder signed an arbitrary message, enabling delegation without embedding the key in the script. Add CTV, and the internal key holder can authorize specific pre-committed transactions, forming the basis for vaults, LN-Symmetry channels, and non-interactive channel opens.

A separate proposal, BIP 448, also includes OP_INTERNALKEY alongside OP_TEMPLATEHASH (BIP 446) and CSFS as a "taproot-native covenant bundle." Both proposals share OP_INTERNALKEY as a common component, reflecting broad agreement on its utility even where other opcode choices differ.

Use Cases

Recursive Covenants

A covenant restricts how a UTXO can be spent, and a recursive covenant carries those restrictions forward across multiple transactions. BIP 349 enables a clean recursive pattern: a script that references the internal key via OP_INTERNALKEY persists across state transitions because the opcode dynamically resolves to whatever key the current output commits to. This avoids hardcoding keys into covenant scripts, which would break the recursion when keys change.

Combined with CTV (which constrains the transaction template) and CSFS (which validates authorization signatures), OP_INTERNALKEY allows covenant scripts that enforce spending rules indefinitely, without growing in size or complexity at each step.

Delegation

Delegation allows the internal key holder to authorize a third party to spend under specific conditions without revealing their private key or constructing a new multisig arrangement. The pattern works by combining OP_INTERNALKEY with CSFS:

# Delegation script: internal key holder signs a message
# authorizing a specific spending condition
<message> OP_INTERNALKEY OP_CHECKSIGFROMSTACKVERIFY
# ... additional conditions follow

The internal key holder signs a message off-chain that encodes the delegated spending conditions. Anyone can then satisfy the script by providing this signature, without needing the internal key holder to be online. This is valuable for Lightning channel constructions where one party may be offline during force-close scenarios.

LN-Symmetry Channels

LN-Symmetry (formerly eltoo) replaces Lightning's penalty-based channel model with a symmetric design where the latest state always wins. OP_INTERNALKEY lets update scripts reference the channel's aggregate signing key without hardcoding it. This simplifies channel state machines and reduces on-chain costs during unilateral closes. The LNHANCE soft-fork proposal details how these opcodes compose for practical Lightning improvements.

Timeout Trees and Channel Factories

Channel factories and timeout trees use hierarchical taptree structures where potentially hundreds of script leaves reference the same key. OP_INTERNALKEY saves 32 bytes per leaf, which compounds significantly in these large constructions. Combined with CTV for pre-committed exit paths, these structures can scale Lightning onboarding by orders of magnitude.

Vaults

Vault constructions use time-delayed spending to protect against key compromise. CTV commits to the exact withdrawal template, while OP_INTERNALKEY and CSFS provide flexible authorization. A watchtower can monitor for unauthorized withdrawal attempts and trigger a clawback before the timelock expires. The internal key holder authorizes withdrawals, but CTV ensures funds can only move to pre-committed destinations.

Why It Matters

BIP 349 is a small specification with outsized implications for Bitcoin's programmability. By giving tapscript access to data the consensus layer already possesses, it avoids adding new cryptographic complexity while unlocking expressive new spending conditions. Protocols that build on taproot, including Layer 2 networks like Spark, benefit from richer scripting capabilities that enable more sophisticated off-chain constructions with trustless on-chain settlement.

The proposal also demonstrates a design philosophy gaining traction in Bitcoin development: instead of purpose-built opcodes for specific features, BIP 349 provides a general-purpose primitive that composes with other opcodes. This composability is why it appears in multiple competing soft-fork bundles. As the covenant activation debate continues, OP_INTERNALKEY's minimal footprint and clear utility make it one of the least controversial components under discussion.

Risks and Considerations

Draft Status

BIP 349 has no activation parameters defined. Whether it ships as part of LNHANCE, BIP 448, or a standalone soft fork remains an open question. Developers building on OP_INTERNALKEY should treat it as experimental until a concrete activation path is established.

Bundling Dynamics

OP_INTERNALKEY's inclusion in multiple soft-fork bundles is a double-edged outcome. While it reflects consensus on the opcode's value, bundling means disagreement over a companion opcode (such as CTV or CSFS) could delay OP_INTERNALKEY's activation alongside it. The Bitcoin development community continues to debate whether bundling or individual activation produces better outcomes.

Limited Standalone Value

On its own, OP_INTERNALKEY provides a modest 8 vByte savings per key reference. Its transformative use cases (recursive covenants, delegation, LN-Symmetry) require companion opcodes. This creates a chicken-and-egg dynamic: the opcode's full value depends on other proposals also activating.

OP_SUCCESS Replacement

Redefining an OP_SUCCESS opcode is among the safest soft-fork approaches because it strictly narrows validity. However, anyone relying on OP_SUCCESS203's unconditional success behavior would have their spends invalidated after activation. While no known scripts depend on this specific behavior, the risk is non-zero for any OP_SUCCESS replacement.

Tapscript Scope

OP_INTERNALKEY works only in tapscript spends with leaf version 0xc0. It cannot be used in legacy scripts, P2WPKH, or P2WSH outputs. Protocols operating on pre-taproot output types cannot benefit from this opcode, limiting its applicability to newer taproot-native constructions.

This glossary entry is for informational purposes only and does not constitute financial or investment advice. Always do your own research before using any protocol or technology.