Skip to content

SIGHASH Flags & the SIGHASH_SINGLE Bug

When you sign a Bitcoin transaction, you are not signing “the transaction.” You are signing a message derived from a chosen subset of the transaction. Which subset is controlled by a single byte appended to every signature: the SIGHASH flag. Almost no one outside protocol developers understands this, yet it’s the key that unlocks crowdfunding, partial transactions, atomic swaps — and a notorious bug that can silently destroy funds.

The core idea: a signature commits to a digest

Section titled “The core idea: a signature commits to a digest”

To validate a signature, a node reconstructs the exact message the signer signed (the sighash), then checks the signature against it and the public key. The SIGHASH byte tells everyone how to build that message — specifically, which inputs and outputs are included in the digest.

signature bytes = DER(r, s) || SIGHASH_BYTE
└─ the last byte: tells verifiers what was committed to

Anything not included in the sighash is left open — it can be changed by other parties without invalidating your signature. That openness is not a bug; it’s a feature you can deploy on purpose.

There are three base modes for outputs, optionally combined with ANYONECANPAY for inputs.

FlagCommits to which outputsCommits to which inputsMeaning
SIGHASH_ALL (0x01)all outputsall inputs”I approve this entire transaction.” The default.
SIGHASH_NONE (0x02)noneall inputs”I authorize spending my input — I don’t care where the money goes.”
SIGHASH_SINGLE (0x03)only the output at my input’s indexall inputs”I commit to my output; other outputs are free.”
… | ANYONECANPAY (0x80)(as above)only my own input”Other people may add their own inputs freely.”

SIGHASH_ALL (everyone’s normal experience) is the strict one: it nails down the whole transaction, which is why it provides the malleability-resistant guarantees you usually want. The interesting behavior is in the looser modes.

SIGHASH_ALL SIGHASH_NONE SIGHASH_SINGLE
┌─ inputs ─┐ ┌ outputs ┐ ┌─ inputs ─┐ ┌outputs┐ ┌─ inputs ─┐ ┌ outputs ┐
│ in0 ✔ │ │ out0 ✔ │ │ in0 ✔ │ │ out0 │ │ in0 ✔ │ │ out0 ✔ │ ← my index
│ in1 ✔ │ │ out1 ✔ │ │ in1 ✔ │ │ out1 │ │ in1 ✔ │ │ out1 │
└──────────┘ └─────────┘ └──────────┘ └───────┘ └──────────┘ └─────────┘
(✔ = signed/committed; blank = left open to change)

Use-cases that only make sense once you understand SIGHASH

Section titled “Use-cases that only make sense once you understand SIGHASH”

Crowdfunding / assurance contracts — SIGHASH_ALL | ANYONECANPAY

Section titled “Crowdfunding / assurance contracts — SIGHASH_ALL | ANYONECANPAY”

You want to raise 10 BTC for a project, but only if the full amount is pledged — otherwise no one should lose money. Create a transaction with one fixed output: “10 BTC to the project.” Each backer signs their own input with SIGHASH_ALL | ANYONECANPAY:

  • ALL → they commit to the output (the 10 BTC destination is locked in).
  • ANYONECANPAY → they commit to only their own input, so others can keep adding inputs.
output: 10 BTC → project (fixed; everyone commits to it via ALL)
inputs: backer A (1 BTC) signed ANYONECANPAY ┐
backer B (3 BTC) signed ANYONECANPAY ├─ all combine into ONE valid tx
backer C (6 BTC) signed ANYONECANPAY ┘ only once inputs sum to ≥ 10 BTC

Backers’ partial signatures are valid but the transaction can’t confirm until the inputs cover the output. It’s an all-or-nothing pledge with no escrow and no trusted party. (This is Tabarrok’s “dominant assurance contract” idea, expressible directly in Script.)

Atomic swaps & collaborative transactions — SIGHASH_SINGLE | ANYONECANPAY

Section titled “Atomic swaps & collaborative transactions — SIGHASH_SINGLE | ANYONECANPAY”

SINGLE | ANYONECANPAY means “I commit to exactly one input/output pair — mine — and don’t care what anyone else adds.” Two parties can each contribute an (input, output) pair to one transaction, sign independently, and assemble a trade where either both legs happen or neither does. This is a building block for non-interactive swaps and is the spirit behind modern coordinated transactions.

SIGHASH_SINGLE commits to “the output at the same index as my input.” Reasonable — until you ask: what if there is no output at that index?

inputs: outputs:
in0 out0
in1 out1
in2 ◄── signed SINGLE (no out2 exists!)

In the original implementation, when an input’s index exceeds the number of outputs, the code did not error out. Instead it returned a hardcoded digest of 1:

sighash = 0x0000...0001 ← the same constant for ANY such transaction

So the signature now commits to the constant 1 instead of to any real transaction data. The consequence is catastrophic:

This is sometimes called the SIGHASH_SINGLE bug (or “the 1 bug”). It is preserved in legacy script for backward compatibility — you cannot remove a consensus quirk without a fork — so the rule is simply: never sign with SIGHASH_SINGLE an input whose index has no matching output. Good libraries refuse to.

How does this help untrusting strangers agree on one ledger? SIGHASH is the dial that controls how much of an agreement each party is locking in. Crank it to ALL and you’ve signed a complete, tamper-proof deal. Loosen it and you let mutually distrusting parties each commit to only their own slice — enabling pledges and swaps that would otherwise need an escrow agent. The bug is the dark mirror: loosen the commitment too far (to the constant 1) and you’ve agreed to anything, which is the same as agreeing to be robbed.

The SIGHASH byte is a commitment dial — a deliberate design choice about how much of a deal you sign:

  • Why does it exist? Because a signature commits to a chosen subset of a transaction, not the whole thing — the SIGHASH byte decides which inputs and outputs enter the digest, leaving the rest open by design so other parties can fill them in.
  • What problem does it solve? It lets mutually distrusting parties each commit to only their own slice: SIGHASH_ALL | ANYONECANPAY builds all-or-nothing crowdfunds (a fixed output, backers freely add inputs); SIGHASH_SINGLE | ANYONECANPAY builds atomic swaps and collaborative transactions with no escrow agent.
  • What are the trade-offs? Loosening the commitment is a footgun — the legacy SIGHASH_SINGLE bug returns a digest of 1 when no output matches the input index, turning the signature into a blank cheque any observer can reuse to steal the coins.
  • When should I avoid it? Use plain SIGHASH_ALL for ordinary payments; the loose modes are the wrong tool unless you specifically need open inputs/outputs — and never sign SIGHASH_SINGLE for an input with no matching output (prefer SegWit/Taproot, which defused the bug).
  • What breaks if I remove it? Without flexible modes the protocol could only express “I approve this entire transaction,” so crowdfunds and swaps would need a trusted escrow — eliminating the partial-commitment contracts that make trustless multi-party deals possible.
  1. A Bitcoin signature does not sign “the transaction.” What does it actually sign, and what selects it?
  2. Explain why SIGHASH_ALL | ANYONECANPAY is exactly the right tool for an all-or-nothing crowdfund.
  3. What does SIGHASH_SINGLE commit to, and what’s the precondition for using it safely?
  4. Precisely why does signing an out-of-range SIGHASH_SINGLE input let a stranger steal the coins?
  5. Why does this dangerous behavior still exist in Bitcoin instead of being patched away, and which transaction types are immune?
Show answers
  1. It signs a message (the sighash) derived from a chosen subset of the transaction, not the whole transaction. The SIGHASH byte appended to the signature selects which inputs and outputs go into that digest; anything left out can be changed without invalidating the signature.
  2. ALL commits to the output — so the “10 BTC to the project” destination is locked in for everyone — while ANYONECANPAY commits to only the signer’s own input, letting other backers keep adding their inputs. The result is an all-or-nothing pledge: partial signatures are valid but the transaction can’t confirm until the inputs sum to cover the fixed output, with no escrow or trusted party.
  3. It commits to the output at the same index as the signer’s input, leaving other outputs free. The precondition for safety: there must actually be an output at that index — never sign SIGHASH_SINGLE for an input whose index has no matching output.
  4. With no output at the input’s index, the original code returned a hardcoded digest of 1 instead of erroring. A signature over the constant 1 isn’t bound to any specific outputs, amounts, or destinations, so any observer can reuse it to build a different transaction spending the same coins to themselves — a blank cheque.
  5. Removing a consensus quirk would require a fork, and the behavior is preserved in legacy script for backward compatibility. SegWit (BIP 143) v0 witness inputs and Taproot (BIP 341) redesigned the digest and are immune; the bug only lives on in legacy pre-SegWit SIGHASH_SINGLE usage.