From 056a42d5bb20955181248e6776b48d0d483b6332 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 12:47:17 +0000 Subject: [PATCH 1/3] docs: gap analysis after phase 1; carry hidden checklist items into the task ladder docs/GAPS.md records the self-audit: uncalibrated tolerance band, missing within-band damage scenario, centralized dispute resolver, grindable round seed, the skipped two-GPU replay criterion, verifier economics at zero fraud, and repo hygiene (LICENSE, CI, SECURITY.md). TASKS.md gains the items that existed only as footnotes: within-band budget scenario, production verifier daemon, multi-epoch re-join, Genesis bond values, dispute-limitation disclosure (Phase 2); security audit, VRF hardening, bonded dispute committee, mainnet key regeneration (Phase 3). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01KbsW5FgMKkc9kXBhWoumjW --- README.md | 1 + docs/GAPS.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ docs/TASKS.md | 13 ++++++- 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 docs/GAPS.md diff --git a/README.md b/README.md index 1103906..c50f036 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ Phase 0: the proof that the security economics survive contact with real transfo | `docs/DECISIONS.md` | prior-art sweep and the six locked decisions, sources inline | | `docs/ARCHITECTURE.md` | on-chain programs, daemons, round lifecycle, determinism strategy | | `docs/PRD.md`, `docs/TASKS.md` | phase acceptance criteria and the task ladder | +| `docs/GAPS.md` | self-audit: where the docs promise more than the code delivers, ordered by risk | Reproduce the sim: diff --git a/docs/GAPS.md b/docs/GAPS.md new file mode 100644 index 0000000..33d0404 --- /dev/null +++ b/docs/GAPS.md @@ -0,0 +1,96 @@ +# Gap analysis + +Snapshot 2026-07-20, after Phase 1 closed on devnet. A self-audit of what the docs promise +against what the code and task ladder actually deliver. Ordered by how much each gap can hurt. +Items marked (T) were folded into docs/TASKS.md. + +## 1. Security-model gaps + +### Tolerance band is uncalibrated +The band is the core security parameter — its width is, by our own definition, the adversary's +safe cheating margin — and it has only ever been exercised at 0.05 on one machine against a +synthetic 1% drift. The cross-hardware calibration harness (4090, 3090, A100, H100) is a Phase 2 +task that nothing else may ship before: a band set too tight slashes honest nodes and violates +the PRD's "zero honest-node slashes" metric; too loose and it hands stealth coalitions a budget. +(T: already in Phase 2.) + +### No within-band damage scenario in the sim +ARCHITECTURE.md claims "sim/ tracks how much loss damage fits inside a given band so the +parameter is chosen with eyes open." No scenario in run.py does this: the seven scenarios cover +sign-flip, ALIE and non-IID FPR, but none measures the maximum training bias an adversary can +inflict while staying inside the published band. The whitepaper's honest-limitations section +admits the weakness; the sim does not yet quantify it. Docs and code disagree. (T: Phase 2.) + +### Dispute resolution is a single authority +`slash_client` is main_authority gated. The bonded multi-verifier vote with reporter bounty is +deferred to Phase 3, which means the entire Genesis Run — 50+ external nodes with real bonded +value at (testnet) stake — runs with a centralized slash decision. That may be acceptable, but +it must be published as a limitation on the Genesis dashboard, with the scope and the timeline +for decentralizing it. (T: Phase 2 disclosure, Phase 3 mechanism.) + +### Weak round randomness is not on the task ladder +`get_random_seed(clock)` = sha256(unix_timestamp, slot) is grindable by a leader. CODEMAP.md +acknowledges this and says hardening "goes on the mainnet checklist" — but TASKS.md Phase 3 has +no such entry. A checklist item that is not on the checklist is a lost item. (T: Phase 3.) + +### Two-GPU replay reproduction was skipped, not done +PRD Phase 1 acceptance requires the replay verifier to reproduce a sampled contribution within +the band on two different GPUs. It was folded into 1.9's swarm bring-up and then the swarm ran +on a single MPS machine. Phase 1 is declared complete with this criterion unmet. Carry it +forward explicitly rather than letting the fold hide it. (T: Phase 2.) + +### No production verifier daemon +The decision core exists twice — Python sim (verifier.py) and the 1.8 replay experiment — but +the daemon that subscribes as Committee::Verifier, replays real gradients and calls run_slash +does not. It shares the libtorch prerequisite that 1.9 already solved, so the path is open. +(T: Phase 2.) + +### Multi-epoch re-join resilience +Deferred out of 1.9. A 7-day churn run cannot pass without it. (T: already implied by Phase 2 +rehearsal; now explicit.) + +## 2. Economics gaps + +### Verifier income has no zero-fraud sustainability story +The whitepaper says slashed stake pays the verifiers hunting it. In the equilibrium we actually +want — no fraud — verifier income is purely the audit_fee drawn from the treasury. Who funds +sustained audit pressure, at what burn rate, and for how long, is unmodeled. The economy sim has +the ledger; add the projection. (T: Phase 2.) + +### Genesis bond parameters are undefined +Real-bond calibration is Phase 3, but Phase 2 Genesis Run needs concrete (testnet) bond values, +and the choice communicates the economics to the first external cohort. Decide and publish them +with the same (1-p)/p discipline. (T: Phase 2.) + +## 3. Repository hygiene + +- **No LICENSE.** A project whose thesis is "the model belongs to the network" currently + reserves all rights by default. The fork substrate is Apache-2.0; this repo needs an explicit + choice before anything is made public. Owner decision, not picked unilaterally here. +- **No tests, no CI.** The README's headline numbers (2.175 honest loss, 9.8-round mean catch + time) are reproducible only by hand. A CI job that runs a short-horizon sim and asserts the + qualitative outcomes (clip neutralizes sign-flip, audit catches ALIE, zero honest FP) would + keep the claims honest as the sim evolves. (T: Phase 2.) +- **No SECURITY.md.** The red-team bounty is a Phase 2 design task, but a responsible-disclosure + channel should exist from the first day the repo is public. (T: Phase 2.) +- **No CONTRIBUTING.md.** Low priority while private; required before Genesis opens the doors. + +## 4. Doc drift + +- README still says "Private under wienerlabs while Phase 1 lands" — Phase 1 has landed; the + sentence and the publicity decision need refreshing. +- PRD Phase 1 acceptance said "4-process local swarm trains the 826k-parameter sim model through + the real chain path"; what actually ran was a single-client Nano-Llama epoch on devnet. The + substitution is defensible (live chain beats local processes) but the PRD should record it + instead of silently diverging. +- CODEMAP inherits the upstream `comptue_hash` misspelling; cheap to fix in the fork, and the + note in CODEMAP should flip from "grep accordingly" to "fixed at " when it is. +- Devnet program keypairs live in the private repo history. CODEMAP already rules them + devnet-only; the mainnet key regeneration must be a task, not a footnote. (T: Phase 3.) + +## 5. What is explicitly not a gap + +Scope discipline the docs already get right, listed so nobody re-litigates it: no on-chain +tensors, no Windows nodes, no generic compute marketplace, no frontier-quality claims, no +cryptographic-proof claims. The honest-claims policy (D6) is the standard this file holds the +rest of the project to. diff --git a/docs/TASKS.md b/docs/TASKS.md index 75635cf..0921a3a 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -29,15 +29,26 @@ Exit criteria: recorded end-to-end conviction demo, anchor test suite green, two ## Phase 2, Genesis Run - [ ] Tolerance band calibration harness across hardware classes (4090, 3090, A100, H100) +- [ ] Two-GPU tolerance-band replay reproduction (Phase 1 exit criterion carried forward; folded into 1.9 but never run on two distinct GPUs) +- [ ] Sim: within-band adversary budget scenario — maximum loss damage a coalition can inflict while staying inside the published band (closes the ARCHITECTURE.md claim the sim does not yet back) +- [ ] Production verifier daemon: subscribe as Committee::Verifier, replay real gradients, call run_slash (decision core done in 1.8; shares the 1.9 libtorch toolchain) +- [ ] Multi-epoch re-join resilience (deferred out of 1.9; prerequisite for any churn run) +- [ ] Verifier economics: zero-fraud treasury burn projection for sustained audit pressure; publish Genesis testnet bond values under the (1-p)/p discipline +- [ ] Publish the centralized-dispute limitation on the Genesis dashboard (slash_client is main_authority gated until the Phase 3 committee lands) - [ ] One-line installer + wallet flow + bond funding UX - [ ] Public site: live loss curve, node map, leaderboard, audit/slash feed - [ ] Relay infrastructure for non-hole-punchable nodes -- [ ] Red-team bounty program design +- [ ] Red-team bounty program design; SECURITY.md responsible-disclosure channel before the repo goes public +- [ ] Repo hygiene before going public: LICENSE decision, CI job asserting sim outcomes (clip neutralizes sign-flip, audit catches ALIE, zero honest FP), CONTRIBUTING.md - [ ] 350M run rehearsal with internal nodes, then 1B public run ## Phase 3, mainnet - [ ] Legal review of token design +- [ ] Independent security audit of the Anchor programs (coordinator, treasurer, authorizer) before real bonds +- [ ] Round randomness hardening: VRF or recent-blockhash commitment replacing sha256(unix_timestamp, slot) (grindable, flagged in CODEMAP.md) +- [ ] Bonded multi-verifier dispute committee with reporter bounty and treasury remainder, replacing the main_authority resolver (evolution locked in 1.6) +- [ ] Regenerate program keypairs for mainnet (devnet keypairs live in private repo history, devnet-only per CODEMAP.md) - [ ] TGE via Wiener Launchpad rails; distributor program reuse for airdrop and vesting - [ ] Real-bond parameters from sim calibration; treasury and endowment wiring - [ ] Inference v0: vLLM workers + TOPLOC validation + settlement program From 1c2d71dab02939a885add9066f193b6eb6b9932b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 13:15:12 +0000 Subject: [PATCH 2/3] sim: within-band adversary budget, zero-fraud verifier economics, tests and CI The within_band attack spends exactly the published tolerance band: each malicious worker biases against the honest mean at 0.9x band, so replay audits pass by construction and only aggregation pushes back. The --band-sweep experiment measures loss damage across band widths with explicit replay evidence per band. economy.py gains the zero-fraud projection (audit fees are pure treasury burn when there is nobody to slash, ~9.2% of rewards at p=0.1) and genesis_parameters() publishing the Genesis operating point under the (1-p)/p discipline. 28-test suite over aggregation, attacks, replay verification, economy and sparse transport runs on CPU torch with a synthetic corpus; CI runs it on every PR. SECURITY.md opens a private-disclosure channel, CONTRIBUTING.md documents the workflow, PRD records the Phase 1 as-built substitution, and TASKS/GAPS reflect what closed. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01KbsW5FgMKkc9kXBhWoumjW --- .github/workflows/ci.yml | 23 ++++++ CONTRIBUTING.md | 53 ++++++++++++++ README.md | 2 +- SECURITY.md | 34 +++++++++ docs/GAPS.md | 8 ++ docs/PRD.md | 2 + docs/TASKS.md | 10 ++- sim/leviathan_sim/attacks.py | 22 +++++- sim/leviathan_sim/economy.py | 50 +++++++++++++ sim/leviathan_sim/run.py | 133 +++++++++++++++++++++++++++++++++- sim/pyproject.toml | 8 ++ sim/tests/conftest.py | 12 +++ sim/tests/test_aggregators.py | 49 +++++++++++++ sim/tests/test_attacks.py | 89 +++++++++++++++++++++++ sim/tests/test_economy.py | 64 ++++++++++++++++ sim/tests/test_sparse.py | 35 +++++++++ sim/tests/test_verifier.py | 70 ++++++++++++++++++ 17 files changed, 656 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 sim/tests/conftest.py create mode 100644 sim/tests/test_aggregators.py create mode 100644 sim/tests/test_attacks.py create mode 100644 sim/tests/test_economy.py create mode 100644 sim/tests/test_sparse.py create mode 100644 sim/tests/test_verifier.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c68eadd --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: ci + +on: + push: + branches: [main] + pull_request: + +jobs: + sim-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + - name: Install CPU torch and test dependencies + run: | + pip install torch --index-url https://download.pytorch.org/whl/cpu + pip install numpy matplotlib pytest + - name: Sim test suite + working-directory: sim + run: python -m pytest tests -q diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e3000b6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,53 @@ +# Contributing + +This repository holds the Phase 0 proof: the simulation and the documents. The network +substrate (Anchor programs, trainer, verifier daemon) lives in wienerlabs/leviathan-net; the +site lives in wienerlabs/leviathan-web. + +## Setup + +``` +cd sim +uv venv && uv pip install torch numpy matplotlib pytest # CPU torch is fine +``` + +The full sim needs the tinyshakespeare corpus at `sim/data/shakespeare.txt` (gitignored): + +``` +curl -sSL -o data/shakespeare.txt \ + https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt +``` + +## Tests + +``` +cd sim && python -m pytest tests -q +``` + +The suite runs on CPU with a synthetic corpus and no downloads; CI runs exactly this. Every +qualitative claim the README makes should have a test that would fail if the sim stopped +backing it — if you change aggregation, attacks, the verifier or the economy, extend the tests +with the claim your change adds. + +## Experiments + +``` +python -m leviathan_sim.run --rounds 30 # the seven headline scenarios +python -m leviathan_sim.run --verify # tolerance-band replay experiment +python -m leviathan_sim.run --sparse # dense vs SparseLoCo transport +python -m leviathan_sim.run --band-sweep # within-band adversary budget +``` + +Add `--smoke` for a 3-round sanity pass. Outputs land in `sim/out/` (gitignored); curated +results are promoted to `docs/assets/` by hand, together with the numbers quoted in README and +docs. If you regenerate an asset, update every number that quotes it in the same commit. + +## Docs discipline + +The honest-claims policy (docs/DECISIONS.md, D6) binds prose, not just marketing: no claim in +README, the whitepaper or ARCHITECTURE.md should outrun what the sim or the fork actually +demonstrates. If you find one that does, that is a bug — file it or fix it via docs/GAPS.md. + +## Security findings + +See SECURITY.md — exploitable findings go through private reporting, not public issues. diff --git a/README.md b/README.md index c50f036..f1b5490 100644 --- a/README.md +++ b/README.md @@ -151,4 +151,4 @@ PYTORCH_ENABLE_MPS_FALLBACK=1 uv run python -m leviathan_sim.run --rounds 30 Built on the shoulders of, and differentiated from: Psyche/nousnet (Apache-2.0 fork substrate: Solana coordinator, iroh P2P, Rust DisTrO), SparseLoCo (MIT compression recipe), TOPLOC (MIT inference verification), OVIG (the audit loop's academic validation), Condorcet (wienerlabs: the aggregation and bond-economics research core), DanteGPU (supply), Wienerpad (futarchy governance), zk-lokomotive (Arweave rail). -Private under wienerlabs while Phase 1 lands. +Private under wienerlabs while the Genesis Run is prepared; going public is gated on the Phase 2 repo-hygiene checklist in docs/TASKS.md (the LICENSE decision is the open item — CI, tests, SECURITY.md and CONTRIBUTING.md are in place). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..7d200ee --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,34 @@ +# Security policy + +Leviathan's product is a trust machine; a vulnerability report is a contribution to the core +product, not an embarrassment. The honest-claims policy (docs/DECISIONS.md, D6) applies here +first: we would rather publish a weakness than pretend it does not exist. + +## Reporting a vulnerability + +Use GitHub's private vulnerability reporting on this repository (Security tab → "Report a +vulnerability"). Reports go to the wienerlabs maintainers privately; do not open a public issue +for anything exploitable. + +In scope, in rough order of how much we care: + +1. Economic-security breaks: any way to cheat the bond/audit/slash loop with positive expected + value — grinding the round seed, dodging the audit lottery, submitting work that survives + replay without doing it, draining or freezing bonds in the treasurer. +2. Aggregation breaks: attacks that get more damage past centered-clip + excision than the + published within-band budget implies, at coalition sizes below the excision threshold. +3. Sim errors that would make a published number wrong (loss tables, catch times, bond curve). +4. Classic vulnerabilities in the Anchor programs or daemons (leviathan-net) once public. + +Out of scope: volume/DoS against public devnet endpoints, findings that require the run +authority's own keys, and anything already listed as a known limitation in docs/GAPS.md or the +whitepaper's honest-limitations section — though sharpening a known limitation into a concrete +exploit is very much in scope. + +## What to expect + +Acknowledgement within 72 hours, a severity assessment within a week, credit in the fix's +release notes unless you prefer otherwise. There is no paid bounty yet; the red-team bounty +program is a Phase 2 deliverable (docs/TASKS.md) and this policy will be updated when it ships. + +Good-faith research within this policy will not be met with legal action. diff --git a/docs/GAPS.md b/docs/GAPS.md index 33d0404..7d8e741 100644 --- a/docs/GAPS.md +++ b/docs/GAPS.md @@ -4,6 +4,14 @@ Snapshot 2026-07-20, after Phase 1 closed on devnet. A self-audit of what the do against what the code and task ladder actually deliver. Ordered by how much each gap can hurt. Items marked (T) were folded into docs/TASKS.md. +Status update, same day: the gaps closable in this repo were closed — the within-band damage +scenario (`--band-sweep`, with explicit replay evidence that audits pass by construction), the +zero-fraud verifier burn projection and Genesis operating point in economy.py, a 28-test sim +suite with CI, SECURITY.md, CONTRIBUTING.md, and the doc-drift fixes (PRD as-built note, README +privacy line). Still open: the LICENSE decision (owner call), and everything that lives in +leviathan-net or requires hardware — band calibration across GPU classes, two-GPU replay, the +production verifier daemon, multi-epoch re-join, randomness hardening, the dispute committee. + ## 1. Security-model gaps ### Tolerance band is uncalibrated diff --git a/docs/PRD.md b/docs/PRD.md index 91457ad..86f9715 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -23,6 +23,8 @@ - End-to-end conviction demo: a node submitting fabricated deltas is audited, disputed, slashed and ejected, recorded in one continuous capture. - All state transitions covered by anchor tests; replay verifier reproduces a sampled contribution within the tolerance band on two different GPUs. +As-built note, 2026-07-20: the swarm criterion was met by substitution — a single-client Nano-Llama epoch on live devnet (17 rounds, DisTrO over iroh, real Join/Witness/Tick transactions) instead of a 4-process local swarm on the 826k sim model. Live chain beats local processes, so the substitution strengthens the criterion it replaced. The two-GPU replay reproduction was NOT met: it was folded into the swarm bring-up, which ran on one machine. It is carried forward as an explicit Phase 2 task in docs/TASKS.md. + ## Phase 2 acceptance (Genesis Run) - 50+ external nodes sustain a 350M to 1B run for 7+ days with churn. diff --git a/docs/TASKS.md b/docs/TASKS.md index 0921a3a..d5009ee 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -30,16 +30,18 @@ Exit criteria: recorded end-to-end conviction demo, anchor test suite green, two - [ ] Tolerance band calibration harness across hardware classes (4090, 3090, A100, H100) - [ ] Two-GPU tolerance-band replay reproduction (Phase 1 exit criterion carried forward; folded into 1.9 but never run on two distinct GPUs) -- [ ] Sim: within-band adversary budget scenario — maximum loss damage a coalition can inflict while staying inside the published band (closes the ARCHITECTURE.md claim the sim does not yet back) +- [x] Sim: within-band adversary budget scenario — `--band-sweep` runs a 5/16 coalition that biases against the honest mean at 0.9x the published band; explicit replay evidence shows the audit passes by construction (distance = 0.9 x band, fraud=false) at every band width, so aggregation is the only layer that pushes back; damage-vs-band curve written to band_sweep_results.json - [ ] Production verifier daemon: subscribe as Committee::Verifier, replay real gradients, call run_slash (decision core done in 1.8; shares the 1.9 libtorch toolchain) - [ ] Multi-epoch re-join resilience (deferred out of 1.9; prerequisite for any churn run) -- [ ] Verifier economics: zero-fraud treasury burn projection for sustained audit pressure; publish Genesis testnet bond values under the (1-p)/p discipline +- [x] Verifier economics: economy.py gains the zero-fraud projection (audit fee = 1.1x per-contribution H100 cost; treasury burn ~9.2% of rewards at p=0.1, preset-independent) and genesis_parameters() publishing the Genesis operating point (1B preset, p=0.1, band 0.05, bond = 9 rounds of reward); both emitted into results.json - [ ] Publish the centralized-dispute limitation on the Genesis dashboard (slash_client is main_authority gated until the Phase 3 committee lands) - [ ] One-line installer + wallet flow + bond funding UX - [ ] Public site: live loss curve, node map, leaderboard, audit/slash feed - [ ] Relay infrastructure for non-hole-punchable nodes -- [ ] Red-team bounty program design; SECURITY.md responsible-disclosure channel before the repo goes public -- [ ] Repo hygiene before going public: LICENSE decision, CI job asserting sim outcomes (clip neutralizes sign-flip, audit catches ALIE, zero honest FP), CONTRIBUTING.md +- [x] SECURITY.md responsible-disclosure channel (GitHub private vulnerability reporting; economic-security breaks explicitly in scope, no paid bounty until the program below ships) +- [ ] Red-team bounty program design +- [x] CI + tests: .github/workflows/ci.yml runs the 28-test sim suite on CPU torch (aggregation excises the outlier, within-band stays inside the band, replay catches sign-flip/lazy and passes honest, economy math); CONTRIBUTING.md documents setup, experiments and the docs discipline +- [ ] LICENSE decision (owner call; blocks going public) - [ ] 350M run rehearsal with internal nodes, then 1B public run ## Phase 3, mainnet diff --git a/sim/leviathan_sim/attacks.py b/sim/leviathan_sim/attacks.py index eac5006..72cb582 100644 --- a/sim/leviathan_sim/attacks.py +++ b/sim/leviathan_sim/attacks.py @@ -4,7 +4,7 @@ from leviathan_sim.swarm import LocalUpdate -VALID_ATTACKS = frozenset({"none", "sign_flip", "gaussian", "lazy", "alie"}) +VALID_ATTACKS = frozenset({"none", "sign_flip", "gaussian", "lazy", "alie", "within_band"}) @dataclass(frozen=True) @@ -14,6 +14,8 @@ class InjectionConfig: sign_flip_scale: float = 5.0 gaussian_std: float = 0.02 alie_z: float = 1.5 + band: float = 0.05 + band_margin: float = 0.9 @dataclass(frozen=True) @@ -37,6 +39,24 @@ def _attack(self, updates: dict[int, LocalUpdate]) -> dict[int, LocalUpdate]: if config.attack == "none" or not self.malicious_ids: return updates out = dict(updates) + if config.attack == "within_band": + # The adversary's whole budget is the published tolerance band: each + # malicious worker submits its honest delta plus a coordinated bias + # scaled so the relative replay distance stays at band_margin * band. + # Replay audits pass by construction; only aggregation limits this. + honest = torch.stack( + [u.delta for wid, u in updates.items() if wid not in self.malicious_ids] + ) + direction = honest.mean(dim=0) + direction = direction / torch.linalg.vector_norm(direction).clamp(min=1e-12) + budget = config.band * config.band_margin + for wid in self.malicious_ids: + if wid not in out: + continue + update = out[wid] + bias = -direction * budget * torch.linalg.vector_norm(update.delta) + out[wid] = replace(update, delta=update.delta + bias) + return out if config.attack == "alie": honest = torch.stack( [u.delta for wid, u in updates.items() if wid not in self.malicious_ids] diff --git a/sim/leviathan_sim/economy.py b/sim/leviathan_sim/economy.py index 2e11343..16feee4 100644 --- a/sim/leviathan_sim/economy.py +++ b/sim/leviathan_sim/economy.py @@ -99,3 +99,53 @@ def calibration_table(audit_probabilities: list[float]) -> list[dict]: } ) return rows + + +def audit_burn_projection( + audit_probabilities: list[float], + n_workers: int = 100, + fee_multiplier: float = 1.1, +) -> list[dict]: + """Zero-fraud equilibrium: with nobody to slash, verifier income is pure + treasury burn. A replay audit redoes one contribution's compute, so the fee + floor is the per-contribution H100 cost times a verifier margin.""" + rows = [] + for preset in PRESETS: + cost = h100_round_cost_usd(preset) + reward = 1.2 * cost + for p in audit_probabilities: + fee = fee_multiplier * cost + burn = p * n_workers * fee + rewards_paid = n_workers * reward + rows.append( + { + "preset": preset.label, + "audit_probability": p, + "n_workers": n_workers, + "audit_fee_usd": fee, + "expected_audits_per_round": p * n_workers, + "treasury_burn_per_round_usd": burn, + "burn_share_of_rewards": burn / rewards_paid, + } + ) + return rows + + +def genesis_parameters(audit_probability: float = 0.1, band: float = 0.05) -> dict: + """The published operating point for the Genesis Run testnet, derived from + the same (1-p)/p discipline as the calibration table. Values are per worker + per the 1B preset; testnet bonds use these numbers denominated in the + testnet collateral mint.""" + preset = next(p for p in PRESETS if p.params_billion == 1.0) + cost = h100_round_cost_usd(preset) + reward = 1.2 * cost + return { + "preset": preset.label, + "audit_probability": audit_probability, + "tolerance_band": band, + "round_reward_usd": reward, + "bond_usd": break_even_bond(audit_probability, reward), + "bond_rounds_of_reward": (1.0 - audit_probability) / audit_probability, + "expected_rounds_to_catch": 1.0 / audit_probability, + "audit_burn_share_of_rewards": audit_probability * 1.1 / 1.2, + } diff --git a/sim/leviathan_sim/run.py b/sim/leviathan_sim/run.py index 8b9a2f0..2b22617 100644 --- a/sim/leviathan_sim/run.py +++ b/sim/leviathan_sim/run.py @@ -14,7 +14,13 @@ from leviathan_sim.aggregators import CenteredClipAggregator, MeanAggregator from leviathan_sim.attacks import InjectionConfig, Injector -from leviathan_sim.economy import EconomyConfig, StakeLedger, calibration_table +from leviathan_sim.economy import ( + EconomyConfig, + StakeLedger, + audit_burn_projection, + calibration_table, + genesis_parameters, +) from leviathan_sim.model import build_model, parameter_count from leviathan_sim.sparse import chunked_topk_sign from leviathan_sim.swarm import NesterovOuter, SwarmWorker, evaluate, load_corpus @@ -78,6 +84,7 @@ def run_scenario( device: torch.device, sparse_density: float | None = None, sparse_chunk: int = 64, + attack_band: float = 0.05, ) -> dict: model = build_model(corpus.vocab_size, block_size, seed=7, device=device) theta = parameters_to_vector(model.parameters()).detach().clone() @@ -98,7 +105,7 @@ def run_scenario( for wid in worker_ids ] injector = Injector( - InjectionConfig(n_malicious=scenario.n_malicious, attack=scenario.attack), + InjectionConfig(n_malicious=scenario.n_malicious, attack=scenario.attack, band=attack_band), worker_ids, seed=11, ) @@ -299,6 +306,7 @@ def main(): parser.add_argument("--sparse", action="store_true") parser.add_argument("--sparse-density", type=float, default=0.02) parser.add_argument("--verify", action="store_true") + parser.add_argument("--band-sweep", action="store_true") parser.add_argument("--drift", type=float, default=0.01) parser.add_argument("--band", type=float, default=0.05) args = parser.parse_args() @@ -311,6 +319,9 @@ def main(): if args.verify: run_verify_experiment(args, corpus, device, base) return + if args.band_sweep: + run_band_sweep(args, corpus, rounds, device, base, probe) + return if args.sparse: run_sparse_comparison(args, corpus, rounds, device, base, probe) return @@ -352,6 +363,8 @@ def main(): }, "scenarios": results, "economy_calibration": calibration, + "audit_burn_projection": audit_burn_projection([0.02, 0.05, 0.1, 0.2, 0.3]), + "genesis_parameters": genesis_parameters(), } (out_dir / f"{prefix}results.json").write_text(json.dumps(payload, indent=2)) plot_loss_curves(results, out_dir / f"{prefix}loss_curves.png") @@ -437,6 +450,122 @@ def plot_verify(rows, band, drift, out_path: Path): plt.close(fig) +BAND_SWEEP = [0.02, 0.05, 0.1, 0.2] + + +def within_band_replay_evidence(args, corpus, device, band: float) -> dict: + """One round of explicit replay: a within-band submission must pass the + audit at exactly band_margin * band relative distance. This is the proof + that layer 2 is blind to this adversary, not an assumption.""" + n_workers = args.workers + model = build_model(corpus.vocab_size, args.block_size, seed=7, device=device) + theta = parameters_to_vector(model.parameters()).detach().clone() + workers = [ + SwarmWorker( + wid, corpus, n_workers, True, args.inner_steps, 2e-3, + args.batch_size, args.block_size, 100 + wid, device, + ) + for wid in range(n_workers) + ] + updates = {w.wid: w.local_round(theta, model, 0) for w in workers} + injector = Injector( + InjectionConfig(n_malicious=5, attack="within_band", band=band), + list(range(n_workers)), + seed=11, + ) + attacked, report = injector.apply(updates) + target_wid = min(report.malicious_ids) + verdict = replay_and_verify( + workers[target_wid], model, theta, 0, attacked[target_wid].delta, band + ) + return {"wid": target_wid, "distance": verdict.distance, "fraud": verdict.fraud} + + +def run_band_sweep(args, corpus, rounds, device, base, probe): + honest = run_scenario( + Scenario("withinband-honest", "Honest, clip", True, "none", 0, "clip", 0.0), + corpus, rounds, args.workers, args.inner_steps, + args.batch_size, args.block_size, device, + ) + print(f"withinband-honest: final={honest['final_val_loss']:.3f}") + rows = [] + for band in BAND_SWEEP: + scenario = Scenario( + f"withinband-{band}", f"Within-band 5/16, band {band}", True, + "within_band", 5, "clip", 0.0, + ) + result = run_scenario( + scenario, corpus, rounds, args.workers, args.inner_steps, + args.batch_size, args.block_size, device, attack_band=band, + ) + evidence = within_band_replay_evidence(args, corpus, device, band) + row = { + "band": band, + "final_val_loss": result["final_val_loss"], + "damage_vs_honest": result["final_val_loss"] - honest["final_val_loss"], + "malicious_selection_rate": result["malicious_selection_rate"], + "replay_distance": evidence["distance"], + "replay_fraud": evidence["fraud"], + "val_losses": result["val_losses"], + } + rows.append(row) + print( + f"withinband band={band}: final={row['final_val_loss']:.3f} " + f"damage={row['damage_vs_honest']:+.3f} " + f"mal_sel={fmt(row['malicious_selection_rate'])} " + f"replay_distance={row['replay_distance']:.4f} " + f"replay_fraud={row['replay_fraud']}" + ) + out_dir = base / "out" + out_dir.mkdir(exist_ok=True) + prefix = "smoke_band_" if args.smoke else "band_" + payload = { + "config": { + "rounds": rounds, "workers": args.workers, "n_malicious": 5, + "device": device.type, "model_parameters": parameter_count(probe), + "bands": BAND_SWEEP, + }, + "honest_reference": { + "final_val_loss": honest["final_val_loss"], + "val_losses": honest["val_losses"], + }, + "sweep": rows, + } + (out_dir / f"{prefix}sweep_results.json").write_text(json.dumps(payload, indent=2)) + plot_band_sweep(rows, honest["final_val_loss"], out_dir / f"{prefix}sweep.png") + print(f"wrote {out_dir / (prefix + 'sweep_results.json')}") + + +def plot_band_sweep(rows, honest_final, out_path: Path): + fig, (left, right) = plt.subplots(1, 2, figsize=(12.5, 4.6), facecolor=SURFACE) + bands = [r["band"] for r in rows] + style_axes(left) + left.plot(bands, [r["final_val_loss"] for r in rows], color=PALETTE[5], + linewidth=2.0, marker="o", markersize=5, label="within-band 5/16, clip") + left.axhline(honest_final, color=PALETTE[1], linewidth=1.6, linestyle="--", + label="honest reference") + left.set_xlabel("published tolerance band", color=INK_SECONDARY, fontsize=9) + left.set_ylabel("final validation loss", color=INK_SECONDARY, fontsize=9) + left.set_title("Loss damage that fits inside the band", color=INK, + fontsize=11, loc="left", pad=10) + left.legend(frameon=False, fontsize=8, labelcolor=INK_SECONDARY) + style_axes(right) + right.bar([str(b) for b in bands], [r["malicious_selection_rate"] or 0.0 for r in rows], + color=PALETTE[7], width=0.6) + right.set_ylim(0, 1.05) + right.set_xlabel("published tolerance band", color=INK_SECONDARY, fontsize=9) + right.set_ylabel("malicious selection rate", color=INK_SECONDARY, fontsize=9) + right.set_title("Aggregation is the only layer that pushes back", color=INK, + fontsize=11, loc="left", pad=10) + fig.suptitle( + "The band is the adversary's budget: replay audits pass by construction", + color=INK, fontsize=13, x=0.01, ha="left", + ) + fig.tight_layout(rect=(0, 0, 1, 0.94)) + fig.savefig(out_path, dpi=180, facecolor=SURFACE) + plt.close(fig) + + SPARSE_KEYS = [ "honest-mean-iid", "signflip-clip-iid", diff --git a/sim/pyproject.toml b/sim/pyproject.toml index 22be38f..44dcd07 100644 --- a/sim/pyproject.toml +++ b/sim/pyproject.toml @@ -9,5 +9,13 @@ dependencies = [ "matplotlib>=3.9", ] +[dependency-groups] +dev = [ + "pytest>=8.0", +] + [tool.uv] package = false + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/sim/tests/conftest.py b/sim/tests/conftest.py new file mode 100644 index 0000000..c36a7ef --- /dev/null +++ b/sim/tests/conftest.py @@ -0,0 +1,12 @@ +import torch +import pytest + +from leviathan_sim.swarm import CharCorpus + + +@pytest.fixture(scope="session") +def corpus() -> CharCorpus: + generator = torch.Generator().manual_seed(3) + vocab_size = 16 + tokens = torch.randint(0, vocab_size, (4096,), generator=generator) + return CharCorpus(tokens[:3600], tokens[3600:], vocab_size) diff --git a/sim/tests/test_aggregators.py b/sim/tests/test_aggregators.py new file mode 100644 index 0000000..e6fa827 --- /dev/null +++ b/sim/tests/test_aggregators.py @@ -0,0 +1,49 @@ +import torch + +from leviathan_sim.aggregators import CenteredClipAggregator, MeanAggregator + + +def make_deltas(n_honest: int = 7, n_malicious: int = 1, scale: float = 40.0): + generator = torch.Generator().manual_seed(5) + base = torch.randn(64, generator=generator) + deltas = {} + for wid in range(n_honest): + noise = torch.randn(64, generator=generator) * 0.05 + deltas[wid] = base + noise + for wid in range(n_honest, n_honest + n_malicious): + deltas[wid] = -scale * base + honest_mean = torch.stack([deltas[w] for w in range(n_honest)]).mean(dim=0) + return deltas, honest_mean, set(range(n_honest, n_honest + n_malicious)) + + +def test_mean_is_corrupted_by_one_outlier(): + deltas, honest_mean, _ = make_deltas() + agg, mask = MeanAggregator().aggregate(deltas) + assert all(mask.values()) + assert torch.linalg.vector_norm(agg - honest_mean) > torch.linalg.vector_norm(honest_mean) + + +def test_clip_excises_far_outlier_and_tracks_honest_mean(): + deltas, honest_mean, malicious = make_deltas() + agg, mask = CenteredClipAggregator().aggregate(deltas) + for wid in malicious: + assert mask[wid] is False + for wid in range(7): + assert mask[wid] is True + error = torch.linalg.vector_norm(agg - honest_mean) / torch.linalg.vector_norm(honest_mean) + assert error < 0.1 + + +def test_clip_center_persists_across_rounds(): + deltas, _, _ = make_deltas(n_malicious=0) + aggregator = CenteredClipAggregator() + first, _ = aggregator.aggregate(deltas) + assert aggregator.center is not None + second, _ = aggregator.aggregate(deltas) + assert torch.linalg.vector_norm(second - first) < torch.linalg.vector_norm(first) + + +def test_excision_falls_back_to_keep_all_when_everything_is_far(): + deltas = {0: torch.full((8,), 100.0), 1: torch.full((8,), -100.0)} + _, mask = CenteredClipAggregator(excision_multiplier=0.0).aggregate(deltas) + assert all(mask.values()) diff --git a/sim/tests/test_attacks.py b/sim/tests/test_attacks.py new file mode 100644 index 0000000..4965387 --- /dev/null +++ b/sim/tests/test_attacks.py @@ -0,0 +1,89 @@ +import torch + +from leviathan_sim.attacks import InjectionConfig, Injector +from leviathan_sim.swarm import LocalUpdate + + +def make_updates(n_workers: int = 6, dim: int = 128, seed: int = 9): + generator = torch.Generator().manual_seed(seed) + return { + wid: LocalUpdate(wid, torch.randn(dim, generator=generator), 8) + for wid in range(n_workers) + } + + +def relative_distance(a: torch.Tensor, b: torch.Tensor) -> float: + return float(torch.linalg.vector_norm(a - b) / torch.linalg.vector_norm(b)) + + +def test_within_band_stays_inside_the_published_band(): + band = 0.05 + updates = make_updates() + injector = Injector( + InjectionConfig(n_malicious=2, attack="within_band", band=band), list(range(6)), seed=1 + ) + attacked, report = injector.apply(updates) + assert report.malicious_ids == frozenset({0, 1}) + for wid in report.malicious_ids: + distance = relative_distance(attacked[wid].delta, updates[wid].delta) + assert distance <= band + assert abs(distance - band * 0.9) < 1e-5 + for wid in range(2, 6): + assert torch.equal(attacked[wid].delta, updates[wid].delta) + + +def test_within_band_scales_with_the_band(): + updates = make_updates() + for band in (0.02, 0.2): + injector = Injector( + InjectionConfig(n_malicious=1, attack="within_band", band=band), list(range(6)), seed=1 + ) + attacked, _ = injector.apply(updates) + assert abs(relative_distance(attacked[0].delta, updates[0].delta) - band * 0.9) < 1e-5 + + +def test_within_band_bias_opposes_honest_mean(): + updates = make_updates() + injector = Injector( + InjectionConfig(n_malicious=1, attack="within_band", band=0.1), list(range(6)), seed=1 + ) + attacked, _ = injector.apply(updates) + honest_mean = torch.stack([updates[w].delta for w in range(1, 6)]).mean(dim=0) + bias = attacked[0].delta - updates[0].delta + cosine = torch.dot(bias, honest_mean) / ( + torch.linalg.vector_norm(bias) * torch.linalg.vector_norm(honest_mean) + ) + assert cosine < -0.99 + + +def test_sign_flip_scales_and_negates(): + updates = make_updates() + injector = Injector(InjectionConfig(n_malicious=1, attack="sign_flip"), list(range(6)), seed=1) + attacked, _ = injector.apply(updates) + assert torch.allclose(attacked[0].delta, -5.0 * updates[0].delta) + + +def test_lazy_zeroes_delta_and_work(): + updates = make_updates() + injector = Injector(InjectionConfig(n_malicious=1, attack="lazy"), list(range(6)), seed=1) + attacked, _ = injector.apply(updates) + assert torch.count_nonzero(attacked[0].delta) == 0 + assert attacked[0].work_done == 0 + + +def test_alie_coalition_submits_identical_crafted_delta(): + updates = make_updates() + injector = Injector(InjectionConfig(n_malicious=2, attack="alie"), list(range(6)), seed=1) + attacked, _ = injector.apply(updates) + assert torch.equal(attacked[0].delta, attacked[1].delta) + honest = torch.stack([updates[w].delta for w in range(2, 6)]) + expected = honest.mean(dim=0) + 1.5 * honest.std(dim=0) + assert torch.allclose(attacked[0].delta, expected) + + +def test_unknown_attack_rejected(): + try: + Injector(InjectionConfig(attack="nope"), [0], seed=1) + except ValueError: + return + raise AssertionError("expected ValueError") diff --git a/sim/tests/test_economy.py b/sim/tests/test_economy.py new file mode 100644 index 0000000..d55e90c --- /dev/null +++ b/sim/tests/test_economy.py @@ -0,0 +1,64 @@ +from leviathan_sim.economy import ( + EconomyConfig, + StakeLedger, + audit_burn_projection, + break_even_bond, + calibration_table, + genesis_parameters, +) + + +def test_break_even_bond_is_nine_rewards_at_p_10(): + assert break_even_bond(0.1, 1.0) == 9.0 + + +def test_certain_audit_slashes_malicious_on_first_round(): + ledger = StakeLedger([0, 1], EconomyConfig(audit_probability=1.0), seed=17) + caught = ledger.settle_round(0, {0: True, 1: True}, frozenset({1})) + assert caught == [1] + assert ledger.balances[1] == 0.0 + assert 1 not in ledger.active_ids + assert ledger.caught[1] == 0 + + +def test_slash_splits_bond_between_bounty_and_treasury(): + config = EconomyConfig(bond=10.0, slash_bounty_fraction=0.5) + ledger = StakeLedger([0], config, seed=17) + ledger.slash(0) + assert ledger.verifier_income == 5.0 + assert ledger.treasury == 5.0 + assert ledger.pnl()[0] == -10.0 + + +def test_honest_selected_worker_earns_reward(): + ledger = StakeLedger([0], EconomyConfig(audit_probability=0.0), seed=17) + ledger.settle_round(0, {0: True}, frozenset()) + assert ledger.pnl()[0] == ledger.config.reward_selected + + +def test_calibration_bond_scales_with_reward(): + rows = calibration_table([0.1]) + by_preset = {row["preset"]: row for row in rows} + for row in rows: + assert abs(row["break_even_bond_usd"] - 9.0 * row["round_reward_usd"]) < 1e-9 + assert ( + by_preset["7B scale run"]["break_even_bond_usd"] + > by_preset["125M proof run"]["break_even_bond_usd"] + ) + + +def test_zero_fraud_burn_share_is_preset_independent(): + rows = audit_burn_projection([0.1]) + expected = 0.1 * 1.1 / 1.2 + for row in rows: + assert abs(row["burn_share_of_rewards"] - expected) < 1e-9 + assert row["treasury_burn_per_round_usd"] > 0.0 + + +def test_genesis_parameters_follow_the_published_discipline(): + params = genesis_parameters(audit_probability=0.1, band=0.05) + assert params["preset"] == "1B genesis run" + assert params["bond_rounds_of_reward"] == 9.0 + assert abs(params["bond_usd"] - 9.0 * params["round_reward_usd"]) < 1e-9 + assert params["expected_rounds_to_catch"] == 10.0 + assert params["tolerance_band"] == 0.05 diff --git a/sim/tests/test_sparse.py b/sim/tests/test_sparse.py new file mode 100644 index 0000000..72f24b7 --- /dev/null +++ b/sim/tests/test_sparse.py @@ -0,0 +1,35 @@ +import torch + +from leviathan_sim.sparse import chunked_topk_sign + + +def test_density_close_to_target(): + vector = torch.randn(64 * 100, generator=torch.Generator().manual_seed(4)) + compressed = chunked_topk_sign(vector, density=0.02, chunk_size=64, quantize_sign=True) + density = float((compressed != 0).float().mean()) + assert 0.005 <= density <= 0.05 + + +def test_shape_preserved_with_padding(): + vector = torch.randn(1000, generator=torch.Generator().manual_seed(4)) + compressed = chunked_topk_sign(vector, density=0.05, chunk_size=64, quantize_sign=True) + assert compressed.shape == vector.shape + + +def test_signs_match_original(): + vector = torch.randn(256, generator=torch.Generator().manual_seed(4)) + compressed = chunked_topk_sign(vector, density=0.1, chunk_size=64, quantize_sign=True) + kept = compressed != 0 + assert torch.equal(torch.sign(compressed[kept]), torch.sign(vector[kept])) + + +def test_full_density_is_identity(): + vector = torch.randn(128, generator=torch.Generator().manual_seed(4)) + assert torch.equal(chunked_topk_sign(vector, 1.0, 64, True), vector) + + +def test_kept_entries_are_the_largest_magnitudes(): + vector = torch.arange(1.0, 65.0) + compressed = chunked_topk_sign(vector, density=0.05, chunk_size=64, quantize_sign=False) + kept_indices = torch.nonzero(compressed).flatten().tolist() + assert kept_indices == [61, 62, 63] diff --git a/sim/tests/test_verifier.py b/sim/tests/test_verifier.py new file mode 100644 index 0000000..469d90e --- /dev/null +++ b/sim/tests/test_verifier.py @@ -0,0 +1,70 @@ +import torch +from torch.nn.utils import parameters_to_vector + +from leviathan_sim.attacks import InjectionConfig, Injector +from leviathan_sim.model import build_model +from leviathan_sim.swarm import SwarmWorker +from leviathan_sim.verifier import relative_distance, replay_and_verify + +BAND = 0.05 +BLOCK_SIZE = 16 +DEVICE = torch.device("cpu") + + +def make_swarm(corpus, n_workers: int = 4): + model = build_model(corpus.vocab_size, BLOCK_SIZE, seed=7, device=DEVICE) + theta = parameters_to_vector(model.parameters()).detach().clone() + workers = [ + SwarmWorker( + wid, corpus, n_workers, True, inner_steps=2, inner_lr=2e-3, + batch_size=4, block_size=BLOCK_SIZE, seed=100 + wid, device=DEVICE, + ) + for wid in range(n_workers) + ] + return model, theta, workers + + +def test_honest_replay_is_exact(corpus): + model, theta, workers = make_swarm(corpus) + submitted = workers[0].local_round(theta, model, 0).delta + verdict = replay_and_verify(workers[0], model, theta, 0, submitted, BAND) + assert verdict.distance < 1e-6 + assert not verdict.fraud + + +def test_sign_flip_lands_far_above_the_band(corpus): + model, theta, workers = make_swarm(corpus) + honest = workers[1].local_round(theta, model, 0).delta + verdict = replay_and_verify(workers[1], model, theta, 0, -5.0 * honest, BAND) + assert verdict.distance > 1.0 + assert verdict.fraud + + +def test_lazy_zero_delta_is_caught(corpus): + model, theta, workers = make_swarm(corpus) + verdict = replay_and_verify( + workers[2], model, theta, 0, torch.zeros_like(theta), BAND + ) + assert abs(verdict.distance - 1.0) < 1e-5 + assert verdict.fraud + + +def test_within_band_submission_passes_replay(corpus): + model, theta, workers = make_swarm(corpus) + updates = {w.wid: w.local_round(theta, model, 0) for w in workers} + injector = Injector( + InjectionConfig(n_malicious=1, attack="within_band", band=BAND), + [w.wid for w in workers], + seed=11, + ) + attacked, report = injector.apply(updates) + wid = min(report.malicious_ids) + verdict = replay_and_verify(workers[wid], model, theta, 0, attacked[wid].delta, BAND) + assert not verdict.fraud + assert abs(verdict.distance - BAND * 0.9) < 1e-4 + + +def test_relative_distance_is_scale_invariant(): + a = torch.randn(32, generator=torch.Generator().manual_seed(2)) + assert relative_distance(3.0 * a, 3.0 * a) == 0.0 + assert abs(relative_distance(1.1 * a, a) - 0.1) < 1e-6 From 5f3299c5820c46ae56d906a947e5539043404713 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 14:14:22 +0000 Subject: [PATCH 3/3] sim: measured within-band adversary budget published into the docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 30 rounds, 5/16 coalition at 0.9x the band, replay audits blind by construction (distance = 0.9x band, zero fraud verdicts), coalition accepted 100% throughout. Damage vs the 2.173 honest reference: +0.012 at band 0.02, +0.019 at the 0.05 operating point (~0.9%), +0.032 at 0.1, +0.063 at 0.2 — roughly linear in band width. Curve and replay evidence promoted to docs/assets; README, WHITEPAPER honest-limitations, TASKS and GAPS now quote the measured number instead of admitting it qualitatively. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01KbsW5FgMKkc9kXBhWoumjW --- README.md | 4 + docs/GAPS.md | 3 +- docs/TASKS.md | 2 +- docs/WHITEPAPER.md | 2 +- docs/assets/band_sweep.png | Bin 0 -> 104878 bytes docs/assets/band_sweep_results.json | 212 ++++++++++++++++++++++++++++ 6 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 docs/assets/band_sweep.png create mode 100644 docs/assets/band_sweep_results.json diff --git a/README.md b/README.md index f1b5490..87cd296 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,10 @@ The break-even bond law held on a real transformer run: at audit probability p = ![security economics](docs/assets/security_economics.png) +The published tolerance band is, by construction, the adversary's undetectable budget — so the sim prices it. A 5/16 coalition biasing against the honest mean at 0.9x the band passes every replay audit (measured distance exactly 0.9x band, zero fraud verdicts) and is fully accepted by the aggregator; what it buys is the damage curve below. At the operating band of 0.05 the whole coalition purchases +0.019 final loss against the 2.173 honest reference, about 0.9%, and the cost grows roughly linearly with band width. That number is why the band is a published dashboard metric and why calibrating it tightly per hardware class is a Genesis-gating task. + +![band sweep](docs/assets/band_sweep.png) + ## Phase 1 progress The network substrate is a private fork of PsycheFoundation/nousnet (Apache-2.0) at wienerlabs/leviathan-net, carrying the layer upstream designed but left unimplemented: bonds, replay audits, slashing. diff --git a/docs/GAPS.md b/docs/GAPS.md index 7d8e741..5fff052 100644 --- a/docs/GAPS.md +++ b/docs/GAPS.md @@ -5,7 +5,8 @@ against what the code and task ladder actually deliver. Ordered by how much each Items marked (T) were folded into docs/TASKS.md. Status update, same day: the gaps closable in this repo were closed — the within-band damage -scenario (`--band-sweep`, with explicit replay evidence that audits pass by construction), the +scenario (`--band-sweep`: +0.019 loss at the 0.05 operating band for a 5/16 coalition, ~0.9%, +roughly linear in band width, with explicit replay evidence that audits pass by construction), the zero-fraud verifier burn projection and Genesis operating point in economy.py, a 28-test sim suite with CI, SECURITY.md, CONTRIBUTING.md, and the doc-drift fixes (PRD as-built note, README privacy line). Still open: the LICENSE decision (owner call), and everything that lives in diff --git a/docs/TASKS.md b/docs/TASKS.md index d5009ee..c610d38 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -30,7 +30,7 @@ Exit criteria: recorded end-to-end conviction demo, anchor test suite green, two - [ ] Tolerance band calibration harness across hardware classes (4090, 3090, A100, H100) - [ ] Two-GPU tolerance-band replay reproduction (Phase 1 exit criterion carried forward; folded into 1.9 but never run on two distinct GPUs) -- [x] Sim: within-band adversary budget scenario — `--band-sweep` runs a 5/16 coalition that biases against the honest mean at 0.9x the published band; explicit replay evidence shows the audit passes by construction (distance = 0.9 x band, fraud=false) at every band width, so aggregation is the only layer that pushes back; damage-vs-band curve written to band_sweep_results.json +- [x] Sim: within-band adversary budget scenario — `--band-sweep` runs a 5/16 coalition that biases against the honest mean at 0.9x the published band; explicit replay evidence shows the audit passes by construction (distance = 0.9 x band, fraud=false) at every band width, so aggregation is the only layer that pushes back. Measured over 30 rounds vs the 2.173 honest reference: +0.012 loss at band 0.02, +0.019 at 0.05 (the operating point, ~0.9%), +0.032 at 0.1, +0.063 at 0.2 — roughly linear in band width, coalition accepted 100% throughout (docs/assets/band_sweep.png) - [ ] Production verifier daemon: subscribe as Committee::Verifier, replay real gradients, call run_slash (decision core done in 1.8; shares the 1.9 libtorch toolchain) - [ ] Multi-epoch re-join resilience (deferred out of 1.9; prerequisite for any churn run) - [x] Verifier economics: economy.py gains the zero-fraud projection (audit fee = 1.1x per-contribution H100 cost; treasury burn ~9.2% of rewards at p=0.1, preset-independent) and genesis_parameters() publishing the Genesis operating point (1B preset, p=0.1, band 0.05, bond = 9 rounds of reward); both emitted into results.json diff --git a/docs/WHITEPAPER.md b/docs/WHITEPAPER.md index 174022c..7fa746f 100644 --- a/docs/WHITEPAPER.md +++ b/docs/WHITEPAPER.md @@ -67,7 +67,7 @@ Flywheel: inference fees (vLLM workers verified by TOPLOC activation commitments ## 8. Honest limitations -- Economic security has a floor: coalitions below the excision threshold that also stay inside the tolerance band can bias training slowly. The published band is the adversary's budget; we choose it with the sim, and we would rather publish that number than pretend it is zero. +- Economic security has a floor: coalitions below the excision threshold that also stay inside the tolerance band can bias training slowly. The published band is the adversary's budget; we choose it with the sim, and we would rather publish that number than pretend it is zero. The number, measured: a 5/16 within-band coalition passes every replay audit by construction and buys +0.019 final loss (~0.9% of the 2.173 honest reference) at the 0.05 operating band, scaling roughly linearly to +0.063 at band 0.2. This is what tight per-hardware band calibration is worth. - A 1B volunteer-trained model is not a frontier model. The claim is a proven path, not an arrived destination: each run scales the trust machine that a frontier-scale run requires. - Solana congestion or program bugs stall rounds; the coordinator inherits Psyche's pause and resume machinery, and the fork keeps its centralized backend for disaster recovery. - Token design must survive regulation; utility-first, no yield promises, legal review before TGE. diff --git a/docs/assets/band_sweep.png b/docs/assets/band_sweep.png new file mode 100644 index 0000000000000000000000000000000000000000..bc37245bfaea958ac8a23f65d7fcb8ac673d346f GIT binary patch literal 104878 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 d}*r#bjVRLeF8i?NqJ~x*ByzDc!771J+{H8kD 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 R^2p6Y$=|zEaQnfZ{|o;~HDUk& literal 0 HcmV?d00001 diff --git a/docs/assets/band_sweep_results.json b/docs/assets/band_sweep_results.json new file mode 100644 index 0000000..4d8b303 --- /dev/null +++ b/docs/assets/band_sweep_results.json @@ -0,0 +1,212 @@ +{ + "config": { + "rounds": 30, + "workers": 16, + "n_malicious": 5, + "device": "cpu", + "model_parameters": 826368, + "bands": [ + 0.02, + 0.05, + 0.1, + 0.2 + ] + }, + "honest_reference": { + "final_val_loss": 2.173410952091217, + "val_losses": [ + 2.770378962159157, + 2.60888409614563, + 2.543832153081894, + 2.508126735687256, + 2.486658349633217, + 2.474105015397072, + 2.4595586955547333, + 2.450014144182205, + 2.442861393094063, + 2.4389298856258392, + 2.43013197183609, + 2.421166941523552, + 2.411952391266823, + 2.4090156108140945, + 2.4044787734746933, + 2.3957075625658035, + 2.3918637335300446, + 2.381677985191345, + 2.376203015446663, + 2.371914267539978, + 2.3553656339645386, + 2.343187779188156, + 2.3320121318101883, + 2.318408355116844, + 2.313457652926445, + 2.28567735850811, + 2.2693462818861008, + 2.2291798889636993, + 2.2051504105329514, + 2.173410952091217 + ] + }, + "sweep": [ + { + "band": 0.02, + "final_val_loss": 2.1857606917619705, + "damage_vs_honest": 0.012349739670753479, + "malicious_selection_rate": 1.0, + "replay_distance": 0.018000036478042603, + "replay_fraud": false, + "val_losses": [ + 2.7701356112957, + 2.6086870282888412, + 2.5437359660863876, + 2.5076621919870377, + 2.4862434715032578, + 2.4740501940250397, + 2.460038959980011, + 2.450979769229889, + 2.4425385743379593, + 2.44069904088974, + 2.429084986448288, + 2.4198197722434998, + 2.412846550345421, + 2.408899664878845, + 2.4049845784902573, + 2.3951989114284515, + 2.390958398580551, + 2.381100833415985, + 2.375213637948036, + 2.37044657766819, + 2.3560407906770706, + 2.346657931804657, + 2.3309069871902466, + 2.324841544032097, + 2.306785002350807, + 2.2894617468118668, + 2.286434456706047, + 2.2406703531742096, + 2.2160698622465134, + 2.1857606917619705 + ] + }, + { + "band": 0.05, + "final_val_loss": 2.192902535200119, + "damage_vs_honest": 0.019491583108901978, + "malicious_selection_rate": 1.0, + "replay_distance": 0.045000094920396805, + "replay_fraud": false, + "val_losses": [ + 2.769803687930107, + 2.6083097606897354, + 2.543393239378929, + 2.5068593621253967, + 2.4855589121580124, + 2.4738713800907135, + 2.4603098779916763, + 2.451279506087303, + 2.4427553862333298, + 2.440789893269539, + 2.4283525943756104, + 2.4201632887125015, + 2.4137205481529236, + 2.4100505858659744, + 2.4057970196008682, + 2.3954578042030334, + 2.3913018107414246, + 2.3814733922481537, + 2.375205308198929, + 2.369316577911377, + 2.3576837927103043, + 2.3469138890504837, + 2.3355731517076492, + 2.3230367302894592, + 2.313744932413101, + 2.292399510741234, + 2.281036853790283, + 2.242164269089699, + 2.2211836725473404, + 2.192902535200119 + ] + }, + { + "band": 0.1, + "final_val_loss": 2.20535109937191, + "damage_vs_honest": 0.031940147280693054, + "malicious_selection_rate": 1.0, + "replay_distance": 0.09000018984079361, + "replay_fraud": false, + "val_losses": [ + 2.7693415135145187, + 2.60792475938797, + 2.542679116129875, + 2.505945608019829, + 2.484595239162445, + 2.4737804532051086, + 2.460836261510849, + 2.451386868953705, + 2.442452296614647, + 2.440273940563202, + 2.4281808733940125, + 2.421149030327797, + 2.415631502866745, + 2.4107213020324707, + 2.4053375869989395, + 2.3949068039655685, + 2.390969842672348, + 2.3814787417650223, + 2.37664195895195, + 2.369709238409996, + 2.3622162342071533, + 2.3499815315008163, + 2.340071141719818, + 2.327909767627716, + 2.314935952425003, + 2.3002878576517105, + 2.2984621673822403, + 2.255413994193077, + 2.234349101781845, + 2.20535109937191 + ] + }, + { + "band": 0.2, + "final_val_loss": 2.2362954765558243, + "damage_vs_honest": 0.06288452446460724, + "malicious_selection_rate": 1.0, + "replay_distance": 0.18000037968158722, + "replay_fraud": false, + "val_losses": [ + 2.768751382827759, + 2.6073982417583466, + 2.5410755276679993, + 2.505242884159088, + 2.4836170226335526, + 2.473900690674782, + 2.4607941061258316, + 2.4521148204803467, + 2.442139372229576, + 2.4397433400154114, + 2.4293213188648224, + 2.4214515388011932, + 2.4157699197530746, + 2.412311851978302, + 2.406392380595207, + 2.3969900757074356, + 2.391791358590126, + 2.3835081458091736, + 2.3786582201719284, + 2.3734836876392365, + 2.365206152200699, + 2.3547249138355255, + 2.35318922996521, + 2.340775266289711, + 2.333874449133873, + 2.3210405111312866, + 2.311697021126747, + 2.277094006538391, + 2.256584584712982, + 2.2362954765558243 + ] + } + ] +} \ No newline at end of file