Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Liuhaai committed Feb 18, 2023
2 parents 27738df + 043e7f8 commit 0210a7d
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 39 deletions.
30 changes: 30 additions & 0 deletions api/coreservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"strconv"
"time"

"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/pkg/errors"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
Expand All @@ -26,6 +28,7 @@ import (
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/go-pkgs/util"
"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-election/committee"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
Expand Down Expand Up @@ -141,6 +144,8 @@ type (
ReceiveBlock(blk *block.Block) error
// BlockHashByBlockHeight returns block hash by block height
BlockHashByBlockHeight(blkHeight uint64) (hash.Hash256, error)
// TraceTransaction returns the trace result of a transaction
TraceTransaction(ctx context.Context, actHash string, config *logger.Config) ([]byte, *action.Receipt, *logger.StructLogger, error)
}

// coreService implements the CoreService interface
Expand Down Expand Up @@ -1616,3 +1621,28 @@ func (core *coreService) SyncingProgress() (uint64, uint64, uint64) {
startingHeight, currentHeight, targetHeight, _ := core.bs.SyncStatus()
return startingHeight, currentHeight, targetHeight
}

// TraceTransaction returns the trace result of transaction
func (core *coreService) TraceTransaction(ctx context.Context, actHash string, config *logger.Config) ([]byte, *action.Receipt, *logger.StructLogger, error) {
actInfo, err := core.Action(util.Remove0xPrefix(actHash), false)
if err != nil {
return nil, nil, nil, err
}
act, err := (&action.Deserializer{}).SetEvmNetworkID(core.EVMNetworkID()).ActionToSealedEnvelope(actInfo.Action)
if err != nil {
return nil, nil, nil, err
}
sc, ok := act.Action().(*action.Execution)
if !ok {
return nil, nil, nil, errors.New("the type of action is not supported")
}
traces := logger.NewStructLogger(config)
ctx = protocol.WithVMConfigCtx(ctx, vm.Config{
Debug: true,
Tracer: traces,
NoBaseFee: true,
})
addr, _ := address.FromString(address.ZeroAddress)
retval, receipt, err := core.SimulateExecution(ctx, addr, sc)
return retval, receipt, traces, err
}
37 changes: 37 additions & 0 deletions api/coreservice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ package api

import (
"context"
"encoding/hex"
"math/big"
"strconv"
"testing"

"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/golang/mock/gomock"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/pkg/errors"
Expand All @@ -21,6 +24,7 @@ import (
"github.com/iotexproject/iotex-core/api/logfilter"
"github.com/iotexproject/iotex-core/blockchain"
"github.com/iotexproject/iotex-core/blockchain/blockdao"
"github.com/iotexproject/iotex-core/test/identityset"
"github.com/iotexproject/iotex-core/test/mock/mock_blockindex"
"github.com/iotexproject/iotex-core/testutil"
)
Expand Down Expand Up @@ -199,6 +203,39 @@ func TestEstimateGasForAction(t *testing.T) {
require.Contains(err.Error(), action.ErrNilProto.Error())
}

func TestTraceTransaction(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
svr, bc, _, ap, cleanCallback := setupTestCoreSerivce()
defer cleanCallback()
ctx := context.Background()
tsf, err := action.SignedExecution(identityset.Address(29).String(),
identityset.PrivateKey(29), 1, big.NewInt(0), testutil.TestGasLimit,
big.NewInt(testutil.TestGasPriceInt64), []byte{})
require.NoError(err)
tsfhash, err := tsf.Hash()

blk1Time := testutil.TimestampNow()
require.NoError(ap.Add(ctx, tsf))
blk, err := bc.MintNewBlock(blk1Time)
require.NoError(err)
require.NoError(bc.CommitBlock(blk))
cfg := &logger.Config{
EnableMemory: true,
DisableStack: false,
DisableStorage: false,
EnableReturnData: true,
}
retval, receipt, traces, err := svr.TraceTransaction(ctx, hex.EncodeToString(tsfhash[:]), cfg)
require.NoError(err)
require.Equal("0x", byteToHex(retval))
require.Equal(uint64(1), receipt.Status)
require.Equal(uint64(0x2710), receipt.GasConsumed)
require.Empty(receipt.ExecutionRevertMsg())
require.Equal(0, len(traces.StructLogs()))
}

func TestProofAndCompareReverseActions(t *testing.T) {
sliceN := func(n uint64) (value []uint64) {
value = make([]uint64, 0, n)
Expand Down
33 changes: 8 additions & 25 deletions api/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@ import (
"strconv"
"time"

"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/go-pkgs/util"
"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
Expand All @@ -39,7 +37,6 @@ import (
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/action/protocol"
"github.com/iotexproject/iotex-core/api/logfilter"
apitypes "github.com/iotexproject/iotex-core/api/types"
"github.com/iotexproject/iotex-core/blockchain/block"
Expand Down Expand Up @@ -659,32 +656,18 @@ func (svr *gRPCHandler) ReadContractStorage(ctx context.Context, in *iotexapi.Re

// TraceTransactionStructLogs get trace transaction struct logs
func (svr *gRPCHandler) TraceTransactionStructLogs(ctx context.Context, in *iotexapi.TraceTransactionStructLogsRequest) (*iotexapi.TraceTransactionStructLogsResponse, error) {
actInfo, err := svr.coreService.Action(util.Remove0xPrefix(in.GetActionHash()), false)
if err != nil {
return nil, err
cfg := &logger.Config{
EnableMemory: true,
DisableStack: false,
DisableStorage: false,
EnableReturnData: true,
}
act, err := (&action.Deserializer{}).SetEvmNetworkID(svr.coreService.EVMNetworkID()).ActionToSealedEnvelope(actInfo.Action)
_, _, traces, err := svr.coreService.TraceTransaction(ctx, in.GetActionHash(), cfg)
if err != nil {
return nil, err
}
sc, ok := act.Action().(*action.Execution)
if !ok {
return nil, status.Error(codes.InvalidArgument, "the type of action is not supported")
}
tracer := logger.NewStructLogger(nil)
ctx = protocol.WithVMConfigCtx(ctx, vm.Config{
Debug: true,
Tracer: tracer,
NoBaseFee: true,
})

_, _, err = svr.coreService.SimulateExecution(ctx, act.SenderAddress(), sc)
if err != nil {
return nil, err
return nil, status.Error(codes.Internal, err.Error())
}

structLogs := make([]*iotextypes.TransactionStructLog, 0)
for _, log := range tracer.StructLogs() {
for _, log := range traces.StructLogs() {
var stack []string
for _, s := range log.Stack {
stack = append(stack, s.String())
Expand Down
14 changes: 2 additions & 12 deletions api/grpcserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/golang/mock/gomock"
"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
Expand Down Expand Up @@ -1019,18 +1020,7 @@ func TestGrpcServer_TraceTransactionStructLogs(t *testing.T) {
core := mock_apicoreservice.NewMockCoreService(ctrl)
grpcSvr := newGRPCHandler(core)

addr1 := identityset.Address(28).String()
priKey1 := identityset.PrivateKey(29)
ex1, err := action.SignedExecution(addr1, priKey1, uint64(1), big.NewInt(10), uint64(100000), big.NewInt(0), []byte{})
require.NoError(err)
act := &iotexapi.ActionInfo{
Index: 0,
ActHash: "_test",
Action: ex1.Proto(),
}
core.EXPECT().Action(gomock.Any(), gomock.Any()).Return(act, nil)
core.EXPECT().EVMNetworkID().Return(uint32(11))
core.EXPECT().SimulateExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil)
core.EXPECT().TraceTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, logger.NewStructLogger(nil), nil)
resp, err := grpcSvr.TraceTransactionStructLogs(context.Background(), &iotexapi.TraceTransactionStructLogsRequest{
ActionHash: "_actionHash",
})
Expand Down
57 changes: 57 additions & 0 deletions api/web3server.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"strconv"
"time"

"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/go-pkgs/util"
"github.com/iotexproject/iotex-address/address"
Expand Down Expand Up @@ -205,6 +207,8 @@ func (svr *web3Handler) handleWeb3Req(ctx context.Context, web3Req *gjson.Result
res, err = svr.subscribe(web3Req, writer)
case "eth_unsubscribe":
res, err = svr.unsubscribe(web3Req)
case "debug_traceTransaction":
res, err = svr.traceTransaction(ctx, web3Req)
case "eth_coinbase", "eth_getUncleCountByBlockHash", "eth_getUncleCountByBlockNumber",
"eth_sign", "eth_signTransaction", "eth_sendTransaction", "eth_getUncleByBlockHashAndIndex",
"eth_getUncleByBlockNumberAndIndex", "eth_pendingTransactions":
Expand Down Expand Up @@ -880,6 +884,59 @@ func (svr *web3Handler) unsubscribe(in *gjson.Result) (interface{}, error) {
return chainListener.RemoveResponder(id.String())
}

func (svr *web3Handler) traceTransaction(ctx context.Context, in *gjson.Result) (interface{}, error) {
actHash, options := in.Get("params.0"), in.Get("params.1")
if !actHash.Exists() {
return nil, errInvalidFormat
}
var (
enableMemory, disableStack, disableStorage, enableReturnData bool
)
if options.Exists() {
enableMemory = options.Get("enableMemory").Bool()
disableStack = options.Get("disableStack").Bool()
disableStorage = options.Get("disableStorage").Bool()
enableReturnData = options.Get("enableReturnData").Bool()
}
cfg := &logger.Config{
EnableMemory: enableMemory,
DisableStack: disableStack,
DisableStorage: disableStorage,
EnableReturnData: enableReturnData,
}
retval, receipt, traces, err := svr.coreService.TraceTransaction(ctx, actHash.String(), cfg)
if err != nil {
return nil, err
}

structLogs := make([]structLog, 0)
for _, s := range traces.StructLogs() {
var enc structLog
enc.Pc = s.Pc
enc.Op = s.Op
enc.Gas = math.HexOrDecimal64(s.Gas)
enc.GasCost = math.HexOrDecimal64(s.GasCost)
enc.Memory = s.Memory
enc.MemorySize = s.MemorySize
enc.Stack = s.Stack
enc.ReturnData = s.ReturnData
enc.Storage = s.Storage
enc.Depth = s.Depth
enc.RefundCounter = s.RefundCounter
enc.OpName = s.OpName()
enc.ErrorString = s.ErrorString()
structLogs = append(structLogs, enc)
}

return &debugTraceTransactionResult{
Failed: receipt.Status != uint64(iotextypes.ReceiptStatus_Success),
Revert: receipt.ExecutionRevertMsg(),
ReturnValue: byteToHex(retval),
StructLogs: structLogs,
Gas: receipt.GasConsumed,
}, nil
}

func (svr *web3Handler) unimplemented() (interface{}, error) {
return nil, errNotImplemented
}
27 changes: 27 additions & 0 deletions api/web3server_marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import (
"encoding/hex"
"encoding/json"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/holiman/uint256"
"github.com/iotexproject/go-pkgs/crypto"
"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/iotex-address/address"
Expand Down Expand Up @@ -75,6 +80,28 @@ type (
CurrentBlock string `json:"currentBlock"`
HighestBlock string `json:"highestBlock"`
}
structLog struct {
Pc uint64 `json:"pc"`
Op vm.OpCode `json:"op"`
Gas math.HexOrDecimal64 `json:"gas"`
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory"`
MemorySize int `json:"memSize"`
Stack []uint256.Int `json:"stack"`
ReturnData hexutil.Bytes `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"storage"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
OpName string `json:"opName"`
ErrorString string `json:"error"`
}
debugTraceTransactionResult struct {
Failed bool `json:"failed"`
Revert string `json:"revert"`
ReturnValue string `json:"returnValue"`
Gas uint64 `json:"gas"`
StructLogs []structLog `json:"structLogs"`
}
)

var (
Expand Down
34 changes: 34 additions & 0 deletions api/web3server_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"context"
"encoding/hex"
"fmt"
"io"
Expand All @@ -13,6 +14,7 @@ import (
"testing"
"time"

"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/golang/mock/gomock"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
Expand All @@ -30,6 +32,7 @@ import (
"github.com/iotexproject/iotex-core/test/identityset"
"github.com/iotexproject/iotex-core/test/mock/mock_apicoreservice"
mock_apitypes "github.com/iotexproject/iotex-core/test/mock/mock_apiresponder"
"github.com/iotexproject/iotex-core/testutil"
)

func TestGetWeb3Reqs(t *testing.T) {
Expand Down Expand Up @@ -980,3 +983,34 @@ func TestLocalAPICache(t *testing.T) {
_, exist = cacheLocal.Get(testKey)
require.False(exist)
}

func TestDebugTraceTransaction(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
core := mock_apicoreservice.NewMockCoreService(ctrl)
web3svr := &web3Handler{core, nil}

ctx := context.Background()
tsf, err := action.SignedExecution(identityset.Address(29).String(),
identityset.PrivateKey(29), 1, big.NewInt(0), testutil.TestGasLimit,
big.NewInt(testutil.TestGasPriceInt64), []byte{})
require.NoError(err)
tsfhash, err := tsf.Hash()
require.NoError(err)
receipt := &action.Receipt{Status: 1, BlockHeight: 1, ActionHash: tsfhash, GasConsumed: 100000}
structLogger := &logger.StructLogger{}

core.EXPECT().TraceTransaction(ctx, gomock.Any(), gomock.Any()).AnyTimes().Return([]byte{0x01}, receipt, structLogger, nil)

in := gjson.Parse(`{"params":["` + hex.EncodeToString(tsfhash[:]) + `"]}`)
ret, err := web3svr.traceTransaction(ctx, &in)
require.NoError(err)
rlt, ok := ret.(*debugTraceTransactionResult)
require.True(ok)
require.Equal("0x01", rlt.ReturnValue)
require.False(rlt.Failed)
require.Equal(uint64(100000), rlt.Gas)
require.Empty(rlt.Revert)
require.Equal(0, len(rlt.StructLogs))
}
Loading

0 comments on commit 0210a7d

Please sign in to comment.