From c814e829ee2eab20d1ac2fa3d7e68b28d1577816 Mon Sep 17 00:00:00 2001 From: ramil Date: Wed, 30 Jun 2021 04:17:12 +0300 Subject: [PATCH] #11 support params in debug_TraceTransaction method --- pkg/eth/debug.go | 95 +++++++++++++++++++++++++++++----------- test/integration_test.go | 81 ++++++++++++++++++++++++++-------- 2 files changed, 131 insertions(+), 45 deletions(-) diff --git a/pkg/eth/debug.go b/pkg/eth/debug.go index e1cf43f..c5a83b3 100644 --- a/pkg/eth/debug.go +++ b/pkg/eth/debug.go @@ -2,27 +2,37 @@ package eth import ( "context" + "errors" "fmt" "math/big" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/rpc" - "github.com/vulcanize/ipld-eth-server/pkg/eth" + ipldEth "github.com/vulcanize/ipld-eth-server/pkg/eth" "github.com/vulcanize/tracing-api/pkg/cache" "github.com/vulcanize/tracing-api/pkg/eth/tracer" ) +const ( + // defaultTraceTimeout is the amount of time a single transaction can execute + // by default before being forcefully aborted. + defaultTraceTimeout = 5 * time.Second +) + type DebugAPI struct { // Local db backend - backend *eth.Backend + backend *ipldEth.Backend cache *cache.Service } -func NewDebugAPI(b *eth.Backend, cache *cache.Service) *DebugAPI { +func NewDebugAPI(b *ipldEth.Backend, cache *cache.Service) *DebugAPI { return &DebugAPI{b, cache} } @@ -34,7 +44,7 @@ func (api *DebugAPI) WriteTxTraceGraph(ctx context.Context, hash common.Hash) (* return nil, api.cache.SaveTxTraceGraph(data) } -func (api *DebugAPI) prepareEvm(ctx context.Context, hash common.Hash, ttracer vm.Tracer) (*vm.EVM, *transaction, error) { +func (api *DebugAPI) prepareEvm(ctx context.Context, hash common.Hash, tracer vm.Tracer) (*vm.EVM, *transaction, error) { tx, _, blockNum, txIndex, err := api.backend.GetTransaction(ctx, hash) if err != nil { return nil, nil, err @@ -76,7 +86,7 @@ func (api *DebugAPI) prepareEvm(ctx context.Context, hash common.Hash, ttracer v cfg := api.backend.Config.VmConfig cfg.Debug = true - cfg.Tracer = ttracer + cfg.Tracer = tracer evm := vm.NewEVM(vmctx, txContext, statedb, api.backend.Config.ChainConfig, cfg) internalTx := transaction{ @@ -123,35 +133,68 @@ func (api *DebugAPI) TxTraceGraph(ctx context.Context, hash common.Hash) (*cache }, nil } -func (api *DebugAPI) TraceTransaction(ctx context.Context, hash common.Hash) (*ExecutionResult, error) { - degugger := vm.NewStructLogger(&vm.LogConfig{ - DisableMemory: false, - DisableStack: false, - DisableStorage: false, - DisableReturnData: false, - Debug: true, - Limit: 0, - }) +func (api *DebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *eth.TraceConfig) (interface{}, error) { + var ( + tracer vm.Tracer + err error + ) + switch { + case config != nil && config.Tracer != nil: + // Define a meaningful timeout of a single transaction trace + timeout := defaultTraceTimeout + if config.Timeout != nil { + if timeout, err = time.ParseDuration(*config.Timeout); err != nil { + return nil, err + } + } + // Constuct the JavaScript tracer to execute with + if tracer, err = tracers.New(*config.Tracer); err != nil { + return nil, err + } + // Handle timeouts and RPC cancellations + deadlineCtx, cancel := context.WithTimeout(ctx, timeout) + go func() { + <-deadlineCtx.Done() + tracer.(*tracers.Tracer).Stop(errors.New("execution timeout")) + }() + defer cancel() + + case config == nil: + tracer = vm.NewStructLogger(nil) + + default: + tracer = vm.NewStructLogger(config.LogConfig) + } - evm, tx, err := api.prepareEvm(ctx, hash, degugger) + evm, tx, err := api.prepareEvm(ctx, hash, tracer) if err != nil { return nil, err } result, err := core.ApplyMessage(evm, tx.Message, new(core.GasPool).AddGas(math.MaxUint64)) if err != nil { - return nil, err + return nil, fmt.Errorf("tracing failed: %v", err) } - returnVal := fmt.Sprintf("%x", result.Return()) - if len(result.Revert()) > 0 { - returnVal = fmt.Sprintf("%x", result.Revert()) + // Depending on the tracer type, format and return the output + switch tracer := tracer.(type) { + case *vm.StructLogger: + // If the result contains a revert reason, return it. + returnVal := fmt.Sprintf("%x", result.Return()) + if len(result.Revert()) > 0 { + returnVal = fmt.Sprintf("%x", result.Revert()) + } + return &ExecutionResult{ + Gas: result.UsedGas, + Failed: result.Failed(), + ReturnValue: returnVal, + StructLogs: FormatLogs(tracer.StructLogs()), + }, nil + + case *tracers.Tracer: + return tracer.GetResult() + + default: + panic(fmt.Sprintf("bad tracer type %T", tracer)) } - - return &ExecutionResult{ - Gas: result.UsedGas, - Failed: result.Failed(), - ReturnValue: returnVal, - StructLogs: FormatLogs(degugger.StructLogs()), - }, nil } diff --git a/test/integration_test.go b/test/integration_test.go index f7a3ade..73c881f 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -6,8 +6,11 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth" "io/ioutil" "net/http" + "strconv" "strings" "testing" "time" @@ -330,41 +333,80 @@ func testTxTraceGraph(hash string) func(t *testing.T) { } } -func testTraceTransaction(hash string) func(t *testing.T) { - return func(t *testing.T) { - requestBody := fmt.Sprintf( - `{ +func callDebugTraceTransaction(hash string, url string, config *eth.TraceConfig) ([]byte, error) { + var requestBody string + requestTemplate := `{ "jsonrpc": "2.0", "id": 0, "method": "debug_traceTransaction", - "params": ["%s"] - }`, - hash, - ) + "params": ["%s", %s] + }` + if config == nil { + requestBody = fmt.Sprintf(requestTemplate, hash, "{}") + } else { + paramStr := fmt.Sprintf("{\"disableStorage\": %s, \"disableMemory\": %s, \"disableStack\": %s}", strconv.FormatBool(config.DisableStorage), + strconv.FormatBool(config.DisableMemory), strconv.FormatBool(config.DisableStack)) + requestBody = fmt.Sprintf(requestTemplate, hash, paramStr) + } + + res, err := http.Post(url, "application/json", strings.NewReader(requestBody)) + if err != nil { + return nil, err + } + defer res.Body.Close() + + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + return data, nil +} + +func testTraceTransaction(hash string) func(t *testing.T) { + return func(t *testing.T) { t.Logf("call tracing-api debug_traceTransaction") - tracingApiRes, err := http.Post("http://127.0.0.1:8083", "application/json", strings.NewReader(requestBody)) + rawTracingApiData, err := callDebugTraceTransaction(hash, "http://127.0.0.1:8083", nil) if err != nil { - t.Fatalf(" error: %s", err) + t.Fatalf(" error: %s", err) } - defer tracingApiRes.Body.Close() + t.Logf(" done") - t.Logf(" read data") - rawTracingApiData, err := ioutil.ReadAll(tracingApiRes.Body) + t.Logf("call geth debug_traceTransaction") + rawGethData, err := callDebugTraceTransaction(hash, "http://127.0.0.1:8545", nil) if err != nil { t.Fatalf(" error: %s", err) } t.Logf(" done") - t.Logf("call geth debug_traceTransaction") - gethRes, err := http.Post("http://127.0.0.1:8545", "application/json", strings.NewReader(requestBody)) + if !bytes.Equal(rawTracingApiData, rawGethData) { + t.Error("bad tracing api data") + } + } +} + +func testTraceTransactionWithParams(hash string) func(t *testing.T) { + return func(t *testing.T) { + + config := ð.TraceConfig{ + LogConfig: &vm.LogConfig{ + DisableMemory: true, + DisableStack: true, + DisableStorage: true, + DisableReturnData: true, + }, + } + + t.Logf("call tracing-api debug_traceTransaction") + rawTracingApiData, err := callDebugTraceTransaction(hash, "http://127.0.0.1:8083", config) if err != nil { - t.Fatalf(" error: %s", err) + t.Fatalf(" error: %s", err) } - defer gethRes.Body.Close() + t.Logf(" done") - t.Logf(" read data") - rawGethData, err := ioutil.ReadAll(gethRes.Body) + t.Logf("call geth debug_traceTransaction") + rawGethData, err := callDebugTraceTransaction(hash, "http://127.0.0.1:8545", config) if err != nil { t.Fatalf(" error: %s", err) } @@ -388,4 +430,5 @@ func TestMain(t *testing.T) { t.Run("test TxTraceGraph", testTxTraceGraph(hash)) t.Run("test TraceTransaction", testTraceTransaction(hash)) + t.Run("test TraceTransaction with params", testTraceTransactionWithParams(hash)) }