From f51630e509475f1b5e2bdc49e9e331fc8c286268 Mon Sep 17 00:00:00 2001 From: Marcin Pawlowski Date: Thu, 6 Aug 2026 12:20:07 +0200 Subject: [PATCH] Rename the simulator and report from pd to blend The study started as a peering-degree question and grew well past it: propagation, adversary exposure, deanonymization and time-to-link, reliability under uniform and correlated churn, messaging redundancy, and cover traffic. The pd name no longer describes it. tools/simulators/blend/pd/ -> tools/simulators/blend/, package src/pd -> src/blend, and reports/blend/pd/ -> reports/blend/. Moved with git mv so history follows. The text substitutions are deliberately narrow. pd is also the conventional pandas alias, and pandas genuinely has a pd.plotting submodule, so a blanket pd. -> blend. rewrite would have corrupted four files. Only package-unambiguous forms were changed: from pd.X, -m pd.X, pd., PD_BYTES_BUDGET, src/pd, and the pyproject name. All four import pandas as pd lines are untouched and verified. Both READMEs reframed: peering degree is now presented as the primary axis that ties the others together rather than as the subject, and the relative links, which lost a directory level in the move, are corrected. Verified after the move: ruff clean, 101 tests, 45 verify anchors, make targets, the script shims, an end-to-end smoke run, and data/report_numbers.py still reproducing the report tables from the checked-in evidence. Co-Authored-By: Claude Opus 5 (1M context) --- reports/blend/{pd => }/README.md | 8 ++++---- reports/blend/{pd => }/data/README.md | 4 ++-- .../data/correlated-churn/adversary.parquet | Bin .../data/correlated-churn/deanon.parquet | Bin .../data/correlated-churn/propagation.parquet | Bin .../data/cover-traffic/adversary.parquet | Bin .../{pd => }/data/cover-traffic/deanon.parquet | Bin .../data/cover-traffic/propagation.parquet | Bin .../{pd => }/data/cover-traffic/traffic.parquet | Bin .../{pd => }/data/default/adversary.parquet | Bin .../blend/{pd => }/data/default/deanon.parquet | Bin .../{pd => }/data/default/propagation.parquet | Bin .../{pd => }/data/fullscale/adversary.parquet | Bin .../{pd => }/data/fullscale/deanon.parquet | Bin .../{pd => }/data/fullscale/propagation.parquet | Bin .../{pd => }/data/percolation/adversary.parquet | Bin .../{pd => }/data/percolation/deanon.parquet | Bin .../data/percolation/propagation.parquet | Bin .../{pd => }/data/redundancy/adversary.parquet | Bin .../{pd => }/data/redundancy/deanon.parquet | Bin .../data/redundancy/propagation.parquet | Bin reports/blend/{pd => }/data/report_numbers.py | 0 .../report-figures/01_delay_vs_degree.png | Bin .../report-figures/02_delay_vs_blendhops.png | Bin .../{pd => }/report-figures/03_delay_vs_N.png | Bin .../report-figures/04_observed_vs_fadv.png | Bin .../report-figures/05_eclipse_vs_fadv.png | Bin .../report-figures/06_observed_vs_degree.png | Bin .../report-figures/07_eclipse_vs_degree.png | Bin .../report-figures/08_heatmap_observed.png | Bin .../report-figures/09_heatmap_eclipse.png | Bin .../10_delivery_vs_unresponsive.png | Bin .../11_coverage_vs_unresponsive.png | Bin .../report-figures/12_deanon_vs_blendhops.png | Bin .../13_full_deanon_vs_blendhops.png | Bin .../report-figures/14_full_deanon_vs_fadv.png | Bin .../report-figures/15_full_deanon_vs_degree.png | Bin .../report-figures/16_time_to_link_vs_stake.png | Bin .../17_time_to_link_vs_stake_redundancy.png | Bin .../18_time_to_stake_vs_threshold.png | Bin .../report-figures/19_redundancy_tradeoff.png | Bin .../report-figures/20_coverage_percolation.png | Bin .../21_redundancy_time_to_link.png | Bin .../22_churn_correlated_vs_uniform.png | Bin .../23_blending_vs_rate_and_delay.png | Bin .../report-figures/24_quota_stake_ceiling.png | Bin tools/simulators/blend/{pd => }/.gitignore | 0 tools/simulators/blend/{pd => }/Makefile | 16 ++++++++-------- tools/simulators/blend/{pd => }/README.md | 15 +++++++++------ .../{pd => }/configs/correlated-churn.yaml | 0 .../blend/{pd => }/configs/cover-traffic.yaml | 0 .../blend/{pd => }/configs/default.yaml | 0 .../blend/{pd => }/configs/fullscale.yaml | 0 .../blend/{pd => }/configs/percolation.yaml | 0 .../blend/{pd => }/configs/redundancy.yaml | 0 .../blend/{pd => }/configs/smoke.yaml | 0 tools/simulators/blend/{pd => }/pyproject.toml | 10 +++++----- .../blend/{pd => }/requirements-dev.txt | 0 .../simulators/blend/{pd => }/requirements.txt | 0 .../blend/{pd => }/scripts/make_figures.py | 4 ++-- .../blend/{pd => }/scripts/run_sweep.py | 4 ++-- .../simulators/blend/{pd => }/scripts/verify.py | 4 ++-- .../blend/{pd/src/pd => src/blend}/__init__.py | 0 .../blend/{pd/src/pd => src/blend}/adversary.py | 0 .../blend/{pd/src/pd => src/blend}/config.py | 0 .../blend/{pd/src/pd => src/blend}/constants.py | 0 .../blend/{pd/src/pd => src/blend}/engine.py | 0 .../blend/{pd/src/pd => src/blend}/graph.py | 0 .../blend/{pd/src/pd => src/blend}/latency.py | 0 .../{pd/src/pd => src/blend}/linkability.py | 0 .../blend/{pd/src/pd => src/blend}/memguard.py | 4 ++-- .../blend/{pd/src/pd => src/blend}/metrics.py | 0 .../blend/{pd/src/pd => src/blend}/mixclock.py | 0 .../src/pd => src/blend}/plotting/__init__.py | 0 .../src/pd => src/blend}/plotting/figures.py | 0 .../pd => src/blend}/plotting/make_figures.py | 0 .../{pd/src/pd => src/blend}/plotting/style.py | 0 .../{pd/src/pd => src/blend}/propagation.py | 0 .../blend/{pd/src/pd => src/blend}/quota.py | 0 .../blend/{pd/src/pd => src/blend}/rng.py | 0 .../blend/{pd/src/pd => src/blend}/sweep.py | 0 .../blend/{pd/src/pd => src/blend}/traffic.py | 0 .../blend/{pd/src/pd => src/blend}/verify.py | 0 .../blend/{pd => }/tests/test_adversary.py | 6 +++--- .../blend/{pd => }/tests/test_config.py | 2 +- .../blend/{pd => }/tests/test_deanon.py | 8 ++++---- .../blend/{pd => }/tests/test_graph.py | 4 ++-- .../blend/{pd => }/tests/test_linkability.py | 2 +- .../blend/{pd => }/tests/test_mixclock.py | 2 +- .../blend/{pd => }/tests/test_propagation.py | 16 ++++++++-------- .../blend/{pd => }/tests/test_quota.py | 2 +- .../simulators/blend/{pd => }/tests/test_rng.py | 4 ++-- .../blend/{pd => }/tests/test_traffic.py | 6 +++--- 93 files changed, 62 insertions(+), 59 deletions(-) rename reports/blend/{pd => }/README.md (94%) rename reports/blend/{pd => }/data/README.md (95%) rename reports/blend/{pd => }/data/correlated-churn/adversary.parquet (100%) rename reports/blend/{pd => }/data/correlated-churn/deanon.parquet (100%) rename reports/blend/{pd => }/data/correlated-churn/propagation.parquet (100%) rename reports/blend/{pd => }/data/cover-traffic/adversary.parquet (100%) rename reports/blend/{pd => }/data/cover-traffic/deanon.parquet (100%) rename reports/blend/{pd => }/data/cover-traffic/propagation.parquet (100%) rename reports/blend/{pd => }/data/cover-traffic/traffic.parquet (100%) rename reports/blend/{pd => }/data/default/adversary.parquet (100%) rename reports/blend/{pd => }/data/default/deanon.parquet (100%) rename reports/blend/{pd => }/data/default/propagation.parquet (100%) rename reports/blend/{pd => }/data/fullscale/adversary.parquet (100%) rename reports/blend/{pd => }/data/fullscale/deanon.parquet (100%) rename reports/blend/{pd => }/data/fullscale/propagation.parquet (100%) rename reports/blend/{pd => }/data/percolation/adversary.parquet (100%) rename reports/blend/{pd => }/data/percolation/deanon.parquet (100%) rename reports/blend/{pd => }/data/percolation/propagation.parquet (100%) rename reports/blend/{pd => }/data/redundancy/adversary.parquet (100%) rename reports/blend/{pd => }/data/redundancy/deanon.parquet (100%) rename reports/blend/{pd => }/data/redundancy/propagation.parquet (100%) rename reports/blend/{pd => }/data/report_numbers.py (100%) rename reports/blend/{pd => }/report-figures/01_delay_vs_degree.png (100%) rename reports/blend/{pd => }/report-figures/02_delay_vs_blendhops.png (100%) rename reports/blend/{pd => }/report-figures/03_delay_vs_N.png (100%) rename reports/blend/{pd => }/report-figures/04_observed_vs_fadv.png (100%) rename reports/blend/{pd => }/report-figures/05_eclipse_vs_fadv.png (100%) rename reports/blend/{pd => }/report-figures/06_observed_vs_degree.png (100%) rename reports/blend/{pd => }/report-figures/07_eclipse_vs_degree.png (100%) rename reports/blend/{pd => }/report-figures/08_heatmap_observed.png (100%) rename reports/blend/{pd => }/report-figures/09_heatmap_eclipse.png (100%) rename reports/blend/{pd => }/report-figures/10_delivery_vs_unresponsive.png (100%) rename reports/blend/{pd => }/report-figures/11_coverage_vs_unresponsive.png (100%) rename reports/blend/{pd => }/report-figures/12_deanon_vs_blendhops.png (100%) rename reports/blend/{pd => }/report-figures/13_full_deanon_vs_blendhops.png (100%) rename reports/blend/{pd => }/report-figures/14_full_deanon_vs_fadv.png (100%) rename reports/blend/{pd => }/report-figures/15_full_deanon_vs_degree.png (100%) rename reports/blend/{pd => }/report-figures/16_time_to_link_vs_stake.png (100%) rename reports/blend/{pd => }/report-figures/17_time_to_link_vs_stake_redundancy.png (100%) rename reports/blend/{pd => }/report-figures/18_time_to_stake_vs_threshold.png (100%) rename reports/blend/{pd => }/report-figures/19_redundancy_tradeoff.png (100%) rename reports/blend/{pd => }/report-figures/20_coverage_percolation.png (100%) rename reports/blend/{pd => }/report-figures/21_redundancy_time_to_link.png (100%) rename reports/blend/{pd => }/report-figures/22_churn_correlated_vs_uniform.png (100%) rename reports/blend/{pd => }/report-figures/23_blending_vs_rate_and_delay.png (100%) rename reports/blend/{pd => }/report-figures/24_quota_stake_ceiling.png (100%) rename tools/simulators/blend/{pd => }/.gitignore (100%) rename tools/simulators/blend/{pd => }/Makefile (76%) rename tools/simulators/blend/{pd => }/README.md (92%) rename tools/simulators/blend/{pd => }/configs/correlated-churn.yaml (100%) rename tools/simulators/blend/{pd => }/configs/cover-traffic.yaml (100%) rename tools/simulators/blend/{pd => }/configs/default.yaml (100%) rename tools/simulators/blend/{pd => }/configs/fullscale.yaml (100%) rename tools/simulators/blend/{pd => }/configs/percolation.yaml (100%) rename tools/simulators/blend/{pd => }/configs/redundancy.yaml (100%) rename tools/simulators/blend/{pd => }/configs/smoke.yaml (100%) rename tools/simulators/blend/{pd => }/pyproject.toml (83%) rename tools/simulators/blend/{pd => }/requirements-dev.txt (100%) rename tools/simulators/blend/{pd => }/requirements.txt (100%) rename tools/simulators/blend/{pd => }/scripts/make_figures.py (57%) rename tools/simulators/blend/{pd => }/scripts/run_sweep.py (64%) rename tools/simulators/blend/{pd => }/scripts/verify.py (63%) rename tools/simulators/blend/{pd/src/pd => src/blend}/__init__.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/adversary.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/config.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/constants.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/engine.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/graph.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/latency.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/linkability.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/memguard.py (92%) rename tools/simulators/blend/{pd/src/pd => src/blend}/metrics.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/mixclock.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/plotting/__init__.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/plotting/figures.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/plotting/make_figures.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/plotting/style.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/propagation.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/quota.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/rng.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/sweep.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/traffic.py (100%) rename tools/simulators/blend/{pd/src/pd => src/blend}/verify.py (100%) rename tools/simulators/blend/{pd => }/tests/test_adversary.py (93%) rename tools/simulators/blend/{pd => }/tests/test_config.py (98%) rename tools/simulators/blend/{pd => }/tests/test_deanon.py (96%) rename tools/simulators/blend/{pd => }/tests/test_graph.py (96%) rename tools/simulators/blend/{pd => }/tests/test_linkability.py (98%) rename tools/simulators/blend/{pd => }/tests/test_mixclock.py (91%) rename tools/simulators/blend/{pd => }/tests/test_propagation.py (96%) rename tools/simulators/blend/{pd => }/tests/test_quota.py (99%) rename tools/simulators/blend/{pd => }/tests/test_rng.py (96%) rename tools/simulators/blend/{pd => }/tests/test_traffic.py (97%) diff --git a/reports/blend/pd/README.md b/reports/blend/README.md similarity index 94% rename from reports/blend/pd/README.md rename to reports/blend/README.md index b7c8a1d..181da7b 100644 --- a/reports/blend/pd/README.md +++ b/reports/blend/README.md @@ -1,8 +1,8 @@ -# Peering degree in the Blend network — a Monte-Carlo study +# The Blend network — a Monte-Carlo study -*Static-graph network simulation of the Blend message cascade. Simulator: [`pd`](../../../tools/simulators/blend/pd). All delays in **milliseconds**; the free-running mix clock's maximum interval (`max_blend_delay`) is in **whole seconds**. Adversary and deanonymization metrics are exact at every network size; propagation is Monte-Carlo over random senders.* +*Network simulation of the Blend message cascade. Simulator: [`blend`](../../tools/simulators/blend). All delays in **milliseconds**; the free-running mix clock's maximum interval (`max_blend_delay`) is in **whole seconds**. Adversary and deanonymization metrics are exact at every network size; propagation is Monte-Carlo over random senders.* -This report quantifies how a node's **peering degree** — the number of symmetric peers it keeps — trades off four properties of the Blend network at once: how fast a message propagates, how much of the network an adversary observes, how often a message is deanonymized, and how reliably messages are delivered when a fraction of nodes go dark. The headline is a single tension: **raising the peering degree improves propagation speed, eclipse resistance, and churn resilience, but *worsens* observation and sender deanonymization.** The anonymity axis has its own, degree-independent control — the **blend-path length** — so the two knobs separate cleanly: set the degree for the transport goals, set the path length for the anonymity goal. Two further questions follow from the deanonymization rates: *how long* an adversary needs to link an emitter to its messages and to learn its stake — which scales inversely with the node's own stake — and how **messaging redundancy** (sending each message over several independent cascades) trades reliability against anonymity, amplifying both by the very same factor. +This report measures the Blend network end to end: how fast a message propagates, how much of the network an adversary observes, how often a message is deanonymized and how quickly a node can be linked to one, how reliably messages are delivered when nodes go dark or whole regions fail, and what cover traffic buys. **Peering degree** is the first axis and the one that ties the others together, because it trades off several of these at once: the headline is a single tension: **raising the peering degree improves propagation speed, eclipse resistance, and churn resilience, but *worsens* observation and sender deanonymization.** The anonymity axis has its own, degree-independent control — the **blend-path length** — so the two knobs separate cleanly: set the degree for the transport goals, set the path length for the anonymity goal. Two further questions follow from the deanonymization rates: *how long* an adversary needs to link an emitter to its messages and to learn its stake — which scales inversely with the node's own stake — and how **messaging redundancy** (sending each message over several independent cascades) trades reliability against anonymity, amplifying both by the very same factor. ## Headline @@ -392,7 +392,7 @@ Because `s_max` is expressed against `D̂`, an estimator that runs low tightens ## 6. Reproducibility -The simulator, configs, and analytic checks live in [`tools/simulators/blend/pd`](../../../tools/simulators/blend/pd). From that directory: `make install`, then `make sweep` runs the main grid (`configs/default.yaml`: N up to 10⁵, degree 3–16, 1–5 blend hops, `f_adv` up to 0.5, unresponsive fractions to 0.5, all three placement modes, 8 topology seeds) into `runs/_default/`, writing three tables — `propagation.parquet`, `adversary.parquet`, and `deanon.parquet` — and rendering the figures. `make sweep-fullscale` extends the exact metrics to 10⁶ nodes. The messaging-redundancy study (§3.8) and the linkability figures come from `configs/redundancy.yaml` (`python -m pd.sweep --config configs/redundancy.yaml`), which sweeps `redundancy` ∈ {1, 2, 3, 4} alongside the churn and adversary grids; the churn-threshold study (§3.5, Fig 20) comes from `configs/percolation.yaml`, which walks the unresponsive fraction to 0.9 so each degree's collapse can be located against `u_c = 1 − 1/(degree − 1)`; and the correlated-outage study (§3.9, Fig 22) from `configs/correlated-churn.yaml` (`make correlated-churn`), which partitions the network into failure domains and runs both churn modes on the same topologies. `make sweep-fullscale` produces the 10⁶ scaling check described in §5; and the cover-traffic study (§3.10, Figs 23–24) comes from `configs/cover-traffic.yaml`, which sweeps the emission rate over three decades against three release delays and pairs each timeline with the epoch-scale emission budget. Round counts in all three configs are set for statistical resolution, not speed — see the sampling-error note in §5. `make verify` runs the analytic anchors (d-regularity; `observed ≈ 1 − (1 − f)^degree`; `eclipsed ≈ f^degree`; delivery `≈ (1 − u)^blend_hops`; both deanonymization rates against a direct Monte-Carlo of the same draw; and — check 6 — `deanon_R` / `delivery_R = 1 − (1 − x)^R` for R independent cascades and the time-to-link geometric law), and `make test` the unit suite (`test_linkability.py` covers the time-to-link and stake formulae). The time-to-link and stake-inference curves are computed by `pd.linkability` from these exact rates. +The simulator, configs, and analytic checks live in [`tools/simulators/blend`](../../tools/simulators/blend). From that directory: `make install`, then `make sweep` runs the main grid (`configs/default.yaml`: N up to 10⁵, degree 3–16, 1–5 blend hops, `f_adv` up to 0.5, unresponsive fractions to 0.5, all three placement modes, 8 topology seeds) into `runs/_default/`, writing three tables — `propagation.parquet`, `adversary.parquet`, and `deanon.parquet` — and rendering the figures. `make sweep-fullscale` extends the exact metrics to 10⁶ nodes. The messaging-redundancy study (§3.8) and the linkability figures come from `configs/redundancy.yaml` (`python -m blend.sweep --config configs/redundancy.yaml`), which sweeps `redundancy` ∈ {1, 2, 3, 4} alongside the churn and adversary grids; the churn-threshold study (§3.5, Fig 20) comes from `configs/percolation.yaml`, which walks the unresponsive fraction to 0.9 so each degree's collapse can be located against `u_c = 1 − 1/(degree − 1)`; and the correlated-outage study (§3.9, Fig 22) from `configs/correlated-churn.yaml` (`make correlated-churn`), which partitions the network into failure domains and runs both churn modes on the same topologies. `make sweep-fullscale` produces the 10⁶ scaling check described in §5; and the cover-traffic study (§3.10, Figs 23–24) comes from `configs/cover-traffic.yaml`, which sweeps the emission rate over three decades against three release delays and pairs each timeline with the epoch-scale emission budget. Round counts in all three configs are set for statistical resolution, not speed — see the sampling-error note in §5. `make verify` runs the analytic anchors (d-regularity; `observed ≈ 1 − (1 − f)^degree`; `eclipsed ≈ f^degree`; delivery `≈ (1 − u)^blend_hops`; both deanonymization rates against a direct Monte-Carlo of the same draw; and — check 6 — `deanon_R` / `delivery_R = 1 − (1 − x)^R` for R independent cascades and the time-to-link geometric law), and `make test` the unit suite (`test_linkability.py` covers the time-to-link and stake formulae). The time-to-link and stake-inference curves are computed by `blend.linkability` from these exact rates. The figures of record for this report are the copies checked in under [`report-figures/`](report-figures); the simulator does not commit its own generated figures. To regenerate: run the sweeps above, then copy `runs/<…>/figures/*.png` into `report-figures/`. diff --git a/reports/blend/pd/data/README.md b/reports/blend/data/README.md similarity index 95% rename from reports/blend/pd/data/README.md rename to reports/blend/data/README.md index c5921a5..eb52f24 100644 --- a/reports/blend/pd/data/README.md +++ b/reports/blend/data/README.md @@ -17,7 +17,7 @@ Each run directory holds the three tables the simulator writes: `propagation.par | `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` | 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 — `pd.linkability` +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 @@ -33,7 +33,7 @@ runs instead. ## Regenerating the data itself -From [`tools/simulators/blend/pd`](../../../../tools/simulators/blend/pd): `make sweep`, +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/_