interval

Interval. Testing & Freeze Evidence

Release 1.0.2 · protocol spec v1.02 · consensus spec v1.9 · rules hash b961c123b73d67c0….

This document states exactly what is tested, with what inputs, for how long. Coverage is finite and enumerated, the claims below are about the specific scenarios, seeds, and durations listed, not about all possible executions.

Unit + property suite (npm test)

node --test test/*.test.mjs, 340 tests across:

Adversarial simulation (npm run advsim)

advsim.mjs is a deterministic, seeded, event-driven network under a hostile transport. Each run is a pure function of (scenario, seed, durationMs), identical inputs replay identically (asserted by a determinism test).

Every witnessed scenario declares a Byzantine threshold f and its actor count never exceeds it (a scenario that spawns more Byzantine actors than n,q,f tolerates is rejected as a scenario bug, testing outside the model would make a fork “expected”).

Invariants asserted every scenario, every seed:

Scenarios (n witnesses, quorum q):

scenario n/q transport faults liveness floor
benign 4/3 clean , slowest ≥ 15 finalized
lossy 4/3 25% loss, 10–900ms, 30% dup , slowest ≥ 2
crashes 4/3 5% loss crash-restart, 50%/tick fastest ≥ 5
partitions 5/3 5% loss asymmetric splits 70%/tick fastest ≥ 4
equivocator 4/3 5% loss Byzantine proposer (2 bundles + double-sign) fastest ≥ 0
liar 4/3 5% loss Byzantine attester (corrupt result hash) slowest ≥ 3
replayer 4/3 5% loss, 10% dup replayed bundles/attestations slowest ≥ 3
garbage 4/3 5% loss malformed message floods slowest ≥ 3
chaos 7/5 (f=2) 20% loss, 10–700ms, 25% dup crashes + partitions + 2 Byzantine fastest ≥ 0
heal 4/3 10% loss partition burst then quiet tail slowest ≥ 3, spread = 0 (requiredSpread)
byzantine-max 7/5 (f=2) 5% loss two equivocators at the boundary fastest ≥ 0, no fork
lockstorm 7/5 (f=2) 10% loss 2 equivocators + early partition burst, then heal no fork; halts only with evidence

Liveness floors are deliberately conservative: under simultaneous faults the model promises safety always, liveness when able. A “fastest ≥ 0” floor means the scenario asserts only safety and convergence, not progress, because a hard-enough fault storm may legitimately finalize nothing while never forking.

CI sample (test/adversarial.test.mjs, 15 tests): each scenario at seed 7919 (heal at 26s for full convergence), plus convergence-mechanism checks (heal enforces spread 0; the check has teeth) and a determinism check. Full battery: node advsim.mjs all 3 30000 (all scenarios × 3 seeds × 30s). Single scenario: node advsim.mjs <name> <seeds> <ms>.

Seeds are seed_index × 7919; the CLI default is 3 seeds. These are the tested seeds, other seeds are not claimed.

Live surfaces (socket-binding; INTERVAL_LIVE=1)

Both are environment-sensitive (they bind real TCP sockets) and are excluded from the default evidence run; set INTERVAL_LIVE=1 to include them in freeze-evidence.sh.

Supported runtime

Node >=22.5.0 (declared in package.json engines) the minimum for the built-in node:sqlite used by the production backend. The engine resolves SHA hashing through Node’s built-in crypto when present and falls back to @noble/hashes in browsers; hashing is lazily resolved so concurrent dynamic import() of the engine is race-free across Node versions (a prior ordering bug under 22.16 is fixed). The full suite runs under node --test.

Release test structure

Split by purpose (storage brief §8):

Storage backends

The finality store is selectable behind one interface: SQLite (production default) or the flat-file append log (finalityBackend: 'flatfile', dev/compat). SQLite uses journal_mode=WAL, synchronous=FULL, foreign_keys=ON, an indexed (world_id, tick) primary key, and schema-enforced append-only immutability. migrateFlatFileToSqlite() performs a validated one-time migration preserving the source as a read-only backup. Storage choice never changes protocol records.

Storage operations tooling

storage-ops.mjs operates on a witness’s SQLite finality store without touching consensus:

Large-history benchmark

node bench-storage.mjs [ticks] [sqlite|flatfile] builds a synthetic history and measures append throughput, indexed lookup, startup validation, integrity check, and online backup. At 1,000,000 ticks the SQLite backend measures (on this environment): batched append ≈168k rows/s, random lookup ≈15 µs, ≈402 bytes/row, integrity quick_check ≈275 ms, online backup ≈4.8 s. Bounded startup validation is ≈0.4 s vs ≈22 s unbounded, startup is constant-time in history length.

Reproducible evidence (npm run evidence)

freeze-evidence.sh captures runtime environment, dependency lockfile, exact commands, per-stage exit codes, and full logs into freeze-evidence/. Its own exit code is nonzero if any stage failed, so it doubles as the freeze gate.

npm ci                             # exact reproduction from the committed lockfile
npm run evidence                   # core suites
INTERVAL_LIVE=1 npm run evidence   # + live libp2p and multi-process E2E