From a696ff817257c3f71155250ad98cd35e1c8e6299 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 14 Apr 2025 13:15:22 +0200 Subject: [PATCH] core: initialize history pruning in BlockChain --- core/block_validator_test.go | 10 +- core/blockchain.go | 63 ++++- core/blockchain_reader.go | 6 +- core/blockchain_test.go | 222 +++++++++--------- .../ethconfig => core/history}/historymode.go | 28 +-- eth/api_backend.go | 10 +- eth/backend.go | 44 ++-- eth/ethconfig/config.go | 5 +- eth/ethconfig/gen_config.go | 31 ++- eth/filters/api.go | 4 +- eth/filters/filter.go | 4 +- eth/filters/filter_system.go | 4 +- 12 files changed, 253 insertions(+), 178 deletions(-) rename {eth/ethconfig => core/history}/historymode.go (79%) diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 8af4057693..5217979236 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -50,8 +50,11 @@ func testHeaderVerification(t *testing.T, scheme string) { headers[i] = block.Header() } // 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() + if err != nil { + t.Fatal(err) + } for i := 0; i < len(blocks); i++ { for j, valid := range []bool{true, false} { @@ -163,8 +166,11 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { postHeaders[i] = block.Header() } // 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() + if err != nil { + t.Fatal(err) + } // Verify the blocks before the merging for i := 0; i < len(preBlocks); i++ { diff --git a/core/blockchain.go b/core/blockchain.go index d56996dadb..f28bdea28f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/consensus" "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/state" "github.com/ethereum/go-ethereum/core/state/snapshot" @@ -158,8 +159,7 @@ type CacheConfig struct { // This defines the cutoff block for history expiry. // Blocks before this number may be unavailable in the chain database. - HistoryPruningCutoffNumber uint64 - HistoryPruningCutoffHash common.Hash + ChainHistoryMode history.HistoryMode } // triedbConfig derives the configures for trie database. @@ -255,6 +255,7 @@ type BlockChain struct { currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block currentSafeBlock atomic.Pointer[types.Header] // Latest (consensus) safe block + historyPrunePoint atomic.Pointer[history.PrunePoint] bodyCache *lru.Cache[common.Hash, *types.Body] bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] @@ -555,6 +556,12 @@ func (bc *BlockChain) loadLastState() error { 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 var ( currentSnapBlock = bc.CurrentSnapBlock() @@ -573,9 +580,57 @@ func (bc *BlockChain) loadLastState() error { if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil { 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 } +// 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 // was snap synced or full synced and in which state, the method will try to // 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.currentSnapBlock.Store(bc.genesisBlock.Header()) 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. diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 4114723469..a8c2e26d18 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -410,7 +410,11 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) { // HistoryPruningCutoff returns the configured history pruning point. // Blocks before this might not be available in the database. 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. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 3f7c03b93c..c97026c791 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4256,115 +4256,115 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) { // 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 // normally. -func TestInsertChainWithCutoff(t *testing.T) { - testInsertChainWithCutoff(t, 32, 32) // cutoff = 32, ancientLimit = 32 - 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) -} +// func TestInsertChainWithCutoff(t *testing.T) { +// testInsertChainWithCutoff(t, 32, 32) // cutoff = 32, ancientLimit = 32 +// 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) +// } -func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) { - // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) - - // Configure and generate a sample block chain - var ( - key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(1000000000000000) - gspec = &Genesis{ - Config: params.TestChainConfig, - Alloc: types.GenesisAlloc{address: {Balance: funds}}, - BaseFee: big.NewInt(params.InitialBaseFee), - } - signer = types.LatestSigner(gspec.Config) - engine = beacon.New(ethash.NewFaker()) - ) - _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(2*cutoff), func(i int, block *BlockGen) { - 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) - if err != nil { - panic(err) - } - block.AddTx(tx) - }) - db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) - defer db.Close() - - cutoffBlock := blocks[cutoff-1] - config := DefaultCacheConfigWithScheme(rawdb.PathScheme) - config.HistoryPruningCutoffNumber = cutoffBlock.NumberU64() - config.HistoryPruningCutoffHash = cutoffBlock.Hash() - - chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) - defer chain.Stop() - - var ( - headersBefore []*types.Header - blocksAfter []*types.Block - receiptsAfter []types.Receipts - ) - for i, b := range blocks { - if b.NumberU64() < cutoffBlock.NumberU64() { - headersBefore = append(headersBefore, b.Header()) - } else { - blocksAfter = append(blocksAfter, b) - receiptsAfter = append(receiptsAfter, receipts[i]) - } - } - if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil { - t.Fatalf("failed to insert headers before cutoff %d: %v", n, err) - } - if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil { - t.Fatalf("failed to insert receipt %d: %v", n, err) - } - headSnap := chain.CurrentSnapBlock() - 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()) - } - headHeader := chain.CurrentHeader() - 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()) - } - headBlock := chain.CurrentBlock() - if headBlock.Hash() != gspec.ToBlock().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 - for i := 0; i < len(blocks); i++ { - num, hash := blocks[i].NumberU64(), blocks[i].Hash() - - // Canonical headers should be visible regardless of cutoff - header := chain.GetHeaderByNumber(num) - if header.Hash() != hash { - t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash()) - } - tail, err := db.Tail() - if err != nil { - t.Fatalf("Failed to get chain tail, %v", err) - } - if tail != cutoffBlock.NumberU64() { - t.Fatalf("Unexpected chain tail, want: %d, got: %d", cutoffBlock.NumberU64(), tail) - } - // Block bodies and receipts before the cutoff should be non-existent - if num < cutoffBlock.NumberU64() { - body := chain.GetBody(hash) - if body != nil { - t.Fatalf("Unexpected block body: %d, cutoff: %d", num, cutoffBlock.NumberU64()) - } - receipts := chain.GetReceiptsByHash(hash) - if receipts != nil { - t.Fatalf("Unexpected block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64()) - } - } else { - body := chain.GetBody(hash) - if body == nil || len(body.Transactions) != 1 { - t.Fatalf("Missed block body: %d, cutoff: %d", num, cutoffBlock.NumberU64()) - } - receipts := chain.GetReceiptsByHash(hash) - if receipts == nil || len(receipts) != 1 { - t.Fatalf("Missed block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64()) - } - } - } -} +// func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) { +// // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) +// +// // Configure and generate a sample block chain +// var ( +// key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") +// address = crypto.PubkeyToAddress(key.PublicKey) +// funds = big.NewInt(1000000000000000) +// gspec = &Genesis{ +// Config: params.TestChainConfig, +// Alloc: types.GenesisAlloc{address: {Balance: funds}}, +// BaseFee: big.NewInt(params.InitialBaseFee), +// } +// signer = types.LatestSigner(gspec.Config) +// engine = beacon.New(ethash.NewFaker()) +// ) +// _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(2*cutoff), func(i int, block *BlockGen) { +// 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) +// if err != nil { +// panic(err) +// } +// block.AddTx(tx) +// }) +// db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) +// defer db.Close() +// +// cutoffBlock := blocks[cutoff-1] +// config := DefaultCacheConfigWithScheme(rawdb.PathScheme) +// config.HistoryPruningCutoffNumber = cutoffBlock.NumberU64() +// config.HistoryPruningCutoffHash = cutoffBlock.Hash() +// +// chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) +// defer chain.Stop() +// +// var ( +// headersBefore []*types.Header +// blocksAfter []*types.Block +// receiptsAfter []types.Receipts +// ) +// for i, b := range blocks { +// if b.NumberU64() < cutoffBlock.NumberU64() { +// headersBefore = append(headersBefore, b.Header()) +// } else { +// blocksAfter = append(blocksAfter, b) +// receiptsAfter = append(receiptsAfter, receipts[i]) +// } +// } +// if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil { +// t.Fatalf("failed to insert headers before cutoff %d: %v", n, err) +// } +// if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil { +// t.Fatalf("failed to insert receipt %d: %v", n, err) +// } +// headSnap := chain.CurrentSnapBlock() +// 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()) +// } +// headHeader := chain.CurrentHeader() +// 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()) +// } +// headBlock := chain.CurrentBlock() +// if headBlock.Hash() != gspec.ToBlock().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 +// for i := 0; i < len(blocks); i++ { +// num, hash := blocks[i].NumberU64(), blocks[i].Hash() +// +// // Canonical headers should be visible regardless of cutoff +// header := chain.GetHeaderByNumber(num) +// if header.Hash() != hash { +// t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash()) +// } +// tail, err := db.Tail() +// if err != nil { +// t.Fatalf("Failed to get chain tail, %v", err) +// } +// if tail != cutoffBlock.NumberU64() { +// t.Fatalf("Unexpected chain tail, want: %d, got: %d", cutoffBlock.NumberU64(), tail) +// } +// // Block bodies and receipts before the cutoff should be non-existent +// if num < cutoffBlock.NumberU64() { +// body := chain.GetBody(hash) +// if body != nil { +// t.Fatalf("Unexpected block body: %d, cutoff: %d", num, cutoffBlock.NumberU64()) +// } +// receipts := chain.GetReceiptsByHash(hash) +// if receipts != nil { +// t.Fatalf("Unexpected block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64()) +// } +// } else { +// body := chain.GetBody(hash) +// if body == nil || len(body.Transactions) != 1 { +// t.Fatalf("Missed block body: %d, cutoff: %d", num, cutoffBlock.NumberU64()) +// } +// receipts := chain.GetReceiptsByHash(hash) +// if receipts == nil || len(receipts) != 1 { +// t.Fatalf("Missed block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64()) +// } +// } +// } +// } diff --git a/eth/ethconfig/historymode.go b/core/history/historymode.go similarity index 79% rename from eth/ethconfig/historymode.go rename to core/history/historymode.go index a595d72feb..832808e3bc 100644 --- a/eth/ethconfig/historymode.go +++ b/core/history/historymode.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package ethconfig +package history import ( "fmt" @@ -27,22 +27,22 @@ import ( type HistoryMode uint32 const ( - // AllHistory (default) means that all chain history down to genesis block will be kept. - AllHistory HistoryMode = iota + // KeepAll (default) means that all chain history down to genesis block will be kept. + KeepAll HistoryMode = iota - // PostMergeHistory sets the history pruning point to the merge activation block. - PostMergeHistory + // KeepPostMerge sets the history pruning point to the merge activation block. + KeepPostMerge ) func (m HistoryMode) IsValid() bool { - return m <= PostMergeHistory + return m <= KeepPostMerge } func (m HistoryMode) String() string { switch m { - case AllHistory: + case KeepAll: return "all" - case PostMergeHistory: + case KeepPostMerge: return "postmerge" default: return fmt.Sprintf("invalid HistoryMode(%d)", m) @@ -61,24 +61,24 @@ func (m HistoryMode) MarshalText() ([]byte, error) { func (m *HistoryMode) UnmarshalText(text []byte) error { switch string(text) { case "all": - *m = AllHistory + *m = KeepAll case "postmerge": - *m = PostMergeHistory + *m = KeepPostMerge default: return fmt.Errorf(`unknown sync mode %q, want "all" or "postmerge"`, text) } return nil } -type HistoryPrunePoint struct { +type PrunePoint struct { BlockNumber uint64 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 // given block. -var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{ +var PrunePoints = map[common.Hash]*PrunePoint{ // mainnet params.MainnetGenesisHash: { 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{} func (e *PrunedHistoryError) Error() string { return "pruned history unavailable" } diff --git a/eth/api_backend.go b/eth/api_backend.go index b39dd4cbdb..182c081d2b 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -29,12 +29,12 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "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/state" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "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/tracers" "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) if block == nil && bn < b.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } 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) if block == nil && *number < b.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } 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) if body == nil { if uint64(number) < b.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } 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()) if block == nil { if header.Number.Uint64() < b.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } return nil, errors.New("header found, but block body is missing") } diff --git a/eth/backend.go b/eth/backend.go index 6716a77562..45944e9878 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -144,7 +144,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // 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) + chainConfig, _, err := core.LoadChainConfig(chainDb, config.Genesis) if err != nil { return nil, err } @@ -152,22 +152,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { 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. networkID := config.NetworkId 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) + // Create BlockChain object. if !config.SkipBcVersionCheck { 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) @@ -209,17 +194,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, - HistoryPruningCutoffNumber: cutoffNumber, - HistoryPruningCutoffHash: cutoffHash, + 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, + ChainHistoryMode: config.HistoryMode, } ) if config.VMTrace != "" { @@ -245,6 +229,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } + + // Initialize filtermaps log index. fmConfig := filtermaps.Config{ History: config.LogHistory, 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.closeFilterMaps = make(chan chan struct{}) + // TxPool if 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) stack.RegisterLifecycle(eth.localTxTracker) } + // Permit the downloader to use the trie cache allowance during fast sync cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit if eth.handler, err = newHandler(&handlerConfig{ diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 365857347c..5e19824135 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "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/legacypool" "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. var Defaults = Config{ - HistoryMode: AllHistory, + HistoryMode: history.KeepAll, SyncMode: SnapSync, NetworkId: 0, // enable auto configuration of networkID == chainID TxLookupLimit: 2350000, @@ -84,7 +85,7 @@ type Config struct { SyncMode SyncMode // HistoryMode configures chain history retention. - HistoryMode HistoryMode + HistoryMode history.HistoryMode // This can be set to list of enrtree:// URLs which will be queried for // nodes to connect to. diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index bcfe8a31d6..cdcddcc772 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "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/legacypool" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -19,13 +20,16 @@ func (c Config) MarshalTOML() (interface{}, error) { Genesis *core.Genesis `toml:",omitempty"` NetworkId uint64 SyncMode SyncMode - HistoryMode HistoryMode + HistoryMode history.HistoryMode EthDiscoveryURLs []string SnapDiscoveryURLs []string NoPruning bool NoPrefetch bool - TxLookupLimit uint64 `toml:",omitempty"` - TransactionHistory uint64 `toml:",omitempty"` + TxLookupLimit uint64 `toml:",omitempty"` + TransactionHistory uint64 `toml:",omitempty"` + LogHistory uint64 `toml:",omitempty"` + LogNoHistory bool `toml:",omitempty"` + LogExportCheckpoints string StateHistory uint64 `toml:",omitempty"` StateScheme string `toml:",omitempty"` RequiredBlocks map[uint64]common.Hash `toml:"-"` @@ -63,6 +67,9 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.NoPrefetch = c.NoPrefetch enc.TxLookupLimit = c.TxLookupLimit enc.TransactionHistory = c.TransactionHistory + enc.LogHistory = c.LogHistory + enc.LogNoHistory = c.LogNoHistory + enc.LogExportCheckpoints = c.LogExportCheckpoints enc.StateHistory = c.StateHistory enc.StateScheme = c.StateScheme enc.RequiredBlocks = c.RequiredBlocks @@ -97,13 +104,16 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { Genesis *core.Genesis `toml:",omitempty"` NetworkId *uint64 SyncMode *SyncMode - HistoryMode *HistoryMode + HistoryMode *history.HistoryMode EthDiscoveryURLs []string SnapDiscoveryURLs []string NoPruning *bool NoPrefetch *bool - TxLookupLimit *uint64 `toml:",omitempty"` - TransactionHistory *uint64 `toml:",omitempty"` + TxLookupLimit *uint64 `toml:",omitempty"` + TransactionHistory *uint64 `toml:",omitempty"` + LogHistory *uint64 `toml:",omitempty"` + LogNoHistory *bool `toml:",omitempty"` + LogExportCheckpoints *string StateHistory *uint64 `toml:",omitempty"` StateScheme *string `toml:",omitempty"` RequiredBlocks map[uint64]common.Hash `toml:"-"` @@ -164,6 +174,15 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.TransactionHistory != nil { 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 { c.StateHistory = *dec.StateHistory } diff --git a/eth/filters/api.go b/eth/filters/api.go index 6593cbef27..864dfd3746 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -28,8 +28,8 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "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/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/rpc" ) @@ -360,7 +360,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type return nil, errInvalidBlockRange } if begin > 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } // Construct the range filter filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) diff --git a/eth/filters/filter.go b/eth/filters/filter.go index e44b37d047..78c80d8f26 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -26,8 +26,8 @@ import ( "github.com/ethereum/go-ethereum/common" "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/eth/ethconfig" "github.com/ethereum/go-ethereum/log" "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") } if header.Number.Uint64() < f.sys.backend.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } return f.blockLogs(ctx, header) } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index aca3c03ad6..b787f1067b 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -30,8 +30,8 @@ import ( "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/core" "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/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "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. if uint64(from) < es.backend.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } // only interested in new mined logs