Skip to content

[Soak] Calibration dress rehearsal: snapshot-free boot, sustained sync, block production, PDP — zero Lotus (v1.9.0 final gate) #104

Description

@Reiers

Goal

One supervised live session that proves the whole three-tier story end-to-end on calibration, with no Lotus anywhere in the stack: Lantern boots without a snapshot, stays in sync, serves a real Curio binary, produces a block, and runs the PDP flow. This is the release gate for v1.9.0 final, and the acceptance evidence for #90, #94 and #76 in one run.

Every checkbox below carries an explicit Test / Pass / Evidence so a tick is never a vibe — it's a measurement posted as a comment.

Ground rules

  • Our node is part of what keeps calibration stable. The soak node runs alongside existing infra, never replacing a production component until its phase has passed. No experiment may disrupt the network: block production uses our own miner identity, PDP uses our own datasets.
  • Rollback is always one step: stop the soak binary, existing infra unaffected.
  • Every phase gets its numbers written into the issue as a comment (counters, epochs, tx hashes) - this document is the evidence trail.
  • Node under test is always the soak Lantern (127.0.0.1:1235), never the Lotus-backed production stack running alongside.

Tier matrix — Light / PDP / Full

The three tiers are one binary + two dials (retention, verification). They differ, so the soak exercises each where it diverges rather than running three identical soaks.

Tier Retention Validation Head corroboration Block submit Soak coverage
Light in-memory (~1 GB) none off (divergence monitor + fork choice) no Phase 1 boot + read-serve + zero-Glif check
PDP persistent 2-5 GB cache none off opt-in Phase 1 boot + Phases 3-4 (Curio + PDP e2e)
Full persistent cache + full chain surface observe-mode pure-Go consensus (#90) 2 distinct peers (auto) opt-in Phases 1-2 + Phase 5 (superset)

Full is the end-to-end superset. Light and PDP get targeted differential checks (boot, footprint, cache present/absent, validation on/off, corroboration default). Each tier's boot is its own checkbox in Phase 1.

Setup

  • Build soak binary from main with feat(#80 part 2, #97): head-source corroboration + per-topic gossipsub scoring #101 (head corroboration + topic scoring), chore(#102): build with Go 1.26.x #103 (Go 1.26.4), feat(#96): FRC-0089 EC finality calculator (observed-data finality) #105 (EC finality), feat(#93): wallet export/import (Lotus hex-KeyInfo) + import-lotus #106 (wallet import), feat(#98): VM-bridge cross-check auditor (observe-only divergence alarm) #107 (VM cross-check) merged; record commit hash + lantern version
    • Test: git describe on the running binary; confirm all five PRs are in the commit.
    • Pass: version string embeds the main-HEAD commit and all five PRs are merged into it.
    • Evidence: ✅ commit 3021887, lantern v1.9.0-rc1-11-g3021887 (comment #4864587138).
  • Optional: soak build with GOEXPERIMENT=goroutineleakprofile for the leak check in Phase 5 (Move toolchain to Go 1.26.x (Green Tea GC default, heap ASLR, goroutine-leak profiler) #102 follow-up) — deferred to Phase 5 (optional; avoid resetting the sustained-sync clock)
    • Test: build with the experiment flag; curl /debug/pprof/goroutineleak.
    • Pass: endpoint responds; profile parses.
    • Evidence: deferred; only built if we want the Phase-5 leak data point.
  • Full tier selected (nodeprofile Full), FullValidation on, observe mode (not fatal)
    • Test: lantern node-type full; on next boot the log prints full block validation ON (observe mode, #90).
    • Pass: node-profile.json has tier=full; FullValidation() true; validator wired non-fatal.
    • Evidence:{ "tier": "full", "allow_block_submit": true } staged for Phase-1 boot (comment #4864626157). Boot-log line confirmed at Phase 1.
  • Bridge-off config: NoFallbackRPC / no Glif; gateway configured only if a phase explicitly tests fallback
    • Test: launch with the explicit no-RPC-fallback head source; grep launch flags + startup log for the head-source mode; confirm no Glif endpoint configured.
    • Pass: head source = live gossip/quorum only, no silent RPC fallback; gateway present only as last-resort cold-block fetch (not head).
    • Evidence: exact launch command + startup head-source log line.
  • VM bridge target for block production is Forest (never Lotus - the point of this soak)
    • Test: stand up a Forest node on calibration; point --vm-bridge-rpc at Forest; confirm it is Forest (version string), not the prod Lotus.
    • Pass: bridge RPC answers and self-identifies as Forest; Phase 5 uses only this endpoint for the post-execution state root.
    • Evidence: Forest version + RPC URL; note that reads/validation do NOT use it (proven by the Phase-5 outage drill).
  • --vm-crosscheck enabled (FVM cross-check mode: use the VM bridge as an auditor (state-root divergence alarm) #98, landed): the auditor rides the same Forest bridge
    • Test: launch with --vm-crosscheck --vm-bridge-rpc <forest>; watch the cross-check card/counter.
    • Pass: auditor runs each interval, compares local vs Forest state root, zero DIVERGE under normal operation.
    • Evidence: cross-check counter + zero-DIVERGE over the window.
  • Dashboard reachable; note the dev-page URL for stat screenshots
    • Test: curl the --metrics dashboard/dev page over the tunnel.
    • Pass: dashboard serves; dev page shows gossip/corroboration/EC-finality/cross-check cards.
    • Evidence: dev-page URL + a screenshot reference.

Phase 1 — Snapshot-free boot (per tier)

  • Light cold start on calibration with no snapshot import: boot from the multi-source F3-finalized anchor quorum
    • Test: wipe chain state (keys untouched), boot tier=light, time from process start to live head.
    • Pass: reaches tip with no snapshot; in-memory footprint stays ~1 GB.
    • Evidence: time-to-tip + RSS.
  • PDP cold start, snapshot-free, persistent cache provisioned
    • Test: boot tier=pdp, confirm persistent cache dir created and populated.
    • Pass: reaches tip; disk footprint in the 2-5 GB cache budget.
    • Evidence: time-to-tip + cache-dir size.
  • Full cold start, snapshot-free (this is the soak node for Phases 2 & 5)
    • Test: boot tier=full; confirm full block validation ON (observe mode) log line.
    • Pass: reaches tip; validator active; footprint single-digit GB.
    • Evidence: time-to-tip + footprint after boot and after 1h.
  • If chain/exchange BlockSync port: snapshot-free full-node boot #91 (BlockSync tail-fill) has landed: verify headers-only deep-tail fetch + record header-store depth; if not, note boot anchor depth as the chain/exchange BlockSync port: snapshot-free full-node boot #91 baseline

Phase 2 — Sustained sync (24h minimum, Full tier)

Phase 3 — Curio binds (real binary, no Lotus; PDP/Full tier)

  • Stock Curio binary binds the soak Lantern as its chain node; startup clean, no missing-RPC errors
    • Test: point a separate soak Curio (not prod) at FULLNODE_API_INFO=<soak Lantern :1235>; start it.
    • Pass: Curio starts; zero missing-RPC / method-not-found errors in the chain path.
    • Evidence: Curio startup log excerpt; RPC-error count = 0.
  • Curio chain-facing pages/ops work against Lantern (head, messages, wallet balances)
    • Test: drive Curio's chain views/ops.
    • Pass: head, messages, balances all render correct values matching Lantern RPC.
    • Evidence: side-by-side value spot-checks.
  • Message path: send → confirm → receipt through Lantern's mpool (pending → confirmed, rebroadcast if needed)
    • Test: send a small self-message via Curio→Lantern; track through mpool.
    • Pass: pending → confirmed → receipt, on our own identity.
    • Evidence: message CID + confirm epoch + receipt.
  • 24h co-run: no RPC-parity regressions logged ([Tracking] Lantern bridge-off parity for stock Curio (maxboom / PR #1311) #76 evidence)
    • Test: run the Curio↔Lantern co-run 24h; scan for parity errors.
    • Pass: zero RPC-parity regressions.
    • Evidence: error scan summary.

Phase 4 — PDP end-to-end (PDP tier, our own datasets)

Phase 5a — Block production (zero Lotus; Forest as VM bridge; Full tier) — v1.9.0 gate

Honest scope: this binary has no pure-Go FVM (#89). Creating a block requires the post-execution ParentStateRoot, which is an FVM execution. So block production uses an external VM bridge = Forest for that state root only. Everything else (base info, ticket/election, block assembly, gossip submit, all reads/validation) is Lantern. This proves we are not Lotus-locked, not that we are zero-external. Uses our own miner identity.

  • MinerGetBaseInfo / MinerCreateBlock / SyncSubmitBlock served by Lantern, VM bridge = Forest for the post-execution state root
    • Test: exercise the three miner RPCs via Curio→Lantern with --vm-bridge-rpc <forest>.
    • Pass: all three return correct data; only the state-root call touches Forest.
    • Evidence: RPC traces; confirm Forest is the sole external touch.
  • Winning epoch produces a block: created, submitted, lands on calibration and propagates (seen by an independent node)
    • Test: at a winning epoch, create + submit a block on our identity.
    • Pass: block lands and is seen by an independent node.
    • Evidence: height + block CID + independent-node confirmation.
  • Produced block passes our own fullvalidate + is adopted by our own head path
    • Test: run our observe-validator against our own produced block.
    • Pass: passes; adopted by our head.
    • Evidence: validator result + head adoption.
  • Restart resilience: restart Lantern mid-soak → Curio reconnects, proving resumes, no manual repair
    • Test: restart the soak daemon during the run.
    • Pass: Curio reconnects; proving resumes automatically.
    • Evidence: restart timestamp + resume evidence.
  • Bridge outage drill: kill Forest bridge → reads keep working (bridge is production-only), alarms fire, block production degrades gracefully and resumes when bridge returns
    • Test: kill Forest mid-run; verify reads/validation continue; restore.
    • Pass: reads/validation unaffected (proves bridge-off reads); production pauses + alarms; resumes on restore.
    • Evidence: read continuity during outage + alarm + resume.
  • If leak-profile build (Setup Keystore passphrase silently falls back to empty string when LANTERN_PASS is unset #2): /debug/pprof/goroutineleak clean after the full soak
    • Test: query the leak profiler post-soak (only if the leak build was used).
    • Pass: no leaked goroutines.
    • Evidence: profile summary.

Phase 5b — Zero-external block production (#89, pure-Go FVM) — NOT v1.9.0; future gate

The real full-node claim: compute the post-execution state root in-process, pure-Go FVM (wazero), so block production needs zero external VM (no Forest, no Lotus). This is gated on #89 Stage C and is explicitly out of scope for v1.9.0. Tracked here so we never overclaim "full node" while Phase 5a still leans on Forest.

  • (Blocked on Stage C: pure-Go FVM via wazero (remove filecoin-ffi dep #2) #89) MinerCreateBlock computes ParentStateRoot via pure-Go FVM; no --vm-bridge-rpc; block lands on calibration
    • Test: produce a block with the VM bridge fully disconnected.
    • Pass: block lands with a state root computed in-process.
    • Evidence: height + CID + confirmation the bridge was down.

Exit criteria

Phases 1-4 + 5a checked, numbers posted as comments. Then:

Related: #87 (epic), #100 (head-trust epic), #89 (pure-Go FVM, Phase 5b gate), PR #101, PR #103.

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureTrust model / swarm designp1High priority

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions