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.

  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?