|
62 | 62 | headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil) |
63 | 63 | headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil) |
64 | 64 |
|
| 65 | + tailBlockGauge = metrics.NewRegisteredGauge("chain/tail/block", nil) |
| 66 | + tailHeaderGauge = metrics.NewRegisteredGauge("chain/tail/header", nil) |
| 67 | + |
65 | 68 | chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil) |
66 | 69 |
|
67 | 70 | accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil) |
@@ -248,6 +251,7 @@ type BlockChain struct { |
248 | 251 | currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync |
249 | 252 | currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block |
250 | 253 | currentSafeBlock atomic.Pointer[types.Header] // Latest (consensus) safe block |
| 254 | + earliestBlock atomic.Pointer[types.Header] // Earliest head of the chain |
251 | 255 |
|
252 | 256 | bodyCache *lru.Cache[common.Hash, *types.Body] |
253 | 257 | bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] |
@@ -524,10 +528,24 @@ func (bc *BlockChain) loadLastState() error { |
524 | 528 | headHeader = header |
525 | 529 | } |
526 | 530 | } |
| 531 | + |
| 532 | + tailNumber := uint64(0) |
| 533 | + if dataConfig := rawdb.ReadChainDataConfig(bc.db); dataConfig != nil && dataConfig.DesiredChainDataStart != nil { |
| 534 | + tailNumber = *dataConfig.DesiredChainDataStart |
| 535 | + } |
| 536 | + tailBlock := bc.GetBlockByNumber(tailNumber) |
| 537 | + if tailBlock == nil { |
| 538 | + // Corrupt or empty database, init from scratch |
| 539 | + log.Warn("Tail block missing, resetting chain", "number", tailNumber) |
| 540 | + return bc.Reset() |
| 541 | + } |
| 542 | + |
527 | 543 | bc.hc.SetCurrentHeader(headHeader) |
| 544 | + bc.hc.SetEarliestHeader(tailBlock.Header()) |
528 | 545 |
|
529 | 546 | // Restore the last known head snap block |
530 | 547 | bc.currentSnapBlock.Store(headBlock.Header()) |
| 548 | + bc.earliestBlock.Store(tailBlock.Header()) |
531 | 549 | headFastBlockGauge.Update(int64(headBlock.NumberU64())) |
532 | 550 |
|
533 | 551 | if head := rawdb.ReadHeadFastBlockHash(bc.db); head != (common.Hash{}) { |
@@ -900,6 +918,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha |
900 | 918 | // removed in the hc.SetHead function. |
901 | 919 | rawdb.DeleteBody(db, hash, num) |
902 | 920 | rawdb.DeleteReceipts(db, hash, num) |
| 921 | + rawdb.DeleteTxBloom(db, hash, num) |
903 | 922 | } |
904 | 923 | // Todo(rjl493456442) txlookup, bloombits, etc |
905 | 924 | } |
@@ -936,6 +955,8 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha |
936 | 955 | log.Error("SetHead invalidated finalized block") |
937 | 956 | bc.SetFinalized(nil) |
938 | 957 | } |
| 958 | + |
| 959 | + log.Info("Rewinding blockchain complete", "target", head) |
939 | 960 | return rootNumber, bc.loadLastState() |
940 | 961 | } |
941 | 962 |
|
@@ -1152,7 +1173,10 @@ func (bc *BlockChain) Stop() { |
1152 | 1173 | } |
1153 | 1174 | } |
1154 | 1175 | for !bc.triegc.Empty() { |
1155 | | - triedb.Dereference(bc.triegc.PopItem()) |
| 1176 | + // @sq-change |
| 1177 | + root, number := bc.triegc.Pop() |
| 1178 | + triedb.Dereference(root) |
| 1179 | + triedb.SetTail(uint64(-number)) |
1156 | 1180 | } |
1157 | 1181 | if _, nodes, _ := triedb.Size(); nodes != 0 { // all memory is contained within the nodes return for hashdb |
1158 | 1182 | log.Error("Dangling trie nodes after full cleanup") |
@@ -1526,6 +1550,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. |
1526 | 1550 | break |
1527 | 1551 | } |
1528 | 1552 | bc.triedb.Dereference(root) |
| 1553 | + bc.triedb.SetTail(uint64(-number)) |
1529 | 1554 | } |
1530 | 1555 | return nil |
1531 | 1556 | } |
@@ -1716,6 +1741,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness |
1716 | 1741 | log.Debug("Abort during block processing") |
1717 | 1742 | break |
1718 | 1743 | } |
| 1744 | + |
| 1745 | + desiredChainEnd := rawdb.ReadChainDataConfig(bc.db).DesiredChainDataEnd |
| 1746 | + if desiredChainEnd != nil && block.Number().Uint64() > *desiredChainEnd { |
| 1747 | + log.Info("Block after data end, skipping") |
| 1748 | + continue |
| 1749 | + } |
1719 | 1750 | // If the block is known (in the middle of the chain), it's a special case for |
1720 | 1751 | // Clique blocks where they can share state among each other, so importing an |
1721 | 1752 | // older block might complete the state of the subsequent one. In this case, |
@@ -2521,3 +2552,113 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) { |
2521 | 2552 | func (bc *BlockChain) GetTrieFlushInterval() time.Duration { |
2522 | 2553 | return time.Duration(bc.flushInterval.Load()) |
2523 | 2554 | } |
| 2555 | + |
| 2556 | +// ----- @sq changed |
| 2557 | +func (bc *BlockChain) truncateStateTail(newTail uint64) error { |
| 2558 | + currentTail := bc.triedb.GetTail() |
| 2559 | + if newTail <= currentTail { |
| 2560 | + return nil |
| 2561 | + } |
| 2562 | + for height := currentTail; height < newTail; height++ { |
| 2563 | + header := bc.GetHeaderByNumber(height) |
| 2564 | + // This isn't right,.height cant be used as a priority |
| 2565 | + bc.triegc.Push(header.Root, -int64(height)) |
| 2566 | + } |
| 2567 | + // TODO: need to enforce trie truncate on local disk |
| 2568 | + return nil |
| 2569 | +} |
| 2570 | + |
| 2571 | +func (bc *BlockChain) SetTail(height uint64) error { |
| 2572 | + delFn := func(db ethdb.KeyValueWriter, hash common.Hash, num uint64) { |
| 2573 | + frozen, _ := bc.db.Ancients() |
| 2574 | + |
| 2575 | + if num+1 <= frozen { |
| 2576 | + // Headerchain should clear out all relevant ancients data |
| 2577 | + log.Crit("delFn called on frozen data") |
| 2578 | + } else { |
| 2579 | + // Remove body and receipts from the active store, remainder will be cleared in the hc.SetTail |
| 2580 | + rawdb.DeleteBody(db, hash, num) |
| 2581 | + rawdb.DeleteReceipts(db, hash, num) |
| 2582 | + rawdb.DeleteTxBloom(db, hash, num) |
| 2583 | + } |
| 2584 | + // Todo(stwiname) from equivalent in SetHead.delFn |
| 2585 | + // Todo(rjl493456442) txlookup, bloombits, etc |
| 2586 | + } |
| 2587 | + |
| 2588 | + if err := bc.hc.SetTail(height, delFn); err != nil { |
| 2589 | + return err |
| 2590 | + } |
| 2591 | + |
| 2592 | + // TODO delete relevant state |
| 2593 | + // bc.triedb.TruncateTail(num) |
| 2594 | + |
| 2595 | + // Clear out any stale content from the caches |
| 2596 | + bc.bodyCache.Purge() |
| 2597 | + bc.bodyRLPCache.Purge() |
| 2598 | + bc.receiptsCache.Purge() |
| 2599 | + bc.blockCache.Purge() |
| 2600 | + bc.txLookupCache.Purge() |
| 2601 | + |
| 2602 | + return nil |
| 2603 | +} |
| 2604 | + |
| 2605 | +func (bc *BlockChain) SetShardStartHeight(height uint64) error { |
| 2606 | + config := rawdb.ReadChainDataConfig(bc.db) |
| 2607 | + |
| 2608 | + if config.DesiredChainDataEnd != nil && *config.DesiredChainDataEnd <= height { |
| 2609 | + return fmt.Errorf("Start height cannot be after end height") |
| 2610 | + } |
| 2611 | + |
| 2612 | + if config.DesiredChainDataStart != nil && *config.DesiredChainDataStart > height { |
| 2613 | + return fmt.Errorf("Start height cannot be decreased, there is no way to recover state") |
| 2614 | + } |
| 2615 | + |
| 2616 | + if err := bc.SetTail(height); err != nil { |
| 2617 | + return err |
| 2618 | + } |
| 2619 | + |
| 2620 | + // TODO perisisting this should happen with batch in SetTail |
| 2621 | + config.DesiredChainDataStart = &height |
| 2622 | + rawdb.WriteChainDataConfig(bc.db, config) |
| 2623 | + |
| 2624 | + return nil |
| 2625 | +} |
| 2626 | + |
| 2627 | +func (bc *BlockChain) SetShardEndHeight(height *uint64) error { |
| 2628 | + config := rawdb.ReadChainDataConfig(bc.db) |
| 2629 | + |
| 2630 | + if config.DesiredChainDataStart != nil && *config.DesiredChainDataStart >= *height { |
| 2631 | + return fmt.Errorf("End height cannot be before start height") |
| 2632 | + } |
| 2633 | + |
| 2634 | + if height != nil && bc.CurrentHeader().Number.Uint64() > *height { |
| 2635 | + // rollback |
| 2636 | + if err := bc.SetHead(*height); err != nil { |
| 2637 | + return err |
| 2638 | + } |
| 2639 | + |
| 2640 | + // TODO truncate state |
| 2641 | + |
| 2642 | + |
| 2643 | + // type freezer interface { |
| 2644 | + // Freeze(threshold uint64) error |
| 2645 | + // } |
| 2646 | + // err = api.eth.chainDb.(freezer).Freeze(0) |
| 2647 | + // if err != nil { |
| 2648 | + // return false, err |
| 2649 | + // } |
| 2650 | + // TODO truncate state head |
| 2651 | + // err = api.eth.blockchain.TrieDB().SetHead(*height) |
| 2652 | + // if err != nil { |
| 2653 | + // return false, err |
| 2654 | + // } |
| 2655 | + |
| 2656 | + // TODO call blockchain.Stop()? |
| 2657 | + } |
| 2658 | + // else blockchain.go will limit this once the desired height is reached |
| 2659 | + |
| 2660 | + config.DesiredChainDataEnd = height |
| 2661 | + rawdb.WriteChainDataConfig(bc.db, config) |
| 2662 | + |
| 2663 | + return nil |
| 2664 | +} |
0 commit comments