You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(paper): tighten TL;DR, drop self-references, reframe positively
Shrink the abstract block from ~50 lines to 16, removing the
extended protocol-explanation parenthetical and the in-line
corpus methodology that already lives in section 4. Drop all
references to 'earlier versions of this paper' / 'previous
version of this paper' (sections 0 and 11); the paper has not
been published, so its own version history is not part of the
narrative.
Reframe the headline as 'robust today and concretely
improvable by a client-default change': lead with the 60.4%
of CJs that lose no candidate at all, present 'uniform_fee' as
a clean deployable fix rather than as a privacy-budget
shortfall. No numerical claim changes; only framing.
<h1id="joinmarket-maker-wallet-clustering-and-taker-anonymity-set-reduction">JoinMarket Maker Wallet Clustering and Taker Anonymity-Set Reduction</h1>
109
-
<blockquote><p><strong>TL;DR.</strong> JoinMarket is a Bitcoin CoinJoin protocol where a <em>taker</em> pays a small fee to one or more <em>makers</em> to mix coins into a single transaction with several equal-value outputs; every equal output is supposed to be indistinguishable from every other equal output in the same round (makers want anonymity from each other and from the taker, just as the taker wants anonymity from the makers). We ran a passive on-chain experiment on exactly one year of mainnet JoinMarket activity (heights 894,697 to 947,358, 2025-05-01 to 2026-05-01, 10,581 JoinMarket CoinJoins, of which 7,400 are fully ILP-decoded and 3,158 yield partial maker slots via greedy preprocessing, <spanclass="math inline">$\S 7.4$</span>) and clustered maker wallets using only protocol-forced signals: the JoinMarket mixdepth state machine (a maker's change stays in the source mixdepth and its equal output advances to the next, and the maker chooses the mixdepth with the highest balance to re-advertise), the integer-linear-program (ILP) recovery of slot membership (which inputs and change output belong to the same participant) inside each CoinJoin so that the Common Input Ownership Heuristic (CIOH) can be applied across the on-CJ and off-CJ sides of a maker wallet, and the maker's published fee schedule used as a per-CoinJoin disambiguator. A simple example of that last signal: if a maker charges 0.1% in one CJ and an equal output of that CJ then participates in a later CJ whose ILP-recovered slot also charges 0.1% (and no other maker in the producer CJ charges 0.1% at that amount), the equal output is uniquely bound to that producer slot. This <em>fee-fingerprint</em> attribution is the only chain edge in this attack that ties a specific equal output to a specific maker slot; the change-chain and the auxiliary edges (CIOH, FB-funding) cluster <em>slots across CoinJoins</em> but cannot label which of the indistinguishable equal outputs of a producer CJ belongs to which maker. The result is more measured than earlier versions of this paper suggested. The mean published anonymity set is 8.06 equal outputs per CJ; the mean <em>residual</em> anonymity set after removing makers whose equal outputs are univocally attributed by the fee fingerprint (or co-spent with a cluster-mate's attributed output in a later CJ) is 7.55. On 39.6% of CJs at least one maker's equal output is certified; on 3 CJs (0.03%) the round collapses fully to the taker. The fee fingerprint is therefore a measurable but bounded leak. None of the edges in this attack are probabilistic: every merge is a hard same-wallet conclusion or no merge at all. The single largest remediation is <em>fee-policy homogenization</em>: in the simulator, when every maker runs the reference client's default policy the fingerprint cannot disambiguate and the residual rises to its theoretical ceiling <spanclass="math inline">$n_{eq}$</span> (<spanclass="math inline">$\S 9.4$</span>). Practical mitigations and their trade-offs are discussed in <spanclass="math inline">$\S 9$</span>.</p></blockquote>
109
+
<blockquote><p><strong>TL;DR.</strong> JoinMarket holds up well in practice. On one year of mainnet activity (10,464 CoinJoins), a passive on-chain adversary using only protocol-forced signals trims the mean anonymity set from 8.06 to 7.55 equal outputs per CJ at precision = 1.0, a 6.3% reduction. 60.4% of CJs leak no maker at all; only 0.03% collapse to the taker alone. The single dominant edge is the <em>fee fingerprint</em>: when a maker's published fee policy uniquely matches the realized fee of a producer slot at a later consumer CJ's amount, the equal output is hard-bound to that slot. Because that leak has one identifiable cause, it also has one deployable fix: <em>fee-policy homogenization</em>. When every maker runs the reference client's default policy, the fingerprint stops disambiguating and the residual returns to the <spanclass="math inline">$n_{eq}$</span> ceiling (<spanclass="math inline">$\S 9.4$</span>). The protocol is therefore both robust today and concretely improvable by a client-default change.</p></blockquote>
110
110
<h2id="scope-and-motivation">1. Scope and motivation</h2>
111
111
<p>A JoinMarket CoinJoin (CJ) is an atomic transaction in which one <em>taker</em> and <spanclass="math inline">$M$</span><em>makers</em> contribute inputs and produce <spanclass="math inline">$n_{eq} = M + 1$</span> equal-amount outputs (the <em>equal outputs</em>) plus up to <spanclass="math inline">$n_{eq}$</span> change outputs (one per participant who needs change; typically all <spanclass="math inline">$M$</span> makers and usually the taker too). Each participant contributes one <em>slot</em>: a bundle of one or more inputs they own, exactly one equal-amount output, and at most one change output. The published anonymity property is that the taker's equal output is indistinguishable from the makers' equal outputs: the taker hides in a set of <spanclass="math inline">$n_{eq}$</span> candidates per round.</p>
112
112
<p>JoinMarket defends this set in several layered ways:</p>
@@ -307,9 +307,9 @@ <h2 id="appendix-a-forensic-slot-sample-v7-3-1y-merged-corpus">Appendix A. Foren
307
307
<p>The two `<code>cert_a</code><code> rows are slots whose published equal output later appears as an input to another CJ where the fee fingerprint of the producer slot is corpus-univocal (no other producer slot in any decoded CJ in the window would have charged the same absolute and relative coordinator fee at the consumer amount). The </code><code>cert_b</code><code> rows are slots that are <em>not</em> fingerprint-attributed at the consumer CJ, but whose producer cluster appears exactly once in the consumer's equal-input candidate set and at least once in its change-input cluster set, so the cluster-co-spend rule of <spanclass="math inline">$\S 7$</span> Path B disambiguates them deterministically. The </code><code>large_cluster</code><code> rows sit in clusters of <spanclass="math inline">$\geq 28$</span> slots (the top 1% of the 23,562-cluster distribution) yet remain uncertified: their cluster is large enough to participate in CIOH and round-trip edges but no consumer CJ in the window happens to anchor a same-cluster change at the same time as an equal-input from this producer. The </code><code>partial_only</code>` rows are recovered by the greedy preprocessing pass of <spanclass="math inline">$\S 7.4$</span> and would have been silently dropped in a full-ILP-only pipeline; both remain uncertified here because no consumer CJ links back to them in the 1y window, but they enlarge the denominator of the published <spanclass="math inline">$n_{eq}$</span> figures by contributing real maker slots whose existence the analyst can establish without the full ILP.</p>
308
308
<p>A reader can replay any row by piping the producer txid into `<code>joinmarket_analyzer.solver.solve_all_solutions</code><code> (for </code><code>partial=no</code><code> slots) or </code><code>greedy_preprocessing</code><code> (for </code><code>partial=yes</code><code> slots) at </code><code>max_fee_rel = 0.05</code>`, intersecting the result with the consumer txid's input set, and checking the Path A or Path B rule of <spanclass="math inline">$\S 7$</span>. The full per-slot trace contains 73,981 rows (one per merged maker slot, including provenance, cluster id, and the FB-anchored / large-cluster flags) and is included in the corpus release for full auditability.</p>
309
309
<h2id="conclusion">11. Conclusion</h2>
310
-
<p>The JoinMarket equal-output anonymity set as published per round (<spanclass="math inline">$n_{eq}$</span>) overstates the protocol's privacy budget against a passive on-chain adversary, but the overstatement is bounded. A protocol-correct chain-following clusterer at precision = 1.0 reduces the published anonymity set from a mean of 8.06 to 7.55 on the 1y mainnet corpus (a 6.3% reduction across 10,464 CJs, combining 7,400 full-ILP decompositions with 3,158 partial-ILP recoveries per <spanclass="math inline">$\S 7.4$</span>), with 39.6% of CJs losing at least one candidate to certified-maker removal and 3 CJs (0.03%) collapsing fully to the taker. The structural channel the attack exploits, namely JoinMarket's fee-fingerprint signal (<spanclass="math inline">$\S 5.2$</span>) that ties a specific equal output of producer CJ <spanclass="math inline">$T$</span> to a specific producer slot of <spanclass="math inline">$T$</span>, is intrinsic to the protocol and to the typical maker fee-advertisement workflow; it is not a fixable implementation bug.</p>
310
+
<p>JoinMarket's equal-output anonymity set as published per round (<spanclass="math inline">$n_{eq}$</span>) is largely realized in practice. Against a passive on-chain adversary running a protocol-correct chain-following clusterer at precision = 1.0, the published mean of 8.06 contracts only to 7.55 on the 1y mainnet corpus (a 6.3% reduction across 10,464 CJs, combining 7,400 full-ILP decompositions with 3,158 partial-ILP recoveries per <spanclass="math inline">$\S 7.4$</span>). 60.4% of CJs lose no candidate at all; 3 CJs (0.03%) collapse fully to the taker. The structural channel the attack exploits, namely JoinMarket's fee-fingerprint signal (<spanclass="math inline">$\S 5.2$</span>) that ties a specific equal output of producer CJ <spanclass="math inline">$T$</span> to a specific producer slot of <spanclass="math inline">$T$</span>, is intrinsic to the protocol and to the typical maker fee-advertisement workflow, and it has a clean deployable fix.</p>
311
311
<p>The precision = 1.0 guarantee is what makes the result actionable: under the per-CJ loose gate the clusterer never merges two distinct maker wallets on this corpus, validated by three independent ground-truth sources. Each certified maker the analyst extracts is a <em>deterministic</em> hide-set reduction, not a probabilistic one. The precision guarantee is gate-and-corpus dependent: the scaled simulator (<spanclass="math inline">$\S 6.1$</span>) shows that under adversarial fee jitter the per-CJ gate breaks at <spanclass="math inline">$\sim 3\%$</span> of edges and only the corpus-unique gate restores precision = 1.0 by construction. On the present mainnet snapshot the gates are empirically indistinguishable; on a future snapshot in which an adversary deliberately concentrates many makers near similar fee policies the analyst should switch to the corpus-unique gate at a recall cost of about 5%.</p>
312
-
<p>The practical implication for JoinMarket users is that the relevant privacy figure for a round is not its published <spanclass="math inline">$n_{eq}$</span> but the v7.3 residual: today, around 94% of <spanclass="math inline">$n_{eq}$</span>. The simulator (<spanclass="math inline">$\S 9.4$</span>) identifies the deployable mitigation: <strong>fee-policy homogenization</strong> (<code>uniform_fee</code>, every maker on the reference client's default policy) drives the residual to the full <spanclass="math inline">$n_{eq}$</span> ceiling and produces zero Path A attributions. The behavioral knobs that the previous version of this paper proposed (<code>no_change_as_input</code>, <code>maker_only_cj</code>) are at best neutral and at worst <em>counterproductive</em>: by suppressing the v6 change-chain cluster-merging edge without touching the fee fingerprint, they force makers to recycle equal outputs more aggressively and <em>increase</em> the Path A attribution surface. The protocol is therefore hardenable today, by a coordinated client default change, without any protocol-level cryptographic addition; a future JMP-0005-class equal-output permutation commitment would close the residual leak in depth against an adaptive adversary. Until a JoinMarket client release ships the <code>uniform_fee</code> default, the v7.3 residual is the privacy budget the protocol gives its users.</p>
312
+
<p>The practical implication for JoinMarket users is that the relevant privacy figure for a round is its v7.3 residual, today around 94% of <spanclass="math inline">$n_{eq}$</span>, and that this figure is <em>improvable</em> by a single client-default change. The simulator (<spanclass="math inline">$\S 9.4$</span>) identifies the mitigation: <strong>fee-policy homogenization</strong> (<code>uniform_fee</code>, every maker on the reference client's default policy) drives the residual to the full <spanclass="math inline">$n_{eq}$</span> ceiling and produces zero Path A attributions. Alternative behavioral knobs (<code>no_change_as_input</code>, <code>maker_only_cj</code>) are at best neutral and at worst <em>counterproductive</em>: by suppressing the v6 change-chain cluster-merging edge without touching the fee fingerprint, they force makers to recycle equal outputs more aggressively and <em>increase</em> the Path A attribution surface. The headline result is therefore that JoinMarket is robust today and straightforwardly hardenable, by a coordinated client default change, without any protocol-level cryptographic addition; a future JMP-0005-class equal-output permutation commitment would close the residual leak in depth against an adaptive adversary.</p>
0 commit comments