You sign a cheque for $200, payable to a specific landlord, for a specific apartment. Someone photocopies that signature and pastes it onto a blank cheque. That's the attack Bitcoin's sighash commitment scheme was built to make impossible.

The mechanism is less exotic than it sounds. When you authorise a Bitcoin transaction, your private key doesn't sign the transaction ID or a summary hash. It signs a serialised commitment that encodes specific fields from the transaction itself, and changing any of those fields breaks the signature. Which fields get included depends on the sighash flag appended to the signature. Getting that choice wrong has historically cost people real bitcoin.

What the commitment actually covers

Every Bitcoin signature is produced over a message called the sighash preimage. Under the original scheme, still used for legacy inputs, constructing it works roughly like this: take the transaction version, the inputs with their previous output references, the output scripts and values, the locktime, and a four-byte sighash type indicator, then double-SHA256 the whole thing. The private key signs that digest. The network, when verifying, reconstructs the same digest from the transaction it received and checks whether the signature fits.

The sighash type is the lever that controls scope.

`SIGHASH_ALL` (0x01) commits to every input and every output. That's the default, and it means the signature is bound to this exact set of recipients for this exact set of amounts. Swap any output and the signature is invalid. Full stop.

`SIGHASH_NONE` (0x02) commits to the inputs but signs no outputs at all. Useful in some multi-party constructions, genuinely dangerous if misunderstood: it lets anyone rewrite where the money goes after you've signed. Think of it as a cheque where the payee line is legally binding but the amount field is left in pencil.

`SIGHASH_SINGLE` (0x03) commits the signing input to exactly one corresponding output, the output at the same index, leaving the rest open to modification. There is a known edge case here: if the output index doesn't exist, legacy code returns a hash of `0x01...` instead of erroring. That behaviour has been exploited.

The `SIGHASH_ANYONECANPAY` modifier (0x80) can be OR'd with any of the above. It narrows input commitment to only the signing input, leaving other inputs open. That is how crowdfunding-style constructions work on Bitcoin: each contributor signs their own input without caring what other inputs end up in the final transaction.

Why replay is the actual threat model

A signature replay attack means taking a valid signature from one context and making it valid in another. Bitcoin's sighash design defeats the obvious form: because the signature commits to specific previous outputs (the UTXOs being spent), you cannot lift a signature from transaction A and paste it into transaction B spending different coins. The preimage differs, the hash differs, verification fails.

The subtler threat is cross-chain replay, and this is where the design's weaknesses became impossible to ignore.

When Bitcoin Cash forked from Bitcoin in August 2017, both chains initially shared transaction history. A signature valid on one chain was, for a period, technically valid on the other, because the transaction format was identical. The fix was to mandate `SIGHASH_FORKID` on Bitcoin Cash, a new flag that injects the value of the UTXO being spent into the preimage. That value was absent from legacy Bitcoin signatures. Different preimage, different hash, signatures cannot cross. It was an inelegant patch for a problem that should have been designed out earlier, and the fact that it had to be invented post-fork says something unflattering about the original spec.

Segwit (BIP143) solved a related problem for native SegWit inputs. The old preimage construction had a quadratic hashing bug: signing an input required hashing all other inputs and outputs, and cost grew quadratically with transaction size. BIP143 restructured the preimage so that certain data, like all input sequences and all output hashes, is pre-computed once and reused. It also committed the UTXO value directly into the signature. That value commitment is what prevents a class of hardware-wallet attacks where a malicious host lies about how much a UTXO is worth.

Taproot (BIP341) went further. Its sighash commits to every output amount and script in the transaction, to the annex if present, and to the full script path in script-spend cases. You cannot reuse a Taproot signature against a transaction with different output values, even if every other field matches. The commitment surface is wider; the attack surface is narrower.

The practical upshot

If you are auditing wallet code, the sighash type is visible in every serialised input: it is the last byte before the signature's DER encoding closes. If you are seeing anything other than `0x01` in a standard payment, you are looking at something unusual, and you should understand why before you proceed. That is not a counsel of paranoia. It is basic hygiene.

Ask yourself how many wallets shipping today have had their sighash construction independently reviewed. The answer is not reassuring.

Bitcoin's security against signature replay is not a single gate but a layered set of commitments tightened with each major upgrade. Legacy had gaps that forks exposed. SegWit closed the value-blinding gap. Taproot closed the partial-output-value gap. Each iteration was a direct response to a concrete attack surface, not a theoretical exercise.

Cryptographic signatures are only as strong as what they commit to. Read Bitcoin's sighash history not as a record of improvements, but as a catalogue of every moment the commitment turned out to be less airtight than everyone assumed.