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
68 changes: 60 additions & 8 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/CortexFoundation/CortexTheseus/common/mclock"
"github.com/CortexFoundation/CortexTheseus/common/prque"
"github.com/CortexFoundation/CortexTheseus/consensus"
"github.com/CortexFoundation/CortexTheseus/core/history"
"github.com/CortexFoundation/CortexTheseus/core/rawdb"
"github.com/CortexFoundation/CortexTheseus/core/state"
"github.com/CortexFoundation/CortexTheseus/core/state/snapshot"
Expand Down Expand Up @@ -153,7 +154,7 @@ type CacheConfig struct {

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

// defaultCacheConfig are the default caching values if none are specified by the
Expand Down Expand Up @@ -222,6 +223,7 @@ type BlockChain struct {
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
currentFinalizedBlock atomic.Value // Current finalized head
currentSafeBlock atomic.Value // Current safe head
historyPrunePoint atomic.Pointer[history.PrunePoint]

stateCache *state.CachingDB // State database to reuse between imports (contains state cache)
txIndexer *txIndexer
Expand Down Expand Up @@ -503,6 +505,12 @@ func (bc *BlockChain) loadLastState() error {
}
bc.hc.SetCurrentHeader(currentHeader)

// Initialize history pruning.
latest := max(currentBlock.NumberU64(), currentHeader.Number.Uint64())
if err := bc.initializeHistoryPruning(latest); err != nil {
return err
}

// Restore the last known head fast block
bc.currentFastBlock.Store(currentBlock)
headFastBlockGauge.Update(int64(currentBlock.NumberU64()))
Expand Down Expand Up @@ -541,9 +549,57 @@ func (bc *BlockChain) loadLastState() error {
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
log.Info("Loaded last fast-sync pivot marker", "number", *pivot)
}
if pruning := bc.historyPrunePoint.Load(); pruning != nil {
log.Info("Chain history is pruned", "earliest", pruning.BlockNumber, "hash", pruning.BlockHash)
}
return nil
}

// initializeHistoryPruning sets bc.historyPrunePoint.
func (bc *BlockChain) initializeHistoryPruning(latest uint64) error {
freezerTail, _ := bc.db.Tail()

switch bc.cacheConfig.ChainHistoryMode {
case history.KeepAll:
if freezerTail == 0 {
return nil
}
// The database was pruned somehow, so we need to figure out if it's a known
// configuration or an error.
predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()]
if predefinedPoint == nil || freezerTail != predefinedPoint.BlockNumber {
log.Error("Chain history database is pruned with unknown configuration", "tail", freezerTail)
return fmt.Errorf("unexpected database tail")
}
bc.historyPrunePoint.Store(predefinedPoint)
return nil

case history.KeepPostMerge:
if freezerTail == 0 && latest != 0 {
// This is the case where a user is trying to run with --history.chain
// postmerge directly on an existing DB. We could just trigger the pruning
// here, but it'd be a bit dangerous since they may not have intended this
// action to happen. So just tell them how to do it.
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cacheConfig.ChainHistoryMode.String()))
log.Error(fmt.Sprintf("Run 'geth prune-history' to prune pre-merge history."))
return fmt.Errorf("history pruning requested via configuration")
}
predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()]
if predefinedPoint == nil {
log.Error("Chain history pruning is not supported for this network", "genesis", bc.genesisBlock.Hash())
return fmt.Errorf("history pruning requested for unknown network")
} else if freezerTail != predefinedPoint.BlockNumber {
log.Error("Chain history database is pruned to unknown block", "tail", freezerTail)
return fmt.Errorf("unexpected database tail")
}
bc.historyPrunePoint.Store(predefinedPoint)
return nil

default:
return fmt.Errorf("invalid history mode: %d", bc.cacheConfig.ChainHistoryMode)
}
}

// SetHead rewinds the local chain to a new head. Depending on whether the node
// was fast synced or full synced and in which state, the method will try to
// delete minimal data from disk whilst retaining chain consistency.
Expand Down Expand Up @@ -846,7 +902,9 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
bc.currentFastBlock.Store(bc.genesisBlock)
headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
return nil

// Reset history pruning status.
return bc.initializeHistoryPruning(0)
}

// Export writes the active chain to the given writer.
Expand Down Expand Up @@ -2325,9 +2383,3 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
_, err := bc.hc.InsertHeaderChain(chain, start)
return 0, err
}

// 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
}
10 changes: 10 additions & 0 deletions core/blockchain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,3 +458,13 @@ func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscript
func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription {
return bc.scope.Track(bc.blockProcFeed.Subscribe(ch))
}

// HistoryPruningCutoff returns the configured history pruning point.
// Blocks before this might not be available in the database.
func (bc *BlockChain) HistoryPruningCutoff() (uint64, common.Hash) {
pt := bc.historyPrunePoint.Load()
if pt == nil {
return 0, bc.genesisBlock.Hash()
}
return pt.BlockNumber, pt.BlockHash
}
93 changes: 93 additions & 0 deletions core/history/historymode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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 history

import (
"fmt"

"github.com/CortexFoundation/CortexTheseus/common"
"github.com/CortexFoundation/CortexTheseus/params"
)

// HistoryMode configures history pruning.
type HistoryMode uint32

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

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

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

func (m HistoryMode) String() string {
switch m {
case KeepAll:
return "all"
case KeepPostMerge:
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 = KeepAll
case "postmerge":
*m = KeepPostMerge
default:
return fmt.Errorf(`unknown sync mode %q, want "all" or "postmerge"`, text)
}
return nil
}

type PrunePoint struct {
BlockNumber uint64
BlockHash common.Hash
}

// PrunePoints the pre-defined history pruning cutoff blocks for known networks.
// They point to the first post-merge block. Any pruning should truncate *up to* but excluding
// given block.
var PrunePoints = map[common.Hash]*PrunePoint{
// mainnet
params.MainnetGenesisHash: {
BlockNumber: 15537393,
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
},
}

// PrunedHistoryError is returned by APIs when the requested history is pruned.
type PrunedHistoryError struct{}

func (e *PrunedHistoryError) Error() string { return "pruned history unavailable" }
func (e *PrunedHistoryError) ErrorCode() int { return 4444 }
3 changes: 2 additions & 1 deletion core/txindexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ type txIndexer struct {

// newTxIndexer initializes the transaction indexer.
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
cutoff, _ := chain.HistoryPruningCutoff()
indexer := &txIndexer{
limit: limit,
cutoff: chain.HistoryPruningCutoff(),
cutoff: cutoff,
db: chain.db,
progress: make(chan chan TxIndexProgress),
term: make(chan chan struct{}),
Expand Down
5 changes: 3 additions & 2 deletions ctxc/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ func New(stack *node.Node, config *Config) (*Cortex, error) {
//StateHistory: config.StateHistory,
//StateScheme: scheme,
//HistoryPruningCutoff: historyPruningCutoff,
ChainHistoryMode: config.HistoryMode,
}
)
ctxc.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, ctxc.chainConfig, ctxc.engine, vmConfig, ctxc.shouldPreserve, &config.TxLookupLimit)
Expand All @@ -233,7 +234,7 @@ func New(stack *node.Node, config *Config) (*Cortex, error) {
}

chainView := ctxc.newChainView(ctxc.blockchain.CurrentBlock().Header())
historyCutoff := ctxc.blockchain.HistoryPruningCutoff()
historyCutoff, _ := ctxc.blockchain.HistoryPruningCutoff()
var finalBlock uint64
if fb := ctxc.blockchain.CurrentFinalizedBlock(); fb != nil {
finalBlock = fb.Header().Number.Uint64()
Expand Down Expand Up @@ -667,7 +668,7 @@ func (s *Cortex) updateFilterMapsHeads() {
if head == nil || newHead.Hash() != head.Hash() {
head = newHead
chainView := s.newChainView(head)
historyCutoff := s.blockchain.HistoryPruningCutoff()
historyCutoff, _ := s.blockchain.HistoryPruningCutoff()
var finalBlock uint64
if fb := s.blockchain.CurrentFinalizedBlock(); fb != nil {
finalBlock = fb.Header().Number.Uint64()
Expand Down
8 changes: 6 additions & 2 deletions ctxc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/CortexFoundation/CortexTheseus/common"
"github.com/CortexFoundation/CortexTheseus/consensus/cuckoo"
"github.com/CortexFoundation/CortexTheseus/core"
"github.com/CortexFoundation/CortexTheseus/core/history"
"github.com/CortexFoundation/CortexTheseus/core/txpool"
"github.com/CortexFoundation/CortexTheseus/ctxc/downloader"
"github.com/CortexFoundation/CortexTheseus/ctxc/gasprice"
Expand All @@ -48,6 +49,7 @@ var DefaultLightGPOConfig = gasprice.Config{

// DefaultConfig contains default settings for use on the Cortex main net.
var DefaultConfig = Config{
HistoryMode: history.KeepAll,
SyncMode: downloader.FullSync,
Cuckoo: cuckoo.Config{},
NetworkId: 0,
Expand Down Expand Up @@ -87,8 +89,10 @@ type Config struct {
Genesis *core.Genesis `toml:",omitempty"`

// Protocol options
NetworkId uint64 // Network ID to use for selecting peers to connect to
SyncMode downloader.SyncMode
NetworkId uint64 // Network ID to use for selecting peers to connect to
SyncMode downloader.SyncMode
// HistoryMode configures chain history retention.
HistoryMode history.HistoryMode
DiscoveryURLs []string
NoPruning bool
NoPrefetch bool // Whether to disable prefetching and only load state on demand
Expand Down
41 changes: 39 additions & 2 deletions ctxc/gen_config.go

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

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
github.com/CortexFoundation/inference v1.0.2-0.20230307032835-9197d586a4e8
github.com/CortexFoundation/statik v0.0.0-20210315012922-8bb8a7b5dc66
github.com/CortexFoundation/torrentfs v1.0.69-0.20250415112856-b1b452a24746
github.com/CortexFoundation/torrentfs v1.0.69-0.20250415134051-0b782fe8b66d
github.com/VictoriaMetrics/fastcache v1.12.2
github.com/arsham/figurine v1.3.0
github.com/aws/aws-sdk-go-v2 v1.36.3
Expand Down
Loading