From 67a91c72d60763407124c5530ea807a746398fc2 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Thu, 20 Oct 2022 12:03:10 -0600 Subject: [PATCH 1/2] Update test suite to add historical rpc --- eth/tracers/api_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index a96f3efdad..0de409f797 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -23,6 +23,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum/node" "math/big" "net" "net/http" From 0e50c59da4dbd7a74bf1e59e13788dc28eac6648 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Tue, 25 Oct 2022 13:32:05 -0400 Subject: [PATCH 2/2] Add historical endpoint to TraceCall Update tests to handle ethereum.NotFound --- eth/tracers/api.go | 23 +++++++++++++++++ eth/tracers/api_test.go | 55 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 8d1cb264ab..ac313f8205 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -860,6 +860,29 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } else { return nil, errors.New("invalid arguments; neither block nor hash specified") } + + // If block still holds no value, but we have an error, then one of the two previous conditions + // was entered, meaning: + // 1. blockNrOrHash has either a valid block or hash + // 2. we don't have that block locally + if block == nil && errors.Is(err, ethereum.NotFound) && api.backend.HistoricalRPCService() != nil { + var histResult json.RawMessage + err = api.backend.HistoricalRPCService().CallContext(ctx, &histResult, "debug_traceCall", args, blockNrOrHash, config) + if err != nil && err.Error() == "not found" { + // Not found locally or in history. We need to return different errors based on the input + // in order match geth's native behavior + if hash, ok := blockNrOrHash.Hash(); ok { + return nil, fmt.Errorf("block %s %w", hash, ethereum.NotFound) + } else if number, ok := blockNrOrHash.Number(); ok { + return nil, fmt.Errorf("block #%d %w", number, ethereum.NotFound) + } + } else if err != nil { + return nil, fmt.Errorf("error querying historical RPC: %w", err) + } + + return histResult, nil + } + if err != nil { return nil, err } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 0de409f797..9ed538414f 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -23,7 +23,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/ethereum/go-ethereum/node" "math/big" "net" "net/http" @@ -52,8 +51,9 @@ import ( ) var ( - errStateNotFound = errors.New("state not found") - errBlockNotFound = errors.New("block not found") + errStateNotFound = errors.New("state not found") + errBlockNotFound = errors.New("block not found") + errFailingUpstream = errors.New("historical query failed") ) type mockHistoricalBackend struct{} @@ -85,6 +85,17 @@ func (m *mockHistoricalBackend) TraceTransaction(ctx context.Context, hash commo return nil, ethereum.NotFound } +func (m *mockHistoricalBackend) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { + num, ok := blockNrOrHash.Number() + if ok && num == 777 { + return json.RawMessage(`{"gas":21000,"failed":false,"returnValue":"777","structLogs":[]}`), nil + } + if ok && num == 12345 { + return nil, errFailingUpstream + } + return nil, ethereum.NotFound +} + func newMockHistoricalBackend(t *testing.T) string { s := rpc.NewServer() err := node.RegisterApis([]rpc.API{ @@ -324,6 +335,40 @@ func TestTraceCall(t *testing.T) { expectErr: fmt.Errorf("block #%d %w", genBlocks+1, ethereum.NotFound), //expect: nil, }, + // Optimism: Trace block on the historical chain + { + blockNumber: rpc.BlockNumber(777), + call: ethapi.TransactionArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: nil, + expectErr: nil, + expect: `{"gas":21000,"failed":false,"returnValue":"777","structLogs":[]}`, + }, + // Optimism: Trace block that doesn't exist anywhere + { + blockNumber: rpc.BlockNumber(39347856), + call: ethapi.TransactionArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: nil, + expectErr: fmt.Errorf("block #39347856 %w", ethereum.NotFound), + }, + // Optimism: Trace block with failing historical upstream + { + blockNumber: rpc.BlockNumber(12345), + call: ethapi.TransactionArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: nil, + expectErr: fmt.Errorf("error querying historical RPC: %w", errFailingUpstream), + }, // Standard JSON trace upon the latest block { blockNumber: rpc.LatestBlockNumber, @@ -369,8 +414,10 @@ func TestTraceCall(t *testing.T) { t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr) continue } + // Have to introduce this diff to reflect the fact that errors + // from the upstream will not preserve pointer equality. if err.Error() != testspec.expectErr.Error() { - t.Errorf("test %d: error mismatch, want %v, git %v", i, testspec.expectErr, err) + t.Errorf("test %d: error mismatch, want %v, got %v", i, testspec.expectErr, err) } } else { if err != nil {