Three-tier node (Light/PDP/Full) + head-reliability hardening#95
Merged
Conversation
…e monitor #85 item 1 (header propagation): tighten the block gossipsub TopicValidator so Lantern only ValidationAccepts - and therefore only re-propagates - blocks that fully consume the payload (no trailing bytes) and whose header round-trips through canonical CBOR to its declared CID. Propagation and ingestion now share one integrity bar; Lantern can't be turned into an amplifier for a malformed or fake-CID header. #85 item 2 / #80 part 2 (robust head discovery): new chain/headcheck package - a periodic running-head divergence monitor. It asks a diversity of INDEPENDENT head sources (counted by source Kind, so N URLs of one upstream count once) what epoch the head is at and checks agreement within a 3-block lookback. On a quorum of independent sources diverging from our gossip head it raises an eclipse/fork alarm. It never moves our head (gossip + #79 fork choice do that) and never makes a trusted RPC the head oracle - it only corroborates or disputes. Wired best-effort into pkg/daemon (Config.HeadCheckRPCs); disabled when empty so it can't sink head-tracking. TRUST-MODEL.md threat-model row updated to reflect the stacked tip defenses (#79 weight fork choice, #80 peer floor, #85 propagation gate + divergence monitor; F3 closes it on mainnet). Tests: chain/headcheck full suite (agreement, kind-diversity collapse, divergence alarm, insufficient-sources, median, start/stop); blockpub validator trailing- garbage + nil-header rejection. Full suite 44 pkgs green (-short, LANTERN_OFFLINE=1), build+vet+gofmt clean.
Light-node hardening pass so all message/RPC paths work bridge-off and an embedded node self-heals from downtime. - pkg/daemon: wire StaleResetThreshold auto-heal (#51) into the EMBEDDED path (was only in standalone cmd/lantern). Config.StaleResetThreshold knob (0=default 2880 epochs ~1d, <0=disabled) + OnStaleReset log. Fixes the wedge that forced embedded testers to manually 'lantern reset --chain-state' after downtime > MaxBacktrack. Extracted resolveStaleResetThreshold + regression tests. - eth_sendRawTransaction: fail LOUD with an actionable error when the local send path is unavailable (gossipsub mpool not wired) and no bridge is set, instead of the misleading generic 'FEVM method requires --vm-bridge-rpc'. (beck's AddPiece error root.) - eth_feeHistory: NEW local-first path served from header-store ParentBaseFee (last read method on the SP write path that hard-failed bridge-off). Bridge stays as out-of-window fallback. +parse/fallback tests. - eth_subscribe(logs): allow bridge-off operation with a header store (EthGetLogs is local-first since #73); refuse only when neither a header store nor a bridge exists. Corrected stale comment + test. Full suite green (44 pkgs, -short LANTERN_OFFLINE=1), build+vet+gofmt clean.
…blockpub gate) into lightnode fixes Combines the light-node bridge-off/auto-heal work with snadrus#85 (PR #86): - chain/headcheck: running-head divergence monitor (corroborate-don't-oracle, Kind-diversity counting, 3-block lookback) wired best-effort into pkg/daemon via Config.HeadCheckRPCs (disabled when empty). - net/blockpub: header-propagation integrity gate (full-payload consume + canonical-CBOR CID re-derive; can't be an amplifier for a malformed/fake CID). - TRUST-MODEL.md: gossipsub-eclipse threat row updated for stacked tip defenses. Clean 3-way auto-merge (StaleReset + headcheck touch disjoint regions). Full suite green (build+vet+gofmt), 0 failures.
…ble status (#79/#80) Close the deferred halves of #79 (periodic re-quorum) / #80 (head-source diversity) on top of the #86 divergence monitor: - chain/headcheck: NEW GatewayHeadSource (HTTP /state/root) so the Lantern gateway is an independent-KIND corroborating head source (KindLanternGateway), not just operator RPC URLs. +adapter tests. - pkg/daemon: build a Kind-DIVERSE headcheck source set automatically - operator HeadCheckRPCs (Forest) + gateway (LanternGateway) + fallback/Glif (Forest) - so the running head is continuously corroborated by a multi-kind quorum (counts agreement by Kind, N URLs of one kind = 1). Monitor now starts whenever >=1 corroborating source exists; self-reports StatusInsufficient until enough distinct kinds are reachable (safe to enable broadly). - Daemon.HeadCorroboration(): expose the monitor's latest verdict (agree/ diverge/insufficient + local vs median-external head + tallies) so an eclipse alarm is VISIBLE (dashboard/health/RPC), not just a log line. Status-only: never auto-halts (a false positive must not self-DoS); the head still advances on gossip + #79 heaviest-weight fork choice. Full suite green (build+vet+gofmt), 0 failures.
…DP tier The light node runs cache-first on an in-memory MemBlockStore, so every restart is cold: a restarted node re-fetches its whole warm set (PDP/ payments/registry/USDFC contract subtrees) from the gateway on the first head advance. Fine for a wallet; not fine for a PDP node that must prove/ settle against warm contract state on a schedule and cannot stall inside a proving window. state/cache: NEW persistent, bounded, CID-keyed block cache backed by Badger (already a dep via the header store). The warm set SURVIVES restart. - CID-keyed exactly like MemBlockStore: content-addressed trust unchanged; PutVerify re-hashes untrusted bytes and rejects mismatch (no cache poison). - Bounded: soft byte budget (default 3 GiB = middle of the 2-5 GB PDP tier) with sampled-LRU eviction over UNPINNED blocks; pinned warm-set subtrees are never evicted (hard floor). - Live-bytes counter persisted + recomputed on Open so the cap holds across restarts. Single-flight background eviction + blocking evictNow for the sync path/tests. - Satisfies combined.Cache (drop-in for MemBlockStore). Iface assertion lives in the test pkg to avoid a state/cache->net/combined import cycle. pkg/daemon: Config.PersistentCache / PersistentCacheBytes opt-in. When set, the daemon opens the cache at <DataDir>/<network>/blockcache, wires it as the fetcher cache, closes it in stopInternal, and exposes BlockCacheStats(). Light tier leaves it false and stays memory-cached. Tests (race-clean): put/get/has, PutVerify mismatch rejection, persistence across reopen (the PDP guarantee), LRU eviction respecting soft cap + pins. Full suite green (build+vet+gofmt), 0 failures.
The persistent cache survives restart, but without pinning it's just persistent - LRU could still evict the exact PDP/payments/registry/USDFC contract state a PDP node proves/settles against. This closes the loop: - state/kamt: WalkSubtree gains an optional OnNode(cid) callback, fired once per fetched+verified node (root included). +tests (visits-every-node, nil-noop). - state/prefetch: Prefetcher gains an optional Pinner (Pin(cid) error, satisfied by *state/cache.Store) + SetPinner. On a STATIC (configured) contract walk it pins the storage root, bytecode CID, and every walked storage-trie node. DYNAMIC (client-learned, eth_call-miss) addresses are deliberately NOT pinned - that set is attacker-influenceable and must stay evictable, or a hostile client could spray addresses to pin-flood the cache. - pkg/daemon: when PersistentCache is on, SetPinner(blockCache) on the prefetcher so the warm set persists un-evicted across restart. Net: a restarted PDP node comes up with its contract warm set already resident (no cold gateway re-fetch inside a proving window), and LRU only reclaims incidental/dynamic blocks. Full suite green, 0 failures.
…de block submit) The block-production surface (MinerGetBaseInfo -> MinerCreateBlock -> sign -> SyncSubmitBlock -> blockpub /fil/blocks) already existed and is gated on AllowBlockSubmit + a VM bridge (needed for a valid post-execution state root; the no-CGo wall). But the embedded daemon DISCARDED the *blockpub.Publisher that blockingest.Start returns (`ing, _, err :=`), so an embedded PDP/backup node could create a block but had no way to publish it - SyncSubmitBlock's c.Mpool.(BlockPublisher) assert always failed because net/mpool publishes on /fil/msgs, not /fil/blocks. - pkg/daemon: capture the block publisher from blockingest.Start, store it (d.blockPub), and wire it onto the ChainAPI via SetBlockPublisher. Cleared on stop. Unconditional wiring; SyncSubmitBlock still gates on AllowBlockSubmit. - rpc/handlers: ChainAPI gains an explicit blockPub field + SetBlockPublisher. SyncSubmitBlock now PREFERS the explicit /fil/blocks publisher, falls back to a block-capable Mpool, and returns a clear error (not a nil-deref) when none is wired. BlockPublisher doc corrected (it's net/blockpub, not an mpool ext). - Tests: gate-off no-op, no-publisher error, explicit-publisher happy path, publish-error propagation. Net: a backup-tier daemon (AllowBlockSubmit + VM bridge) can now actually submit blocks to the network - the last missing hop for the PDP node to double as a backup block producer. Full suite green, 0 failures.
…time flag Nicklas's correction: the node CLASS must be chosen at INSTALL time, not via a runtime flag on one universal binary - otherwise a wallet-only light node still carries the PDP footprint. The installer asks, and only the chosen tier provisions the larger footprint. - pkg/nodeprofile: persist the tier at <home>/<network>/node-profile.json (atomic write). Light => in-memory cache (small). PDP/Full => persistent 2-5 GB cache + block-submit opt-in. Missing file => Light (safe default for pre-tier installs); malformed => error (never silently downgrade a PDP node). Per-network + forward-compatible. +tests. - cmd/lantern daemon: load the profile at startup and pick the cache tier from it - MemBlockStore for light, persistent state/cache (survives restart) for PDP/Full, with the tier's cache budget. Profile AllowBlockSubmit is honored (still requires --vm-bridge-rpc). Also wired the previously-discarded /fil/blocks publisher into the CLI ChainAPI so PDP can actually submit blocks. - cmd/lantern node-type: new subcommand to read/set the tier (`lantern node-type pdp --cache-gb 5 --allow-block-submit`). Fixed a flag-ordering bug (tier positional pulled off the front before flag parse; smoke test caught --network/--cache-gb being dropped when placed after the tier arg). - install.sh: 3-way node-type selector (Light default / PDP / Full) that persists the choice via `lantern node-type`. Non-interactive via LANTERN_NODE_TYPE + LANTERN_PDP_CACHE_GB + LANTERN_ALLOW_BLOCK_SUBMIT. Smoke-tested live: set pdp calibration 5GB+submit -> correct profile written; mainnet stays light; per-network independent. Full suite green, 0 failures.
…ming Clearer 3-way selector wording (Light Node / PDP Node / Full Node), Light described as clients/wallets/backup, Full framed as snapshot-free full node (track in progress). No behavior change.
Stage B groundwork to remove filecoin-ffi (Rust) proof-verify dep. Reuses go-state-types/proof types; Verify() is an honest ErrNotImplemented stub with the port checklist (challenge derivation, public inputs, Groth16/BLS12-381 pairing, vk load). Green test + CGO-off build. No real verify yet.
…idate) Second-pass consensus checks a Full node can run with resident F3-anchored state: block signature (worker key), election + ticket VRF, win-count recompute vs parent QA-power. Byte-for-byte mirrors Lotus filcns MINUS the two ffi calls (WinningPoSt SNARK #88, FVM re-exec #89), which stay F3-trusted. Result flags WinningPoStVerified/StateReExecuted stay false (test-pinned) so the trust boundary can't silently drift. ChainAPI.FullValidateView() adapts existing StateMinerInfo/StateAccountKey/StateMinerPower into the StateView. Pure-Go, CGO-off build+vet+gofmt+tests green.
…-by-default) Sync.SetBlockValidator(fn, fatal) runs the chain/fullvalidate consensus check per ingested block after the shape check. nil on Light/PDP (zero cost); the Full tier wires it via profile.FullValidation(). Observe mode logs without rejecting; fatal mode turns a bad block into a fetch hole so it never becomes head (matches the #33 non-stall design). Config.FullValidation[Fatal] exposes the same to embedded consumers. Both CLI + embedded daemon paths wired. Tests: observe mode advances head despite always-fail validator; fatal mode refuses to advance head to a rejected block. Full module CGO-off green. (Note: pkg/daemon TestStartAnchorsAgainstNetwork needs :1234 free; unrelated flake when a live lantern daemon holds the port.)
…plete) - StateView.MinerEligible mirrors Lotus MinerEligibleToMine (post-v3): non-empty QA-power claim + no fee debt + no active consensus fault. Adapter reads power claim, miner FeeDebt(), and ConsensusFaultElapsed vs head. Rejected block from an ineligible miner now fails validation (Result.EligibilityOK). - Store.LatestBeaconEntry walks a block's own entries then parents (bounded 20) to supply prevBeacon, so beacon-entry-less blocks validate fully instead of observe-only. Both CLI + embedded closures now pass a resolved prevBeacon. - Tests + mock updated. Full module CGO-off build/vet/gofmt/tests green.
Closes the last #79 acceptance gap. Item 1 (heaviest-ParentWeight fork choice) already shipped; this adds item 2's 'no-adopt on disagreement': - Ingestor.SetHeadAdoptionGate(fn) gates head adoption. While headcheck reports the running head DIVERGES from the independent-source quorum, the gate closes and the ingestor HOLDS head (still receives + backfills, just won't walk head onto an uncorroborated tip). Reopens on re-corroboration. nil = always adopt (Light/PDP / no sources). New HeldDiverged stat. - Daemon wires the gate to the headcheck monitor: StatusDiverge closes it (loud warn), StatusAgree reopens it, StatusInsufficient leaves it OPEN (never freeze a lightly-corroborated node). Tests: gate-closed holds a valid heavier head; reopened adopts; nil always adopts; existing fork-choice tests still green. Full module CGO-off green.
- README: Lantern is now Light / PDP / Full (chosen at install time). New tier table, tier section, install prompt, audience rows, full-node roadmap pointer (epic #87). Badge -> v1.9.0-rc1. - Scrub internal/private references from source comments + changelog: drop private-thread + individual-handle attributions in favor of neutral public issue numbers. No code behavior change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Turns Lantern from a single light client into a three-tier node (Light / PDP / Full), chosen at install time, and lands the head-reliability + validation hardening needed before the next release.
Install-time node tiers
install.sh3-way selector (Light / PDP / Full);pkg/nodeprofilepersists the choice per-network;lantern node-typecommand; daemon reads it at start. Light stays light — only the picked tier provisions the larger cache/footprint.PDP / mid tier (F1)
state/cache, restart-warm) + warm-set pinning of static contract subtrees +/fil/blocksblock-submit for a backup producer.Full-node foundation (epic #87)
chain/fullvalidate: pure-Go per-block consensus validation (signature, election + ticket VRF, miner eligibility, win-count) — the Lotusfilcnsset minus the two ffi calls, which stay F3-trusted. Tier-gated, observe-by-default, wired into ingest.proofs/winningpost: scaffold for the pure-Go WinningPoSt SNARK verify (Stage B, Stage B: pure-Go WinningPoSt verify (remove filecoin-ffi dep #1) #88 — stub, not a working verifier yet).Head reliability / eclipse hardening
Docs / hygiene
Verification
Full module builds
CGO_ENABLED=0(no CGo / no Rust); vet + gofmt clean; test suites green. (pkg/daemonTestStartAnchorsAgainstNetworkneeds port :1234 free — passes when no local daemon holds it.)Issue status
Closes #79
Closes #85
Advances (kept open, pending live calibration soak / further work): #87 #88 #90 #94 #76.
Supersedes #86 (its commit is included here).