Some time ago, we proposed a mechanism to address a long-standing issue in Bitcoin: the vulnerability of zero-confirmation (0-conf) transactions to double-spending attacks. Zero-confirmation transactions are transactions that merchants or services accept before they are confirmed in a block, often for practical reasons like improving user experience and reducing wait times. Our goal was to provide a lightweight, incentive-compatible way to discourage double-spending, without requiring changes to Bitcoin’s core consensus rules or relying on trusted third parties.

The solution we designed was, unfortunately, ahead of its time (or maybe too late?). The mechanism depended on a particular opcode (OP_AND) which had existed in Bitcoin’s scripting language early on but had since been disabled. Without this opcode, our construction couldn’t be deployed directly on Bitcoin. Despite its potential, the proposal remained theoretical.

Fast forward to today, and the Bitcoin scripting landscape is once again alive with possibilities. The community is engaged in a lively debate about re-enabling OP_CAT, another opcode from the early days of Bitcoin that was removed due to concerns about script complexity. The excitement around OP_CAT stems from the powerful new primitives it enables for Bitcoin scripts, from covenants to composable smart contracts… and our double-spending prevention mechanism! It turns out that OP_CAT can be used to implement our double-spending prevention mechanism, without the need for OP_AND (using a construction proposed by CremaLabs). In other words, the renewed interest in Bitcoin script extensibility might finally open the door to deploying our solution on the Bitcoin mainnet.

In this post, we’ll first summarize the core ideas of our original paper: how the protocol works, what it aims to prevent, and why it’s useful. Then, we’ll explain how a variant of the protocol can be implemented using OP_CAT, providing a concrete example of how small changes in Bitcoin’s script language can unlock meaningful improvements in transaction security.

Double-spending prevention: The Original Version

Double spending (attempting to spend the same digital token more than once) is one of the fundamental challenges digital currencies must address. Unlike physical cash, which is inherently resistant to duplication and changes hands in a tangible way, digital tokens can be trivially copied and reused unless strong security mechanisms are in place.

Bitcoin tackles this problem by maintaining an append-only ledger, the blockchain, that is replicated across all full nodes in the network. Transactions are considered final once they are confirmed and embedded in the blockchain, making it computationally infeasible to reverse or duplicate them without considerable effort.

To enable faster payments, however, Bitcoin users and services often rely on zero-confirmation transactions (those that have been broadcast to the network but are still waiting to be included in a block). These transactions allow for instant payments in use cases where latency matters: buying snacks from vending machines, paying at fast food counters, or withdrawing cash from Bitcoin-enabled ATMs. However, this convenience comes at a cost: since these transactions aren’t yet confirmed, they lack the protection of the blockchain and are vulnerable to double-spending attacks.

In our paper, we proposed a mechanism to reduce this vulnerability and discourage double-spending in zero-confirmation scenarios. The idea is to use the expressive power of Bitcoin’s scripting language alongside a subtle and well-known property (vulnerability?) of the ECDSA signature scheme. By combining these two elements, our construction provides a deterrent to attackers, making double-spending attempts riskier and less attractive in practice.

ECDSA vulnerability

Bitcoin originally relied on the Elliptic Curve Digital Signature Algorithm (ECDSA) for transaction authentication. While the more recent Taproot upgrade has introduced Schnorr signatures as an alternative, our original proposal was developed in the context of ECDSA.

One important characteristic of ECDSA is that it is probabilistic. This means that for a given message and private key, there isn’t a single unique signature. The exact signature produced depends on the choice of a random integer, usually denoted as k, which is selected freshly for every signature. This randomness is essential to the security of the scheme.

Let’s briefly outline how an ECDSA signature is computed:

  1. A random integer k is chosen.
  2. A point (x, y) is computed on the elliptic curve by multiplying k with the curve’s generator point G.
  3. The value r = x mod q becomes part of the signature.
  4. The second signature component, s, is calculated as: s = k⁻¹ (m + r . d) mod q, where d is the private key, and q is the order of the curve.

The signature is then made of two components, the values r and s.

The key point here is that the same value of k must never be reused for different messages (for the same private key). If it is, and an attacker can observe two such signatures, they can recover the private key.

This vulnerability is well-documented and has led to real-world key leaks in cases where poor randomness or nonce reuse occurred (famously in some implementations of Bitcoin wallets). In our proposal, we turn this vulnerability into a feature, intentionally constructing conditions that penalize malicious behavior by creating an incentive trap: if a double-spender tries to cheat, they risk leaking their private key.

Fixed-r Pay-to-Pubkey (FR-P2PK)

To implement our defense against double-spending, we introduce a custom Bitcoin script that we call the Fixed-r Pay-to-Pubkey (FR-P2PK) script. This construction is a variation of the standard Pay-to-Pubkey (P2PK) script, which requires a valid signature to spend an output. The key innovation in FR-P2PK is the addition of a condition: not only must the signature be valid, but it must also contain a specific, fixed r value.

This small change has big consequences. Because of the ECDSA vulnerability described earlier, if an attacker attempts to double-spend an FR-P2PK output by broadcasting two different transactions signed with the same r, they risk exposing their private key. Any node that sees both transactions can exploit the reused r to recover the private key – and with it, claim all the funds held by the attacker. In this way, the script creates a built-in disincentive: double-spending puts the attacker’s own money at risk.

The structure of the script is as follows:


ScriptPubKey:  OP_DUP <pubKey> OP_CHECKSIGVERIFY
               OP_SIZE <0x47> OP_EQUALVERIFY
               <sigmask> OP_AND <r> OP_EQUAL

ScriptSig:     <sig>

To spend an FR-P2PK output, the spender must provide a single ECDSA signature. This signature must both verify against the provided public key and include a specific r value when parsed. If the signature is malformed or if the r does not match the required value, the script will fail and the transaction will be invalid.

By enforcing a fixed r, we effectively create a trap for attackers. Honest users will never need to reuse r (they only sign once, with the correct parameters). But a malicious actor who attempts to double-spend the same output faces a dangerous trade-off: signing twice with the same r opens the door to full key compromise.

Double-spending prevention with OP_CAT and Shcnorr

A new proposal by CremaLabs called PurrSettle builds on this idea. While our original construction relied on ECDSA and the OP_AND opcode, PurrSettle adapts the approach to Schnorr signatures and the proposed reactivation of OP_CAT.

Instead of encoding the r value directly in the scriptPubKey of the output, PurrSettle embedds the r value into the output using a Taproot tweak, which commits to a script containing r. The witness then provides s, and the script concatenates it with r using OP_CAT to reconstruct the full signature. This reconstructed signature is then verified using OP_CHECKSIG.

The script structure looks as follows:


ScriptPubKey:  OP_1 <Q>
Witness:       <s> <OP_PUSHBYTES_32 R OP_SWAP OP_CAT OP_PUSHBYTES_32 P OP_CHECKSIG> <controlblock>

where <Q> is the Taproot output key, which commits to a script tree that includes the commitment to R.

The stack evaluation of the proposed script is explained in the PurrSettle blog, and an example transaction that spends from a (taproot-based) FR-P2PK output can be seen in the StarkWare signet.

While our original proposal for preventing double-spending on zero-confirmation transactions in Bitcoin laid the groundwork disencentivising double-spending in fast payments, the evolution of Bitcoin’s scripting capabilities has opened new avenues for improvement. PurrSettle takes advantage of these advancements, using Schnorr signatures and OP_CAT to refine and strengthen the concept. By splitting the signature into two parts (one committed to the script and the other provided during the transaction), it provides a way to implement the k-reuse vulnerability to prevent double-spending. As Bitcoin continues to evolve, mechanisms like PurrSettle represent an exciting direction for practical applications of cryptographic security in everyday transactions.

Leave a Reply

Your email address will not be published. Required fields are marked *