From 533392083440e75a5063125ac6c496a7acf81d2a Mon Sep 17 00:00:00 2001 From: Maurelian Date: Mon, 31 Oct 2022 20:03:42 -0400 Subject: [PATCH] Add historical endpoint to TraceTransaction (#20) Co-authored-by: Matthew Slipper --- eth/tracers/api.go | 34 +++++++++++++++++++++---- eth/tracers/api_test.go | 56 +++++++++++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 5d9cf923d7fc..a39d88605ade 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -28,6 +28,7 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" @@ -137,7 +138,7 @@ func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*typ return nil, err } if block == nil { - return nil, fmt.Errorf("block #%d not found", number) + return nil, fmt.Errorf("block #%d %w", number, ethereum.NotFound) } return block, nil } @@ -150,7 +151,7 @@ func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block return nil, err } if block == nil { - return nil, fmt.Errorf("block %s not found", hash.Hex()) + return nil, fmt.Errorf("block %s %w", hash.Hex(), ethereum.NotFound) } return block, nil } @@ -455,7 +456,14 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // EVM and returns them as a JSON object. func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { block, err := api.blockByNumber(ctx, number) - if err != nil { + if errors.Is(err, ethereum.NotFound) && api.backend.HistoricalRPCService() != nil { + var histResult []*txTraceResult + err = api.backend.HistoricalRPCService().CallContext(ctx, &histResult, "debug_traceBlockByNumber", number, config) + if err != nil && err.Error() == "not found" { + return nil, fmt.Errorf("block #%d %w", number, ethereum.NotFound) + } + return histResult, err + } else if err != nil { return nil, err } return api.traceBlock(ctx, block, config) @@ -465,7 +473,14 @@ func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, // EVM and returns them as a JSON object. func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { block, err := api.blockByHash(ctx, hash) - if err != nil { + if errors.Is(err, ethereum.NotFound) && api.backend.HistoricalRPCService() != nil { + var histResult []*txTraceResult + err = api.backend.HistoricalRPCService().CallContext(ctx, &histResult, "debug_traceBlockByHash", hash, config) + if err != nil && err.Error() == "not found" { + return nil, fmt.Errorf("block #%d %w", hash, ethereum.NotFound) + } + return histResult, err + } else if err != nil { return nil, err } return api.traceBlock(ctx, block, config) @@ -807,10 +822,19 @@ func containsTx(block *types.Block, hash common.Hash) bool { // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { - _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) + // GetTransaction returns 0 for the blocknumber if the transaction is not found + tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) if err != nil { return nil, err } + if tx == nil { + var histResult []*txTraceResult + err = api.backend.HistoricalRPCService().CallContext(ctx, &histResult, "debug_traceTransaction", hash, config) + if err != nil && err.Error() == "not found" { + return nil, fmt.Errorf("transaction %s %w", hash, ethereum.NotFound) + } + return histResult, err + } // It shouldn't happen in practice. if blockNumber == 0 { return nil, errors.New("genesis is not traceable") diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 4f366c8293c6..231dba72642b 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -32,8 +32,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -48,25 +46,50 @@ import ( "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) var ( - errStateNotFound = errors.New("state not found") - errBlockNotFound = errors.New("block not found") - errTransactionNotFound = errors.New("transaction not found") + errStateNotFound = errors.New("state not found") + errBlockNotFound = errors.New("block not found") ) type mockHistoricalBackend struct{} -func (m *mockHistoricalBackend) Dummy() {} // dummy RPC method +func (m *mockHistoricalBackend) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { + if hash == common.HexToHash("0xabba") { + result := make([]*txTraceResult, 1) + result[0] = &txTraceResult{Result: "0xabba"} + return result, nil + } + return nil, ethereum.NotFound +} + +func (m *mockHistoricalBackend) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { + if number == 999 { + result := make([]*txTraceResult, 1) + result[0] = &txTraceResult{Result: "0xabba"} + return result, nil + } + return nil, ethereum.NotFound +} + +func (m *mockHistoricalBackend) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { + if hash == common.HexToHash("0xACDC") { + result := make([]*txTraceResult, 1) + result[0] = &txTraceResult{Result: "0x8888"} + return result, nil + } + return nil, ethereum.NotFound +} func newMockHistoricalBackend(t *testing.T) string { s := rpc.NewServer() err := node.RegisterApis([]rpc.API{ { - Namespace: "eth", + Namespace: "debug", Service: new(mockHistoricalBackend), Public: true, Authenticated: false, @@ -172,7 +195,7 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash) if tx == nil { - return nil, common.Hash{}, 0, 0, errTransactionNotFound + return nil, common.Hash{}, 0, 0, nil } return tx, hash, blockNumber, index, nil } @@ -424,6 +447,18 @@ func TestTraceTransaction(t *testing.T) { }) { t.Error("Transaction tracing result is different") } + + // test TraceTransaction for a historical transaction + result2, err := api.TraceTransaction(context.Background(), common.HexToHash("0xACDC"), nil) + resBytes, _ := json.Marshal(result2) + have2 := string(resBytes) + if err != nil { + t.Errorf("want no error, have %v", err) + } + want2 := `[{"result":"0x8888"}]` + if have2 != want2 { + t.Errorf("test result mismatch, have\n%v\n, want\n%v\n", have2, want2) + } } func TestTraceBlock(t *testing.T) { @@ -472,6 +507,11 @@ func TestTraceBlock(t *testing.T) { blockNumber: rpc.BlockNumber(genBlocks + 1), expectErr: fmt.Errorf("block #%d %w", genBlocks+1, ethereum.NotFound), }, + // Optimism: Trace block on the historical chain + { + blockNumber: rpc.BlockNumber(999), + want: `[{"result":"0xabba"}]`, + }, // Trace latest block { blockNumber: rpc.LatestBlockNumber,