Two node/bootstrap-layer fixes for a clean rolling validator upgrade. Consensus
engine untouched (v1.35.5 boot-seed from v1.32.10 carried forward).
[HIGH — correctness] info.isBootstrapped(C) tracks REAL state, not premature true.
manager.IsBootstrapped(id) returned true the instant a chain merely EXISTED in
m.chains (set right after createChain launched the async, possibly-stalling
bootstrap goroutine) — so a C-Chain stalled at genesis (head 0x0) reported
info.isBootstrapped(C)=true, masking the stall from any wait-for-healthy gate.
Now keys on the SAME sb.Bootstrapped signal the readiness health check uses
(m.Nets.IsChainBootstrapped), set only after runInitialSync reaches the named
frontier and the VM goes to normal operation (head advanced, eth-RPC live).
GetChains().Bootstrapped fixed identically. New per-chain query on nets.Net +
chains.Nets (nil-safe). Regression test TestIsBootstrappedTracksRealConvergence.
[MEDIUM — robustness] WIPE-path GetAncestors fetch hardened for ≥2 peers at genesis.
Applying the proven avalanchego contract (studied in snowman/bootstrap +
getter): (a) Ancestors buffers the whole sample and SKIPS empty batches,
returning the first NON-EMPTY one — a size-1 channel let a fast empty reply from
a genesis peer win the race and starve a peer that actually held the ancestry;
(b) sampleAncestorBeacons PREFERS beacons the frontier round found genuinely
ahead (they hold the ancestry) — ava's PeerTracker "ask a prover" bias sourced
from the replies we already have, no separate tracker. Tests:
TestNodeBootstrap_WipePath_MixedGenesisPeers_ObtainsAncestry,
TestSampleAncestorBeacons_PrefersAheadBeacons.
Build: main+chains+nets+service/info compile CGO_ENABLED=0 (ARC/Dockerfile path);
full chains+nets suites green.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>