Skip to content

Anatomy of a signed CoinJoin

Low-level — byte by byte

A Bitcoin transaction is just a byte string: a list of inputs it unlocks and outputs it locks. A CoinJoin is one such string that many people build and sign together — here it is field by field, using a real CoinJoin our reference signer broadcast.

Structure

Every SegWit transaction serializes to the same fields, in this order:

Field Size Format Description
version 4 bytes little-endian transaction format version
marker 1 byte 00 flags a SegWit transaction
flag 1 byte 01 witness data is present
input count varint compactSize number of inputs
  txid 32 bytes reversed the UTXO being spent
  vout 4 bytes little-endian which output of it
  scriptSig size varint compactSize 00 for SegWit inputs
  sequence 4 bytes little-endian RBF / locktime signalling
output count varint compactSize number of outputs
  amount 8 bytes little-endian value in satoshis
  scriptPubKey varies script where the coins go
witness varies stack per input the signatures (SegWit)
locktime 4 bytes little-endian earliest spend height/time

Two encodings to keep in mind: numbers are little-endian, and lengths/counts use compactSize varints (one byte under 253; an fd prefix + two bytes up to 65,535).

A CoinJoin, byte by byte

A real transaction our reference signer broadcast on testnet4 — f051aaf0..2a843476e. Eight inputs, six equal pool outputs, a change output and the coordinator fee. It spends the outputs of an earlier round, 05def0ad..1666edd0.

version      01000000              version 1
marker,flag  00 01                 SegWit transaction
input count  08                    8 inputs

input #1
  txid       d0ed6616..adf0de05    32 B, internal (reversed) order — this is 05def0ad..1666edd0
  vout       00000000              spends its output #0
  scriptSig  00                    empty — the signature is in the witness
  sequence   fdffffff              opt-in RBF (BIP-125)
  inputs #2–#8: identical 41-byte shape, vout 01000000 … 07000000

output count 08                    8 outputs
  out #1  8038010000000000  0014 c73a9f99..a4fbe34b   80,000 sat  ┐
  out #2  8038010000000000  0014 9d5f0b3b..535ad129   80,000 sat  │
  out #3  8038010000000000  0014 bde75497..0ef7fd74   80,000 sat  ├ equal pool outputs
  out #4  8038010000000000  0014 d386d5bc..d8e418ae   80,000 sat  │  (the anonymity set)
  out #5  8038010000000000  0014 a3823ba9..67232424   80,000 sat  │
  out #6  8038010000000000  0014 9ca891f7..8f82fad5   80,000 sat  ┘
  out #7  e5e5000000000000  0014 ba804126..7c9842ff   58,853 sat  change
  out #8  5315000000000000  0014 c73a9f99..a4fbe34b    5,459 sat  coordinator fee

witness #1   02  47 3044..5a37b54f..95d 01   21 0277a493..3831ffa4
             └ 2 items: [ 71-byte DER signature + SIGHASH_ALL byte ] , [ 33-byte compressed pubkey ]
  witnesses #2–#8: one stack per input — each input signed on its own

locktime     00000000              spendable immediately

The six identical 80,000-sat outputs are the pool; the larger output is change and the smallest is the coordinator fee. Inputs carry no signature inline — every signature lives in the witness section, one stack per input. That separation is what lets many people's signatures be assembled into one transaction.

This round was a single-participant test

testnet4, and one wallet — it exercises the full pipeline (batching, fee + commission split, signature collection, broadcast), not a multi-party anonymity set. The byte layout of a multi-participant round is identical; the inputs and their witnesses just belong to different owners. See the Verify page for the list of rounds, and the mainnet example at the end of this page for what it looks like at scale.

What each participant signs

A signature does not cover an input in isolation — it commits to the whole transaction through a sighash. For SegWit v0 (BIP-143) the hashed message is, in essence:

sighash = dSHA256(
    nVersion,
    hashPrevouts,     // hash of ALL inputs' (txid, vout)
    hashSequence,     // hash of ALL inputs' sequences
    thisOutpoint, scriptCode, thisAmount, thisSequence,
    hashOutputs,      // hash of ALL outputs (value + script)
    nLocktime, SIGHASH_ALL )
  • It commits to every input and every output. With SIGHASH_ALL your signature means "I approve exactly these inputs and exactly these outputs."
  • Nothing can change after you sign. Add, drop, reorder or retarget anything and every signature breaks — so handing a half-built CoinJoin to a coordinator is safe.
  • Signatures merge independently. Each participant signs only their own inputs; the coordinator drops each witness into the matching slot. Keys are never shared.
  • Taproot inputs use the BIP-341 sighash and a 64-byte Schnorr signature, but the principle is identical.

TXID and wTXID

A transaction has two identifiers, both double-SHA256 hashes — but over different bytes:

  • txid = hash of the non-witness fields only (version, inputs, outputs, locktime).
  • wtxid = hash of the full serialization, including marker, flag and all witnesses.

Because the txid excludes the witness, adding signatures does not change the txid. That is exactly why participants can sign in turn and have their witnesses merged: the transaction they each signed keeps the same txid throughout.

Size, weight and fee

Witness bytes are discounted 4×. The transaction above is 1,446 bytes on the wire, but for fees it counts as a weight of 3,204 units, i.e. 801 vbytes (weight ÷ 4). The fee itself is simply what is left over: inputs − outputs = 545,947 − 544,312 = 1,635 sat (≈ 2.0 sat/vB). No field stores the fee; miners compute it from the difference.

At scale: a real mainnet CoinJoin

The same structure, with hundreds of participants. A confirmed Wasabi (WabiSabi) CoinJoin on mainnet, tx 198aee6e..b3ef9b70, block 755,972:

Field Value
Inputs 374, from many independent owners
Outputs 408
Virtual size 68,012 vbytes
Fee 93,558 sat (≈ 1.4 sat/vB)
Scripts all native SegWit (P2WPKH)

The outputs cluster on shared denominations (powers of 2 and 3). Each repeated value is an anonymity set — 43 distinct denominations in that one transaction, so identical chunks from different users become interchangeable.

Note

Many inputs, each signed independently; equal outputs no one can tell apart; one transaction, one shared fee — and nothing in it can change without every signer noticing.