mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 15:47:21 +00:00
Merge b249bf9962 into 406a852ec8
This commit is contained in:
commit
7d880c722c
4 changed files with 156 additions and 47 deletions
|
|
@ -208,13 +208,19 @@ This command dumps out the state for a given block (or latest, if none provided)
|
|||
pruneHistoryCommand = &cli.Command{
|
||||
Action: pruneHistory,
|
||||
Name: "prune-history",
|
||||
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
|
||||
Usage: "Prune blockchain history (block bodies and receipts) up to a specified point",
|
||||
ArgsUsage: "",
|
||||
Flags: utils.DatabaseFlags,
|
||||
Flags: slices.Concat(utils.DatabaseFlags, []cli.Flag{
|
||||
utils.ChainHistoryFlag,
|
||||
}),
|
||||
Description: `
|
||||
The prune-history command removes historical block bodies and receipts from the
|
||||
blockchain database up to the merge block, while preserving block headers. This
|
||||
helps reduce storage requirements for nodes that don't need full historical data.`,
|
||||
blockchain database up to a specified point, while preserving block headers. This
|
||||
helps reduce storage requirements for nodes that don't need full historical data.
|
||||
|
||||
The --history.chain flag is required to specify the pruning target:
|
||||
- postmerge: Prune up to the merge block
|
||||
- postcancun: Prune up to the Cancun (Dencun) upgrade block`,
|
||||
}
|
||||
|
||||
downloadEraCommand = &cli.Command{
|
||||
|
|
@ -703,47 +709,74 @@ func hashish(x string) bool {
|
|||
}
|
||||
|
||||
func pruneHistory(ctx *cli.Context) error {
|
||||
// Parse and validate the history mode flag.
|
||||
if !ctx.IsSet(utils.ChainHistoryFlag.Name) {
|
||||
return errors.New("--history.chain flag is required (use 'postmerge' or 'postcancun')")
|
||||
}
|
||||
var mode history.HistoryMode
|
||||
if err := mode.UnmarshalText([]byte(ctx.String(utils.ChainHistoryFlag.Name))); err != nil {
|
||||
return err
|
||||
}
|
||||
if mode == history.KeepAll {
|
||||
return errors.New("--history.chain=all is not valid for pruning. To restore history, use 'geth import-history'")
|
||||
}
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
// Open the chain database
|
||||
// Open the chain database.
|
||||
chain, chaindb := utils.MakeChain(ctx, stack, false)
|
||||
defer chaindb.Close()
|
||||
defer chain.Stop()
|
||||
|
||||
// Determine the prune point. This will be the first PoS block.
|
||||
prunePoint, ok := history.PrunePoints[chain.Genesis().Hash()]
|
||||
if !ok || prunePoint == nil {
|
||||
return errors.New("prune point not found")
|
||||
// Determine the prune point based on the history mode.
|
||||
genesisHash := chain.Genesis().Hash()
|
||||
prunePoint := history.GetPrunePoint(genesisHash, mode)
|
||||
if prunePoint == nil {
|
||||
return fmt.Errorf("prune point for %q not found for this network", mode.String())
|
||||
}
|
||||
var (
|
||||
mergeBlock = prunePoint.BlockNumber
|
||||
mergeBlockHash = prunePoint.BlockHash.Hex()
|
||||
targetBlock = prunePoint.BlockNumber
|
||||
targetBlockHash = prunePoint.BlockHash
|
||||
)
|
||||
|
||||
// Check we're far enough past merge to ensure all data is in freezer
|
||||
// Check the current freezer tail to see if pruning is needed/possible.
|
||||
freezerTail, _ := chaindb.Tail()
|
||||
if freezerTail > 0 {
|
||||
if freezerTail == targetBlock {
|
||||
log.Info("Database already pruned to target block", "tail", freezerTail)
|
||||
return nil
|
||||
}
|
||||
if freezerTail > targetBlock {
|
||||
// Database is pruned beyond the target - can't unprune.
|
||||
return fmt.Errorf("database is already pruned to block %d, which is beyond target %d. Cannot unprune. To restore history, use 'geth import-history'", freezerTail, targetBlock)
|
||||
}
|
||||
// freezerTail < targetBlock: we can prune further, continue below.
|
||||
}
|
||||
|
||||
// Check we're far enough past the target to ensure all data is in freezer.
|
||||
currentHeader := chain.CurrentHeader()
|
||||
if currentHeader == nil {
|
||||
return errors.New("current header not found")
|
||||
}
|
||||
if currentHeader.Number.Uint64() < mergeBlock+params.FullImmutabilityThreshold {
|
||||
return fmt.Errorf("chain not far enough past merge block, need %d more blocks",
|
||||
mergeBlock+params.FullImmutabilityThreshold-currentHeader.Number.Uint64())
|
||||
if currentHeader.Number.Uint64() < targetBlock+params.FullImmutabilityThreshold {
|
||||
return fmt.Errorf("chain not far enough past target block %d, need %d more blocks",
|
||||
targetBlock, targetBlock+params.FullImmutabilityThreshold-currentHeader.Number.Uint64())
|
||||
}
|
||||
|
||||
// Double-check the prune block in db has the expected hash.
|
||||
hash := rawdb.ReadCanonicalHash(chaindb, mergeBlock)
|
||||
if hash != common.HexToHash(mergeBlockHash) {
|
||||
return fmt.Errorf("merge block hash mismatch: got %s, want %s", hash.Hex(), mergeBlockHash)
|
||||
// Double-check the target block in db has the expected hash.
|
||||
hash := rawdb.ReadCanonicalHash(chaindb, targetBlock)
|
||||
if hash != targetBlockHash {
|
||||
return fmt.Errorf("target block hash mismatch: got %s, want %s", hash.Hex(), targetBlockHash.Hex())
|
||||
}
|
||||
|
||||
log.Info("Starting history pruning", "head", currentHeader.Number, "tail", mergeBlock, "tailHash", mergeBlockHash)
|
||||
log.Info("Starting history pruning", "head", currentHeader.Number, "target", targetBlock, "targetHash", targetBlockHash.Hex())
|
||||
start := time.Now()
|
||||
rawdb.PruneTransactionIndex(chaindb, mergeBlock)
|
||||
if _, err := chaindb.TruncateTail(mergeBlock); err != nil {
|
||||
rawdb.PruneTransactionIndex(chaindb, targetBlock)
|
||||
if _, err := chaindb.TruncateTail(targetBlock); err != nil {
|
||||
return fmt.Errorf("failed to truncate ancient data: %v", err)
|
||||
}
|
||||
log.Info("History pruning completed", "tail", mergeBlock, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
log.Info("History pruning completed", "tail", targetBlock, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
// TODO(s1na): what if there is a crash between the two prune operations?
|
||||
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ var (
|
|||
}
|
||||
ChainHistoryFlag = &cli.StringFlag{
|
||||
Name: "history.chain",
|
||||
Usage: `Blockchain history retention ("all" or "postmerge")`,
|
||||
Usage: `Blockchain history retention ("all", "postmerge", or "postcancun")`,
|
||||
Value: ethconfig.Defaults.HistoryMode.String(),
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -716,8 +716,12 @@ func (bc *BlockChain) loadLastState() error {
|
|||
|
||||
// initializeHistoryPruning sets bc.historyPrunePoint.
|
||||
func (bc *BlockChain) initializeHistoryPruning(latest uint64) error {
|
||||
freezerTail, _ := bc.db.Tail()
|
||||
|
||||
var (
|
||||
freezerTail, _ = bc.db.Tail()
|
||||
genesisHash = bc.genesisBlock.Hash()
|
||||
mergePoint = history.MergePrunePoints[genesisHash]
|
||||
cancunPoint = history.CancunPrunePoints[genesisHash]
|
||||
)
|
||||
switch bc.cfg.ChainHistoryMode {
|
||||
case history.KeepAll:
|
||||
if freezerTail == 0 {
|
||||
|
|
@ -725,33 +729,65 @@ func (bc *BlockChain) initializeHistoryPruning(latest uint64) error {
|
|||
}
|
||||
// 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 errors.New("unexpected database tail")
|
||||
if mergePoint != nil && freezerTail == mergePoint.BlockNumber {
|
||||
bc.historyPrunePoint.Store(mergePoint)
|
||||
return nil
|
||||
}
|
||||
bc.historyPrunePoint.Store(predefinedPoint)
|
||||
return nil
|
||||
if cancunPoint != nil && freezerTail == cancunPoint.BlockNumber {
|
||||
bc.historyPrunePoint.Store(cancunPoint)
|
||||
return nil
|
||||
}
|
||||
log.Error("Chain history database is pruned with unknown configuration", "tail", freezerTail)
|
||||
return errors.New("unexpected database tail")
|
||||
|
||||
case history.KeepPostMerge:
|
||||
if mergePoint == nil {
|
||||
return errors.New("history pruning requested for unknown network")
|
||||
}
|
||||
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.cfg.ChainHistoryMode.String()))
|
||||
log.Error(fmt.Sprintf("Run 'geth prune-history' to prune pre-merge history."))
|
||||
log.Error("Run 'geth prune-history --history.chain postmerge' to prune pre-merge history.")
|
||||
return errors.New("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())
|
||||
// Check if DB is pruned further than requested (to Cancun).
|
||||
if cancunPoint != nil && freezerTail == cancunPoint.BlockNumber {
|
||||
log.Error("Chain history database is pruned to Cancun block, but postmerge mode was requested.")
|
||||
log.Error("History cannot be unpruned. To restore history, use 'geth import-history'.")
|
||||
log.Error("If you intended to keep post-Cancun history, use '--history.chain postcancun' instead.")
|
||||
return errors.New("database pruned beyond requested history mode")
|
||||
}
|
||||
if freezerTail > 0 && freezerTail != mergePoint.BlockNumber {
|
||||
return errors.New("chain history database pruned to unknown block")
|
||||
}
|
||||
bc.historyPrunePoint.Store(mergePoint)
|
||||
return nil
|
||||
|
||||
case history.KeepPostCancun:
|
||||
if cancunPoint == nil {
|
||||
return errors.New("history pruning requested for unknown network")
|
||||
} else if freezerTail > 0 && freezerTail != predefinedPoint.BlockNumber {
|
||||
}
|
||||
// Check if already at the cancun prune point.
|
||||
if freezerTail == cancunPoint.BlockNumber {
|
||||
bc.historyPrunePoint.Store(cancunPoint)
|
||||
return nil
|
||||
}
|
||||
// Check if database needs pruning.
|
||||
if latest != 0 {
|
||||
if freezerTail == 0 {
|
||||
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cfg.ChainHistoryMode.String()))
|
||||
log.Error("Run 'geth prune-history --history.chain postcancun' to prune pre-Cancun history.")
|
||||
return errors.New("history pruning requested via configuration")
|
||||
}
|
||||
if mergePoint != nil && freezerTail == mergePoint.BlockNumber {
|
||||
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is only pruned to merge block.", bc.cfg.ChainHistoryMode.String()))
|
||||
log.Error("Run 'geth prune-history --history.chain postcancun' to prune pre-Cancun history.")
|
||||
return errors.New("history pruning requested via configuration")
|
||||
}
|
||||
log.Error("Chain history database is pruned to unknown block", "tail", freezerTail)
|
||||
return errors.New("unexpected database tail")
|
||||
}
|
||||
bc.historyPrunePoint.Store(predefinedPoint)
|
||||
// Fresh database (latest == 0), will sync from cancun point.
|
||||
bc.historyPrunePoint.Store(cancunPoint)
|
||||
return nil
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -32,10 +32,13 @@ const (
|
|||
|
||||
// KeepPostMerge sets the history pruning point to the merge activation block.
|
||||
KeepPostMerge
|
||||
|
||||
// KeepPostCancun sets the history pruning point to the Cancun (Dencun) activation block.
|
||||
KeepPostCancun
|
||||
)
|
||||
|
||||
func (m HistoryMode) IsValid() bool {
|
||||
return m <= KeepPostMerge
|
||||
return m <= KeepPostCancun
|
||||
}
|
||||
|
||||
func (m HistoryMode) String() string {
|
||||
|
|
@ -44,6 +47,8 @@ func (m HistoryMode) String() string {
|
|||
return "all"
|
||||
case KeepPostMerge:
|
||||
return "postmerge"
|
||||
case KeepPostCancun:
|
||||
return "postcancun"
|
||||
default:
|
||||
return fmt.Sprintf("invalid HistoryMode(%d)", m)
|
||||
}
|
||||
|
|
@ -64,8 +69,10 @@ func (m *HistoryMode) UnmarshalText(text []byte) error {
|
|||
*m = KeepAll
|
||||
case "postmerge":
|
||||
*m = KeepPostMerge
|
||||
case "postcancun":
|
||||
*m = KeepPostCancun
|
||||
default:
|
||||
return fmt.Errorf(`unknown sync mode %q, want "all" or "postmerge"`, text)
|
||||
return fmt.Errorf(`unknown history mode %q, want "all", "postmerge", or "postcancun"`, text)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -75,10 +82,10 @@ type PrunePoint struct {
|
|||
BlockHash common.Hash
|
||||
}
|
||||
|
||||
// PrunePoints the pre-defined history pruning cutoff blocks for known networks.
|
||||
// MergePrunePoints contains 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{
|
||||
// the given block.
|
||||
var MergePrunePoints = map[common.Hash]*PrunePoint{
|
||||
// mainnet
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 15537393,
|
||||
|
|
@ -91,6 +98,39 @@ var PrunePoints = map[common.Hash]*PrunePoint{
|
|||
},
|
||||
}
|
||||
|
||||
// CancunPrunePoints contains the pre-defined history pruning cutoff blocks for the Cancun
|
||||
// (Dencun) upgrade. They point to the first post-Cancun block. Any pruning should truncate
|
||||
// *up to* but excluding the given block.
|
||||
var CancunPrunePoints = map[common.Hash]*PrunePoint{
|
||||
// mainnet - first Cancun block (March 13, 2024)
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 19426587,
|
||||
BlockHash: common.HexToHash("0xf8e2f40d98fe5862bc947c8c83d34799c50fb344d7445d020a8a946d891b62ee"),
|
||||
},
|
||||
// sepolia - first Cancun block (January 30, 2024)
|
||||
params.SepoliaGenesisHash: {
|
||||
BlockNumber: 5187023,
|
||||
BlockHash: common.HexToHash("0x8f9753667f95418f70db36279a269ed6523cea399ecc3f4cfa2f1689a3a4b130"),
|
||||
},
|
||||
}
|
||||
|
||||
// PrunePoints is an alias for MergePrunePoints for backward compatibility.
|
||||
// Deprecated: Use GetPrunePoint or MergePrunePoints directly.
|
||||
var PrunePoints = MergePrunePoints
|
||||
|
||||
// GetPrunePoint returns the prune point for the given genesis hash and history mode.
|
||||
// Returns nil if no prune point is defined for the given combination.
|
||||
func GetPrunePoint(genesisHash common.Hash, mode HistoryMode) *PrunePoint {
|
||||
switch mode {
|
||||
case KeepPostMerge:
|
||||
return MergePrunePoints[genesisHash]
|
||||
case KeepPostCancun:
|
||||
return CancunPrunePoints[genesisHash]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// PrunedHistoryError is returned by APIs when the requested history is pruned.
|
||||
type PrunedHistoryError struct{}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue