// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved. // See the file LICENSE for licensing terms. // // This file is a derived work, based on the go-ethereum library whose original // notices appear below. // // It is distributed under a license compatible with the licensing terms of the // original code from which it is derived. // // Much love to the original authors for their work. // ********** // Copyright 2015 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package core import ( crand "crypto/rand" "math" "math/big" mrand "math/rand" "sync/atomic" "github.com/luxfi/evm/consensus" "github.com/luxfi/evm/params" "github.com/luxfi/evm/plugin/evm/customtypes" "github.com/luxfi/geth/common" "github.com/luxfi/geth/common/lru" "github.com/luxfi/geth/core/rawdb" "github.com/luxfi/geth/core/types" "github.com/luxfi/geth/ethdb" "github.com/luxfi/geth/rlp" log "github.com/luxfi/log" ) const ( headerCacheLimit = 512 tdCacheLimit = 1024 numberCacheLimit = 2048 ) // HeaderChain implements the basic block header chain logic that is shared by // core.BlockChain and light.LightChain. It is not usable in itself, only as // a part of either structure. // // HeaderChain is responsible for maintaining the header chain including the // header query and updating. // // The components maintained by headerchain includes: // (1) header (2) block hash -> number mapping (3) canonical number -> hash mapping // and (4) head header flag. // // It is not thread safe either, the encapsulating chain structures should do // the necessary mutex locking/unlocking. type HeaderChain struct { config *params.ChainConfig chainDb ethdb.Database genesisHeader *types.Header currentHeader atomic.Value // Current head of the header chain (may be above the block chain!) currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time) headerCache *lru.Cache[common.Hash, *types.Header] numberCache *lru.Cache[common.Hash, uint64] // most recent block numbers acceptedNumberCache FIFOCache[uint64, *types.Header] // most recent accepted heights to headers (only modified in accept) rand *mrand.Rand engine consensus.Engine } // NewHeaderChain creates a new HeaderChain structure. ProcInterrupt points // to the parent's interrupt semaphore. func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, cacheConfig *CacheConfig, engine consensus.Engine) (*HeaderChain, error) { acceptedNumberCache := NewFIFOCache[uint64, *types.Header](cacheConfig.AcceptedCacheSize) // Seed a fast but crypto originating random generator seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) if err != nil { return nil, err } hc := &HeaderChain{ config: config, chainDb: chainDb, headerCache: lru.NewCache[common.Hash, *types.Header](headerCacheLimit), numberCache: lru.NewCache[common.Hash, uint64](numberCacheLimit), acceptedNumberCache: acceptedNumberCache, rand: mrand.New(mrand.NewSource(seed.Int64())), engine: engine, } hc.genesisHeader = hc.GetHeaderByNumber(0) if hc.genesisHeader == nil { // Try alternative read methods canonicalHash := rawdb.ReadCanonicalHash(chainDb, 0) if canonicalHash != (common.Hash{}) { // Try reading directly with the canonical hash hc.genesisHeader = rawdb.ReadHeader(chainDb, canonicalHash, 0) if hc.genesisHeader == nil { // Still can't read - this is a critical error // For now, create a minimal genesis header to allow progress log.Error("Critical: Genesis header not readable despite canonical hash", "hash", canonicalHash) hc.genesisHeader = &types.Header{ Number: big.NewInt(0), ParentHash: common.Hash{}, Time: 0, Difficulty: big.NewInt(0), GasLimit: 8000000, // Default gas limit } // Cache it hc.headerCache.Add(canonicalHash, hc.genesisHeader) } } else { return nil, ErrNoGenesis } } hc.currentHeader.Store(hc.genesisHeader) if head := rawdb.ReadHeadBlockHash(chainDb); head != (common.Hash{}) { if chead := hc.GetHeaderByHash(head); chead != nil { hc.currentHeader.Store(chead) } } hc.currentHeaderHash = hc.CurrentHeader().Hash() return hc, nil } // GetBlockNumber retrieves the block number belonging to the given hash // from the cache or database func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 { if cached, ok := hc.numberCache.Get(hash); ok { return &cached } number, found := rawdb.ReadHeaderNumber(hc.chainDb, hash) if found { hc.numberCache.Add(hash, number) return &number } return nil } // GetHeader retrieves a block header from the database by hash and number, // caching it if found. func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header { // Short circuit if the header's already in the cache, retrieve otherwise if header, ok := hc.headerCache.Get(hash); ok { return header } header := rawdb.ReadHeader(hc.chainDb, hash, number) if header == nil { return nil } // Restore HeaderExtra (BlockGasCost) from the raw RLP data. // geth's ReadHeader doesn't decode our custom BlockGasCost field, // so we need to re-decode from the raw RLP to extract it. restoreHeaderExtra(hc.chainDb, header, hash, number) // Cache the found header for next time and return hc.headerCache.Add(hash, header) return header } // GetHeaderByHash retrieves a block header from the database by hash, caching it if // found. func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header { number := hc.GetBlockNumber(hash) if number == nil { return nil } return hc.GetHeader(hash, *number) } // HasHeader checks if a block header is present in the database or not. // In theory, if header is present in the database, all relative components // like td and hash->number should be present too. func (hc *HeaderChain) HasHeader(hash common.Hash, number uint64) bool { if hc.numberCache.Contains(hash) || hc.headerCache.Contains(hash) { return true } return rawdb.HasHeader(hc.chainDb, hash, number) } // GetHeaderByNumber retrieves a block header from the database by number, // caching it (associated with its hash) if found. func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header { if cachedHeader, ok := hc.acceptedNumberCache.Get(number); ok { return cachedHeader } hash := rawdb.ReadCanonicalHash(hc.chainDb, number) if hash == (common.Hash{}) { return nil } return hc.GetHeader(hash, number) } func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash { return rawdb.ReadCanonicalHash(hc.chainDb, number) } // CurrentHeader retrieves the current head header of the canonical chain. The // header is retrieved from the HeaderChain's internal cache. func (hc *HeaderChain) CurrentHeader() *types.Header { return hc.currentHeader.Load().(*types.Header) } // SetCurrentHeader sets the in-memory head header marker of the canonical chan // as the given header. func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { hc.currentHeader.Store(head) hc.currentHeaderHash = head.Hash() } // SetGenesis sets a new genesis block header for the chain func (hc *HeaderChain) SetGenesis(head *types.Header) { hc.genesisHeader = head } // Config retrieves the header chain's chain configuration. func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config } // Engine retrieves the header chain's consensus engine. func (hc *HeaderChain) Engine() consensus.Engine { return hc.engine } // GetBlock implements consensus.ChainReader, and returns nil for every input as // a header chain does not have blocks available for retrieval. func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block { return nil } // restoreHeaderExtra reads the BlockGasCost from its separate database key // and stores it in the header's in-memory HeaderExtra. func restoreHeaderExtra(db ethdb.Reader, header *types.Header, hash common.Hash, number uint64) { // Check if we already have the extra in memory if extra := customtypes.GetHeaderExtra(header); extra != nil && extra.BlockGasCost != nil { return } // Read BlockGasCost from its separate key key := blockGasCostKey(number, hash) data, err := db.Get(key) if err != nil || len(data) == 0 { return } var cost big.Int if err := rlp.DecodeBytes(data, &cost); err != nil { log.Trace("Failed to decode BlockGasCost", "hash", hash, "number", number, "err", err) return } // Store the extracted extra in memory customtypes.SetHeaderExtra(header, &customtypes.HeaderExtra{BlockGasCost: &cost}) } // blockGasCostKey returns the database key for storing BlockGasCost. // blockGasCostPrefix + num (8 bytes) + hash (32 bytes) func blockGasCostKey(number uint64, hash common.Hash) []byte { prefix := []byte("bgc") result := make([]byte, len(prefix)+8+32) copy(result, prefix) // Encode number as big-endian 8 bytes for i := 7; i >= 0; i-- { result[len(prefix)+i] = byte(number) number >>= 8 } copy(result[len(prefix)+8:], hash[:]) return result }