Skip to content

Tracking: chain-observable transaction-level fingerprinting #1597

Description

@bc1cindy

payjoin's privacy collapses when sender's and receiver's wallets produce divergent transaction-level fingerprints. a chain analyst then partitions the merged tx back into per-owner inputs. primarily through the backward channel, since the lib already coerces intra-tx uniformity for nSequence (see receive/common/mod.rs:280-317 and send/mod.rs:391,1295).

complements #1586 (network-level): this covers nSequence divergence in prior transactions feeding a payjoin, what any chain observer sees after broadcast.

verified across the same six integrations. each wallet's own nSequence policy (used in its non-payjoin transactions, which become payjoin inputs) splits into two groups (the second a bug):

group A - uniformly 0xFFFFFFFD (ENABLE_RBF_NO_LOCKTIME):

group C - mixed [0x01, 0xFFFFFFFF, …] (bug):

  • Cake Wallet transaction_builder.dart:402-406 applies replaceByFeeSequence only to inputs[0]; remaining inputs stay at defaultTxSequence. Combined with electrum_wallet.dart:1516, every multi-input cake tx with confirmed UTXOs has the deterministic pattern [0x01, MAX, …, MAX], propagating through the spending graph.

empirical confirmation in How Fingerprints Damage PayJoin Privacy @arminsabouri: a real Cake → BBM payjoin is intra-uniform (the lib coerces receiver to match sender's nSequence), but Cake's prior tx carries the [1, MAX] pattern from the group C bug, propagating Cake-ownership via the backward channel and recovering the partition.

the lib's intra-tx coerce (good!) doesn't reach into transactions that predate the payjoin. prior txs were built with each wallet's standard tx-construction code - the leak lives there.

should I open an issue on cake to fix group C (apply replaceByFeeSequence to all inputs, not just inputs[0])?

zooming out, same family as #1586: would an "integration conformance policy" make sense? lib defines required tx-construction behaviors (uniform nSequence, canonical sighash, etc.), enforced via sender-side validation or documented as protocol requirement, closing the divergence category at the source instead of patching wallet-by-wallet.

the target end-state is only one group. with group B dissolved, 5 of 6 already converge on 0xFFFFFFFD; only Cake's group-C bug diverges. fixing group C therefore closes the nSequence divergence entirely - there's no residual A↔B split (a payjoin-cliBoltz payjoin is already uniform). canonical convergence on 0xFFFFFFFD (Bitcoin Core default since v0.16, now used by 5 of 6) plus sender-side conformance validation is the remaining policy question.

EDIT: group B - uniformly 0xFFFFFFFF (Sequence::MAX) corrected, dissolves into group A. the old Boltz/BBM MAX was their payjoin-contributed input, which the lib overwrites with the sender's sequence at receive/common/mod.rs:285-317 it never reaches the chain. their own txs use 0xFFFFFFFD

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions