Crowdfunding Protocol Overview
24. SIGHASH_ALL vs SIGHASH_ANYONECANPAY in Crowdfunding
The Crowdfunding Challenge in Bitcoin
Traditional Bitcoin transactions require fully formed inputs and outputs before any signing can occur. This makes it difficult to implement "all-or-nothing" crowdfunding where:
-
Multiple contributors must pledge funds
-
The funds only move if the total reaches a target
-
Contributors get refunded automatically if the target isn't met
How SIGHASH_ANYONECANPAY Enables Crowdfunding
SIGHASH_ALL | SIGHASH_ANYONECANPAY (0x81) creates signatures that:
-
Commit to all outputs (ensuring the campaign target is respected)
-
Only sign the contributor's own input (allowing others to add their inputs)
# Crowdfunding Protocol Overview
Step 1: Campaign creator defines the goal
Goal: Raise 5 BTC for Project X
Target output: 5 BTC → <project_pubkey>
Step 2: Each contributor creates a partially-signed transaction
Transaction structure:
Input: <contributor_utxo> (e.g., 0.5 BTC)
Output: 5 BTC → <project_address>
Signed with SIGHASH_ALL | SIGHASH_ANYONECANPAY
→ Signature commits to: this input + ALL outputs (the 5 BTC output)
→ Does NOT commit to: other contributors' inputs
Step 3: Contributions are collected off-chain
Coordinator gathers partially-signed transactions
Step 4: Assembly (when goal is met)
Coordinator combines all signed inputs into one transaction:
Input 0: Contributor A (0.5 BTC, already signed)
Input 1: Contributor B (1.0 BTC, already signed)
Input 2: Contributor C (1.5 BTC, already signed)
Input 3: Contributor D (2.0 BTC, already signed)
Output: 5 BTC → <project_address>
All signatures remain valid! (each only signed their own input)
Step 5: Broadcast
The assembled transaction is broadcast.
All 4 contributors' funds move simultaneously.
Why SIGHASH_ALL (without ANYONECANPAY) Fails for Crowdfunding
# Attempt with SIGHASH_ALL:
Contributor A signs with SIGHASH_ALL:
Commits to Input 0 (their utxo) AND all outputs AND all other inputs
Problem: There are no "other inputs" yet!
→ If contributor B later adds their input,
contributor A's signature becomes INVALID
(it committed to zero other inputs, but now there's one)
→ The transaction can never be assembled
The SIGHASH_ALL | SIGHASH_ANYONECANPAY Mechanism
def create_crowdfunding_contribution(private_key, utxo, target_output):
"""
Create a signed crowdfunding contribution using SIGHASH_ALL|ANYONECANPAY.
"""
# Build a skeleton transaction with just our input and the target output
tx = Transaction(
inputs=[Input(outpoint=utxo.outpoint, sequence=0xFFFFFFFF)],
outputs=[target_output] # The campaign goal output
)
sighash_type = SIGHASH_ALL | SIGHASH_ANYONECANPAY # 0x81
# Compute the sighash
# ANYONECANPAY means: only serialize THIS input, not "all inputs"
z = compute_sighash(tx, input_index=0, sighash_type=sighash_type)
# Sign
r, s = ecdsa_sign(private_key, z)
normalize_low_s(r, s)
sig = encode_der(r, s) + bytes([sighash_type])
return sig, utxo.script_pubkey
Assemble and Verify
def assemble_crowdfunding_tx(contributions, target_output):
"""
Combine individually-signed contributions into a final transaction.
"""
inputs = []
for (sig, pubkey_script, utxo) in contributions:
inp = Input(outpoint=utxo.outpoint)
inp.script_sig = push(sig) + push(pubkey_script)
inputs.append(inp)
tx = Transaction(
inputs=inputs,
outputs=[target_output]
)
# All existing signatures remain valid because:
# Each signature used ANYONECANPAY → only committed to its own input
# Each signature used ALL → committed to ALL outputs → target_output is the same
return tx
Comparison Table
| Feature | SIGHASH_ALL | SIGHASH_ALL|ANYONECANPAY | |
TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation: