Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ var (
utils.SnapshotFlag,
utils.TxLookupLimitFlag, // deprecated
utils.TransactionHistoryFlag,
utils.ChainHistoryFlag,
utils.StateHistoryFlag,
utils.LightServeFlag, // deprecated
utils.LightIngressFlag, // deprecated
Expand Down
21 changes: 18 additions & 3 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ var (
Value: ethconfig.Defaults.TransactionHistory,
Category: flags.StateCategory,
}
ChainHistoryFlag = &cli.StringFlag{
Name: "history.chain",
Usage: `Blockchain history retention ("all" or "postmerge")`,
Value: ethconfig.Defaults.HistoryMode.String(),
Category: flags.StateCategory,
}
// Beacon client light sync settings
BeaconApiFlag = &cli.StringSliceFlag{
Name: "beacon.api",
Expand Down Expand Up @@ -1566,10 +1572,19 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.IsSet(SyncTargetFlag.Name) {
cfg.SyncMode = ethconfig.FullSync // dev sync target forces full sync
} else if ctx.IsSet(SyncModeFlag.Name) {
if err = cfg.SyncMode.UnmarshalText([]byte(ctx.String(SyncModeFlag.Name))); err != nil {
Fatalf("invalid --syncmode flag: %v", err)
value := ctx.String(SyncModeFlag.Name)
if err = cfg.SyncMode.UnmarshalText([]byte(value)); err != nil {
Fatalf("--%v: %v", SyncModeFlag.Name, err)
}
}

if ctx.IsSet(ChainHistoryFlag.Name) {
value := ctx.String(ChainHistoryFlag.Name)
if err = cfg.HistoryMode.UnmarshalText([]byte(value)); err != nil {
Fatalf("--%s: %v", ChainHistoryFlag.Name, err)
}
}

if ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = ctx.Uint64(NetworkIdFlag.Name)
}
Expand Down Expand Up @@ -2086,7 +2101,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
gspec = MakeGenesis(ctx)
chainDb = MakeChainDatabase(ctx, stack, readonly)
)
config, err := core.LoadChainConfig(chainDb, gspec)
config, _, err := core.LoadChainConfig(chainDb, gspec)
if err != nil {
Fatalf("%v", err)
}
Expand Down
10 changes: 10 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ type CacheConfig struct {

SnapshotNoBuild bool // Whether the background generation is allowed
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it

// This defines the cutoff block for history expiry.
// Blocks before this number may be unavailable in the chain database.
HistoryPruningCutoff uint64
}

// triedbConfig derives the configures for trie database.
Expand Down Expand Up @@ -2519,3 +2523,9 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load())
}

// HistoryPruningCutoff returns the configured history pruning point.
// Blocks before this might not be available in the database.
func (bc *BlockChain) HistoryPruningCutoff() uint64 {
return bc.cacheConfig.HistoryPruningCutoff
}
15 changes: 8 additions & 7 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,35 +386,36 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g

// LoadChainConfig loads the stored chain config if it is already present in
// database, otherwise, return the config in the provided genesis specification.
func LoadChainConfig(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, error) {
func LoadChainConfig(db ethdb.Database, genesis *Genesis) (cfg *params.ChainConfig, ghash common.Hash, err error) {
// Load the stored chain config from the database. It can be nil
// in case the database is empty. Notably, we only care about the
// chain config corresponds to the canonical chain.
stored := rawdb.ReadCanonicalHash(db, 0)
if stored != (common.Hash{}) {
storedcfg := rawdb.ReadChainConfig(db, stored)
if storedcfg != nil {
return storedcfg, nil
return storedcfg, stored, nil
}
}
// Load the config from the provided genesis specification
if genesis != nil {
// Reject invalid genesis spec without valid chain config
if genesis.Config == nil {
return nil, errGenesisNoConfig
return nil, common.Hash{}, errGenesisNoConfig
}
// If the canonical genesis header is present, but the chain
// config is missing(initialize the empty leveldb with an
// external ancient chain segment), ensure the provided genesis
// is matched.
if stored != (common.Hash{}) && genesis.ToBlock().Hash() != stored {
return nil, &GenesisMismatchError{stored, genesis.ToBlock().Hash()}
ghash := genesis.ToBlock().Hash()
if stored != (common.Hash{}) && ghash != stored {
return nil, ghash, &GenesisMismatchError{stored, ghash}
}
return genesis.Config, nil
return genesis.Config, ghash, nil
}
// There is no stored chain config and no new config provided,
// In this case the default chain config(mainnet) will be used
return params.MainnetChainConfig, nil
return params.MainnetChainConfig, params.MainnetGenesisHash, nil
}

// chainConfigOrDefault retrieves the attached chain configuration. If the genesis
Expand Down
43 changes: 31 additions & 12 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if !config.SyncMode.IsValid() {
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
}
if !config.HistoryMode.IsValid() {
return nil, fmt.Errorf("invalid history mode %d", config.HistoryMode)
}
if config.Miner.GasPrice == nil || config.Miner.GasPrice.Sign() <= 0 {
log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
Expand All @@ -125,7 +128,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)

// Assemble the Ethereum object
chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/", false)
if err != nil {
return nil, err
Expand All @@ -140,19 +142,35 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
log.Error("Failed to recover state", "error", err)
}
}
// Transfer mining-related config to the ethash config.
chainConfig, err := core.LoadChainConfig(chainDb, config.Genesis)

// Here we determine genesis hash and active ChainConfig.
// We need these to figure out the consensus parameters and to set up history pruning.
chainConfig, genesisHash, err := core.LoadChainConfig(chainDb, config.Genesis)
if err != nil {
return nil, err
}
engine, err := ethconfig.CreateConsensusEngine(chainConfig, chainDb)
if err != nil {
return nil, err
}

// Validate history pruning configuration.
var historyPruningCutoff uint64
if config.HistoryMode == ethconfig.PostMergeHistory {
prunecfg, ok := ethconfig.HistoryPrunePoints[genesisHash]
if !ok {
return nil, fmt.Errorf("no history pruning point is defined for genesis %x", genesisHash)
}
historyPruningCutoff = prunecfg.BlockNumber
}

// Set networkID to chainID by default.
networkID := config.NetworkId
if networkID == 0 {
networkID = chainConfig.ChainID.Uint64()
}

// Assemble the Ethereum object.
eth := &Ethereum{
config: config,
chainDb: chainDb,
Expand Down Expand Up @@ -190,15 +208,16 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
EnablePreimageRecording: config.EnablePreimageRecording,
}
cacheConfig = &core.CacheConfig{
TrieCleanLimit: config.TrieCleanCache,
TrieCleanNoPrefetch: config.NoPrefetch,
TrieDirtyLimit: config.TrieDirtyCache,
TrieDirtyDisabled: config.NoPruning,
TrieTimeLimit: config.TrieTimeout,
SnapshotLimit: config.SnapshotCache,
Preimages: config.Preimages,
StateHistory: config.StateHistory,
StateScheme: scheme,
TrieCleanLimit: config.TrieCleanCache,
TrieCleanNoPrefetch: config.NoPrefetch,
TrieDirtyLimit: config.TrieDirtyCache,
TrieDirtyDisabled: config.NoPruning,
TrieTimeLimit: config.TrieTimeout,
SnapshotLimit: config.SnapshotCache,
Preimages: config.Preimages,
StateHistory: config.StateHistory,
StateScheme: scheme,
HistoryPruningCutoff: historyPruningCutoff,
}
)
if config.VMTrace != "" {
Expand Down
4 changes: 4 additions & 0 deletions eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var FullNodeGPO = gasprice.Config{

// Defaults contains default settings for use on the Ethereum main net.
var Defaults = Config{
HistoryMode: AllHistory,
SyncMode: SnapSync,
NetworkId: 0, // enable auto configuration of networkID == chainID
TxLookupLimit: 2350000,
Expand Down Expand Up @@ -81,6 +82,9 @@ type Config struct {
NetworkId uint64
SyncMode SyncMode

// HistoryMode configures chain history retention.
HistoryMode HistoryMode

// This can be set to list of enrtree:// URLs which will be queried for
// nodes to connect to.
EthDiscoveryURLs []string
Expand Down
6 changes: 6 additions & 0 deletions eth/ethconfig/gen_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 90 additions & 0 deletions eth/ethconfig/historymode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2025 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 ethconfig

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)

// HistoryMode configures history pruning.
type HistoryMode uint32

const (
// AllHistory (default) means that all chain history down to genesis block will be kept.
AllHistory HistoryMode = iota

// PostMergeHistory sets the history pruning point to the merge activation block.
PostMergeHistory
)

func (m HistoryMode) IsValid() bool {
return m <= PostMergeHistory
}

func (m HistoryMode) String() string {
switch m {
case AllHistory:
return "all"
case PostMergeHistory:
return "postmerge"
default:
return fmt.Sprintf("invalid HistoryMode(%d)", m)
}
}

// MarshalText implements encoding.TextMarshaler.
func (m HistoryMode) MarshalText() ([]byte, error) {
if m.IsValid() {
return []byte(m.String()), nil
}
return nil, fmt.Errorf("unknown history mode %d", m)
}

// UnmarshalText implements encoding.TextUnmarshaler.
func (m *HistoryMode) UnmarshalText(text []byte) error {
switch string(text) {
case "all":
*m = AllHistory
case "postmerge":
*m = PostMergeHistory
default:
return fmt.Errorf(`unknown sync mode %q, want "all" or "postmerge"`, text)
}
return nil
}

type HistoryPrunePoint struct {
BlockNumber uint64
BlockHash common.Hash
}

// HistoryPrunePoints contains the pre-defined history pruning cutoff blocks for known networks.
var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{
// mainnet
params.MainnetGenesisHash: {
BlockNumber: 15537394,
BlockHash: common.HexToHash("0x56a9bb0302da44b8c0b3df540781424684c3af04d0b7a38d72842b762076a664"),
},
// sepolia
params.SepoliaGenesisHash: {
BlockNumber: 1735371,
BlockHash: common.HexToHash("0x36fb89fba5b7857cf0ca78b5a9625b4043ff4555dfce9b7bcdcdd758a11eb946"),
},
}