When restarting a node that previously crashed at a certain point, the loadLastState function may encounter a situation where head := GetHeadBlockHash(bc.db) is nil (head == (common.Hash{})) or currentBlock := bc.GetBlockByHash(head) is nil. This leads to an empty database or a missing head block, triggering a chain reset. However, during the reset process, specifically at if err := bc.SetHead(0); err != nil { ... } in the ResetWithGenesisBlock function in blockchain.go, causes a panic due to an unhandled nil return type:
panic: interface conversion: interface {} is nil, not *types.Block
After investigate, this panic error from :
// CurrentBlock retrieves the current head block of the canonical chain. The
// block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentBlock() *types.Block {
return bc.currentBlock.Load().(*types.Block)
}
// CurrentFastBlock retrieves the current fast-sync head block of the canonical
// chain. The block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentFastBlock() *types.Block {
return bc.currentFastBlock.Load().(*types.Block)
}
Issue Description
Summary
func (bc *BlockChain) SetHead(head uint64) error {
log.Warn("Rewinding blockchain", "target", head)
bc.mu.Lock()
defer bc.mu.Unlock()
...
// Rewind the block chain, ensuring we don't end up with a stateless head block
if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
}
...
When invoking the SetHead() method in blockchain.go , the application encounters a panic due to an unsafe type assertion in the CurrentBlock() method. Specifically, currentBlock.Load() returns nil, and the subsequent assertion to *types.Block fails, cause the panic.
Suggested Fix
Add a nil check for CurrentBlock() && CurrentFastBlock() in the blockchain.go to handle the nil return type gracefully and avoid the panic.
func (bc *BlockChain) CurrentBlock() *types.Block {
currentBlockInterface := bc.currentBlock.Load()
if currentBlockInterface == nil {
return nil
}
currentBlock, ok := currentBlockInterface.(*types.Block)
if !ok {
return nil
}
return currentBlock
}
func (bc *BlockChain) CurrentFastBlock() *types.Block {
currentFastBlockInterface := bc.currentFastBlock.Load()
if currentFastBlockInterface == nil {
return nil
}
currentFastBlock, ok := currentFastBlockInterface.(*types.Block)
if !ok {
return nil
}
return currentFastBlock
}
When restarting a node that previously crashed at a certain point, the
loadLastStatefunction may encounter a situation wherehead := GetHeadBlockHash(bc.db)is nil (head == (common.Hash{})) orcurrentBlock := bc.GetBlockByHash(head)is nil. This leads to an empty database or a missing head block, triggering a chain reset. However, during the reset process, specifically atif err := bc.SetHead(0); err != nil { ... }in theResetWithGenesisBlockfunction inblockchain.go, causes a panic due to an unhandled nil return type:After investigate, this panic error from :
Issue Description
Summary
When invoking the
SetHead()method in blockchain.go , the application encounters a panic due to an unsafe type assertion in theCurrentBlock()method. Specifically,currentBlock.Load()returns nil, and the subsequent assertion to *types.Block fails, cause the panic.Suggested Fix
Add a nil check for
CurrentBlock()&&CurrentFastBlock()in theblockchain.goto handle the nil return type gracefully and avoid the panic.