Skip to content

Commit

Permalink
Fix some panic cuased by nil block, statedb, header (ethereum#578)
Browse files Browse the repository at this point in the history
* fix panic during rollback

* eth/hooks: check nil stateDB to fix issue ethereum#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
  • Loading branch information
gzliudan authored Aug 3, 2024
1 parent e9e94e6 commit 4976b7c
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 11 deletions.
2 changes: 2 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
20 changes: 15 additions & 5 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
"os"
"path/filepath"
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}

Expand Down
30 changes: 28 additions & 2 deletions eth/api_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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")
}
Expand Down
9 changes: 7 additions & 2 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()})
}
Expand All @@ -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")
}
}

Expand Down
7 changes: 5 additions & 2 deletions eth/hooks/engine_v1_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
23 changes: 23 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:])
Expand Down

0 comments on commit 4976b7c

Please sign in to comment.