forked from cerc-io/plugeth
core, les, light: implement timestamp based sethead and genesis rewinds
This commit is contained in:
parent
08481028fe
commit
d021157820
@ -318,7 +318,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
if diskRoot != (common.Hash{}) {
|
||||
log.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash(), "snaproot", diskRoot)
|
||||
|
||||
snapDisk, err := bc.setHeadBeyondRoot(head.NumberU64(), diskRoot, true)
|
||||
snapDisk, err := bc.setHeadBeyondRoot(head.NumberU64(), 0, diskRoot, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -328,7 +328,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
}
|
||||
} else {
|
||||
log.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash())
|
||||
if _, err := bc.setHeadBeyondRoot(head.NumberU64(), common.Hash{}, true); err != nil {
|
||||
if _, err := bc.setHeadBeyondRoot(head.NumberU64(), 0, common.Hash{}, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@ -428,7 +428,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
||||
if compat.RewindToTime > 0 {
|
||||
log.Crit("Timestamp based rewinds not implemented yet /sad")
|
||||
bc.SetHeadWithTimestamp(compat.RewindToTime)
|
||||
} else {
|
||||
bc.SetHead(compat.RewindToBlock)
|
||||
}
|
||||
@ -536,7 +536,20 @@ func (bc *BlockChain) loadLastState() error {
|
||||
// was fast synced or full synced and in which state, the method will try to
|
||||
// delete minimal data from disk whilst retaining chain consistency.
|
||||
func (bc *BlockChain) SetHead(head uint64) error {
|
||||
if _, err := bc.setHeadBeyondRoot(head, common.Hash{}, false); err != nil {
|
||||
if _, err := bc.setHeadBeyondRoot(head, 0, common.Hash{}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Send chain head event to update the transaction pool
|
||||
bc.chainHeadFeed.Send(ChainHeadEvent{Block: bc.CurrentBlock()})
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHeadWithTimestamp rewinds the local chain to a new head that has at max
|
||||
// the given timestamp. Depending on whether the node was fast synced or full
|
||||
// synced and in which state, the method will try to delete minimal data from
|
||||
// disk whilst retaining chain consistency.
|
||||
func (bc *BlockChain) SetHeadWithTimestamp(timestamp uint64) error {
|
||||
if _, err := bc.setHeadBeyondRoot(0, timestamp, common.Hash{}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Send chain head event to update the transaction pool
|
||||
@ -573,8 +586,12 @@ func (bc *BlockChain) SetSafe(block *types.Block) {
|
||||
// in which state, the method will try to delete minimal data from disk whilst
|
||||
// retaining chain consistency.
|
||||
//
|
||||
// The method also works in timestamp mode if `head == 0` but `time != 0`. In that
|
||||
// case blocks are rolled back until the new head becomes older or equal to the
|
||||
// requested time. If both `head` and `time` is 0, the chain is rewound to genesis.
|
||||
//
|
||||
// The method returns the block number where the requested root cap was found.
|
||||
func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bool) (uint64, error) {
|
||||
func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Hash, repair bool) (uint64, error) {
|
||||
if !bc.chainmu.TryLock() {
|
||||
return 0, errChainStopped
|
||||
}
|
||||
@ -588,7 +605,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
|
||||
pivot := rawdb.ReadLastPivotNumber(bc.db)
|
||||
frozen, _ := bc.db.Ancients()
|
||||
|
||||
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (uint64, bool) {
|
||||
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (*types.Header, bool) {
|
||||
// Rewind the blockchain, ensuring we don't end up with a stateless head
|
||||
// block. Note, depth equality is permitted to allow using SetHead as a
|
||||
// chain reparation mechanism without deleting any data!
|
||||
@ -669,16 +686,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
|
||||
bc.currentFastBlock.Store(newHeadFastBlock)
|
||||
headFastBlockGauge.Update(int64(newHeadFastBlock.NumberU64()))
|
||||
}
|
||||
head := bc.CurrentBlock().NumberU64()
|
||||
|
||||
var (
|
||||
headHeader = bc.CurrentBlock().Header()
|
||||
headNumber = headHeader.Number.Uint64()
|
||||
)
|
||||
// If setHead underflown the freezer threshold and the block processing
|
||||
// intent afterwards is full block importing, delete the chain segment
|
||||
// between the stateful-block and the sethead target.
|
||||
var wipe bool
|
||||
if head+1 < frozen {
|
||||
wipe = pivot == nil || head >= *pivot
|
||||
if headNumber+1 < frozen {
|
||||
wipe = pivot == nil || headNumber >= *pivot
|
||||
}
|
||||
return head, wipe // Only force wipe if full synced
|
||||
return headHeader, wipe // Only force wipe if full synced
|
||||
}
|
||||
// Rewind the header chain, deleting all block bodies until then
|
||||
delFn := func(db ethdb.KeyValueWriter, hash common.Hash, num uint64) {
|
||||
@ -705,13 +724,18 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
|
||||
// touching the header chain altogether, unless the freezer is broken
|
||||
if repair {
|
||||
if target, force := updateFn(bc.db, bc.CurrentBlock().Header()); force {
|
||||
bc.hc.SetHead(target, updateFn, delFn)
|
||||
bc.hc.SetHead(target.Number.Uint64(), updateFn, delFn)
|
||||
}
|
||||
} else {
|
||||
// Rewind the chain to the requested head and keep going backwards until a
|
||||
// block with a state is found or fast sync pivot is passed
|
||||
log.Warn("Rewinding blockchain", "target", head)
|
||||
bc.hc.SetHead(head, updateFn, delFn)
|
||||
if time > 0 {
|
||||
log.Warn("Rewinding blockchain to timestamp", "target", time)
|
||||
bc.hc.SetHeadWithTimestamp(time, updateFn, delFn)
|
||||
} else {
|
||||
log.Warn("Rewinding blockchain to block", "target", head)
|
||||
bc.hc.SetHead(head, updateFn, delFn)
|
||||
}
|
||||
}
|
||||
// Clear out any stale content from the caches
|
||||
bc.bodyCache.Purge()
|
||||
|
@ -556,7 +556,7 @@ type (
|
||||
// before head header is updated. The method will return the actual block it
|
||||
// updated the head to (missing state) and a flag if setHead should continue
|
||||
// rewinding till that forcefully (exceeded ancient limits)
|
||||
UpdateHeadBlocksCallback func(ethdb.KeyValueWriter, *types.Header) (uint64, bool)
|
||||
UpdateHeadBlocksCallback func(ethdb.KeyValueWriter, *types.Header) (*types.Header, bool)
|
||||
|
||||
// DeleteBlockContentCallback is a callback function that is called by SetHead
|
||||
// before each header is deleted.
|
||||
@ -566,15 +566,33 @@ type (
|
||||
// SetHead rewinds the local chain to a new head. Everything above the new head
|
||||
// will be deleted and the new one set.
|
||||
func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, delFn DeleteBlockContentCallback) {
|
||||
hc.setHead(head, 0, updateFn, delFn)
|
||||
}
|
||||
|
||||
// SetHeadWithTimestamp rewinds the local chain to a new head timestamp. Everything
|
||||
// above the new head will be deleted and the new one set.
|
||||
func (hc *HeaderChain) SetHeadWithTimestamp(time uint64, updateFn UpdateHeadBlocksCallback, delFn DeleteBlockContentCallback) {
|
||||
hc.setHead(0, time, updateFn, delFn)
|
||||
}
|
||||
|
||||
// setHead rewinds the local chain to a new head block or a head timestamp.
|
||||
// Everything above the new head will be deleted and the new one set.
|
||||
func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn UpdateHeadBlocksCallback, delFn DeleteBlockContentCallback) {
|
||||
var (
|
||||
parentHash common.Hash
|
||||
batch = hc.chainDb.NewBatch()
|
||||
origin = true
|
||||
)
|
||||
for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() {
|
||||
done := func(header *types.Header) bool {
|
||||
if headBlock != 0 || headTime == 0 {
|
||||
return header.Number.Uint64() <= headBlock
|
||||
}
|
||||
return header.Time <= headTime
|
||||
}
|
||||
for hdr := hc.CurrentHeader(); hdr != nil && !done(hdr); hdr = hc.CurrentHeader() {
|
||||
num := hdr.Number.Uint64()
|
||||
|
||||
// Rewind block chain to new head.
|
||||
// Rewind chain to new head
|
||||
parent := hc.GetHeader(hdr.ParentHash, num-1)
|
||||
if parent == nil {
|
||||
parent = hc.genesisHeader
|
||||
@ -591,9 +609,9 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
|
||||
markerBatch := hc.chainDb.NewBatch()
|
||||
if updateFn != nil {
|
||||
newHead, force := updateFn(markerBatch, parent)
|
||||
if force && newHead < head {
|
||||
log.Warn("Force rewinding till ancient limit", "head", newHead)
|
||||
head = newHead
|
||||
if force && ((headTime > 0 && newHead.Time < headTime) || (headTime == 0 && newHead.Number.Uint64() < headBlock)) {
|
||||
log.Warn("Force rewinding till ancient limit", "head", newHead.Number.Uint64())
|
||||
headBlock, headTime = newHead.Number.Uint64(), 0
|
||||
}
|
||||
}
|
||||
// Update head header then.
|
||||
|
@ -180,7 +180,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
|
||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
||||
if compat.RewindToTime > 0 {
|
||||
log.Crit("Timestamp based rewinds not implemented yet /sad")
|
||||
leth.blockchain.SetHeadWithTimestamp(compat.RewindToTime)
|
||||
} else {
|
||||
leth.blockchain.SetHead(compat.RewindToBlock)
|
||||
}
|
||||
|
@ -178,6 +178,17 @@ func (lc *LightChain) SetHead(head uint64) error {
|
||||
return lc.loadLastState()
|
||||
}
|
||||
|
||||
// SetHeadWithTimestamp rewinds the local chain to a new head that has at max
|
||||
// the given timestamp. Everything above the new head will be deleted and the
|
||||
// new one set.
|
||||
func (lc *LightChain) SetHeadWithTimestamp(timestamp uint64) error {
|
||||
lc.chainmu.Lock()
|
||||
defer lc.chainmu.Unlock()
|
||||
|
||||
lc.hc.SetHeadWithTimestamp(timestamp, nil, nil)
|
||||
return lc.loadLastState()
|
||||
}
|
||||
|
||||
// GasLimit returns the gas limit of the current HEAD block.
|
||||
func (lc *LightChain) GasLimit() uint64 {
|
||||
return lc.hc.CurrentHeader().GasLimit
|
||||
|
Loading…
Reference in New Issue
Block a user