Skip to content

Commit

Permalink
add debug_traceCall rpc
Browse files Browse the repository at this point in the history
  • Loading branch information
laizy committed Dec 13, 2023
1 parent 037df5b commit b69760e
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 1 deletion.
11 changes: 10 additions & 1 deletion core/store/ledgerstore/ledger_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* along with The ontology. If not, see <http://www.gnu.org/licenses/>.
*/
//Storage of ledger

package ledgerstore

import (
Expand Down Expand Up @@ -1447,7 +1448,15 @@ func (this *LedgerStoreImp) PreExecuteContract(tx *types.Transaction) (*sstate.P
return this.PreExecuteContractWithParam(tx, param)
}

func (this *LedgerStoreImp) TraceEip155Tx(msg types3.Message, tracer evm2.Tracer) (*types5.ExecutionResult, error) {
return this.executeEip155Tx(msg, evm2.Config{Debug: true, Tracer: tracer})
}

func (this *LedgerStoreImp) PreExecuteEip155Tx(msg types3.Message) (*types5.ExecutionResult, error) {
return this.executeEip155Tx(msg, evm2.Config{})
}

func (this *LedgerStoreImp) executeEip155Tx(msg types3.Message, conf evm2.Config) (*types5.ExecutionResult, error) {
height := this.GetCurrentBlockHeight()
// use previous block time to make it predictable for easy test
blockTime := uint32(time.Now().Unix())
Expand All @@ -1466,7 +1475,7 @@ func (this *LedgerStoreImp) PreExecuteEip155Tx(msg types3.Message) (*types5.Exec
blockContext := evm.NewEVMBlockContext(height, blockTime, this)
cache := this.GetCacheDB()
statedb := storage.NewStateDB(cache, common2.Hash{}, common2.Hash(ctx.BlockHash), ong.OngBalanceHandle{})
vmenv := evm2.NewEVM(blockContext, txContext, statedb, config, evm2.Config{})
vmenv := evm2.NewEVM(blockContext, txContext, statedb, config, conf)
res, err := evm.ApplyMessage(vmenv, msg, common2.Address(utils.GovernanceContractAddress))
return res, err
}
Expand Down
2 changes: 2 additions & 0 deletions core/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
types3 "github.com/ontio/ontology/smartcontract/service/evm/types"
cstates "github.com/ontio/ontology/smartcontract/states"
"github.com/ontio/ontology/smartcontract/storage"
"github.com/ontio/ontology/vm/evm"
)

type ExecuteResult struct {
Expand Down Expand Up @@ -78,6 +79,7 @@ type LedgerStore interface {
PreExecuteContract(tx *types.Transaction) (*cstates.PreExecResult, error)
PreExecuteContractBatch(txes []*types.Transaction, atomic bool) ([]*cstates.PreExecResult, uint32, error)
PreExecuteEip155Tx(msg types2.Message) (*types3.ExecutionResult, error)
TraceEip155Tx(msg types2.Message, tracer evm.Tracer) (*types3.ExecutionResult, error)
GetEventNotifyByTx(tx common.Uint256) (*event.ExecuteNotify, error)
GetEventNotifyByBlock(height uint32) ([]*event.ExecuteNotify, error)
GetEthCode(hash common2.Hash) ([]byte, error)
Expand Down
129 changes: 129 additions & 0 deletions http/ethrpc/debug/tracer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright (C) 2018 The ontology Authors
* This file is part of The ontology library.
*
* The ontology is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ontology is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with The ontology. If not, see <http://www.gnu.org/licenses/>.
*/

package debug

import (
"fmt"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ontio/ontology/core/ledger"
"github.com/ontio/ontology/http/ethrpc/eth"
types2 "github.com/ontio/ontology/http/ethrpc/types"
"github.com/ontio/ontology/vm/evm"
"github.com/ontio/ontology/vm/evm/tracers"
)

// DebugAPI is the collection of tracing APIs exposed over the private debugging endpoint.
type DebugAPI struct {
}

// NewDebugAPI creates a new DebugAPI definition for the tracing methods of the Ethereum service.
func NewDebugAPI() *DebugAPI {
return &DebugAPI{}
}

// TraceConfig holds extra parameters to trace functions.
type TraceConfig struct {
*evm.LogConfig
Tracer *string
Timeout *string
Reexec *uint64
}

// TraceCallConfig is the config for traceCall DebugAPI. It holds one more
// field to override the state for tracing.
type TraceCallConfig struct {
*evm.LogConfig
Tracer *string
Timeout *string
Reexec *uint64
//StateOverrides *ethapi.StateOverride
}

// TraceCall lets you trace a given eth_call. It collects the structured logs
// created during the execution of EVM if the given transaction was added on
// top of the provided block and returns them as a JSON object.
// You can provide -2 as a block number to trace on top of the pending block.
func (api *DebugAPI) TraceCall(args types2.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
// Execute the trace
msg := args.AsMessage(eth.RPCGasCap)

var traceConfig *TraceConfig
if config != nil {
traceConfig = &TraceConfig{
LogConfig: config.LogConfig,
Tracer: config.Tracer,
Timeout: config.Timeout,
Reexec: config.Reexec,
}
}
return api.traceTx(msg, traceConfig)
}

// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
func (api *DebugAPI) traceTx(message types.Message, config *TraceConfig) (interface{}, error) {
// Assemble the structured logger or the JavaScript tracer
var (
tracer evm.Tracer
err error
)
switch {
case config == nil:
tracer = evm.NewStructLogger(nil)
case config.Tracer != nil:
switch *config.Tracer {
case "callTracer":
tracer = tracers.NewCallTracer()
default:
return nil, fmt.Errorf("unkown tracer type: %s", *config.Tracer)
}
default:
tracer = evm.NewStructLogger(config.LogConfig)
}

result, err := ledger.DefLedger.TraceEip155Tx(message, nil)
if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}

// Depending on the tracer type, format and return the output.
switch tracer := tracer.(type) {
case *evm.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.CallTracer:
return tracer.GetResult()

default:
panic(fmt.Sprintf("bad tracer type %T", tracer))
}
}
87 changes: 87 additions & 0 deletions http/ethrpc/debug/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2018 The ontology Authors
* This file is part of The ontology library.
*
* The ontology is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ontology is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with The ontology. If not, see <http://www.gnu.org/licenses/>.
*/

package debug

import (
"fmt"

"github.com/ethereum/go-ethereum/common/math"
"github.com/ontio/ontology/vm/evm"
)

// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value
type ExecutionResult struct {
Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue string `json:"returnValue"`
StructLogs []StructLogRes `json:"structLogs"`
}

// StructLogRes stores a structured log emitted by the EVM while replaying a
// transaction in debug mode
type StructLogRes struct {
Pc uint64 `json:"pc"`
Op string `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Depth int `json:"depth"`
Error error `json:"error,omitempty"`
Stack *[]string `json:"stack,omitempty"`
Memory *[]string `json:"memory,omitempty"`
Storage *map[string]string `json:"storage,omitempty"`
}

// FormatLogs formats EVM returned structured logs for json output
func FormatLogs(logs []evm.StructLog) []StructLogRes {
formatted := make([]StructLogRes, len(logs))
for index, trace := range logs {
formatted[index] = StructLogRes{
Pc: trace.Pc,
Op: trace.Op.String(),
Gas: trace.Gas,
GasCost: trace.GasCost,
Depth: trace.Depth,
Error: trace.Err,
}
if trace.Stack != nil {
stack := make([]string, len(trace.Stack))
for i, stackValue := range trace.Stack {
stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
}
formatted[index].Stack = &stack
}
if trace.Memory != nil {
memory := make([]string, 0, (len(trace.Memory)+31)/32)
for i := 0; i+32 <= len(trace.Memory); i += 32 {
memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
}
formatted[index].Memory = &memory
}
if trace.Storage != nil {
storage := make(map[string]string)
for i, storageValue := range trace.Storage {
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
}
formatted[index].Storage = &storage
}
}
return formatted
}
4 changes: 4 additions & 0 deletions http/ethrpc/rpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/ontio/ontology/core/store/ledgerstore"
"github.com/ontio/ontology/http/base/actor"
backend2 "github.com/ontio/ontology/http/ethrpc/backend"
"github.com/ontio/ontology/http/ethrpc/debug"
"github.com/ontio/ontology/http/ethrpc/eth"
filters2 "github.com/ontio/ontology/http/ethrpc/filters"
"github.com/ontio/ontology/http/ethrpc/net"
Expand Down Expand Up @@ -68,6 +69,9 @@ func StartEthServer(txpool *tp.TXPoolServer) error {
if err := server.RegisterName("web3", web3.NewAPI()); err != nil {
return err
}
if err := server.RegisterName("debug", debug.NewDebugAPI()); err != nil {
return err
}

// add cors wrapper
wrappedCORSHandler := node.NewHTTPHandlerStack(server, cors, vhosts)
Expand Down

0 comments on commit b69760e

Please sign in to comment.