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

add support params in debug_TraceTransaction method #13

Merged
merged 1 commit into from
Jun 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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))
}