Skip to content
This repository has been archived by the owner on Jun 7, 2022. It is now read-only.

Commit

Permalink
#11 support params in debug_TraceTransaction method
Browse files Browse the repository at this point in the history
  • Loading branch information
ramilexe committed Jun 30, 2021
1 parent 4931fd9 commit c814e82
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 45 deletions.
95 changes: 69 additions & 26 deletions pkg/eth/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
}

Expand All @@ -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
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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
}
81 changes: 62 additions & 19 deletions test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 := &eth.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)
}
Expand All @@ -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))
}

0 comments on commit c814e82

Please sign in to comment.