Proof of key ownership without spending
12. SIGHASH_NONE — Signing Inputs Only
Overview
SIGHASH_NONE (value 0x02) commits to all inputs but signs no outputs. A signature with SIGHASH_NONE says: "I authorize spending my input, but I don't care where the funds go."
Commits to:
✓ All inputs (their outpoints)
✓ Input sequences (for current input; others set to 0)
Does NOT commit to:
✗ Any outputs (completely blank)
✗ Other inputs' sequences
How SIGHASH_NONE Modifies the Transaction Copy
def apply_sighash_none(tx_copy, input_index):
# Remove ALL outputs
tx_copy.outputs = []
# Set all other inputs' sequences to 0
for i, inp in enumerate(tx_copy.inputs):
if i != input_index:
inp.sequence = 0
# Current input's sequence is preserved
return tx_copy
Security Implications
SIGHASH_NONE is extremely dangerous when used naively:
Scenario: Alice creates a SIGHASH_NONE signature for her input.
She intends to send to Bob.
Risk: Anyone who sees this signature can:
1. Take Alice's signed input
2. Create a new transaction with the SAME input but DIFFERENT outputs
3. Send Alice's funds anywhere they want
Alice has signed a "blank check" — she authorized the spend
but left the destination empty for anyone to fill in.
Legitimate Uses of SIGHASH_NONE
Despite the danger, there are legitimate protocols that leverage this behavior:
1. Signaling Without Spending
A party can demonstrate they have signing capability for an input without committing to any specific transaction:
# Proof of key ownership without spending
sighash_none_sig = sign(private_key, sighash_none_preimage)
# Broadcasting this would let miners steal funds, but it proves key ownership
2. Multi-Party Protocols with Trusted Coordinators
In some CoinJoin variants, participants sign with SIGHASH_NONE and trust a coordinator to fill outputs correctly.
3. Replace-by-Fee with Empty Outputs (Historical)
Early RBF experiments used SIGHASH_NONE to indicate inputs that could be repackaged.
Example Transaction Construction
# Creating a SIGHASH_NONE signature (dangerous — educational purposes only)
def sign_sighash_none(private_key, tx, input_index, script_code):
# Modified tx: no outputs, other sequences zeroed
tx_copy = copy_transaction(tx)
tx_copy.outputs.clear()
for i in range(len(tx_copy.inputs)):
if i != input_index:
tx_copy.inputs[i].sequence = 0
tx_copy.inputs[input_index].script = script_code
for i in range(len(tx_copy.inputs)):
if i != input_index:
tx_copy.inputs[i].script = b''
preimage = tx_copy.serialize() + struct.pack('<I', SIGHASH_NONE)
z = double_sha256(preimage)
der_sig = ecdsa_sign_der(private_key, z)
return der_sig + bytes([SIGHASH_NONE])
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: