Skip to content

Panic when resetting the chain after a crash #481

Description

@endale98

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
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions