49 lines
3.2 KiB
Markdown
Raw Normal View History

# Evidence of record
The sweep outputs behind every number in [the report](../README.md). The simulator does not commit
its own `runs/` directory — these are the copies of record, kept so that any figure or table can be
re-derived, or challenged, without re-running hours of compute.
Each run directory holds the three tables the simulator writes: `propagation.parquet`,
`adversary.parquet` and `deanon.parquet`.
| directory | config | sampling | backs |
|---|---|---|---|
| `default/` | `configs/default.yaml` | 1 000 rounds × 8 seeds = **8 000/cell** | §3.1§3.5 — delay, observation, eclipse, deanonymization, delivery, coverage |
| `redundancy/` | `configs/redundancy.yaml` | 1 200 × 8 = **9 600/cell** | §3.8 — messaging redundancy R = 1…4 |
| `percolation/` | `configs/percolation.yaml` | 800 × 8 = **6 400/cell** | §3.5 — the churn threshold `u_c = 1 1/(degree 1)` |
| `correlated-churn/` | `configs/correlated-churn.yaml` | 800 × 8 = **6 400/cell** | §3.9 — correlated AS/region outages vs uniform churn |
| `fullscale/` | `configs/fullscale.yaml` | 64 × 3 = **192/cell** | §5 — the 10⁶ scaling check (deliberately lighter; not a source of headline numbers) |
reports/blend/pd: section 3.10, cover traffic Written from configs/cover-traffic.yaml: the rate swept over three decades against three release delays, each timeline paired with the epoch emission budget. The finding that shapes the section is that mixing never happens. At one message per second a relay holds 0.0014 messages and never more than two; even 256x the rate reaches only 0.39, matching Little law exactly. So the anonymity set is entirely blending -- the broadcasts a relay saw between releases -- which follows rate*(2M+1)/3, twice the mean hold. Measured within ~1% over most of the range. That makes delay the cheap lever: both knobs enter linearly, but bandwidth is paid on every link while delay is paid once per hop. An anonymity set of 100 costs 42.9 msg/s at a 3s delay and 4.9 msg/s at 30s. The quota ceiling is the hard edge. A node proposals must fit its emission budget, capping stake at ln(1-q)/ln(1-f) of INFERRED stake -- about 0.1% at the baseline rate once Poisson fluctuation is allowed for. A 9.5% holder overruns by ~65x and is distinguishable by emission count alone, before any path is captured. Two existing sections needed correcting as a result. 3.6-3.7: with cover traffic running, catching an emission is not catching a block, and the large stakers those sections analyse sit one to two orders of magnitude above the quota ceiling, so their binding exposure is the quota rather than the cascade. 5: the timing- correlation adversary was deferred for want of cover traffic and is now unblocked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 12:14:45 +02:00
| `cover-traffic/` | `configs/cover-traffic.yaml` | 900 s timeline × 4 seeds | §3.10 — blending, mixing, and the emission-quota stake ceiling. Carries a fourth table, `traffic.parquet` |
2026-08-06 17:12:33 +02:00
| `attribution/` | `configs/attribution.yaml` | closed-form + 4 seeds | §3.4 — the attribution bracket at the report's scale: local confidence, attributable fractions, upstream hops, neighbourhood confidence |
| `timing/` | `configs/timing.yaml` | 120 s timeline × 3 seeds | §3.11 — the two release designs under a timing attack, and the minimum-interval control |
The linkability results (§3.6§3.7) and both deanonymization rates are closed forms over these
tables rather than separate measurements, so they have no run of their own — `blend.linkability`
derives them and `make verify` checks them against Monte-Carlo.
## Regenerating the report's numbers
```
python report_numbers.py
```
prints every quoted value straight from the parquets here -- the §3.1§3.5 and §3.8 tables with
their across-topology standard errors, and the §3.9§3.11 tables and the §3.4 attribution bracket
from their own runs.
That is the fastest way to check a table in the report against its evidence. It takes optional
paths (`report_numbers.py <default> <redundancy> <percolation>`) if you want to point it at fresh
runs instead.
## Regenerating the data itself
From [`tools/simulators/blend`](../../../tools/simulators/blend): `make sweep`,
`make redundancy`, `make percolation`, `make correlated-churn`, `make sweep-fullscale`. Results
land in that simulator's `runs/<timestamp>_<label>/`. Note that the seed streams depend on the
configuration, so re-running reproduces the *statistics*, not bit-identical numbers, unless the
config is unchanged — in which case it does reproduce exactly.
Two runs from the same session are deliberately **not** kept: the smoke runs (throwaway, far too
noisy to interpret) and an earlier 144-rounds/cell redundancy grid that was superseded because its
sampling error produced a non-monotonic delivery curve — the reason `redundancy/` samples 9 600.