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 toAnything 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.
The flags
Section titled “The flags”There are three base modes for outputs, optionally combined with ANYONECANPAY for inputs.
| Flag | Commits to which outputs | Commits to which inputs | Meaning |
|---|---|---|---|
SIGHASH_ALL (0x01) | all outputs | all inputs | ”I approve this entire transaction.” The default. |
SIGHASH_NONE (0x02) | none | all inputs | ”I authorize spending my input — I don’t care where the money goes.” |
SIGHASH_SINGLE (0x03) | only the output at my input’s index | all 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 BTCBackers’ 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.
The footgun: the SIGHASH_SINGLE bug
Section titled “The footgun: the SIGHASH_SINGLE bug”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 transactionSo 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.
The thread
Section titled “The thread”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.
Check your understanding
Section titled “Check your understanding”- A Bitcoin signature does not sign “the transaction.” What does it actually sign, and what selects it?
- Explain why
SIGHASH_ALL | ANYONECANPAYis exactly the right tool for an all-or-nothing crowdfund. - What does
SIGHASH_SINGLEcommit to, and what’s the precondition for using it safely? - Precisely why does signing an out-of-range
SIGHASH_SINGLEinput let a stranger steal the coins? - Why does this dangerous behavior still exist in Bitcoin instead of being patched away, and which transaction types are immune?