Skip to content

Transaction Malleability

Here is a fact that surprises nearly every engineer the first time they hear it: before SegWit, a transaction’s ID could change after you signed it — without invalidating the signature and without changing where the money went. A third party (or even a miner) could take your valid transaction, tweak it, and broadcast a version with a different txid that spent the same coins to the same places.

This is transaction malleability, and understanding it teaches you something deep about what a signature does and doesn’t protect.

A txid is the double-SHA-256 hash of the serialized transaction — the whole byte blob: inputs, outputs, amounts, and (pre-SegWit) the unlocking scripts (scriptSig) that contain the signatures.

txid = SHA256( SHA256( serialize(entire transaction including scriptSig) ) )

That last clause is the whole problem. The signature is inside the bytes that get hashed, but a signature cannot sign itself. So there’s a part of the transaction the signature does not commit to — and if you can change that part without breaking the signature, you change the hash, and the txid along with it.

A Bitcoin signature commits to the inputs, outputs, and amounts — what is being spent and where it goes. It does not commit to the exact byte-encoding of the scriptSig that carries it. There were several ways to re-encode the same valid spend:

  • ECDSA signature symmetry. For any valid ECDSA signature (r, s), the value (r, -s mod n) is also a valid signature for the same message and key. Flip s to its complement and the signature still verifies — but the bytes differ, so the hash differs. (This is the “low-S” issue; BIP 62 / BIP 146 later required low-S to shut it down.)
  • DER encoding slack. Signatures were DER-encoded, and sloppy parsers accepted extra padding bytes, non-minimal length encodings, etc. Add a harmless byte → different serialization → different txid.
  • Script push-opcode equivalence. The same data can be pushed onto the stack with different opcodes (OP_PUSHDATA1 vs a direct push), producing different bytes that execute identically.

In every case: same inputs, same outputs, same destination, same signature validity — different txid.

Why this is dangerous: the broken assumption

Section titled “Why this is dangerous: the broken assumption”

Malleability is not a theft vulnerability by itself — the attacker cannot redirect your coins; the outputs are signed. The danger is entirely about systems that assume a txid is stable.

Scenario 1 — the naïve withdrawal system (the “Mt. Gox excuse”)

Section titled “Scenario 1 — the naïve withdrawal system (the “Mt. Gox excuse”)”

Imagine an exchange that processes withdrawals like this:

1. Build & sign a withdrawal tx, record its txid.
2. Broadcast it.
3. Poll the network: "has txid X confirmed?"
4. If X never appears, assume the send failed → retry / re-credit the user.

Now an attacker requests a withdrawal, then grabs the broadcast transaction, mutates it into X' (same coins, same destination — they still receive their money), and rebroadcasts X' fast enough that X' is the version miners confirm. The exchange is watching for X, never sees it, concludes “the withdrawal failed,” and credits the user again or re-sends. The user has now been paid twice, and the exchange’s books don’t balance.

When Mt. Gox collapsed in 2014, it publicly blamed transaction malleability for its missing coins. Researchers later showed malleability accounted for only a tiny fraction of the loss — Gox’s real problems were elsewhere — but the episode burned the concept into the industry’s memory and made the case for fixing it urgent.

Scenario 2 — the one that actually mattered: payment channels

Section titled “Scenario 2 — the one that actually mattered: payment channels”

The deeper casualty was off-chain protocols. A Lightning-style payment channel works by building transactions that spend the output of a not-yet-broadcast transaction.

Funding tx ──► creates a 2-of-2 output (the channel)
Commitment / refund tx ──► spends that output, signed in advance,
held offline as a safety net

The refund transaction’s input must reference the funding output by (txid, index). But if the funding transaction is malleable, its txid can change the instant it’s broadcast — and now the pre-signed refund transaction points at a (txid, index) that no longer exists. Your safety net references a ghost. Your funds can be stuck.

This is why Lightning was not safely possible before malleability was fixed. You cannot build a tower of transactions on top of an unconfirmed one whose identity can shift under you.

Segregated Witness (activated August 2017) fixed malleability at the root by segregating the witness — moving the signatures and unlocking data out of the part of the transaction that the txid commits to.

┌──────────────────────────────────────┐
txid = │ version, inputs (txid:index, sequence)│ ← signatures NO LONGER in here
│ outputs (amounts, scriptPubKey) │
│ locktime │
└──────────────────────────────────────┘
wtxid = the above + ┌───────────────────────┐
│ witness (signatures) │ ← lives here, hashed separately
└───────────────────────┘

After SegWit:

  • the txid is computed over everything except the witness, so changing a signature’s encoding no longer changes the txid;
  • a separate wtxid covers the witness too (used in the witness Merkle commitment).

Because the malleable bits (signatures) are excluded from the txid, the txid becomes stable the moment you’ve decided the inputs and outputs — even before broadcast. That is what made Lightning safe to build: you can now pre-sign a child transaction against a parent’s txid and trust that the txid won’t move.

How does this help untrusting strangers agree on one ledger? Malleability is a case where the ledger agreed on the right answer (the money moved correctly) while disagreeing on the name of the event. Fixing it didn’t change who-owns-what — it gave transactions a stable identity, which is the foundation every higher-layer protocol (channels, vaults, swaps) silently depends on.

  1. Why can’t a signature commit to the entire transaction it’s part of?
  2. Give two distinct mechanisms by which a pre-SegWit transaction could be re-serialized to a new txid without invalidating its signature.
  3. Malleability can’t steal your coins. Explain precisely why a withdrawal-tracking system could still lose money because of it.
  4. Why does building a payment channel require a non-malleable funding transaction?
  5. In one sentence: what did SegWit physically move, and why did that fix the problem?