Fix some panic cuased by nil block, statedb, header (#578)

* fix panic during rollback

* eth/hooks: check nil stateDB to fix issue #271

* internal/ethapi: fix eth_call crash

* all: check nil statedb

* eth: check nil block for tracer api

* internal/ethapi: check nil header and block
This commit is contained in:
Daniel Liu 2024-08-03 08:05:53 +08:00 committed by GitHub
parent e9e94e640d
commit 4976b7cbb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 80 additions and 11 deletions

View file

@ -2584,6 +2584,8 @@ func (bc *BlockChain) UpdateM1() error {
if err != nil {
return err
}
} else if stateDB == nil {
return errors.New("nil stateDB in UpdateM1")
} else {
candidates = state.GetCandidates(stateDB)
}

View file

@ -20,7 +20,6 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
"os"
"path/filepath"
@ -443,7 +442,11 @@ func (b *EthApiBackend) GetVotersRewards(masternodeAddr common.Address) map[comm
state, err := chain.StateAt(lastCheckpointBlock.Root())
if err != nil {
fmt.Println("ERROR Trying to getting state at", lastCheckpointNumber, " Error ", err)
log.Error("fail to get state in GetVotersRewards", "lastCheckpointNumber", lastCheckpointNumber, "err", err)
return nil
}
if state == nil {
log.Error("fail to get state in GetVotersRewards", "lastCheckpointNumber", lastCheckpointNumber)
return nil
}
@ -503,7 +506,11 @@ func (b *EthApiBackend) GetVotersCap(checkpoint *big.Int, masterAddr common.Addr
state, err := chain.StateAt(checkpointBlock.Root())
if err != nil {
fmt.Println("ERROR Trying to getting state at", checkpoint, " Error ", err)
log.Error("fail to get state in GetVotersCap", "checkpoint", checkpoint, "err", err)
return nil
}
if state != nil {
log.Error("fail to get state in GetVotersCap", "checkpoint", checkpoint)
return nil
}
@ -533,9 +540,12 @@ func (b *EthApiBackend) GetEpochDuration() *big.Int {
func (b *EthApiBackend) GetMasternodesCap(checkpoint uint64) map[common.Address]*big.Int {
checkpointBlock := b.eth.blockchain.GetBlockByNumber(checkpoint)
state, err := b.eth.blockchain.StateAt(checkpointBlock.Root())
if err != nil {
fmt.Println("ERROR Trying to getting state at", checkpoint, " Error ", err)
log.Error("fail to get state in GetMasternodesCap", "checkpoint", checkpoint, "err", err)
return nil
}
if state == nil {
log.Error("fail to get state in GetMasternodesCap", "checkpoint", checkpoint)
return nil
}

View file

@ -106,6 +106,32 @@ type txTraceTask struct {
index int // Transaction offset in the block
}
// blockByNumber is the wrapper of the chain access function offered by the backend.
// It will return an error if the block is not found.
func (api *PrivateDebugAPI) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
block, err := api.eth.ApiBackend.BlockByNumber(ctx, number)
if err != nil {
return nil, err
}
if block == nil {
return nil, fmt.Errorf("block #%d not found", number)
}
return block, nil
}
// blockByHash is the wrapper of the chain access function offered by the backend.
// It will return an error if the block is not found.
func (api *PrivateDebugAPI) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
block, err := api.eth.ApiBackend.BlockByHash(ctx, hash)
if err != nil {
return nil, err
}
if block == nil {
return nil, fmt.Errorf("block %s not found", hash.Hex())
}
return block, nil
}
// TraceChain returns the structured logs created during the execution of EVM
// between two blocks (excluding start) and returns them as a JSON object.
func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) {
@ -640,7 +666,7 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.CallArgs,
block *types.Block
)
if hash, ok := blockNrOrHash.Hash(); ok {
block, err = api.eth.ApiBackend.BlockByHash(ctx, hash)
block, err = api.blockByHash(ctx, hash)
} else if number, ok := blockNrOrHash.Number(); ok {
if number == rpc.PendingBlockNumber {
// We don't have access to the miner here. For tracing 'future' transactions,
@ -650,7 +676,7 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.CallArgs,
// of what the next actual block is likely to contain.
return nil, errors.New("tracing on top of pending is not supported")
}
block, err = api.eth.ApiBackend.BlockByNumber(ctx, number)
block, err = api.blockByNumber(ctx, number)
} else {
return nil, errors.New("invalid arguments; neither block nor hash specified")
}

View file

@ -192,11 +192,14 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
eth.orderPool = core.NewOrderPool(eth.chainConfig, eth.blockchain)
eth.lendingPool = core.NewLendingPool(eth.chainConfig, eth.blockchain)
if common.RollbackHash != common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000") {
if common.RollbackHash != (common.Hash{}) {
curBlock := eth.blockchain.CurrentBlock()
if curBlock == nil {
log.Warn("not find current block when rollback")
}
prevBlock := eth.blockchain.GetBlockByHash(common.RollbackHash)
if curBlock.NumberU64() > prevBlock.NumberU64() {
if curBlock != nil && prevBlock != nil && curBlock.NumberU64() > prevBlock.NumberU64() {
for ; curBlock != nil && curBlock.NumberU64() != prevBlock.NumberU64(); curBlock = eth.blockchain.GetBlock(curBlock.ParentHash(), curBlock.NumberU64()-1) {
eth.blockchain.Rollback([]common.Hash{curBlock.Hash()})
}
@ -208,6 +211,8 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
log.Crit("Err Rollback", "err", err)
return nil, err
}
} else {
log.Error("skip SetHead because target block is nil when rollback")
}
}

View file

@ -228,11 +228,14 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
)
stateDB, err := bc.StateAt(bc.GetBlockByHash(block).Root())
candidateAddresses = state.GetCandidates(stateDB)
if err != nil {
return nil, err
}
if stateDB == nil {
return nil, errors.New("nil stateDB in HookGetSignersFromContract")
}
candidateAddresses = state.GetCandidates(stateDB)
for _, address := range candidateAddresses {
v, err := validator.GetCandidateCap(opts, address)
if err != nil {

View file

@ -897,6 +897,10 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd
result[fieldSuccess] = false
return result, err
}
if statedb == nil {
result[fieldSuccess] = false
return result, errors.New("nil statedb in GetCandidateStatus")
}
candidatesAddresses := state.GetCandidates(statedb)
candidates = make([]utils.Masternode, 0, len(candidatesAddresses))
for _, address := range candidatesAddresses {
@ -1052,6 +1056,10 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch
result[fieldSuccess] = false
return result, err
}
if statedb == nil {
result[fieldSuccess] = false
return result, errors.New("nil statedb in GetCandidates")
}
candidatesAddresses := state.GetCandidates(statedb)
candidates = make([]utils.Masternode, 0, len(candidatesAddresses))
for _, address := range candidatesAddresses {
@ -1305,6 +1313,9 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
if statedb == nil || err != nil {
return nil, 0, false, err, nil
}
if header == nil {
return nil, 0, false, errors.New("nil header in DoCall"), nil
}
if err := overrides.Apply(statedb); err != nil {
return nil, 0, false, err, nil
}
@ -1327,6 +1338,9 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
if err != nil {
return nil, 0, false, err, nil
}
if block == nil {
return nil, 0, false, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()), nil
}
author, err := b.GetEngine().Author(block.Header())
if err != nil {
return nil, 0, false, err, nil
@ -1948,6 +1962,9 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
if err != nil {
return nil, 0, nil, err
}
if block == nil {
return nil, 0, nil, fmt.Errorf("nil block in AccessList: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex())
}
author, err := b.GetEngine().Author(block.Header())
if err != nil {
return nil, 0, nil, err
@ -3617,10 +3634,16 @@ func GetSignersFromBlocks(b Backend, blockNumber uint64, blockHash common.Hash,
if err != nil {
return addrs, err
}
if header == nil {
return addrs, errors.New("nil header in GetSignersFromBlocks")
}
blockData, err := b.BlockByNumber(nil, rpc.BlockNumber(i))
if err != nil {
return addrs, err
}
if blockData == nil {
return addrs, errors.New("nil blockData in GetSignersFromBlocks")
}
signTxs := engine.CacheSigningTxs(header.Hash(), blockData.Transactions())
for _, signtx := range signTxs {
blkHash := common.BytesToHash(signtx.Data()[len(signtx.Data())-32:])