BIP 347: OP_CAT in Tapscript
View on GitHub
  BIP: 347
  Layer: Consensus (soft fork)
  Title: OP_CAT in Tapscript
  Author: Ethan Heilman <>
          Armin Sabouri <>
  Status: Draft
  Type: Standards Track
  Created: 2023-12-11
  License: BSD-3-Clause
  Post-History: 2023-10-21: [bitcoin-dev] Proposed BIP for OP_CAT


This BIP introduces OP_CAT as a tapscript opcode which allows the concatenation of two values on the stack. OP_CAT would be activated via a soft fork by redefining the opcode OP_SUCCESS126 (126 in decimal and 0x7e in hexadecimal). This is the same opcode value used by the original OP_CAT.


This document is licensed under the 3-clause BSD license.


When evaluated, the OP_CAT instruction:

  1. Pops the top two values off the stack,
  2. concatenates the popped values together in stack order,
  3. and then pushes the concatenated value on the top of the stack.

Given the stack x2, where x2 is at the top of the stack, OP_CAT will push x1 || x2 onto the stack. By || we denote concatenation. OP_CAT fails if there are fewer than two values on the stack or if a concatenated value would have a combined size greater than the maximum script element size of 520 bytes.

This opcode would be activated via a soft fork by redefining the tapscript opcode OP_SUCCESS126 (126 in decimal and 0x7e in hexadecimal) to OP_CAT.


Bitcoin Tapscript lacks a general purpose way of combining objects on the stack, restricting the expressiveness and power of Tapscript. This prevents, among many other things, the ability to construct and evaluate merkle trees and other hashed data structures in Tapscript. OP_CAT, by adding a general purpose way to concatenate stack values, would overcome this limitation and greatly increase the functionality of Tapscript.

OP_CAT aims to expand the toolbox of the tapscript developer with a simple, modular, and useful opcode in the spirit of Unix R. Pike and B. Kernighan, "Program design in the UNIX environment", 1983, To demonstrate the usefulness of OP_CAT below we provide a non-exhaustive list of some usecases that OP_CAT would enable:

  • Bitstream, a protocol for the atomic swap (fair exchange) of bitcoins for decryption keys, that enables decentralized file hosting systems paid in Bitcoin. While such swaps are currently possible on Bitcoin without OP_CAT, they require the use of complex and computationally expensive Verifiable Computation cryptographic techniques. OP_CAT would remove this requirement on Verifiable Computation, making such protocols far more practical to build in Bitcoin. R. Linus, "BitStream: Decentralized File Hosting Incentivised via Bitcoin Payments", 2023,
  • Tree signatures provide a multisignature script whose size can be logarithmic in the number of public keys and can encode spend conditions beyond n-of-m. For instance a transaction less than 1KB in size could support tree signatures with up to 4,294,967,296 public keys. This also enables generalized logical spend conditions. P. Wuille, "Multisig on steroids using tree signatures", 2015,
  • Post-Quantum Lamport signatures in Bitcoin transactions. Lamport signatures merely require the ability to hash and concatenate values on the stack. J. Rubin, "[bitcoin-dev] OP_CAT Makes Bitcoin Quantum Secure CheckSigFromStack for Arithmetic Values", 2021, It has been proposed that if ECDSA is broken or a powerful computer was on the horizon, there might be an effort to protect ownership of bitcoins by allowing people to mark their taproot outputs as "script-path only" and then move their coins into such outputs with a leaf in the script tree requiring a Lamport signature. It is an open question if a tapscript commitment would preserve the quantum resistance of Lamport signatures. Beyond this question, the use of Lamport Signatures in taproot outputs is unlikely to be quantum resistant even if the script spend-path is made quantum resistant. This is because taproot outputs can also be spent with a key. An attacker with a sufficiently powerful quantum computer could bypass the taproot script spend-path by finding the discrete log of the taproot output and thus spending the output using the key spend-path. The use of "Nothing Up My Sleeve" (NUMS) points as described in BIP341 to disable the key spend-path does not disable the key spend-path against a quantum attacker as NUMS relies on the hardness of finding discrete logs. We are not aware of any mechanism which could disable the key spend-path in a taproot output without a softfork change to taproot.
  • Non-equivocation contracts T. Ruffing, A. Kate, D. Schröder, "Liar, Liar, Coins on Fire: Penalizing Equivocation by Loss of Bitcoins", 2015, in tapscript provide a mechanism to punish equivocation/double spending in Bitcoin payment channels. OP_CAT enables this by enforcing rules on the spending transaction's nonce. The capability is a useful building block for payment channels and other Bitcoin protocols.
  • Vaults M. Moser, I. Eyal, and E. G. Sirer, Bitcoin Covenants, which are a specialized covenant that allows a user to block a malicious party who has compromised the user's secret key from stealing the funds in that output. As shown in A. Poelstra, "CAT and Schnorr Tricks II", 2021, OP_CAT is sufficient to build vaults in Bitcoin.
  • Replicating CheckSigFromStack A. Poelstra, "CAT and Schnorr Tricks I", 2021, which would allow the creation of simple covenants and other advanced contracts without having to presign spending transactions, possibly reducing complexity and the amount of data that needs to be stored. Originally shown to work with Schnorr signatures, this result has been extended to ECDSA signatures R. Linus, "Covenants with CAT and ECDSA", 2023, file-covenants_cat_ecdsa-md.

OP_CAT was available in early versions of Bitcoin. In 2010, a single commit disabled OP_CAT, along with another 15 opcodes. Folklore states that OP_CAT was removed in this commit because it enabled the construction of a script whose evaluation could have memory usage exponential in the size of the script. For example, a script that pushed a 1-byte value on the stack and then repeated the opcodes OP_DUP, OP_CAT 40 times would result in a stack element whose size was greater than 1 terabyte assuming no maximum stack element size. As Bitcoin at that time had a maximum stack element size of 5000 bytes, the effect of this expansion was limited to 5000 bytes. This is no longer an issue because tapscript enforces a maximum stack element size of 520 bytes.


Our decision to reenable OP_CAT by redefining a tapscript OP_SUCCESSx opcode to OP_CAT was motivated to leverage the tapscript softfork opcode upgrade path introduced in BIP342.

We specifically choose to use OP_SUCCESS126 rather than another OP_SUCCESSx as OP_SUCCESS126 uses the same opcode value (126 in decimal and 0x7e in hexadecimal) that was used for OP_CAT prior to it being disabled in Bitcoin. This removes a potential source of confusion that would exist if we had a opcode value different from the one used in the original OP_CAT opcode.

While the OP_SUCCESSx opcode upgrade path could enable us to increase the stack element size while reenabling OP_CAT, we wanted to separate the decision to change the stack element size limit from the decision to reenable OP_CAT. This BIP takes no position in favor or against increasing the stack element size limit.

Backwards Compatibility

OP_CAT usage in a non-tapscript script will continue to trigger the SCRIPT_ERR_DISABLED_OPCODE. The only change would be to OP_CAT usage in tapscript. This change to tapscript would be activated as a soft fork that redefines an OP_SUCCESSx opcode (OP_SUCCESS126) to OP_CAT.

Reference implementation

case OP_CAT:
  if (stack.size() < 2)
    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
  valtype& vch1 = stacktop(-2);
  valtype& vch2 = stacktop(-1);
  if (vch1.size() + vch2.size() > MAX_SCRIPT_ELEMENT_SIZE)
    return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
  vch1.insert(vch1.end(), vch2.begin(), vch2.end());

The value of MAX_SCRIPT_ELEMENT_SIZE is 520.

This implementation is inspired by the original implementation of OP_CAT as it existed in the Bitcoin codebase prior to the commit "misc changes" 4bd188cS. Nakamoto, "misc changes", Aug 25 2010, which disabled it:

case OP_CAT:
    // (x1 x2 -- out)
    if (stack.size() < 2)
        return false;
    valtype& vch1 = stacktop(-2);
    valtype& vch2 = stacktop(-1);
    vch1.insert(vch1.end(), vch2.begin(), vch2.end());
    if (stacktop(-1).size() > 5000)
        return false;

An alternative implementation of OP_CAT can be found in Elements Roose S., Elements Project, "Re-enable several disabled opcodes", 2019,



We wish to acknowledge Dan Gould for encouraging and helping review this effort. We also want to thank Madars Virza, Jeremy Rubin, Andrew Poelstra, Bob Summerwill, Tim Ruffing and Johan T. Halseth for their feedback, review and helpful comments.



See an issue with rendering or formatting? Please submit an issue on GitHub is presented by nickmonad

Stay humble. Stack sats.

All content is owned and licensed by the respective author(s). This website makes no claim of ownership.