3 Commits

Author SHA1 Message Date
Marcin Pawlowski
3c52aeec0e
Settle §6.9's multi-coalition conjecture: refuted for deflation, confirmed for the threshold
§6.9 flagged multi-coalition selfish mining as conjectural on two counts taken
from the literature — that splitting a coalition can deflate D-hat FURTHER than
one coalition of the same size, and that individually sub-threshold coalitions
can be jointly profitable. With rival private chains in the engine, both are now
measured (scripts/multi_coalition.py, fig39), and they point opposite ways.

DEFLATION — REFUTED. Splitting a fixed stake into K rivals reduces estimator
damage, monotonically, at every beta and K tested (t = 4.3 to 14.3):

  beta   K=1     K=2     K=3     K=4
  0.20   0.908   0.928   0.922   0.928
  0.30   0.778   0.858   0.880   0.880
  0.40   0.586*  0.764   0.807   0.823

The mechanism shows in the fork structure: a lone coalition holds one private
chain ~2000 blocks deep, while two rivals cut each other to depth ~2. Rivals
spend their advantage burying each other instead of honest work. So D-hat ~ 0.70
at alpha = 0.4 IS an upper bound on multi-coalition deflation, not the
under-estimate §6.9 warned it might be. (* boundary-affected: 66% of that cell's
adversarial blocks were still private at epoch end even with the lead cap. The
0.2 and 0.3 rows have <1% stranding and carry the result alone.)

THRESHOLD — CONFIRMED, and 1/3 does not hold at this load:

  one coalition at 0.200      0.875x   t = -2.5    does not pay
  one coalition at 0.295      1.232x   t =  2.7    PAYS
  two rivals at 0.200 each    1.045x   t =  2.5    each PAYS
  three rivals at 0.133 each  0.852x   t = -5.1    does not pay

Two results against the folklore. A single coalition already profits at
alpha ~ 0.295, because the honest network forks at ~0.48 here so the public
chain's HEIGHT advances at only (1-a)*f*(1-fork) while a coalition sharing one
view extends privately at the full a*f — the threshold falls with the fork rate.
And a 0.20 coalition that does NOT pay alone DOES pay against a second 0.20
rival: the rival displaces honest blocks too and both collect on the disruption.
That is the "individually sub-threshold, jointly profitable" case, confirmed at
K = 2 and not extending to K >= 3.

The practical split: coalition fragmentation is good for TSI's estimator and bad
for the incentive argument. "No coalition holds 1/3" is not a safety property at
a load where the honest network forks appreciably, which makes rho < 1 an
incentive constraint and not only an accuracy one.

§8.3 item 2 narrowed accordingly; fig39 marks boundary-affected cells hollow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 12:42:54 +02:00
Marcin Pawlowski
097543a5f9
Re-measure the W pairing paired, and correct my own severity numbers
The W = 12 pairing is now measured the way a ~0.001 claim has to be: every
integer W from 8 to 15, 32 replicates, and paired_streams so the whole grid runs
on common random numbers (_base_key excludes both uncle_window_anchor and
window_absorption, so a replicate draws one stake vector, one graph and one
lottery for every cell). The earlier unpaired sweep reported +0.0008 against a
standard error of 0.0009 — it could not resolve its own headline.

Paired against today's recipe (uncle-anchored, W = 10):

  parent W=10   -0.0056 +- 0.0005   t = -10.4
  parent W=11   -0.0024 +- 0.0005   t =  -4.5
  parent W=12   +0.00004 +- 0.00050 t =   0.1   <- parity
  parent W=14   +0.0016 +- 0.0004   t =   3.5

W = 12 is the smallest window reaching parity, and the parity is exact rather
than marginal: W = 11, one interval short, is still resolvably worse. p_ref
agrees at the same window (0.938 vs 0.939) instead of lagging to W = 15 as the
unpaired edition had it. Also states what the sweep makes visible: widening
today's uncle-anchored rule buys +0.0018 on its own, so W = 12 makes the swap
cost-neutral against the CURRENT recipe rather than optimal in absolute terms.

CORRECTIONS to the previous commit, which measured contamination on the wrong
RNG stream. The engine draws stake from seedseq_for(config).spawn(...)[0]; I
used rng_for(config), the root. Both are valid stake draws, neither is the same
vector. Redone properly:

  - The capstone draw was NOT contaminated: 0 of 8 replicates over 1.25x its
    label, worst 0.369 against 0.30, no majority. My "2 of 8, one a 61%
    majority" was wrong and is withdrawn from §8.4 and §9.
  - The finding that survives is sharper: on that same mild overshoot the spec's
    rule moved 0.001 and the parent-anchored variant moved 0.016. A rule leaning
    harder on the reference window is far more sensitive to an oversized
    suppressing coalition.
  - Genuinely contaminated: §6.12's 12-replicate W sweep (2 majorities, worst
    0.720) and §6.8's selfish margin at a=0.3 and a=0.4 (2 and 1 majorities).
    §6.5's variants and §6.8's a=0.2 arm are clean; §8.3 item 20 narrowed to the
    one sweep that still needs re-running.
  - The general severity is worse than first stated, not better: at the report's
    geometry a nominal 0.3 realised a majority in 12% of replicates.

Two more defects found on the way:

  - stake_for(config) added, because scripts used rng_for and the engine uses
    the spawned child — so every script that rebuilt a tree was analysing a
    different network than the trajectory it was compared against. All scripts
    and tests now use it.
  - A coalition member could receive a private block BEFORE its parent: the
    arrival was clamped against the PRODUCER's view of the parent and applied to
    the whole coalition, so a member still awaiting a public parent got the child
    first. Now clamped per member. Caught by the existing arrival-order test once
    the stake derivation was corrected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 12:37:21 +02:00
Marcin Pawlowski
516783146d
Fix adversary coalition sizing; add rival coalitions and a lead cap
Three engine defects, found while building the multi-coalition study §6.9 flags
as open. The first is the serious one.

1. COALITION SIZING (engine._adversary_mask, `random` selection — the default).
   The coalition was the smallest random prefix whose stake reached the target.
   Under a Pareto tail a whale straddling the cut carries it far past its label:
   over 60 replicates, a nominal adversary_frac of 0.4 realised a MAJORITY in
   ~10% of them and reached 0.97, and 0.2 reached 0.90. The median was always
   on-label, which is why it hid — it distorts the tail, not the centre.

   Both other places in the code that size a set by stake had already rejected
   this rule: the `whale` arm uses fit-then-close, and _churn_inactive_mask
   documents the identical failure ("a 30% label realising up to ~53%"). The
   `random` arm kept it. Now fit-then-close in random order, and a draw where
   the tail leaves no subset near the label warns instead of silently running a
   different attacker. Realised stake is now within 0.1% of its label.

   Re-ran the load-bearing studies. §8.4 capstone (2 of 8 replicates
   contaminated, one a 61% majority): spec rule 0.994 -> 0.995, p_ref 0.936 ->
   0.937. The parent-anchored variant is far more sensitive — 0.974 -> 0.990,
   p_ref 0.875 -> 0.923 — because a tighter window and a larger suppressing
   coalition compound, so §8.4's argument for the W = 12 pairing rested on
   0.021 of cost that is really 0.006. The pairing itself survives re-measurement
   and is now better supported: p_ref reaches parity at W = 12 too, not at 15.
   §6.8's uncle-margin sweep and §6.5's random-arm variants are flagged as
   needing re-measurement (§8.3 item 20), not silently carried.

2. SM1 NEVER TERMINATED under a forking honest network. Textbook SM1 waits while
   it leads, assuming the lead returns to zero. But honest blocks fork against
   each other, so the public chain's HEIGHT grows at ~(1-a)*f*(1-fork) while a
   coalition sharing one view extends privately at the full a*f; past a fork rate
   of ~1 - a/(1-a) the private chain outruns the public one and `wait` never
   fires. The lead ran to thousands and every block was stranded at the epoch
   boundary — 98% of adversarial blocks at alpha=0.4, delta_max=8 — scoring an
   attacker that WON the race as having earned nothing. selfish_lead_cap
   (default: the finality depth k) publishes a lead that can no longer be caught.
   Inert unless `wait` stops terminating; pinned paired.

3. RIVAL COALITIONS (adversary_coalitions = K) for the §6.9 study: K private
   chains, each invisible to the others by the same arrival sentinel that hides
   them from honest nodes, so they orphan each other as well as the honest chain.
   Stake-balanced partition (LPT), K=1 bit-identical to the single-coalition path.

Also corrects §8.4's closing paragraph, which still quoted a pre-countable
D-hat/D of 1.001 and fork rates that contradicted its own table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 12:21:00 +02:00