diff --git a/eth/api_debug.go b/eth/api_debug.go index fa211ec1ff..a2eee3093b 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -500,6 +500,12 @@ func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumberOrHash) (*stateless.ExtW if err != nil { return &stateless.ExtWitness{}, fmt.Errorf("block %v not found", bn) } + // BlockByNumberOrHash returns a nil block without an error when the + // requested block does not exist (per the RPC spec). Guard against it + // to avoid a nil pointer dereference below. + if block == nil { + return &stateless.ExtWitness{}, fmt.Errorf("block %v not found", bn) + } parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1) if parent == nil { return &stateless.ExtWitness{}, fmt.Errorf("block %v found, but parent missing", bn) diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index 02681b49dd..f077786fcd 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -41,6 +41,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/triedb" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" @@ -340,6 +341,34 @@ func TestGetModifiedAccounts(t *testing.T) { }) } +// TestExecutionWitnessMissingBlock ensures that debug_executionWitness returns +// an error, rather than panicking, when the requested block does not exist. +// BlockByNumberOrHash returns a nil block without an error for an unknown hash, +// which previously caused a nil pointer dereference. +func TestExecutionWitnessMissingBlock(t *testing.T) { + t.Parallel() + + accounts := newAccounts(1) + genesis := &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(params.Ether)}, + }, + } + blockChain := newTestBlockChain(t, 1, genesis, func(_ int, _ *core.BlockGen) {}) + defer blockChain.Stop() + + eth := &Ethereum{blockchain: blockChain} + eth.APIBackend = &EthAPIBackend{eth: eth} + api := NewDebugAPI(eth) + + // A hash that does not correspond to any known block. This makes + // BlockByNumberOrHash return (nil, nil). + missing := rpc.BlockNumberOrHashWithHash(common.HexToHash("0xdeadbeef"), false) + _, err := api.ExecutionWitness(missing) + assert.Error(t, err, "expected an error for a missing block, got nil") +} + func TestDebugAPI_ClearTxpool(t *testing.T) { // Create test key and genesis testKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")