From 05148de519353cd598ab0cfa048d59cf8362f267 Mon Sep 17 00:00:00 2001 From: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:30:15 +0800 Subject: [PATCH] clean outdated trace fields (#928) * update eth/tracers/logger/logger_trace.go * update types * update `StructLog` logic * update rollup/tracing/tracing.go --- core/types/l2trace.go | 43 ++++----------- eth/tracers/logger/gen_structlog.go | 7 --- eth/tracers/logger/logger.go | 82 ++++++++--------------------- eth/tracers/logger/logger_trace.go | 64 +++++++--------------- rollup/tracing/tracing.go | 72 +++++++++++++------------ 5 files changed, 90 insertions(+), 178 deletions(-) diff --git a/core/types/l2trace.go b/core/types/l2trace.go index bc932c64bd..c0d76f067a 100644 --- a/core/types/l2trace.go +++ b/core/types/l2trace.go @@ -17,6 +17,7 @@ type BlockTrace struct { Header *Header `json:"header"` Transactions []*TransactionData `json:"transactions"` StorageTrace *StorageTrace `json:"storageTrace"` + Bytecodes []*BytecodeTrace `json:"codes"` TxStorageTraces []*StorageTrace `json:"txStorageTraces,omitempty"` ExecutionResults []*ExecutionResult `json:"executionResults"` WithdrawTrieRoot common.Hash `json:"withdraw_trie_root,omitempty"` @@ -41,6 +42,14 @@ type StorageTrace struct { DeletionProofs []hexutil.Bytes `json:"deletionProofs,omitempty"` } +// BytecodeTrace stores all accessed bytecodes +type BytecodeTrace struct { + CodeSize uint64 `json:"codeSize"` + KeccakCodeHash common.Hash `json:"keccakCodeHash"` + PoseidonCodeHash common.Hash `json:"hash"` + Code hexutil.Bytes `json:"code"` +} + // 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 @@ -62,15 +71,9 @@ type ExecutionResult struct { // currently they are just `from` and `to` account AccountsAfter []*AccountWrapper `json:"accountAfter"` - // `PoseidonCodeHash` only exists when tx is a contract call. - PoseidonCodeHash *common.Hash `json:"poseidonCodeHash,omitempty"` - // If it is a contract call, the contract code is returned. - ByteCode string `json:"byteCode,omitempty"` - L1DataFee *hexutil.Big `json:"l1DataFee,omitempty"` - CallTrace json.RawMessage `json:"callTrace"` - PrestateTrace json.RawMessage `json:"prestateTrace"` + CallTrace json.RawMessage `json:"callTrace"` } // StructLogRes stores a structured log emitted by the EVM while replaying a @@ -87,7 +90,6 @@ type StructLogRes struct { Memory *[]string `json:"memory,omitempty"` Storage *map[string]string `json:"storage,omitempty"` RefundCounter uint64 `json:"refund,omitempty"` - ExtraData *ExtraData `json:"extraData,omitempty"` } // NewStructLogResBasic Basic StructLogRes skeleton, Stack&Memory&Storage&ExtraData are separated from it for GC optimization; @@ -108,31 +110,6 @@ func NewStructLogResBasic(pc uint64, op string, gas, gasCost uint64, depth int, return logRes } -type ExtraData struct { - // Indicate the call succeeds or not for CALL/CREATE op - CallFailed bool `json:"callFailed,omitempty"` - // CALL | CALLCODE | DELEGATECALL | STATICCALL: [tx.to address’s code, stack.nth_last(1) address’s code] - // CREATE | CREATE2: [created contract’s code] - // CODESIZE | CODECOPY: [contract’s code] - // EXTCODESIZE | EXTCODECOPY: [stack.nth_last(0) address’s code] - CodeList []string `json:"codeList,omitempty"` - // SSTORE | SLOAD: [storageProof] - // SELFDESTRUCT: [contract address’s account, stack.nth_last(0) address’s account] - // SELFBALANCE: [contract address’s account] - // BALANCE | EXTCODEHASH: [stack.nth_last(0) address’s account] - // CREATE | CREATE2: [created contract address’s account (before constructed), - // created contract address's account (after constructed)] - // CALL | CALLCODE: [caller contract address’s account, - // stack.nth_last(1) (i.e. callee) address’s account, - // callee contract address's account (value updated, before called)] - // STATICCALL: [stack.nth_last(1) (i.e. callee) address’s account, - // callee contract address's account (before called)] - StateList []*AccountWrapper `json:"proofList,omitempty"` - // The status of caller, it would be captured twice: - // 1. before execution and 2. updated in CaptureEnter (for CALL/CALLCODE it duplicated with StateList[0]) - Caller []*AccountWrapper `json:"caller,omitempty"` -} - type AccountWrapper struct { Address common.Address `json:"address"` Nonce uint64 `json:"nonce"` diff --git a/eth/tracers/logger/gen_structlog.go b/eth/tracers/logger/gen_structlog.go index 56c06dfda2..6a8698e27d 100644 --- a/eth/tracers/logger/gen_structlog.go +++ b/eth/tracers/logger/gen_structlog.go @@ -8,7 +8,6 @@ import ( "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/math" - "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/vm" "github.com/holiman/uint256" ) @@ -30,7 +29,6 @@ func (s StructLog) MarshalJSON() ([]byte, error) { Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` Err error `json:"-"` - ExtraData *types.ExtraData `json:"extraData"` OpName string `json:"opName"` ErrorString string `json:"error,omitempty"` } @@ -47,7 +45,6 @@ func (s StructLog) MarshalJSON() ([]byte, error) { enc.Depth = s.Depth enc.RefundCounter = s.RefundCounter enc.Err = s.Err - enc.ExtraData = s.ExtraData enc.OpName = s.OpName() enc.ErrorString = s.ErrorString() return json.Marshal(&enc) @@ -68,7 +65,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { Depth *int `json:"depth"` RefundCounter *uint64 `json:"refund"` Err error `json:"-"` - ExtraData *types.ExtraData `json:"extraData"` } var dec StructLog if err := json.Unmarshal(input, &dec); err != nil { @@ -110,8 +106,5 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { if dec.Err != nil { s.Err = dec.Err } - if dec.ExtraData != nil { - s.ExtraData = dec.ExtraData - } return nil } diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index fb6a18899f..96f1ed4fcf 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -25,6 +25,7 @@ import ( "strings" "sync/atomic" + "github.com/holiman/uint256" "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/math" @@ -34,7 +35,6 @@ import ( "github.com/scroll-tech/go-ethereum/crypto/codehash" "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/params" - "github.com/holiman/uint256" ) // Storage represents a contract's storage. @@ -78,8 +78,6 @@ type StructLog struct { Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` Err error `json:"-"` - // scroll-related - ExtraData *types.ExtraData `json:"extraData"` } func (s *StructLog) clean() { @@ -87,17 +85,9 @@ func (s *StructLog) clean() { s.Stack = s.Stack[:0] s.ReturnData = s.ReturnData[:0] s.Storage = nil - s.ExtraData = nil s.Err = nil } -func (s *StructLog) getOrInitExtraData() *types.ExtraData { - if s.ExtraData == nil { - s.ExtraData = &types.ExtraData{} - } - return s.ExtraData -} - // overrides for gencodec type structLogMarshaling struct { Gas math.HexOrDecimal64 @@ -121,6 +111,13 @@ func (s *StructLog) ErrorString() string { return "" } +type CodeInfo struct { + CodeSize uint64 + KeccakCodeHash common.Hash + PoseidonCodeHash common.Hash + Code []byte +} + // StructLogger is an EVM state logger and implements EVMLogger. // // StructLogger can capture state based on the given Log configuration and also keeps @@ -140,6 +137,8 @@ type StructLogger struct { interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption + // scroll-related + bytecodes map[common.Hash]CodeInfo statesAffected map[common.Address]struct{} createdAccount *types.AccountWrapper callStackLogInd []int @@ -149,6 +148,7 @@ type StructLogger struct { func NewStructLogger(cfg *Config) *StructLogger { logger := &StructLogger{ storage: make(map[common.Address]Storage), + bytecodes: make(map[common.Hash]CodeInfo), statesAffected: make(map[common.Address]struct{}), } if cfg != nil { @@ -163,6 +163,7 @@ func (l *StructLogger) Reset() { l.output = make([]byte, 0) l.logs = l.logs[:0] l.err = nil + l.bytecodes = make(map[common.Hash]CodeInfo) l.statesAffected = make(map[common.Address]struct{}) l.createdAccount = nil l.callStackLogInd = nil @@ -180,7 +181,10 @@ func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common. Nonce: env.StateDB.GetNonce(to), Balance: (*hexutil.Big)(value), } + } else { + traceCodeWithAddress(l, to) } + l.statesAffected[from] = struct{}{} l.statesAffected[to] = struct{}{} } @@ -253,23 +257,18 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s copy(rdata, rData) } // create a new snapshot of the EVM. - structLog := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err, nil} + structLog := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err} execFuncList, ok := OpcodeExecs[op] if ok { // execute trace func list. for _, exec := range execFuncList { - if e := exec(l, scope, structLog.getOrInitExtraData()); e != nil { + if e := exec(l, scope); e != nil { log.Error("Failed to trace data", "opcode", op.String(), "err", e) } } } - // for each "calling" op, pick the caller's state - switch op { - case vm.CALL, vm.CALLCODE, vm.STATICCALL, vm.DELEGATECALL, vm.CREATE, vm.CREATE2: - extraData := structLog.getOrInitExtraData() - extraData.Caller = append(extraData.Caller, getWrappedAccountForAddr(l, scope.Contract.Address())) - } + // in reality it is impossible for CREATE to trigger ErrContractAddressCollision if op == vm.CREATE2 && err == nil { _ = stack.Data()[stackLen-1] // value @@ -286,9 +285,6 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s contractHash := l.env.StateDB.GetKeccakCodeHash(address) if l.env.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != codehash.EmptyKeccakCodeHash) { - extraData := structLog.getOrInitExtraData() - wrappedStatus := getWrappedAccountForAddr(l, address) - extraData.StateList = append(extraData.StateList, wrappedStatus) l.statesAffected[address] = struct{}{} } } @@ -341,16 +337,6 @@ func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to commo panic("unexpected evm depth in capture enter") } l.statesAffected[to] = struct{}{} - theLog := l.logs[lastLogPos] - theLog.getOrInitExtraData() - // handling additional updating for CALL/STATICCALL/CALLCODE/CREATE/CREATE2 only - // append extraData part for the log, capture the account status (the nonce / balance has been updated in capture enter) - wrappedStatus := getWrappedAccountForAddr(l, to) - theLog.ExtraData.StateList = append(theLog.ExtraData.StateList, wrappedStatus) - // finally we update the caller's status (it is possible that nonce and balance being updated) - if len(theLog.ExtraData.Caller) == 1 { - theLog.ExtraData.Caller = append(theLog.ExtraData.Caller, getWrappedAccountForAddr(l, from)) - } } // CaptureExit phase, a CREATE has its target address's code being set and queryable @@ -360,33 +346,7 @@ func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) { panic("unexpected capture exit occur") } - theLogPos := l.callStackLogInd[stackH-1] l.callStackLogInd = l.callStackLogInd[:stackH-1] - theLog := l.logs[theLogPos] - // update "forecast" data - if err != nil { - theLog.ExtraData.CallFailed = true - } - - // handling updating for CREATE only - switch theLog.Op { - case vm.CREATE, vm.CREATE2: - // append extraData part for the log whose op is CREATE(2), capture the account status (the codehash would be updated in capture exit) - dataLen := len(theLog.ExtraData.StateList) - if dataLen == 0 { - panic("unexpected data capture for target op") - } - - lastAccData := theLog.ExtraData.StateList[dataLen-1] - wrappedStatus := getWrappedAccountForAddr(l, lastAccData.Address) - theLog.ExtraData.StateList = append(theLog.ExtraData.StateList, wrappedStatus) - code := getCodeForAddr(l, lastAccData.Address) - theLog.ExtraData.CodeList = append(theLog.ExtraData.CodeList, hexutil.Encode(code)) - default: - //do nothing for other op code - return - } - } func (l *StructLogger) GetResult() (json.RawMessage, error) { @@ -450,6 +410,11 @@ func (l *StructLogger) Error() error { return l.err } // Output returns the VM return value captured by the trace. func (l *StructLogger) Output() []byte { return l.output } +// TracedBytecodes is used to collect all "touched" bytecodes +func (l *StructLogger) TracedBytecodes() map[common.Hash]CodeInfo { + return l.bytecodes +} + // UpdatedAccounts is used to collect all "touched" accounts func (l *StructLogger) UpdatedAccounts() map[common.Address]struct{} { return l.statesAffected @@ -598,7 +563,6 @@ func FormatLogs(logs []StructLog) []types.StructLogRes { Depth: trace.Depth, Error: trace.ErrorString(), RefundCounter: trace.RefundCounter, - ExtraData: trace.ExtraData, } if trace.Stack != nil { stack := make([]string, len(trace.Stack)) diff --git a/eth/tracers/logger/logger_trace.go b/eth/tracers/logger/logger_trace.go index 4d76edc077..be42ccc82a 100644 --- a/eth/tracers/logger/logger_trace.go +++ b/eth/tracers/logger/logger_trace.go @@ -2,12 +2,10 @@ package logger import ( "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/common/hexutil" - "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/vm" ) -type traceFunc func(l *StructLogger, scope *vm.ScopeContext, extraData *types.ExtraData) error +type traceFunc func(l *StructLogger, scope *vm.ScopeContext) error var ( // OpcodeExecs the map to load opcodes' trace funcs. @@ -16,34 +14,27 @@ var ( vm.CALLCODE: {traceToAddressCode, traceLastNAddressCode(1), traceContractAccount, traceLastNAddressAccount(1)}, // contract account is the caller, stack.nth_last(1) is the callee's address vm.DELEGATECALL: {traceToAddressCode, traceLastNAddressCode(1)}, vm.STATICCALL: {traceToAddressCode, traceLastNAddressCode(1), traceLastNAddressAccount(1)}, - vm.CREATE: {}, // caller is already recorded in ExtraData.Caller, callee is recorded in CaptureEnter&CaptureExit - vm.CREATE2: {}, // caller is already recorded in ExtraData.Caller, callee is recorded in CaptureEnter&CaptureExit - vm.SLOAD: {}, // trace storage in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag - vm.SSTORE: {}, // trace storage in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag vm.SELFDESTRUCT: {traceContractAccount, traceLastNAddressAccount(0)}, vm.SELFBALANCE: {traceContractAccount}, vm.BALANCE: {traceLastNAddressAccount(0)}, vm.EXTCODEHASH: {traceLastNAddressAccount(0)}, - vm.CODESIZE: {traceContractCode}, - vm.CODECOPY: {traceContractCode}, - vm.EXTCODESIZE: {traceLastNAddressCode(0)}, + vm.EXTCODESIZE: {traceLastNAddressAccount(0)}, vm.EXTCODECOPY: {traceLastNAddressCode(0)}, } ) // traceToAddressCode gets tx.to address’s code -func traceToAddressCode(l *StructLogger, scope *vm.ScopeContext, extraData *types.ExtraData) error { +func traceToAddressCode(l *StructLogger, scope *vm.ScopeContext) error { if l.env.To == nil { return nil } - code := l.env.StateDB.GetCode(*l.env.To) - extraData.CodeList = append(extraData.CodeList, hexutil.Encode(code)) + traceCodeWithAddress(l, *l.env.To) return nil } // traceLastNAddressCode func traceLastNAddressCode(n int) traceFunc { - return func(l *StructLogger, scope *vm.ScopeContext, extraData *types.ExtraData) error { + return func(l *StructLogger, scope *vm.ScopeContext) error { stack := scope.Stack stackData := stack.Data() stackLen := len(stackData) @@ -51,25 +42,27 @@ func traceLastNAddressCode(n int) traceFunc { return nil } address := common.Address(stackData[stackLen-1-n].Bytes20()) - code := l.env.StateDB.GetCode(address) - extraData.CodeList = append(extraData.CodeList, hexutil.Encode(code)) + traceCodeWithAddress(l, address) l.statesAffected[address] = struct{}{} return nil } } -// traceContractCode gets the contract's code -func traceContractCode(l *StructLogger, scope *vm.ScopeContext, extraData *types.ExtraData) error { - code := l.env.StateDB.GetCode(scope.Contract.Address()) - extraData.CodeList = append(extraData.CodeList, hexutil.Encode(code)) - return nil +func traceCodeWithAddress(l *StructLogger, address common.Address) { + code := l.env.StateDB.GetCode(address) + keccakCodeHash := l.env.StateDB.GetKeccakCodeHash(address) + poseidonCodeHash := l.env.StateDB.GetPoseidonCodeHash(address) + codeSize := l.env.StateDB.GetCodeSize(address) + l.bytecodes[poseidonCodeHash] = CodeInfo{ + codeSize, + keccakCodeHash, + poseidonCodeHash, + code, + } } // traceContractAccount gets the contract's account -func traceContractAccount(l *StructLogger, scope *vm.ScopeContext, extraData *types.ExtraData) error { - // Get account state. - state := getWrappedAccountForAddr(l, scope.Contract.Address()) - extraData.StateList = append(extraData.StateList, state) +func traceContractAccount(l *StructLogger, scope *vm.ScopeContext) error { l.statesAffected[scope.Contract.Address()] = struct{}{} return nil @@ -77,34 +70,17 @@ func traceContractAccount(l *StructLogger, scope *vm.ScopeContext, extraData *ty // traceLastNAddressAccount returns func about the last N's address account. func traceLastNAddressAccount(n int) traceFunc { - return func(l *StructLogger, scope *vm.ScopeContext, extraData *types.ExtraData) error { + return func(l *StructLogger, scope *vm.ScopeContext) error { stack := scope.Stack stackData := stack.Data() stackLen := len(stackData) if stackLen <= n { return nil } + address := common.Address(stackData[stackLen-1-n].Bytes20()) - state := getWrappedAccountForAddr(l, address) - extraData.StateList = append(extraData.StateList, state) l.statesAffected[address] = struct{}{} return nil } } - -// StorageWrapper will be empty -func getWrappedAccountForAddr(l *StructLogger, address common.Address) *types.AccountWrapper { - return &types.AccountWrapper{ - Address: address, - Nonce: l.env.StateDB.GetNonce(address), - Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)), - KeccakCodeHash: l.env.StateDB.GetKeccakCodeHash(address), - PoseidonCodeHash: l.env.StateDB.GetPoseidonCodeHash(address), - CodeSize: l.env.StateDB.GetCodeSize(address), - } -} - -func getCodeForAddr(l *StructLogger, address common.Address) []byte { - return l.env.StateDB.GetCode(address) -} diff --git a/rollup/tracing/tracing.go b/rollup/tracing/tracing.go index 2dfacf42d7..a906461b39 100644 --- a/rollup/tracing/tracing.go +++ b/rollup/tracing/tracing.go @@ -15,6 +15,7 @@ import ( "github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/vm" + "github.com/scroll-tech/go-ethereum/crypto/codehash" "github.com/scroll-tech/go-ethereum/eth/tracers" "github.com/scroll-tech/go-ethereum/eth/tracers/logger" "github.com/scroll-tech/go-ethereum/eth/tracers/native" @@ -24,7 +25,6 @@ import ( "github.com/scroll-tech/go-ethereum/rollup/fees" "github.com/scroll-tech/go-ethereum/rollup/rcfg" "github.com/scroll-tech/go-ethereum/rollup/withdrawtrie" - // "github.com/scroll-tech/go-ethereum/trie/zkproof" ) // TracerWrapper implements ScrollTracerWrapper interface @@ -52,22 +52,22 @@ type TraceEnv struct { coinbase common.Address - // rMu lock is used to protect txs executed in parallel. signer types.Signer state *state.StateDB blockCtx vm.BlockContext - // pMu lock is used to protect Proofs' read and write mutual exclusion, - // since txs are executed in parallel, so this lock is required. - pMu sync.Mutex - // sMu is required because of txs are executed in parallel, - // this lock is used to protect StorageTrace's read and write mutual exclusion. - sMu sync.Mutex + // The following Mutexes are used to protect against parallel read/write, + // since txs are executed in parallel. + pMu sync.Mutex // for `TraceEnv.StorageTrace.Proofs` + sMu sync.Mutex // for `TraceEnv.state`` + cMu sync.Mutex // for `TraceEnv.Codes` + + ExecutionResults []*types.ExecutionResult *types.StorageTrace TxStorageTraces []*types.StorageTrace + Codes map[common.Hash]logger.CodeInfo // zktrie tracer is used for zktrie storage to build additional deletion proof - ZkTrieTracer map[string]state.ZktrieProofTracer - ExecutionResults []*types.ExecutionResult + ZkTrieTracer map[string]state.ZktrieProofTracer // StartL1QueueIndex is the next L1 message queue index that this block can process. // Example: If the parent block included QueueIndex=9, then StartL1QueueIndex will @@ -97,15 +97,16 @@ func CreateTraceEnvHelper(chainConfig *params.ChainConfig, logConfig *logger.Con signer: types.MakeSigner(chainConfig, block.Number(), block.Time()), state: statedb, blockCtx: blockCtx, + ExecutionResults: make([]*types.ExecutionResult, block.Transactions().Len()), StorageTrace: &types.StorageTrace{ RootBefore: rootBefore, RootAfter: block.Root(), Proofs: make(map[string][]hexutil.Bytes), StorageProofs: make(map[string]map[string][]hexutil.Bytes), }, - ZkTrieTracer: make(map[string]state.ZktrieProofTracer), - ExecutionResults: make([]*types.ExecutionResult, block.Transactions().Len()), TxStorageTraces: make([]*types.StorageTrace, block.Transactions().Len()), + Codes: make(map[common.Hash]logger.CodeInfo), + ZkTrieTracer: make(map[string]state.ZktrieProofTracer), StartL1QueueIndex: startL1QueueIndex, } } @@ -314,15 +315,9 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B if err != nil { return fmt.Errorf("failed to create callTracer: %w", err) } - prestateTracerConfig := native.PrestateTracerConfig{DiffMode: false} - prestateTracer, err := native.NewPrestateTracerWithConfig(&tracerContext, prestateTracerConfig) - if err != nil { - return fmt.Errorf("failed to create prestateTracer: %w", err) - } tracer := &native.MuxTracer{} tracer.Append("structLogger", structLogger) tracer.Append("callTracer", callTracer) - tracer.Append("prestateTracer", prestateTracer) // Run the transaction with tracing enabled. vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), state, env.chainConfig, vm.Config{Tracer: tracer, NoBaseFee: true}) @@ -376,6 +371,15 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B txStorageTrace.RootAfter = block.Root() } + // merge bytecodes + env.cMu.Lock() + for codeHash, codeInfo := range structLogger.TracedBytecodes() { + if codeHash != (common.Hash{}) { + env.Codes[codeHash] = codeInfo + } + } + env.cMu.Unlock() + // merge required proof data proofAccounts := structLogger.UpdatedAccounts() proofAccounts[vmenv.FeeRecipient()] = struct{}{} @@ -477,10 +481,6 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B if err != nil { return fmt.Errorf("failed to get callTracer result: %w", err) } - prestateTrace, err := prestateTracer.GetResult() - if err != nil { - return fmt.Errorf("failed to get prestateTracer result: %w", err) - } env.ExecutionResults[index] = &types.ExecutionResult{ From: sender, @@ -493,7 +493,6 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B ReturnValue: fmt.Sprintf("%x", returnVal), StructLogs: logger.FormatLogs(structLogger.StructLogs()), CallTrace: callTrace, - PrestateTrace: prestateTrace, } env.TxStorageTraces[index] = txStorageTrace @@ -564,24 +563,27 @@ func (env *TraceEnv) fillBlockTrace(block *types.Block) (*types.BlockTrace, erro CodeSize: statedb.GetCodeSize(env.coinbase), }, Header: block.Header(), - StorageTrace: env.StorageTrace, ExecutionResults: env.ExecutionResults, + StorageTrace: env.StorageTrace, TxStorageTraces: env.TxStorageTraces, + Bytecodes: make([]*types.BytecodeTrace, 0, len(env.Codes)), Transactions: txs, StartL1QueueIndex: env.StartL1QueueIndex, } - for i, tx := range block.Transactions() { - evmTrace := env.ExecutionResults[i] - // Contract is created. - if tx.To() == nil { - evmTrace.ByteCode = hexutil.Encode(tx.Data()) - } else { // contract call be included at this case, specially fallback call's data is empty. - evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To())) - // Get tx.to address's code hash. - codeHash := statedb.GetPoseidonCodeHash(*tx.To()) - evmTrace.PoseidonCodeHash = &codeHash - } + blockTrace.Bytecodes = append(blockTrace.Bytecodes, &types.BytecodeTrace{ + CodeSize: 0, + KeccakCodeHash: codehash.EmptyKeccakCodeHash, + PoseidonCodeHash: codehash.EmptyPoseidonCodeHash, + Code: hexutil.Bytes{}, + }) + for _, codeInfo := range env.Codes { + blockTrace.Bytecodes = append(blockTrace.Bytecodes, &types.BytecodeTrace{ + CodeSize: codeInfo.CodeSize, + KeccakCodeHash: codeInfo.KeccakCodeHash, + PoseidonCodeHash: codeInfo.PoseidonCodeHash, + Code: codeInfo.Code, + }) } blockTrace.WithdrawTrieRoot = withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, env.state)