What a Signature Can See · 签名能看见什么

When Two Signatures Collide

Two parties, two sighash flags, one transaction. We made them fight on Inquisition signet and let the node judge.


In the previous post we saw that a Bitcoin signature doesn't sign the transaction — it signs a message, assembled from only the fields its sighash flag chooses to see. Everything left blind, anyone can change.

That raises a sharp question the moment more than one party is involved:

If each signature only commits to its own view of the transaction, what happens when two parties in the same transaction pick incompatible views?

Do they conflict? Can the transaction still be built? We didn't want to argue it on a whiteboard. We built it on Inquisition signet and asked the node — with testmempoolaccept, which returns Bitcoin Core's own verdict (allowed / rejected + the exact reason), no hand-waving.

Here's the mental model the experiments confirm.

Signatures don't negotiate. They intersect.

Each signature carves out a set of final transactions it will accept: the fields it sees are pinned to fixed values; the fields it leaves blind can be anything. Put two parties in one transaction and the valid transaction must lie in the intersection of both their accepted sets.

And crucially: the two inputs never read each other's flags. They only both touch the shared transaction. A conflict is not two parties arguing — it's two commitments to the same field that can't both be true at once.

We ran three collisions.

Collision 1 — same field, two values → deadlock

Alice signs SIGHASH_ALL believing the outputs are O1 (all funds to her). Bob signs SIGHASH_ALL believing the outputs are O2 (all funds to him). Now try to assemble the final transaction:

There is no output set that satisfies both. The node, on both assemblies:

allowed = false
reject-reason: mempool-script-verify-flag-failed (Invalid Schnorr signature)

Two SIGHASH_ALL signatures over different bills = empty intersection = a transaction that cannot be built. This is the base case of conflict, and the node states it flatly.

Collision 2 — offered freedom, quietly revoked

This one is subtler and more real. Alice signs ALL | ANYONECANPAY — "anyone can add inputs, I only commit to my own." She is offering freedom: others may join and pay. Bob signs plain SIGHASH_ALL — which commits to the entire input set as it stood when he signed.

allowed = false
reject-reason: mempool-script-verify-flag-failed (Invalid Schnorr signature)

Bob's signature shatters. The freedom Alice offered was only real as long as nobody else locked the input set. The instant one party commits to "all inputs," the door another party held open slams shut — using it destroys the transaction. (Control: when both Alice and Bob use ANYONECANPAY, Carol joins fine → accepted. So the rejection is provably Bob's ALL, nothing else.)

This is the exact texture of multi-party PSBT flows: the party who signs "all inputs" must sign last, or watch their signature die every time the input set changes.

Collision 3 — a covenant vs. a spender's intent

Sighash is one way to fix a field. A covenant is another — and it can collide too. We locked a coin with OP_CHECKTEMPLATEVERIFY (CTV): the covenant fixes the outputs of the next spend to an exact template T ("50000 − fee to address X, one input, one output").

allowed = false
reject-reason: mempool-script-verify-flag-failed
              (Script failed an OP_CHECKTEMPLATEVERIFY operation)

The covenant nails the "where the money goes" cell shut at funding time. Any spender who wants it elsewhere is refused — and the opcode names itself in the rejection. This is a covenant-vs-party conflict: the coin's issuer pre-committed one field, and no later signature can un-commit it.

Conflict is sometimes exactly the point

It's tempting to read "conflict" as failure. It isn't. The base case (Collision 1) is the machinery behind atomic swaps: a buyer who tries to take the goods without paying must build a transaction missing the seller's payment output — but the seller's SINGLE | ANYONECANPAY signature commits to exactly that output. Empty intersection → the theft can't be assembled → goods and money move together or not at all. The conflict is the lock that makes trustless swaps work.

The receipts

All verdicts are Bitcoin Core's own, via testmempoolaccept, on Inquisition signet. The two cooperating cases we also broadcast, as permanent on-chain artifacts:

The full runnable lab — run_A_sighash.py (two-party sighash) and run_B_covenant.py (CTV) — reproduces every verdict against a live node.


So: two signatures in one transaction don't bargain. They intersect. Land inside the overlap and the node accepts; land outside it and the node refuses — sometimes because two parties wanted the same field to be two things, sometimes because one party's covenant already spoke for it. The conditions of a Bitcoin payment are the sum of what every signature can see — and a conflict is just two of those views, touching the same cell, disagreeing.


Sequel to "What a Bitcoin Signature Actually Signs." Both accompany the book What a Signature Can See. Play with the sighash dials: index.html. Reproduce the collisions: the sighash-conflict lab on Inquisition signet.

Part of What a Signature Can See · try the interactive playground.