From ee0f380a25d7f23e0d60c091da2129546cb6967a Mon Sep 17 00:00:00 2001 From: "kai.zhang" Date: Tue, 17 Dec 2024 16:39:03 +0800 Subject: [PATCH 1/6] get innternal transactions by tracer --- cmd/utils/flags_xlayer.go | 8 +- eth/ethconfig/config_xlayer.go | 7 +- eth/tracers/native/innertx_xlayer.go | 406 ++++++++++++++++++++++++ turbo/cli/default_flags.go | 3 +- turbo/cli/flags_xlayer.go | 1 + turbo/jsonrpc/eth_api.go | 13 +- turbo/jsonrpc/eth_innertx_rpc_xlayer.go | 120 +++++++ turbo/jsonrpc/eth_innertx_xlayer.go | 18 ++ zk/apollo/utils.go | 3 + 9 files changed, 569 insertions(+), 10 deletions(-) create mode 100644 eth/tracers/native/innertx_xlayer.go create mode 100644 turbo/jsonrpc/eth_innertx_rpc_xlayer.go diff --git a/cmd/utils/flags_xlayer.go b/cmd/utils/flags_xlayer.go index 5aa6b30bc9c..5e00507c15d 100644 --- a/cmd/utils/flags_xlayer.go +++ b/cmd/utils/flags_xlayer.go @@ -2,10 +2,11 @@ package utils import ( "fmt" - "github.com/urfave/cli/v2" "math/big" "time" + "github.com/urfave/cli/v2" + libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon/eth/ethconfig" "github.com/ledgerwatch/erigon/eth/gasprice/gaspricecfg" @@ -180,6 +181,11 @@ var ( Usage: "Allow the sequencer to proceed internal transactions", Value: false, } + AllowInternalTransactionsByTracer = cli.BoolFlag{ + Name: "zkevm.allow-internal-transactions-tracer", + Usage: "Allow the RPC to get internal transactions by tracer", + Value: false, + } // RPC HTTPApiKeysFlag = cli.StringFlag{ Name: "http.apikeys", diff --git a/eth/ethconfig/config_xlayer.go b/eth/ethconfig/config_xlayer.go index 98694c22411..945636dc1e9 100644 --- a/eth/ethconfig/config_xlayer.go +++ b/eth/ethconfig/config_xlayer.go @@ -6,9 +6,10 @@ import ( // XLayerConfig is the X Layer config used on the eth backend type XLayerConfig struct { - Apollo ApolloClientConfig - Nacos NacosConfig - EnableInnerTx bool + Apollo ApolloClientConfig + Nacos NacosConfig + EnableInnerTx bool + EnableInnerTxByTracer bool // Sequencer SequencerBatchSleepDuration time.Duration } diff --git a/eth/tracers/native/innertx_xlayer.go b/eth/tracers/native/innertx_xlayer.go new file mode 100644 index 00000000000..69e6a3530cc --- /dev/null +++ b/eth/tracers/native/innertx_xlayer.go @@ -0,0 +1,406 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library 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 go-ethereum library 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 go-ethereum library. If not, see . + +package native + +import ( + "encoding/json" + "errors" + "math/big" + "strconv" + "strings" + "sync/atomic" + + "github.com/ledgerwatch/log/v3" + + "github.com/holiman/uint256" + + libcommon "github.com/ledgerwatch/erigon-lib/common" + "github.com/ledgerwatch/erigon-lib/common/hexutil" + "github.com/ledgerwatch/erigon-lib/common/hexutility" + "github.com/ledgerwatch/erigon/accounts/abi" + "github.com/ledgerwatch/erigon/core/vm" + "github.com/ledgerwatch/erigon/eth/tracers" +) + +//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go + +func init() { + register("okTracer", NewOKTracer) +} + +type okLog struct { + Index uint64 `json:"index"` + Address libcommon.Address `json:"address"` + Topics []libcommon.Hash `json:"topics"` + Data hexutility.Bytes `json:"data"` +} + +type okFrame struct { + Type vm.OpCode `json:"-"` + From libcommon.Address `json:"from"` + Gas uint64 `json:"gas"` + GasUsed uint64 `json:"gasUsed"` + To libcommon.Address `json:"to,omitempty" rlp:"optional"` + Input []byte `json:"input" rlp:"optional"` + Output []byte `json:"output,omitempty" rlp:"optional"` + Error string `json:"error,omitempty" rlp:"optional"` + Revertal string `json:"revertReason,omitempty"` + Calls []okFrame `json:"calls,omitempty" rlp:"optional"` + Logs []okLog `json:"logs,omitempty" rlp:"optional"` + // Placed at end on purpose. The RLP will be decoded to 0 instead of + // nil if there are non-empty elements after in the struct. + Value *big.Int `json:"value,omitempty" rlp:"optional"` +} + +func (f okFrame) TypeString() string { + return f.Type.String() +} + +func (f okFrame) failed() bool { + return len(f.Error) > 0 +} + +func (f *okFrame) processOutput(output []byte, err error) { + output = libcommon.CopyBytes(output) + if err == nil { + f.Output = output + return + } + f.Error = err.Error() + if f.Type == vm.CREATE || f.Type == vm.CREATE2 { + f.To = libcommon.Address{} + } + if !errors.Is(err, vm.ErrExecutionReverted) || len(output) == 0 { + return + } + f.Output = output + if len(output) < 4 { + return + } + if unpacked, err := abi.UnpackRevert(output); err == nil { + f.Revertal = unpacked + } +} + +type okFrameMarshaling struct { + TypeString string `json:"type"` + Gas hexutil.Uint64 + GasUsed hexutil.Uint64 + Value *hexutil.Big + Input hexutil.Bytes + Output hexutil.Bytes +} + +type okTracer struct { + noopTracer + callstack []okFrame + config okTracerConfig + gasLimit uint64 + interrupt uint32 // Atomic flag to signal execution interruption + reason error // Textual reason for the interruption +} + +type okTracerConfig struct { + OnlyTopCall bool `json:"onlyTopCall"` // If true, call tracer won't collect any subcalls + WithLog bool `json:"withLog"` // If true, call tracer will collect event logs +} + +// NewOKTracer returns a native go tracer which tracks +// call frames of a tx, and implements fakevm.EVMLogger. +func NewOKTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { + var config okTracerConfig + if cfg != nil { + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, err + } + } + // First callframe contains tx context info + // and is populated on start and end. + return &okTracer{callstack: make([]okFrame, 1), config: config}, nil +} + +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. +func (t *okTracer) CaptureStart(env *vm.EVM, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) { + toCopy := to + t.callstack[0] = okFrame{ + Type: vm.CALL, + From: from, + To: toCopy, + Input: libcommon.CopyBytes(input), + Gas: gas, + } + if value != nil { + t.callstack[0].Value = value.ToBig() + } + if create { + t.callstack[0].Type = vm.CREATE + } +} + +// CaptureEnd is called after the call finishes to finalize the tracing. +func (t *okTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { + t.callstack[0].processOutput(output, err) +} + +// CaptureState implements the EVMLogger interface to trace a single step of VM execution. +func (t *okTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + // Only logs need to be captured via opcode processing + if !t.config.WithLog { + return + } + // Avoid processing nested calls when only caring about top call + if t.config.OnlyTopCall && depth > 0 { + return + } + // Skip if tracing was interrupted + if atomic.LoadUint32(&t.interrupt) > 0 { + return + } + switch op { + case vm.LOG0, vm.LOG1, vm.LOG2, vm.LOG3, vm.LOG4: + size := int(op - vm.LOG0) + + stack := scope.Stack + stackData := stack.Data + + // Don't modify the stack + mStart := stackData[len(stackData)-1] + mSize := stackData[len(stackData)-2] + topics := make([]libcommon.Hash, size) + for i := 0; i < size; i++ { + topic := stackData[len(stackData)-2-(i+1)] + topics[i] = libcommon.Hash(topic.Bytes32()) + } + + data := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) + log := okLog{Address: scope.Contract.Address(), Topics: topics, Data: hexutility.Bytes(data)} + t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, log) + } +} + +// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *okTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) { + if t.config.OnlyTopCall { + return + } + // Skip if tracing was interrupted + if atomic.LoadUint32(&t.interrupt) > 0 { + return + } + + toCopy := to + call := okFrame{ + Type: typ, + From: from, + To: toCopy, + Input: libcommon.CopyBytes(input), + Gas: gas, + } + if value != nil { + call.Value = value.ToBig() + } + t.callstack = append(t.callstack, call) +} + +// CaptureExit is called when EVM exits a scope, even if the scope didn't +// execute any code. +func (t *okTracer) CaptureExit(output []byte, gasUsed uint64, err error) { + if t.config.OnlyTopCall { + return + } + size := len(t.callstack) + if size <= 1 { + return + } + // pop call + call := t.callstack[size-1] + t.callstack = t.callstack[:size-1] + size -= 1 + + call.GasUsed = gasUsed + call.processOutput(output, err) + t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) +} + +func (t *okTracer) CaptureTxStart(gasLimit uint64) { + t.gasLimit = gasLimit +} + +func (t *okTracer) CaptureTxEnd(restGas uint64) { + t.callstack[0].GasUsed = t.gasLimit - restGas + if t.config.WithLog { + // Logs are not emitted when the call fails + clearOKFailedLogs(&t.callstack[0], false) + } +} + +func inArray(target string, list []string) bool { + for _, item := range list { + if item == target { + return true + } + } + return false +} + +func internalTxTraceToInnerTxs(tx okFrame) []*InnerTx { + dfs := Dfs{} + indexMap := make(map[int]int) + indexMap[0] = 0 + var level = 0 + var index = 1 + isError := tx.Error != "" + dfs.dfs(tx, level, index, indexMap, isError) + return dfs.innerTxs +} + +type Dfs struct { + innerTxs []*InnerTx +} + +func (d *Dfs) dfs(tx okFrame, level int, index int, indexMap map[int]int, isError bool) { + if !inArray(strings.ToLower(tx.Type.String()), []string{"call", "create", "create2", + "callcode", "delegatecall", "staticcall", "selfdestruct"}) { + return + } + + name := strings.ToLower(tx.Type.String()) + + for i := 0; i < level; i++ { + if indexMap[i] == 0 { + continue + } + name = name + "_" + strconv.Itoa(indexMap[i]) + } + innerTx := internalTxTraceToInnerTx(tx, name, level, index) + if !isError { + isError = innerTx.IsError + } else { + innerTx.IsError = isError + } + d.innerTxs = append(d.innerTxs, innerTx) + index = 0 + for _, call := range tx.Calls { + index++ + indexMap[level] = index + d.dfs(call, level+1, index+1, indexMap, isError) + } + if len(tx.Calls) == 0 { + return + } +} + +type InnerTx struct { + Dept big.Int `json:"dept"` + InternalIndex big.Int `json:"internal_index"` + From string `json:"from"` + To string `json:"to"` + Input string `json:"input"` + Output string `json:"output"` + + IsError bool `json:"is_error"` + GasUsed uint64 `json:"gas_used"` + Value string `json:"value"` + ValueWei string `json:"value_wei"` + CallValueWei string `json:"call_value_wei"` + Error string `json:"error"` + Gas uint64 `json:"gas"` + //ReturnGas uint64 `json:"return_gas"` + + CallType string `json:"call_type"` + Name string `json:"name"` + TraceAddress string `json:"trace_address"` + CodeAddress string `json:"code_address"` +} + +func internalTxTraceToInnerTx(currentTx okFrame, name string, depth int, index int) *InnerTx { + value := currentTx.Value + if value == nil { + value = big.NewInt(0) + } + var toAddress string + if len(currentTx.To) != 0 { + // 0x2ea4d738775e11d96c5e0c0810cb24e26e1af074c90de038e403858238dd972c + toAddress = currentTx.To.String() + } + callTx := &InnerTx{ + Dept: *big.NewInt(int64(depth)), + From: currentTx.From.String(), + To: toAddress, + ValueWei: value.String(), + CallValueWei: value.String(), + CallType: strings.ToLower(currentTx.Type.String()), + Name: name, + Input: hexutil.Encode(currentTx.Input), + Output: hexutil.Encode(currentTx.Output), + Gas: currentTx.Gas, + GasUsed: currentTx.GasUsed, + IsError: false, + //ReturnGas: currentTx.Gas - currentTx.GasUsed, + } + callTx.InternalIndex = *big.NewInt(int64(index - 1)) + if strings.ToLower(currentTx.Type.String()) == "callcode" { + callTx.CodeAddress = currentTx.To.String() + } + if strings.ToLower(currentTx.Type.String()) == "delegatecall" { + callTx.ValueWei = "" + } + if currentTx.Error != "" { + callTx.Error = currentTx.Error + callTx.IsError = true + } + return callTx +} + +// GetResult returns the json-encoded nested list of call traces, and any +// error arising from the encoding or forceful termination (via `Stop`). +func (t *okTracer) GetResult() (json.RawMessage, error) { + if len(t.callstack) != 1 { + log.Error("incorrect number of top-level calls", "len", len(t.callstack)) + return json.RawMessage([]byte("[]")), nil + } + // Turn the okFrame into a list + innerTxs := internalTxTraceToInnerTxs(t.callstack[0]) + if len(innerTxs) <= 1 { + return json.RawMessage([]byte("[]")), nil + } + + res, err := json.Marshal(innerTxs) + if err != nil { + return nil, err + } + return json.RawMessage(res), t.reason +} + +// Stop terminates execution of the tracer at the first opportune moment. +func (t *okTracer) Stop(err error) { + t.reason = err + atomic.StoreUint32(&t.interrupt, 1) +} + +// clearOKFailedLogs clears the logs of a callframe and all its children +// in case of execution failure. +func clearOKFailedLogs(cf *okFrame, parentFailed bool) { + failed := cf.failed() || parentFailed + // Clear own logs + if failed { + cf.Logs = nil + } + for i := range cf.Calls { + clearOKFailedLogs(&cf.Calls[i], failed) + } +} diff --git a/turbo/cli/default_flags.go b/turbo/cli/default_flags.go index c40d5b7de9d..f72054d53b6 100644 --- a/turbo/cli/default_flags.go +++ b/turbo/cli/default_flags.go @@ -290,6 +290,7 @@ var DefaultFlags = []cli.Flag{ // X Layer Flags &utils.AllowInternalTransactions, + &utils.AllowInternalTransactionsByTracer, &utils.TxPoolPackBatchSpecialList, &utils.TxPoolGasPriceMultiple, &utils.GpoMaxGasPriceFlag, @@ -326,7 +327,7 @@ var DefaultFlags = []cli.Flag{ &utils.TxPoolFreeGasLimit, &utils.HTTPApiKeysFlag, &utils.MethodRateLimitFlag, - + &utils.ACLPrintHistory, &utils.InfoTreeUpdateInterval, &utils.SealBatchImmediatelyOnOverflow, diff --git a/turbo/cli/flags_xlayer.go b/turbo/cli/flags_xlayer.go index 692d5b920e8..0063455715e 100644 --- a/turbo/cli/flags_xlayer.go +++ b/turbo/cli/flags_xlayer.go @@ -23,6 +23,7 @@ func ApplyFlagsForEthXLayerConfig(ctx *cli.Context, cfg *ethconfig.Config) { ExternalListenAddr: ctx.String(utils.NacosExternalListenAddrFlag.Name), }, EnableInnerTx: ctx.Bool(utils.AllowInternalTransactions.Name), + EnableInnerTxByTracer: ctx.Bool(utils.AllowInternalTransactionsByTracer.Name), SequencerBatchSleepDuration: ctx.Duration(utils.SequencerBatchSleepDuration.Name), } diff --git a/turbo/jsonrpc/eth_api.go b/turbo/jsonrpc/eth_api.go index b868ea24501..515616944af 100644 --- a/turbo/jsonrpc/eth_api.go +++ b/turbo/jsonrpc/eth_api.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "github.com/ledgerwatch/erigon/zk/sequencer" "math/big" "sync" "sync/atomic" "time" + "github.com/ledgerwatch/erigon/zk/sequencer" + "github.com/ledgerwatch/erigon-lib/common/hexutil" lru "github.com/hashicorp/golang-lru/v2" @@ -379,8 +380,9 @@ type APIImpl struct { VirtualCountersSmtReduction float64 // For X Layer - L2GasPricer gasprice.L2GasPricer - EnableInnerTx bool + L2GasPricer gasprice.L2GasPricer + EnableInnerTx bool + EnableInnerTxByTracer bool } // NewEthAPI returns APIImpl instance @@ -414,8 +416,9 @@ func NewEthAPI(base *BaseAPI, db kv.RoDB, eth rpchelper.ApiBackend, txPool txpoo logger: logger, VirtualCountersSmtReduction: ethCfg.VirtualCountersSmtReduction, // For X Layer - L2GasPricer: gasprice.NewL2GasPriceSuggester(context.Background(), ethCfg.GPO), - EnableInnerTx: ethCfg.XLayer.EnableInnerTx, + L2GasPricer: gasprice.NewL2GasPriceSuggester(context.Background(), ethCfg.GPO), + EnableInnerTx: ethCfg.XLayer.EnableInnerTx, + EnableInnerTxByTracer: ethCfg.XLayer.EnableInnerTxByTracer, } // For X Layer diff --git a/turbo/jsonrpc/eth_innertx_rpc_xlayer.go b/turbo/jsonrpc/eth_innertx_rpc_xlayer.go new file mode 100644 index 00000000000..69ca4ebbf53 --- /dev/null +++ b/turbo/jsonrpc/eth_innertx_rpc_xlayer.go @@ -0,0 +1,120 @@ +package jsonrpc + +import ( + "context" + "fmt" + + jsoniter "github.com/json-iterator/go" + "github.com/ledgerwatch/erigon-lib/common" + "github.com/ledgerwatch/erigon/core/types" + "github.com/ledgerwatch/erigon/eth/tracers" + "github.com/ledgerwatch/erigon/eth/tracers/logger" + "github.com/ledgerwatch/erigon/turbo/transactions" +) + +var logConfig = &logger.LogConfig{ + DisableMemory: false, + DisableStack: false, + DisableStorage: false, + DisableReturnData: false, + Debug: true, + Overrides: nil, +} + +func (api *APIImpl) getInternalTransactionsByTracer(ctx context.Context, txHash common.Hash, stream *jsoniter.Stream) error { + var config *tracers.TraceConfig_ZkEvm + tx, err := api.db.BeginRo(ctx) + if err != nil { + stream.WriteNil() + return err + } + defer tx.Rollback() + chainConfig, err := api.chainConfig(ctx, tx) + if err != nil { + stream.WriteNil() + return err + } + // Retrieve the transaction and assemble its EVM context + var isBorStateSyncTxn bool + blockNum, ok, err := api.txnLookup(ctx, tx, txHash) + if err != nil { + stream.WriteNil() + return err + } + if !ok { + if chainConfig.Bor == nil { + stream.WriteNil() + return nil + } + + // otherwise this may be a bor state sync transaction - check + blockNum, ok, err = api._blockReader.EventLookup(ctx, tx, txHash) + if err != nil { + stream.WriteNil() + return err + } + if !ok { + stream.WriteNil() + return nil + } + if config == nil || config.BorTraceEnabled == nil || *config.BorTraceEnabled == false { + stream.WriteEmptyArray() // matches maticnetwork/bor API behaviour for consistency + return nil + } + + isBorStateSyncTxn = true + } + + // check pruning to ensure we have history at this block level + err = api.BaseAPI.checkPruneHistory(tx, blockNum) + if err != nil { + stream.WriteNil() + return err + } + + block, err := api.blockByNumberWithSenders(ctx, tx, blockNum) + if err != nil { + stream.WriteNil() + return err + } + if block == nil { + stream.WriteNil() + return nil + } + + var txnIndex int + var txn types.Transaction + for i := 0; i < block.Transactions().Len() && !isBorStateSyncTxn; i++ { + transaction := block.Transactions()[i] + if transaction.Hash() == txHash { + txnIndex = i + txn = transaction + break + } + } + if txn == nil { + if isBorStateSyncTxn { + // bor state sync tx is appended at the end of the block + txnIndex = block.Transactions().Len() + } else { + stream.WriteNil() + return fmt.Errorf("transaction %#x not found", txHash) + } + } + + engine := api.engine() + + txEnv, err := transactions.ComputeTxEnv_ZkEvm(ctx, engine, block, chainConfig, api._blockReader, tx, int(txnIndex), api.historyV3(tx)) + if err != nil { + stream.WriteNil() + return err + } + + tracer := "okTracer" + okTracerConfig := &tracers.TraceConfig_ZkEvm{ + LogConfig: logConfig, + Tracer: &tracer, + } + + return transactions.TraceTx(ctx, txEnv.Msg, txEnv.BlockContext, txEnv.TxContext, txEnv.Ibs, okTracerConfig, chainConfig, stream, api.evmCallTimeout) +} diff --git a/turbo/jsonrpc/eth_innertx_xlayer.go b/turbo/jsonrpc/eth_innertx_xlayer.go index a3ba0c80ab8..3385f8245bd 100644 --- a/turbo/jsonrpc/eth_innertx_xlayer.go +++ b/turbo/jsonrpc/eth_innertx_xlayer.go @@ -1,10 +1,14 @@ package jsonrpc import ( + "bytes" "context" + "encoding/json" "errors" "fmt" + jsoniter "github.com/json-iterator/go" + libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/rpc" @@ -17,6 +21,20 @@ import ( // GetInternalTransactions ... func (api *APIImpl) GetInternalTransactions(ctx context.Context, txnHash libcommon.Hash) ([]*zktypes.InnerTx, error) { + if api.EnableInnerTxByTracer { + var buf bytes.Buffer + stream := jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 4096) + err := api.getInternalTransactionsByTracer(ctx, txnHash, stream) + if err != nil { + return nil, fmt.Errorf("getInternalTransactionsByTracer failed: %v", err) + } + var innerTxs = make([]*zktypes.InnerTx, 0) + err = json.Unmarshal(buf.Bytes(), &innerTxs) + if err != nil { + return nil, fmt.Errorf("json.Unmarshal failed: %v", err) + } + return innerTxs, nil + } if !api.EnableInnerTx { return nil, errors.New("unsupported internal transaction method") } diff --git a/zk/apollo/utils.go b/zk/apollo/utils.go index e7daf1b4f4b..0b7de1ad3c9 100644 --- a/zk/apollo/utils.go +++ b/zk/apollo/utils.go @@ -139,6 +139,9 @@ func loadZkConfig(ctx *cli.Context, ethCfg *ethconfig.Config) { if ctx.IsSet(utils.AllowInternalTransactions.Name) { ethCfg.Zk.XLayer.EnableInnerTx = ctx.Bool(utils.AllowInternalTransactions.Name) } + if ctx.IsSet(utils.AllowInternalTransactionsByTracer.Name) { + ethCfg.Zk.XLayer.EnableInnerTxByTracer = ctx.Bool(utils.AllowInternalTransactionsByTracer.Name) + } } func getNamespacePrefix(namespace string) (string, error) { From 25cd02acdf8c4416aff93232986682e2ce1bd5a2 Mon Sep 17 00:00:00 2001 From: "kai.zhang" Date: Tue, 17 Dec 2024 16:43:03 +0800 Subject: [PATCH 2/6] add get inner tx logs --- turbo/jsonrpc/eth_innertx_xlayer.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/turbo/jsonrpc/eth_innertx_xlayer.go b/turbo/jsonrpc/eth_innertx_xlayer.go index 3385f8245bd..a82b81b9f8c 100644 --- a/turbo/jsonrpc/eth_innertx_xlayer.go +++ b/turbo/jsonrpc/eth_innertx_xlayer.go @@ -33,6 +33,8 @@ func (api *APIImpl) GetInternalTransactions(ctx context.Context, txnHash libcomm if err != nil { return nil, fmt.Errorf("json.Unmarshal failed: %v", err) } + log.Info(fmt.Sprintf("%x GetInternalTransactions: %d inner txs", txnHash, len(innerTxs))) + return innerTxs, nil } if !api.EnableInnerTx { From 88b6be23ed0389af26b28cc08b4ae40fcd1b96e2 Mon Sep 17 00:00:00 2001 From: Vui-Chee Date: Thu, 19 Dec 2024 10:38:34 +0800 Subject: [PATCH 3/6] Adjust fields I know how to adjust --- eth/tracers/native/innertx_xlayer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/tracers/native/innertx_xlayer.go b/eth/tracers/native/innertx_xlayer.go index 69e6a3530cc..2a5da4e4828 100644 --- a/eth/tracers/native/innertx_xlayer.go +++ b/eth/tracers/native/innertx_xlayer.go @@ -342,7 +342,7 @@ func internalTxTraceToInnerTx(currentTx okFrame, name string, depth int, index i From: currentTx.From.String(), To: toAddress, ValueWei: value.String(), - CallValueWei: value.String(), + CallValueWei: hexutil.EncodeBig(value), CallType: strings.ToLower(currentTx.Type.String()), Name: name, Input: hexutil.Encode(currentTx.Input), @@ -352,7 +352,7 @@ func internalTxTraceToInnerTx(currentTx okFrame, name string, depth int, index i IsError: false, //ReturnGas: currentTx.Gas - currentTx.GasUsed, } - callTx.InternalIndex = *big.NewInt(int64(index - 1)) + callTx.InternalIndex = *big.NewInt(int64(index)) if strings.ToLower(currentTx.Type.String()) == "callcode" { callTx.CodeAddress = currentTx.To.String() } From b89048c49acf165f7a31d4ee1d95f85787a3a3e3 Mon Sep 17 00:00:00 2001 From: Vui-Chee Date: Thu, 19 Dec 2024 10:55:51 +0800 Subject: [PATCH 4/6] Match lower seen internal idx and match `value_wei` --- eth/tracers/native/innertx_xlayer.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/eth/tracers/native/innertx_xlayer.go b/eth/tracers/native/innertx_xlayer.go index 2a5da4e4828..482c07fd1b5 100644 --- a/eth/tracers/native/innertx_xlayer.go +++ b/eth/tracers/native/innertx_xlayer.go @@ -352,13 +352,10 @@ func internalTxTraceToInnerTx(currentTx okFrame, name string, depth int, index i IsError: false, //ReturnGas: currentTx.Gas - currentTx.GasUsed, } - callTx.InternalIndex = *big.NewInt(int64(index)) + callTx.InternalIndex = *big.NewInt(int64(index - 2)) if strings.ToLower(currentTx.Type.String()) == "callcode" { callTx.CodeAddress = currentTx.To.String() } - if strings.ToLower(currentTx.Type.String()) == "delegatecall" { - callTx.ValueWei = "" - } if currentTx.Error != "" { callTx.Error = currentTx.Error callTx.IsError = true From cf8b40aba7650cbb3bba7c61dfdac759399f2ad0 Mon Sep 17 00:00:00 2001 From: Vui-Chee Date: Thu, 19 Dec 2024 11:02:49 +0800 Subject: [PATCH 5/6] Set min internal idx and warn if -ve --- eth/tracers/native/innertx_xlayer.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/eth/tracers/native/innertx_xlayer.go b/eth/tracers/native/innertx_xlayer.go index 482c07fd1b5..c1f5055e5b9 100644 --- a/eth/tracers/native/innertx_xlayer.go +++ b/eth/tracers/native/innertx_xlayer.go @@ -352,7 +352,12 @@ func internalTxTraceToInnerTx(currentTx okFrame, name string, depth int, index i IsError: false, //ReturnGas: currentTx.Gas - currentTx.GasUsed, } - callTx.InternalIndex = *big.NewInt(int64(index - 2)) + if index-2 >= 0 { + callTx.InternalIndex = *big.NewInt(int64(index - 2)) + } else { + callTx.InternalIndex = *big.NewInt(0) + log.Error("internalTxTraceToInnerTx got -ve internal index.", "index", index, "calc idx", index-2) + } if strings.ToLower(currentTx.Type.String()) == "callcode" { callTx.CodeAddress = currentTx.To.String() } From 5a7649d5537532e6e1bc0b94c8de29392a6acff7 Mon Sep 17 00:00:00 2001 From: Vui-Chee Date: Thu, 19 Dec 2024 11:21:03 +0800 Subject: [PATCH 6/6] Revise log format + use "%w" over "%v" for err --- turbo/jsonrpc/eth_innertx_xlayer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/turbo/jsonrpc/eth_innertx_xlayer.go b/turbo/jsonrpc/eth_innertx_xlayer.go index a82b81b9f8c..f816884a5ae 100644 --- a/turbo/jsonrpc/eth_innertx_xlayer.go +++ b/turbo/jsonrpc/eth_innertx_xlayer.go @@ -26,14 +26,14 @@ func (api *APIImpl) GetInternalTransactions(ctx context.Context, txnHash libcomm stream := jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 4096) err := api.getInternalTransactionsByTracer(ctx, txnHash, stream) if err != nil { - return nil, fmt.Errorf("getInternalTransactionsByTracer failed: %v", err) + return nil, fmt.Errorf("getInternalTransactionsByTracer failed: %w", err) } var innerTxs = make([]*zktypes.InnerTx, 0) err = json.Unmarshal(buf.Bytes(), &innerTxs) if err != nil { - return nil, fmt.Errorf("json.Unmarshal failed: %v", err) + return nil, fmt.Errorf("json.Unmarshal failed: %w", err) } - log.Info(fmt.Sprintf("%x GetInternalTransactions: %d inner txs", txnHash, len(innerTxs))) + log.Info("GetInternalTransactions", "tx hash", txnHash, "inner txs count", len(innerTxs)) return innerTxs, nil }