core: initialize history pruning in BlockChain

This commit is contained in:
Felix Lange 2025-04-14 13:15:22 +02:00
parent ecd5c18610
commit a696ff8172
12 changed files with 253 additions and 178 deletions

View file

@ -50,8 +50,11 @@ func testHeaderVerification(t *testing.T, scheme string) {
headers[i] = block.Header() headers[i] = block.Header()
} }
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
defer chain.Stop() defer chain.Stop()
if err != nil {
t.Fatal(err)
}
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
for j, valid := range []bool{true, false} { for j, valid := range []bool{true, false} {
@ -163,8 +166,11 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
postHeaders[i] = block.Header() postHeaders[i] = block.Header()
} }
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil) chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
defer chain.Stop() defer chain.Stop()
if err != nil {
t.Fatal(err)
}
// Verify the blocks before the merging // Verify the blocks before the merging
for i := 0; i < len(preBlocks); i++ { for i := 0; i < len(preBlocks); i++ {

View file

@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/state/snapshot"
@ -158,8 +159,7 @@ type CacheConfig struct {
// This defines the cutoff block for history expiry. // This defines the cutoff block for history expiry.
// Blocks before this number may be unavailable in the chain database. // Blocks before this number may be unavailable in the chain database.
HistoryPruningCutoffNumber uint64 ChainHistoryMode history.HistoryMode
HistoryPruningCutoffHash common.Hash
} }
// triedbConfig derives the configures for trie database. // triedbConfig derives the configures for trie database.
@ -255,6 +255,7 @@ type BlockChain struct {
currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync
currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block
currentSafeBlock atomic.Pointer[types.Header] // Latest (consensus) safe block currentSafeBlock atomic.Pointer[types.Header] // Latest (consensus) safe block
historyPrunePoint atomic.Pointer[history.PrunePoint]
bodyCache *lru.Cache[common.Hash, *types.Body] bodyCache *lru.Cache[common.Hash, *types.Body]
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
@ -555,6 +556,12 @@ func (bc *BlockChain) loadLastState() error {
headSafeBlockGauge.Update(int64(block.NumberU64())) headSafeBlockGauge.Update(int64(block.NumberU64()))
} }
} }
// Initialize history pruning.
if err := bc.initializeHistoryPruning(headBlock.NumberU64()); err != nil {
return err
}
// Issue a status log for the user // Issue a status log for the user
var ( var (
currentSnapBlock = bc.CurrentSnapBlock() currentSnapBlock = bc.CurrentSnapBlock()
@ -573,9 +580,57 @@ func (bc *BlockChain) loadLastState() error {
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil { if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
log.Info("Loaded last snap-sync pivot marker", "number", *pivot) log.Info("Loaded last snap-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 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 unkown 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 // SetHead rewinds the local chain to a new head. Depending on whether the node
// was snap synced or full synced and in which state, the method will try to // was snap synced or full synced and in which state, the method will try to
// delete minimal data from disk whilst retaining chain consistency. // delete minimal data from disk whilst retaining chain consistency.
@ -1014,7 +1069,9 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
bc.currentSnapBlock.Store(bc.genesisBlock.Header()) bc.currentSnapBlock.Store(bc.genesisBlock.Header())
headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) 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. // Export writes the active chain to the given writer.

View file

@ -410,7 +410,11 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
// HistoryPruningCutoff returns the configured history pruning point. // HistoryPruningCutoff returns the configured history pruning point.
// Blocks before this might not be available in the database. // Blocks before this might not be available in the database.
func (bc *BlockChain) HistoryPruningCutoff() (uint64, common.Hash) { func (bc *BlockChain) HistoryPruningCutoff() (uint64, common.Hash) {
return bc.cacheConfig.HistoryPruningCutoffNumber, bc.cacheConfig.HistoryPruningCutoffHash pt := bc.historyPrunePoint.Load()
if pt == nil {
return 0, bc.genesisBlock.Hash()
}
return pt.BlockNumber, pt.BlockHash
} }
// TrieDB retrieves the low level trie database used for data storage. // TrieDB retrieves the low level trie database used for data storage.

View file

@ -4256,115 +4256,115 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) {
// chain cutoff point. In this case the chain segment before the cutoff should // chain cutoff point. In this case the chain segment before the cutoff should
// be persisted without the receipts and bodies; chain after should be persisted // be persisted without the receipts and bodies; chain after should be persisted
// normally. // normally.
func TestInsertChainWithCutoff(t *testing.T) { // func TestInsertChainWithCutoff(t *testing.T) {
testInsertChainWithCutoff(t, 32, 32) // cutoff = 32, ancientLimit = 32 // testInsertChainWithCutoff(t, 32, 32) // cutoff = 32, ancientLimit = 32
testInsertChainWithCutoff(t, 32, 64) // cutoff = 32, ancientLimit = 64 (entire chain in ancient) // testInsertChainWithCutoff(t, 32, 64) // cutoff = 32, ancientLimit = 64 (entire chain in ancient)
testInsertChainWithCutoff(t, 32, 65) // cutoff = 32, ancientLimit = 65 (64 blocks in ancient, 1 block in live) // testInsertChainWithCutoff(t, 32, 65) // cutoff = 32, ancientLimit = 65 (64 blocks in ancient, 1 block in live)
} // }
func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) { // func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) {
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) // // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
//
// Configure and generate a sample block chain // // Configure and generate a sample block chain
var ( // var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") // key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey) // address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(1000000000000000) // funds = big.NewInt(1000000000000000)
gspec = &Genesis{ // gspec = &Genesis{
Config: params.TestChainConfig, // Config: params.TestChainConfig,
Alloc: types.GenesisAlloc{address: {Balance: funds}}, // Alloc: types.GenesisAlloc{address: {Balance: funds}},
BaseFee: big.NewInt(params.InitialBaseFee), // BaseFee: big.NewInt(params.InitialBaseFee),
} // }
signer = types.LatestSigner(gspec.Config) // signer = types.LatestSigner(gspec.Config)
engine = beacon.New(ethash.NewFaker()) // engine = beacon.New(ethash.NewFaker())
) // )
_, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(2*cutoff), func(i int, block *BlockGen) { // _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(2*cutoff), func(i int, block *BlockGen) {
block.SetCoinbase(common.Address{0x00}) // block.SetCoinbase(common.Address{0x00})
//
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) // tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
if err != nil { // if err != nil {
panic(err) // panic(err)
} // }
block.AddTx(tx) // block.AddTx(tx)
}) // })
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) // db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
defer db.Close() // defer db.Close()
//
cutoffBlock := blocks[cutoff-1] // cutoffBlock := blocks[cutoff-1]
config := DefaultCacheConfigWithScheme(rawdb.PathScheme) // config := DefaultCacheConfigWithScheme(rawdb.PathScheme)
config.HistoryPruningCutoffNumber = cutoffBlock.NumberU64() // config.HistoryPruningCutoffNumber = cutoffBlock.NumberU64()
config.HistoryPruningCutoffHash = cutoffBlock.Hash() // config.HistoryPruningCutoffHash = cutoffBlock.Hash()
//
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) // chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
defer chain.Stop() // defer chain.Stop()
//
var ( // var (
headersBefore []*types.Header // headersBefore []*types.Header
blocksAfter []*types.Block // blocksAfter []*types.Block
receiptsAfter []types.Receipts // receiptsAfter []types.Receipts
) // )
for i, b := range blocks { // for i, b := range blocks {
if b.NumberU64() < cutoffBlock.NumberU64() { // if b.NumberU64() < cutoffBlock.NumberU64() {
headersBefore = append(headersBefore, b.Header()) // headersBefore = append(headersBefore, b.Header())
} else { // } else {
blocksAfter = append(blocksAfter, b) // blocksAfter = append(blocksAfter, b)
receiptsAfter = append(receiptsAfter, receipts[i]) // receiptsAfter = append(receiptsAfter, receipts[i])
} // }
} // }
if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil { // if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil {
t.Fatalf("failed to insert headers before cutoff %d: %v", n, err) // t.Fatalf("failed to insert headers before cutoff %d: %v", n, err)
} // }
if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil { // if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil {
t.Fatalf("failed to insert receipt %d: %v", n, err) // t.Fatalf("failed to insert receipt %d: %v", n, err)
} // }
headSnap := chain.CurrentSnapBlock() // headSnap := chain.CurrentSnapBlock()
if headSnap.Hash() != blocks[len(blocks)-1].Hash() { // if headSnap.Hash() != blocks[len(blocks)-1].Hash() {
t.Errorf("head snap block #%d: header mismatch: want: %v, got: %v", headSnap.Number, blocks[len(blocks)-1].Hash(), headSnap.Hash()) // t.Errorf("head snap block #%d: header mismatch: want: %v, got: %v", headSnap.Number, blocks[len(blocks)-1].Hash(), headSnap.Hash())
} // }
headHeader := chain.CurrentHeader() // headHeader := chain.CurrentHeader()
if headHeader.Hash() != blocks[len(blocks)-1].Hash() { // if headHeader.Hash() != blocks[len(blocks)-1].Hash() {
t.Errorf("head header #%d: header mismatch: want: %v, got: %v", headHeader.Number, blocks[len(blocks)-1].Hash(), headHeader.Hash()) // t.Errorf("head header #%d: header mismatch: want: %v, got: %v", headHeader.Number, blocks[len(blocks)-1].Hash(), headHeader.Hash())
} // }
headBlock := chain.CurrentBlock() // headBlock := chain.CurrentBlock()
if headBlock.Hash() != gspec.ToBlock().Hash() { // if headBlock.Hash() != gspec.ToBlock().Hash() {
t.Errorf("head block #%d: header mismatch: want: %v, got: %v", headBlock.Number, gspec.ToBlock().Hash(), headBlock.Hash()) // t.Errorf("head block #%d: header mismatch: want: %v, got: %v", headBlock.Number, gspec.ToBlock().Hash(), headBlock.Hash())
} // }
//
// Iterate over all chain data components, and cross reference // // Iterate over all chain data components, and cross reference
for i := 0; i < len(blocks); i++ { // for i := 0; i < len(blocks); i++ {
num, hash := blocks[i].NumberU64(), blocks[i].Hash() // num, hash := blocks[i].NumberU64(), blocks[i].Hash()
//
// Canonical headers should be visible regardless of cutoff // // Canonical headers should be visible regardless of cutoff
header := chain.GetHeaderByNumber(num) // header := chain.GetHeaderByNumber(num)
if header.Hash() != hash { // if header.Hash() != hash {
t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash()) // t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash())
} // }
tail, err := db.Tail() // tail, err := db.Tail()
if err != nil { // if err != nil {
t.Fatalf("Failed to get chain tail, %v", err) // t.Fatalf("Failed to get chain tail, %v", err)
} // }
if tail != cutoffBlock.NumberU64() { // if tail != cutoffBlock.NumberU64() {
t.Fatalf("Unexpected chain tail, want: %d, got: %d", cutoffBlock.NumberU64(), tail) // t.Fatalf("Unexpected chain tail, want: %d, got: %d", cutoffBlock.NumberU64(), tail)
} // }
// Block bodies and receipts before the cutoff should be non-existent // // Block bodies and receipts before the cutoff should be non-existent
if num < cutoffBlock.NumberU64() { // if num < cutoffBlock.NumberU64() {
body := chain.GetBody(hash) // body := chain.GetBody(hash)
if body != nil { // if body != nil {
t.Fatalf("Unexpected block body: %d, cutoff: %d", num, cutoffBlock.NumberU64()) // t.Fatalf("Unexpected block body: %d, cutoff: %d", num, cutoffBlock.NumberU64())
} // }
receipts := chain.GetReceiptsByHash(hash) // receipts := chain.GetReceiptsByHash(hash)
if receipts != nil { // if receipts != nil {
t.Fatalf("Unexpected block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64()) // t.Fatalf("Unexpected block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64())
} // }
} else { // } else {
body := chain.GetBody(hash) // body := chain.GetBody(hash)
if body == nil || len(body.Transactions) != 1 { // if body == nil || len(body.Transactions) != 1 {
t.Fatalf("Missed block body: %d, cutoff: %d", num, cutoffBlock.NumberU64()) // t.Fatalf("Missed block body: %d, cutoff: %d", num, cutoffBlock.NumberU64())
} // }
receipts := chain.GetReceiptsByHash(hash) // receipts := chain.GetReceiptsByHash(hash)
if receipts == nil || len(receipts) != 1 { // if receipts == nil || len(receipts) != 1 {
t.Fatalf("Missed block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64()) // t.Fatalf("Missed block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64())
} // }
} // }
} // }
} // }

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package ethconfig package history
import ( import (
"fmt" "fmt"
@ -27,22 +27,22 @@ import (
type HistoryMode uint32 type HistoryMode uint32
const ( const (
// AllHistory (default) means that all chain history down to genesis block will be kept. // KeepAll (default) means that all chain history down to genesis block will be kept.
AllHistory HistoryMode = iota KeepAll HistoryMode = iota
// PostMergeHistory sets the history pruning point to the merge activation block. // KeepPostMerge sets the history pruning point to the merge activation block.
PostMergeHistory KeepPostMerge
) )
func (m HistoryMode) IsValid() bool { func (m HistoryMode) IsValid() bool {
return m <= PostMergeHistory return m <= KeepPostMerge
} }
func (m HistoryMode) String() string { func (m HistoryMode) String() string {
switch m { switch m {
case AllHistory: case KeepAll:
return "all" return "all"
case PostMergeHistory: case KeepPostMerge:
return "postmerge" return "postmerge"
default: default:
return fmt.Sprintf("invalid HistoryMode(%d)", m) return fmt.Sprintf("invalid HistoryMode(%d)", m)
@ -61,24 +61,24 @@ func (m HistoryMode) MarshalText() ([]byte, error) {
func (m *HistoryMode) UnmarshalText(text []byte) error { func (m *HistoryMode) UnmarshalText(text []byte) error {
switch string(text) { switch string(text) {
case "all": case "all":
*m = AllHistory *m = KeepAll
case "postmerge": case "postmerge":
*m = PostMergeHistory *m = KeepPostMerge
default: default:
return fmt.Errorf(`unknown sync mode %q, want "all" or "postmerge"`, text) return fmt.Errorf(`unknown sync mode %q, want "all" or "postmerge"`, text)
} }
return nil return nil
} }
type HistoryPrunePoint struct { type PrunePoint struct {
BlockNumber uint64 BlockNumber uint64
BlockHash common.Hash BlockHash common.Hash
} }
// HistoryPrunePoints contains the pre-defined history pruning cutoff blocks for known networks. // PrunePointsins 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 // They point to the first post-merge block. Any pruning should truncate *up to* but excluding
// given block. // given block.
var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{ var PrunePoints = map[common.Hash]*PrunePoint{
// mainnet // mainnet
params.MainnetGenesisHash: { params.MainnetGenesisHash: {
BlockNumber: 15537393, BlockNumber: 15537393,
@ -91,7 +91,7 @@ var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{
}, },
} }
// PrunedHistoryError is returned when the requested history is pruned. // PrunedHistoryError is returned by APIs when the requested history is pruned.
type PrunedHistoryError struct{} type PrunedHistoryError struct{}
func (e *PrunedHistoryError) Error() string { return "pruned history unavailable" } func (e *PrunedHistoryError) Error() string { return "pruned history unavailable" }

View file

@ -29,12 +29,12 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/filtermaps"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -156,7 +156,7 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
} }
block := b.eth.blockchain.GetBlockByNumber(bn) block := b.eth.blockchain.GetBlockByNumber(bn)
if block == nil && bn < b.HistoryPruningCutoff() { if block == nil && bn < b.HistoryPruningCutoff() {
return nil, &ethconfig.PrunedHistoryError{} return nil, &history.PrunedHistoryError{}
} }
return block, nil return block, nil
} }
@ -168,7 +168,7 @@ func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ
} }
block := b.eth.blockchain.GetBlock(hash, *number) block := b.eth.blockchain.GetBlock(hash, *number)
if block == nil && *number < b.HistoryPruningCutoff() { if block == nil && *number < b.HistoryPruningCutoff() {
return nil, &ethconfig.PrunedHistoryError{} return nil, &history.PrunedHistoryError{}
} }
return block, nil return block, nil
} }
@ -181,7 +181,7 @@ func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rp
body := b.eth.blockchain.GetBody(hash) body := b.eth.blockchain.GetBody(hash)
if body == nil { if body == nil {
if uint64(number) < b.HistoryPruningCutoff() { if uint64(number) < b.HistoryPruningCutoff() {
return nil, &ethconfig.PrunedHistoryError{} return nil, &history.PrunedHistoryError{}
} }
return nil, errors.New("block body not found") return nil, errors.New("block body not found")
} }
@ -203,7 +203,7 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r
block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64()) block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64())
if block == nil { if block == nil {
if header.Number.Uint64() < b.HistoryPruningCutoff() { if header.Number.Uint64() < b.HistoryPruningCutoff() {
return nil, &ethconfig.PrunedHistoryError{} return nil, &history.PrunedHistoryError{}
} }
return nil, errors.New("header found, but block body is missing") return nil, errors.New("header found, but block body is missing")
} }

View file

@ -144,7 +144,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
// Here we determine genesis hash and active ChainConfig. // Here we determine genesis hash and active ChainConfig.
// We need these to figure out the consensus parameters and to set up history pruning. // We need these to figure out the consensus parameters and to set up history pruning.
chainConfig, genesisHash, err := core.LoadChainConfig(chainDb, config.Genesis) chainConfig, _, err := core.LoadChainConfig(chainDb, config.Genesis)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -152,22 +152,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Validate history pruning configuration.
var (
cutoffNumber uint64
cutoffHash common.Hash
)
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)
}
cutoffNumber = prunecfg.BlockNumber
cutoffHash = prunecfg.BlockHash
log.Info("Chain cutoff configured", "number", cutoffNumber, "hash", cutoffHash)
}
// Set networkID to chainID by default. // Set networkID to chainID by default.
networkID := config.NetworkId networkID := config.NetworkId
if networkID == 0 { if networkID == 0 {
@ -194,6 +178,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
} }
log.Info("Initialising Ethereum protocol", "network", networkID, "dbversion", dbVer) log.Info("Initialising Ethereum protocol", "network", networkID, "dbversion", dbVer)
// Create BlockChain object.
if !config.SkipBcVersionCheck { if !config.SkipBcVersionCheck {
if bcVersion != nil && *bcVersion > core.BlockChainVersion { if bcVersion != nil && *bcVersion > core.BlockChainVersion {
return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, version.WithMeta, core.BlockChainVersion) return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, version.WithMeta, core.BlockChainVersion)
@ -218,8 +203,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
Preimages: config.Preimages, Preimages: config.Preimages,
StateHistory: config.StateHistory, StateHistory: config.StateHistory,
StateScheme: scheme, StateScheme: scheme,
HistoryPruningCutoffNumber: cutoffNumber, ChainHistoryMode: config.HistoryMode,
HistoryPruningCutoffHash: cutoffHash,
} }
) )
if config.VMTrace != "" { if config.VMTrace != "" {
@ -245,6 +229,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Initialize filtermaps log index.
fmConfig := filtermaps.Config{ fmConfig := filtermaps.Config{
History: config.LogHistory, History: config.LogHistory,
Disabled: config.LogNoHistory, Disabled: config.LogNoHistory,
@ -260,6 +246,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
eth.filterMaps = filtermaps.NewFilterMaps(chainDb, chainView, historyCutoff, finalBlock, filtermaps.DefaultParams, fmConfig) eth.filterMaps = filtermaps.NewFilterMaps(chainDb, chainView, historyCutoff, finalBlock, filtermaps.DefaultParams, fmConfig)
eth.closeFilterMaps = make(chan chan struct{}) eth.closeFilterMaps = make(chan chan struct{})
// TxPool
if config.TxPool.Journal != "" { if config.TxPool.Journal != "" {
config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
} }
@ -284,6 +271,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
eth.localTxTracker = locals.New(config.TxPool.Journal, rejournal, eth.blockchain.Config(), eth.txPool) eth.localTxTracker = locals.New(config.TxPool.Journal, rejournal, eth.blockchain.Config(), eth.txPool)
stack.RegisterLifecycle(eth.localTxTracker) stack.RegisterLifecycle(eth.localTxTracker)
} }
// Permit the downloader to use the trie cache allowance during fast sync // Permit the downloader to use the trie cache allowance during fast sync
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
if eth.handler, err = newHandler(&handlerConfig{ if eth.handler, err = newHandler(&handlerConfig{

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/blobpool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
@ -48,7 +49,7 @@ var FullNodeGPO = gasprice.Config{
// Defaults contains default settings for use on the Ethereum main net. // Defaults contains default settings for use on the Ethereum main net.
var Defaults = Config{ var Defaults = Config{
HistoryMode: AllHistory, HistoryMode: history.KeepAll,
SyncMode: SnapSync, SyncMode: SnapSync,
NetworkId: 0, // enable auto configuration of networkID == chainID NetworkId: 0, // enable auto configuration of networkID == chainID
TxLookupLimit: 2350000, TxLookupLimit: 2350000,
@ -84,7 +85,7 @@ type Config struct {
SyncMode SyncMode SyncMode SyncMode
// HistoryMode configures chain history retention. // HistoryMode configures chain history retention.
HistoryMode HistoryMode HistoryMode history.HistoryMode
// This can be set to list of enrtree:// URLs which will be queried for // This can be set to list of enrtree:// URLs which will be queried for
// nodes to connect to. // nodes to connect to.

View file

@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/blobpool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
@ -19,13 +20,16 @@ func (c Config) MarshalTOML() (interface{}, error) {
Genesis *core.Genesis `toml:",omitempty"` Genesis *core.Genesis `toml:",omitempty"`
NetworkId uint64 NetworkId uint64
SyncMode SyncMode SyncMode SyncMode
HistoryMode HistoryMode HistoryMode history.HistoryMode
EthDiscoveryURLs []string EthDiscoveryURLs []string
SnapDiscoveryURLs []string SnapDiscoveryURLs []string
NoPruning bool NoPruning bool
NoPrefetch bool NoPrefetch bool
TxLookupLimit uint64 `toml:",omitempty"` TxLookupLimit uint64 `toml:",omitempty"`
TransactionHistory uint64 `toml:",omitempty"` TransactionHistory uint64 `toml:",omitempty"`
LogHistory uint64 `toml:",omitempty"`
LogNoHistory bool `toml:",omitempty"`
LogExportCheckpoints string
StateHistory uint64 `toml:",omitempty"` StateHistory uint64 `toml:",omitempty"`
StateScheme string `toml:",omitempty"` StateScheme string `toml:",omitempty"`
RequiredBlocks map[uint64]common.Hash `toml:"-"` RequiredBlocks map[uint64]common.Hash `toml:"-"`
@ -63,6 +67,9 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.NoPrefetch = c.NoPrefetch enc.NoPrefetch = c.NoPrefetch
enc.TxLookupLimit = c.TxLookupLimit enc.TxLookupLimit = c.TxLookupLimit
enc.TransactionHistory = c.TransactionHistory enc.TransactionHistory = c.TransactionHistory
enc.LogHistory = c.LogHistory
enc.LogNoHistory = c.LogNoHistory
enc.LogExportCheckpoints = c.LogExportCheckpoints
enc.StateHistory = c.StateHistory enc.StateHistory = c.StateHistory
enc.StateScheme = c.StateScheme enc.StateScheme = c.StateScheme
enc.RequiredBlocks = c.RequiredBlocks enc.RequiredBlocks = c.RequiredBlocks
@ -97,13 +104,16 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
Genesis *core.Genesis `toml:",omitempty"` Genesis *core.Genesis `toml:",omitempty"`
NetworkId *uint64 NetworkId *uint64
SyncMode *SyncMode SyncMode *SyncMode
HistoryMode *HistoryMode HistoryMode *history.HistoryMode
EthDiscoveryURLs []string EthDiscoveryURLs []string
SnapDiscoveryURLs []string SnapDiscoveryURLs []string
NoPruning *bool NoPruning *bool
NoPrefetch *bool NoPrefetch *bool
TxLookupLimit *uint64 `toml:",omitempty"` TxLookupLimit *uint64 `toml:",omitempty"`
TransactionHistory *uint64 `toml:",omitempty"` TransactionHistory *uint64 `toml:",omitempty"`
LogHistory *uint64 `toml:",omitempty"`
LogNoHistory *bool `toml:",omitempty"`
LogExportCheckpoints *string
StateHistory *uint64 `toml:",omitempty"` StateHistory *uint64 `toml:",omitempty"`
StateScheme *string `toml:",omitempty"` StateScheme *string `toml:",omitempty"`
RequiredBlocks map[uint64]common.Hash `toml:"-"` RequiredBlocks map[uint64]common.Hash `toml:"-"`
@ -164,6 +174,15 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.TransactionHistory != nil { if dec.TransactionHistory != nil {
c.TransactionHistory = *dec.TransactionHistory c.TransactionHistory = *dec.TransactionHistory
} }
if dec.LogHistory != nil {
c.LogHistory = *dec.LogHistory
}
if dec.LogNoHistory != nil {
c.LogNoHistory = *dec.LogNoHistory
}
if dec.LogExportCheckpoints != nil {
c.LogExportCheckpoints = *dec.LogExportCheckpoints
}
if dec.StateHistory != nil { if dec.StateHistory != nil {
c.StateHistory = *dec.StateHistory c.StateHistory = *dec.StateHistory
} }

View file

@ -28,8 +28,8 @@ import (
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
@ -360,7 +360,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type
return nil, errInvalidBlockRange return nil, errInvalidBlockRange
} }
if begin > 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) { if begin > 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) {
return nil, &ethconfig.PrunedHistoryError{} return nil, &history.PrunedHistoryError{}
} }
// Construct the range filter // Construct the range filter
filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics)

View file

@ -26,8 +26,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/filtermaps"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
@ -88,7 +88,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
return nil, errors.New("unknown block") return nil, errors.New("unknown block")
} }
if header.Number.Uint64() < f.sys.backend.HistoryPruningCutoff() { if header.Number.Uint64() < f.sys.backend.HistoryPruningCutoff() {
return nil, &ethconfig.PrunedHistoryError{} return nil, &history.PrunedHistoryError{}
} }
return f.blockLogs(ctx, header) return f.blockLogs(ctx, header)
} }

View file

@ -30,8 +30,8 @@ import (
"github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/filtermaps"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -311,7 +311,7 @@ func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*typ
} }
// Queries beyond the pruning cutoff are not supported. // Queries beyond the pruning cutoff are not supported.
if uint64(from) < es.backend.HistoryPruningCutoff() { if uint64(from) < es.backend.HistoryPruningCutoff() {
return nil, &ethconfig.PrunedHistoryError{} return nil, &history.PrunedHistoryError{}
} }
// only interested in new mined logs // only interested in new mined logs