mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
285 lines
9.6 KiB
Go
285 lines
9.6 KiB
Go
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
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
|
|
}
|