diff --git a/checkpoint/checkpoint.go b/checkpoint/checkpoint.go index d683dccc..51909828 100644 --- a/checkpoint/checkpoint.go +++ b/checkpoint/checkpoint.go @@ -10,7 +10,7 @@ import ( "github.com/geanlabs/gean/types" ) -// Timeouts rs L9-13. +// Timeouts const ( CheckpointConnectTimeout = 15 * time.Second CheckpointReadTimeout = 15 * time.Second diff --git a/cmd/gean/main.go b/cmd/gean/main.go index 0ac18b03..6edb268e 100644 --- a/cmd/gean/main.go +++ b/cmd/gean/main.go @@ -25,7 +25,6 @@ import ( ) func main() { - // CLI flags rs L46-79. configDir := flag.String("custom-network-config-dir", "", "Config directory (required)") gossipPort := flag.Int("gossipsub-port", 9000, "P2P listen port (QUIC/UDP)") httpAddr := flag.String("http-address", "127.0.0.1", "Bind address for API + metrics") @@ -41,7 +40,6 @@ func main() { flag.Parse() - // Validate required flags. if *configDir == "" || *nodeKey == "" || *nodeID == "" { fmt.Fprintln(os.Stderr, "required flags: --custom-network-config-dir, --node-key, --node-id") flag.Usage() @@ -58,8 +56,6 @@ func main() { logger.Info(logger.Node, "gean consensus client starting") - // --- Load configuration --- - configPath := filepath.Join(*configDir, "config.yaml") bootnodePath := filepath.Join(*configDir, "nodes.yaml") validatorsPath := filepath.Join(*configDir, "annotated_validators.yaml") @@ -72,7 +68,6 @@ func main() { } logger.Info(logger.Node, "genesis: time=%d validators=%d", genesisConfig.GenesisTime, len(genesisConfig.GenesisValidators)) - // Load bootnodes. bootnodes, err := p2p.LoadBootnodes(bootnodePath) if err != nil { logger.Error(logger.Node, "load bootnodes: %v", err) @@ -80,7 +75,6 @@ func main() { } logger.Info(logger.Node, "bootnodes: %d loaded", len(bootnodes)) - // Load validator keys. keyManager, err := xmss.LoadValidatorKeys(validatorsPath, keysDir, *nodeID) if err != nil { logger.Error(logger.Node, "load validator keys: %v", err) @@ -89,8 +83,6 @@ func main() { defer keyManager.Close() logger.Info(logger.Node, "validators: %d keys loaded for %s", len(keyManager.ValidatorIDs()), *nodeID) - // --- Initialize storage --- - absDataDir, _ := filepath.Abs(*dataDir) os.MkdirAll(absDataDir, 0755) logger.Info(logger.Node, "storage: %s", absDataDir) @@ -104,8 +96,6 @@ func main() { s := node.NewConsensusStore(backend) - // --- Initialize state (DB restore, checkpoint sync, or genesis) --- - genesisValidators := genesisConfig.Validators() // Check if DB already has a valid head state (restart case). @@ -114,7 +104,6 @@ func main() { existingState := s.GetState(existingHead) if existingHeader != nil && existingState != nil && existingHeader.Slot > 0 { - // DB has valid state — restore from it. logger.Info(logger.Node, "restoring from database: slot=%d head=%x justified=%d finalized=%d", existingHeader.Slot, existingHead, s.LatestJustified().Slot, s.LatestFinalized().Slot) @@ -175,18 +164,13 @@ func main() { // fires, opening an 800ms hole at every boot. recoverStoreTime(s, genesisConfig.GenesisTime) - // --- Initialize fork choice --- - headRoot := s.Head() headHeader := s.GetBlockHeader(headRoot) fc := forkchoice.New(headHeader.Slot, headRoot) - // --- Initialize P2P --- - ctx, cancel := context.WithCancel(context.Background()) defer cancel() - // Parse explicit aggregate subnet IDs. var aggregateSubnetIDs []uint64 if *aggregateSubnetIDsStr != "" { for _, s := range strings.Split(*aggregateSubnetIDsStr, ",") { @@ -203,7 +187,6 @@ func main() { } } - // Collect validator IDs for subnet subscription. var validatorIDs []uint64 if keyManager != nil { validatorIDs = keyManager.ValidatorIDs() @@ -227,8 +210,6 @@ func main() { } xmss.EnsureVerifierReady() - // --- Initialize engine --- - // Runtime-toggleable aggregator role. Seeded from --is-aggregator; the // admin API endpoint flips this without restart. Boot-time subscription // decisions (p2p.NewHost above, XMSS prover pre-init below) still use @@ -238,7 +219,6 @@ func main() { n := node.New(s, fc, p2pHost, keyManager, aggCtl, *committeeCount) - // Register P2P stream handlers. p2pHost.RegisterReqRespHandlers( func() *p2p.StatusMessage { finalized := s.LatestFinalized() @@ -263,7 +243,6 @@ func main() { // Wire gossip handlers — P2P pushes to engine channels. p2pHost.StartGossipListeners(n) - // Start engine goroutine. go n.Run(ctx) // Start sync driver: periodic status-poll + BlocksByRange backfill when @@ -275,9 +254,6 @@ func main() { p2p.PeerStatusHook = syncDriver.OnPeerConnected go syncDriver.Run() - // Connect to bootnodes only AFTER PeerStatusHook is wired above, so the - // first peer-connect event sees a non-nil hook and triggers the Status - // reqresp handshake. p2pHost.ConnectBootnodes(ctx, bootnodes) p2pHost.StartBootnodeRedial(ctx, bootnodes) @@ -299,8 +275,6 @@ func main() { } }() - // --- Start HTTP servers --- - apiAddr := fmt.Sprintf("%s:%d", *httpAddr, *apiPort) metricsAddr := fmt.Sprintf("%s:%d", *httpAddr, *metricsPort) @@ -330,8 +304,6 @@ func main() { logger.Info(logger.Node, "gean started: api=%s metrics=%s aggregator=%v", apiAddr, metricsAddr, *isAggregator) - // --- Wait for shutdown --- - sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) <-sigCh @@ -343,7 +315,9 @@ func main() { } // initStoreFromState initializes the consensus store from an anchor state -// and returns the canonical anchor block root. +// and returns the canonical anchor block root (computed after header.StateRoot +// canonicalization — use this value, not a pre-call root, as the +// StorePendingBlock key). // // The anchor state becomes the new latest justified AND latest finalized // checkpoint — both pointing at the served block at header.Slot. This @@ -351,19 +325,11 @@ func main() { // trusts the served state as the new finalization anchor and starts forward // sync from there. // -// The returned root is the canonical anchor block root — computed AFTER the -// header.StateRoot canonicalization step. Callers that need to associate -// out-of-band data with the anchor block (e.g. StorePendingBlock for the -// checkpoint-sync SignedBlock) must use this return value, not a root -// computed before the function ran; the pre-canonicalization root would not -// match what the store records as latest_finalized.Root. -// // Note: state.LatestJustified and state.LatestFinalized inside the served // state point to EARLIER slots (the finalization status from when the block // was processed). We deliberately do NOT use those — the served block IS // the new anchor, regardless of what its internal pointers say. func initStoreFromState(s *node.ConsensusStore, state *types.State) [32]byte { - // Compute anchor block root from header. stateRoot, _ := state.HashTreeRoot() header := state.LatestBlockHeader @@ -376,7 +342,6 @@ func initStoreFromState(s *node.ConsensusStore, state *types.State) [32]byte { // Anchor checkpoint: both justified and finalized point at the served block. anchor := &types.Checkpoint{Root: blockRoot, Slot: header.Slot} - // Store metadata. s.SetConfig(state.Config) s.SetHead(blockRoot) s.SetSafeTarget(blockRoot) @@ -385,7 +350,6 @@ func initStoreFromState(s *node.ConsensusStore, state *types.State) [32]byte { // Store time is rehydrated from wall clock by recoverStoreTime after // every init path; no need to seed it here. - // Store block header and state. s.InsertBlockHeader(blockRoot, header) s.InsertState(blockRoot, state) s.InsertLiveChainEntry(state.Slot, blockRoot, header.ParentRoot) diff --git a/cmd/gean/main_test.go b/cmd/gean/main_test.go deleted file mode 100644 index 601534a2..00000000 --- a/cmd/gean/main_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "testing" - "time" - - "github.com/geanlabs/gean/logger" - "github.com/geanlabs/gean/node" - "github.com/geanlabs/gean/storage" - "github.com/geanlabs/gean/types" -) - -func TestMain(m *testing.M) { - logger.Quiet = true - m.Run() -} - -func newTestStore() *node.ConsensusStore { - return node.NewConsensusStore(storage.NewInMemoryBackend()) -} - -func TestRecoverStoreTime_PostGenesis(t *testing.T) { - s := newTestStore() - nowMs := uint64(time.Now().UnixMilli()) - genesisSec := (nowMs / 1000) - 10 // genesis 10 seconds ago - - recoverStoreTime(s, genesisSec) - - got := s.Time() - expectedMin := uint64(10*1000/types.MillisecondsPerInterval) - 1 - expectedMax := uint64(10*1000/types.MillisecondsPerInterval) + 1 - if got < expectedMin || got > expectedMax { - t.Fatalf("intervals: expected ~%d, got %d", (expectedMin+expectedMax)/2, got) - } -} - -func TestRecoverStoreTime_PreGenesis(t *testing.T) { - s := newTestStore() - // Seed a non-zero time to verify the function overwrites it. - s.SetTime(999) - - farFuture := uint64(time.Now().Unix()) + 86400 - recoverStoreTime(s, farFuture) - - if got := s.Time(); got != 0 { - t.Fatalf("expected time=0 for pre-genesis wall clock, got %d", got) - } -} - -func TestRecoverStoreTime_OverwritesStaleValue(t *testing.T) { - s := newTestStore() - s.SetTime(1) // simulate a stale value persisted from a previous run - - genesisSec := uint64(time.Now().Unix()) - 60 // 60s ago → 75 intervals - recoverStoreTime(s, genesisSec) - - if got := s.Time(); got < 70 { - t.Fatalf("recovered time did not overwrite stale value: got %d, want >=70", got) - } -} diff --git a/node/consensus_store.go b/node/consensus_store.go index 3f986074..7bb34e75 100644 --- a/node/consensus_store.go +++ b/node/consensus_store.go @@ -10,7 +10,7 @@ import ( ) const ( - // Buffer capacities rs L87-91. + // Buffer capacities aggregatedPayloadCap = 0 // unbounded, pruned on finalization only newPayloadCap = 0 // unbounded ) diff --git a/node/node.go b/node/node.go index b023d3c9..ee3e3fce 100644 --- a/node/node.go +++ b/node/node.go @@ -13,8 +13,7 @@ import ( ) // Engine is the consensus coordination loop. -// It owns Store, ForkChoice, and KeyManager as siblings, -// rs L78-95). +// It owns Store, ForkChoice, and KeyManager as siblings // Pending block limits to prevent stuck-forever scenarios. const ( MaxBlockFetchDepth = 512 // Max ancestor chain depth before discarding diff --git a/p2p/encoding.go b/p2p/encoding.go index 93994aab..9a13fb5b 100644 --- a/p2p/encoding.go +++ b/p2p/encoding.go @@ -9,7 +9,7 @@ import ( "github.com/golang/snappy" ) -// Max payload sizes rs L6-9. +// Max payload sizes const ( MaxPayloadSize = 10 * 1024 * 1024 // 10 MiB uncompressed MaxCompressedPayloadSize = 32 + MaxPayloadSize + MaxPayloadSize/6 + 1024 // ~12 MiB diff --git a/p2p/host.go b/p2p/host.go index 9a3dd4e8..d46f6ae3 100644 --- a/p2p/host.go +++ b/p2p/host.go @@ -21,7 +21,7 @@ import ( "github.com/geanlabs/gean/logger" ) -// GossipSub parameters rs L96-119. +// GossipSub parameters rs const ( GossipMeshN = 8 GossipMeshNLow = 6 diff --git a/p2p/msgid.go b/p2p/msgid.go index 25dd098d..68692309 100644 --- a/p2p/msgid.go +++ b/p2p/msgid.go @@ -5,7 +5,7 @@ import ( "encoding/binary" ) -// Message ID domains rs L619-638. +// Message ID domains rs var ( domainValidSnappy = [4]byte{0x01, 0x00, 0x00, 0x00} domainInvalidSnappy = [4]byte{0x00, 0x00, 0x00, 0x00} diff --git a/p2p/peers.go b/p2p/peers.go index 54967a70..47026edc 100644 --- a/p2p/peers.go +++ b/p2p/peers.go @@ -15,7 +15,7 @@ import ( "github.com/geanlabs/gean/types" ) -// Retry parameters rs L56-59. +// Retry parameters const ( MaxFetchRetries = 10 InitialBackoffMs = 5 diff --git a/storage/keys.go b/storage/keys.go index 3b55c9ef..d56fb288 100644 --- a/storage/keys.go +++ b/storage/keys.go @@ -2,7 +2,7 @@ package storage import "encoding/binary" -// Metadata keys rs L62-72. +// Metadata keys var ( KeyTime = []byte("time") KeyConfig = []byte("config") @@ -12,7 +12,7 @@ var ( KeyLatestFinalized = []byte("latest_finalized") ) -// Retention constants rs L75-78. +// Retention constants const ( BlocksToKeep = 21_600 // ~1 day at 4s slots StatesToKeep = 3_000 // ~3.3 hours at 4s slots