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 genuine CoinJoin from our reference signer (testnet — the byte layout is identical on mainnet). Five inputs, two equal outputs, a change output and the coordinator fee:

version      01000000            version 1
marker,flag  00 01               this is a SegWit tx
input count  05                  5 inputs

input #1
  txid       735ea5f0..c235608   32 B, internal byte order
  vout       00000000            spends output index 0
  scriptSig  00                  empty — it is in the witness
  sequence   fdffffff            opt-in RBF
  inputs #2–#5: same 41-byte shape, a different UTXO each (one per participant)

output count 04                  4 outputs
  out #1  8038010000000000  0014 a29a73..0f34   80,000 sat — equal
  out #2  8038010000000000  0014 ac9143..a0bd   80,000 sat — equal (anon set)
  out #3  f12a010000000000  0014 b30667..f0c2   76,529 sat — change
  out #4  5d09000000000000  0014 a5ec1d..52c7    2,397 sat — coordinator fee

witness #1   02 48 3044..01 21 03..d280   [ signature, pubkey ]
  witnesses #2–#5: one stack per input — each participant's own signature

locktime     00000000            spendable immediately

The two identical 80,000-sat outputs are the anonymity set; 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.

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 example above is 880 bytes on the wire, but for fees it counts as a weight of 1,897 units, i.e. about 475 vbytes (weight ÷ 4). The fee itself is simply what is left over: inputs − outputs = 239,726 − 238,926 = 800 sat (≈ 1.7 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.