clean outdated trace fields (#928)

* update eth/tracers/logger/logger_trace.go

* update types

* update `StructLog` logic

* update rollup/tracing/tracing.go
This commit is contained in:
HAOYUatHZ 2024-07-26 15:30:15 +08:00 committed by GitHub
parent 07fe67a904
commit 05148de519
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 90 additions and 178 deletions

View file

@ -17,6 +17,7 @@ type BlockTrace struct {
Header *Header `json:"header"` Header *Header `json:"header"`
Transactions []*TransactionData `json:"transactions"` Transactions []*TransactionData `json:"transactions"`
StorageTrace *StorageTrace `json:"storageTrace"` StorageTrace *StorageTrace `json:"storageTrace"`
Bytecodes []*BytecodeTrace `json:"codes"`
TxStorageTraces []*StorageTrace `json:"txStorageTraces,omitempty"` TxStorageTraces []*StorageTrace `json:"txStorageTraces,omitempty"`
ExecutionResults []*ExecutionResult `json:"executionResults"` ExecutionResults []*ExecutionResult `json:"executionResults"`
WithdrawTrieRoot common.Hash `json:"withdraw_trie_root,omitempty"` WithdrawTrieRoot common.Hash `json:"withdraw_trie_root,omitempty"`
@ -41,6 +42,14 @@ type StorageTrace struct {
DeletionProofs []hexutil.Bytes `json:"deletionProofs,omitempty"` 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 // ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction // while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value // 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 // currently they are just `from` and `to` account
AccountsAfter []*AccountWrapper `json:"accountAfter"` 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"` L1DataFee *hexutil.Big `json:"l1DataFee,omitempty"`
CallTrace json.RawMessage `json:"callTrace"` CallTrace json.RawMessage `json:"callTrace"`
PrestateTrace json.RawMessage `json:"prestateTrace"`
} }
// StructLogRes stores a structured log emitted by the EVM while replaying a // StructLogRes stores a structured log emitted by the EVM while replaying a
@ -87,7 +90,6 @@ type StructLogRes struct {
Memory *[]string `json:"memory,omitempty"` Memory *[]string `json:"memory,omitempty"`
Storage *map[string]string `json:"storage,omitempty"` Storage *map[string]string `json:"storage,omitempty"`
RefundCounter uint64 `json:"refund,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; // 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 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 addresss code, stack.nth_last(1) addresss code]
// CREATE | CREATE2: [created contracts code]
// CODESIZE | CODECOPY: [contracts code]
// EXTCODESIZE | EXTCODECOPY: [stack.nth_last(0) addresss code]
CodeList []string `json:"codeList,omitempty"`
// SSTORE | SLOAD: [storageProof]
// SELFDESTRUCT: [contract addresss account, stack.nth_last(0) addresss account]
// SELFBALANCE: [contract addresss account]
// BALANCE | EXTCODEHASH: [stack.nth_last(0) addresss account]
// CREATE | CREATE2: [created contract addresss account (before constructed),
// created contract address's account (after constructed)]
// CALL | CALLCODE: [caller contract addresss account,
// stack.nth_last(1) (i.e. callee) addresss account,
// callee contract address's account (value updated, before called)]
// STATICCALL: [stack.nth_last(1) (i.e. callee) addresss 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 { type AccountWrapper struct {
Address common.Address `json:"address"` Address common.Address `json:"address"`
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`

View file

@ -8,7 +8,6 @@ import (
"github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/hexutil"
"github.com/scroll-tech/go-ethereum/common/math" "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/scroll-tech/go-ethereum/core/vm"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -30,7 +29,6 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
Depth int `json:"depth"` Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"` RefundCounter uint64 `json:"refund"`
Err error `json:"-"` Err error `json:"-"`
ExtraData *types.ExtraData `json:"extraData"`
OpName string `json:"opName"` OpName string `json:"opName"`
ErrorString string `json:"error,omitempty"` ErrorString string `json:"error,omitempty"`
} }
@ -47,7 +45,6 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.Depth = s.Depth enc.Depth = s.Depth
enc.RefundCounter = s.RefundCounter enc.RefundCounter = s.RefundCounter
enc.Err = s.Err enc.Err = s.Err
enc.ExtraData = s.ExtraData
enc.OpName = s.OpName() enc.OpName = s.OpName()
enc.ErrorString = s.ErrorString() enc.ErrorString = s.ErrorString()
return json.Marshal(&enc) return json.Marshal(&enc)
@ -68,7 +65,6 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
Depth *int `json:"depth"` Depth *int `json:"depth"`
RefundCounter *uint64 `json:"refund"` RefundCounter *uint64 `json:"refund"`
Err error `json:"-"` Err error `json:"-"`
ExtraData *types.ExtraData `json:"extraData"`
} }
var dec StructLog var dec StructLog
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -110,8 +106,5 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
if dec.Err != nil { if dec.Err != nil {
s.Err = dec.Err s.Err = dec.Err
} }
if dec.ExtraData != nil {
s.ExtraData = dec.ExtraData
}
return nil return nil
} }

View file

@ -25,6 +25,7 @@ import (
"strings" "strings"
"sync/atomic" "sync/atomic"
"github.com/holiman/uint256"
"github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/hexutil"
"github.com/scroll-tech/go-ethereum/common/math" "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/crypto/codehash"
"github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/params"
"github.com/holiman/uint256"
) )
// Storage represents a contract's storage. // Storage represents a contract's storage.
@ -78,8 +78,6 @@ type StructLog struct {
Depth int `json:"depth"` Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"` RefundCounter uint64 `json:"refund"`
Err error `json:"-"` Err error `json:"-"`
// scroll-related
ExtraData *types.ExtraData `json:"extraData"`
} }
func (s *StructLog) clean() { func (s *StructLog) clean() {
@ -87,17 +85,9 @@ func (s *StructLog) clean() {
s.Stack = s.Stack[:0] s.Stack = s.Stack[:0]
s.ReturnData = s.ReturnData[:0] s.ReturnData = s.ReturnData[:0]
s.Storage = nil s.Storage = nil
s.ExtraData = nil
s.Err = nil s.Err = nil
} }
func (s *StructLog) getOrInitExtraData() *types.ExtraData {
if s.ExtraData == nil {
s.ExtraData = &types.ExtraData{}
}
return s.ExtraData
}
// overrides for gencodec // overrides for gencodec
type structLogMarshaling struct { type structLogMarshaling struct {
Gas math.HexOrDecimal64 Gas math.HexOrDecimal64
@ -121,6 +111,13 @@ func (s *StructLog) ErrorString() string {
return "" return ""
} }
type CodeInfo struct {
CodeSize uint64
KeccakCodeHash common.Hash
PoseidonCodeHash common.Hash
Code []byte
}
// StructLogger is an EVM state logger and implements EVMLogger. // StructLogger is an EVM state logger and implements EVMLogger.
// //
// StructLogger can capture state based on the given Log configuration and also keeps // 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 interrupt atomic.Bool // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption reason error // Textual reason for the interruption
// scroll-related
bytecodes map[common.Hash]CodeInfo
statesAffected map[common.Address]struct{} statesAffected map[common.Address]struct{}
createdAccount *types.AccountWrapper createdAccount *types.AccountWrapper
callStackLogInd []int callStackLogInd []int
@ -149,6 +148,7 @@ type StructLogger struct {
func NewStructLogger(cfg *Config) *StructLogger { func NewStructLogger(cfg *Config) *StructLogger {
logger := &StructLogger{ logger := &StructLogger{
storage: make(map[common.Address]Storage), storage: make(map[common.Address]Storage),
bytecodes: make(map[common.Hash]CodeInfo),
statesAffected: make(map[common.Address]struct{}), statesAffected: make(map[common.Address]struct{}),
} }
if cfg != nil { if cfg != nil {
@ -163,6 +163,7 @@ func (l *StructLogger) Reset() {
l.output = make([]byte, 0) l.output = make([]byte, 0)
l.logs = l.logs[:0] l.logs = l.logs[:0]
l.err = nil l.err = nil
l.bytecodes = make(map[common.Hash]CodeInfo)
l.statesAffected = make(map[common.Address]struct{}) l.statesAffected = make(map[common.Address]struct{})
l.createdAccount = nil l.createdAccount = nil
l.callStackLogInd = 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), Nonce: env.StateDB.GetNonce(to),
Balance: (*hexutil.Big)(value), Balance: (*hexutil.Big)(value),
} }
} else {
traceCodeWithAddress(l, to)
} }
l.statesAffected[from] = struct{}{} l.statesAffected[from] = struct{}{}
l.statesAffected[to] = 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) copy(rdata, rData)
} }
// create a new snapshot of the EVM. // 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] execFuncList, ok := OpcodeExecs[op]
if ok { if ok {
// execute trace func list. // execute trace func list.
for _, exec := range execFuncList { 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) 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 // in reality it is impossible for CREATE to trigger ErrContractAddressCollision
if op == vm.CREATE2 && err == nil { if op == vm.CREATE2 && err == nil {
_ = stack.Data()[stackLen-1] // value _ = 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) contractHash := l.env.StateDB.GetKeccakCodeHash(address)
if l.env.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != codehash.EmptyKeccakCodeHash) { 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{}{} 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") panic("unexpected evm depth in capture enter")
} }
l.statesAffected[to] = struct{}{} 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 // 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") panic("unexpected capture exit occur")
} }
theLogPos := l.callStackLogInd[stackH-1]
l.callStackLogInd = 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) { 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. // Output returns the VM return value captured by the trace.
func (l *StructLogger) Output() []byte { return l.output } 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 // UpdatedAccounts is used to collect all "touched" accounts
func (l *StructLogger) UpdatedAccounts() map[common.Address]struct{} { func (l *StructLogger) UpdatedAccounts() map[common.Address]struct{} {
return l.statesAffected return l.statesAffected
@ -598,7 +563,6 @@ func FormatLogs(logs []StructLog) []types.StructLogRes {
Depth: trace.Depth, Depth: trace.Depth,
Error: trace.ErrorString(), Error: trace.ErrorString(),
RefundCounter: trace.RefundCounter, RefundCounter: trace.RefundCounter,
ExtraData: trace.ExtraData,
} }
if trace.Stack != nil { if trace.Stack != nil {
stack := make([]string, len(trace.Stack)) stack := make([]string, len(trace.Stack))

View file

@ -2,12 +2,10 @@ package logger
import ( import (
"github.com/scroll-tech/go-ethereum/common" "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" "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 ( var (
// OpcodeExecs the map to load opcodes' trace funcs. // 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.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.DELEGATECALL: {traceToAddressCode, traceLastNAddressCode(1)},
vm.STATICCALL: {traceToAddressCode, traceLastNAddressCode(1), traceLastNAddressAccount(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.SELFDESTRUCT: {traceContractAccount, traceLastNAddressAccount(0)},
vm.SELFBALANCE: {traceContractAccount}, vm.SELFBALANCE: {traceContractAccount},
vm.BALANCE: {traceLastNAddressAccount(0)}, vm.BALANCE: {traceLastNAddressAccount(0)},
vm.EXTCODEHASH: {traceLastNAddressAccount(0)}, vm.EXTCODEHASH: {traceLastNAddressAccount(0)},
vm.CODESIZE: {traceContractCode}, vm.EXTCODESIZE: {traceLastNAddressAccount(0)},
vm.CODECOPY: {traceContractCode},
vm.EXTCODESIZE: {traceLastNAddressCode(0)},
vm.EXTCODECOPY: {traceLastNAddressCode(0)}, vm.EXTCODECOPY: {traceLastNAddressCode(0)},
} }
) )
// traceToAddressCode gets tx.to addresss code // traceToAddressCode gets tx.to addresss 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 { if l.env.To == nil {
return nil return nil
} }
code := l.env.StateDB.GetCode(*l.env.To) traceCodeWithAddress(l, *l.env.To)
extraData.CodeList = append(extraData.CodeList, hexutil.Encode(code))
return nil return nil
} }
// traceLastNAddressCode // traceLastNAddressCode
func traceLastNAddressCode(n int) traceFunc { 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 stack := scope.Stack
stackData := stack.Data() stackData := stack.Data()
stackLen := len(stackData) stackLen := len(stackData)
@ -51,25 +42,27 @@ func traceLastNAddressCode(n int) traceFunc {
return nil return nil
} }
address := common.Address(stackData[stackLen-1-n].Bytes20()) address := common.Address(stackData[stackLen-1-n].Bytes20())
code := l.env.StateDB.GetCode(address) traceCodeWithAddress(l, address)
extraData.CodeList = append(extraData.CodeList, hexutil.Encode(code))
l.statesAffected[address] = struct{}{} l.statesAffected[address] = struct{}{}
return nil return nil
} }
} }
// traceContractCode gets the contract's code func traceCodeWithAddress(l *StructLogger, address common.Address) {
func traceContractCode(l *StructLogger, scope *vm.ScopeContext, extraData *types.ExtraData) error { code := l.env.StateDB.GetCode(address)
code := l.env.StateDB.GetCode(scope.Contract.Address()) keccakCodeHash := l.env.StateDB.GetKeccakCodeHash(address)
extraData.CodeList = append(extraData.CodeList, hexutil.Encode(code)) poseidonCodeHash := l.env.StateDB.GetPoseidonCodeHash(address)
return nil codeSize := l.env.StateDB.GetCodeSize(address)
l.bytecodes[poseidonCodeHash] = CodeInfo{
codeSize,
keccakCodeHash,
poseidonCodeHash,
code,
}
} }
// traceContractAccount gets the contract's account // traceContractAccount gets the contract's account
func traceContractAccount(l *StructLogger, scope *vm.ScopeContext, extraData *types.ExtraData) error { func traceContractAccount(l *StructLogger, scope *vm.ScopeContext) error {
// Get account state.
state := getWrappedAccountForAddr(l, scope.Contract.Address())
extraData.StateList = append(extraData.StateList, state)
l.statesAffected[scope.Contract.Address()] = struct{}{} l.statesAffected[scope.Contract.Address()] = struct{}{}
return nil 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. // traceLastNAddressAccount returns func about the last N's address account.
func traceLastNAddressAccount(n int) traceFunc { 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 stack := scope.Stack
stackData := stack.Data() stackData := stack.Data()
stackLen := len(stackData) stackLen := len(stackData)
if stackLen <= n { if stackLen <= n {
return nil return nil
} }
address := common.Address(stackData[stackLen-1-n].Bytes20()) address := common.Address(stackData[stackLen-1-n].Bytes20())
state := getWrappedAccountForAddr(l, address)
extraData.StateList = append(extraData.StateList, state)
l.statesAffected[address] = struct{}{} l.statesAffected[address] = struct{}{}
return nil 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)
}

View file

@ -15,6 +15,7 @@ import (
"github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/state"
"github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/core/vm" "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"
"github.com/scroll-tech/go-ethereum/eth/tracers/logger" "github.com/scroll-tech/go-ethereum/eth/tracers/logger"
"github.com/scroll-tech/go-ethereum/eth/tracers/native" "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/fees"
"github.com/scroll-tech/go-ethereum/rollup/rcfg" "github.com/scroll-tech/go-ethereum/rollup/rcfg"
"github.com/scroll-tech/go-ethereum/rollup/withdrawtrie" "github.com/scroll-tech/go-ethereum/rollup/withdrawtrie"
// "github.com/scroll-tech/go-ethereum/trie/zkproof"
) )
// TracerWrapper implements ScrollTracerWrapper interface // TracerWrapper implements ScrollTracerWrapper interface
@ -52,22 +52,22 @@ type TraceEnv struct {
coinbase common.Address coinbase common.Address
// rMu lock is used to protect txs executed in parallel.
signer types.Signer signer types.Signer
state *state.StateDB state *state.StateDB
blockCtx vm.BlockContext blockCtx vm.BlockContext
// pMu lock is used to protect Proofs' read and write mutual exclusion, // The following Mutexes are used to protect against parallel read/write,
// since txs are executed in parallel, so this lock is required. // since txs are executed in parallel.
pMu sync.Mutex pMu sync.Mutex // for `TraceEnv.StorageTrace.Proofs`
// sMu is required because of txs are executed in parallel, sMu sync.Mutex // for `TraceEnv.state``
// this lock is used to protect StorageTrace's read and write mutual exclusion. cMu sync.Mutex // for `TraceEnv.Codes`
sMu sync.Mutex
ExecutionResults []*types.ExecutionResult
*types.StorageTrace *types.StorageTrace
TxStorageTraces []*types.StorageTrace TxStorageTraces []*types.StorageTrace
Codes map[common.Hash]logger.CodeInfo
// zktrie tracer is used for zktrie storage to build additional deletion proof // zktrie tracer is used for zktrie storage to build additional deletion proof
ZkTrieTracer map[string]state.ZktrieProofTracer ZkTrieTracer map[string]state.ZktrieProofTracer
ExecutionResults []*types.ExecutionResult
// StartL1QueueIndex is the next L1 message queue index that this block can process. // StartL1QueueIndex is the next L1 message queue index that this block can process.
// Example: If the parent block included QueueIndex=9, then StartL1QueueIndex will // 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()), signer: types.MakeSigner(chainConfig, block.Number(), block.Time()),
state: statedb, state: statedb,
blockCtx: blockCtx, blockCtx: blockCtx,
ExecutionResults: make([]*types.ExecutionResult, block.Transactions().Len()),
StorageTrace: &types.StorageTrace{ StorageTrace: &types.StorageTrace{
RootBefore: rootBefore, RootBefore: rootBefore,
RootAfter: block.Root(), RootAfter: block.Root(),
Proofs: make(map[string][]hexutil.Bytes), Proofs: make(map[string][]hexutil.Bytes),
StorageProofs: make(map[string]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()), TxStorageTraces: make([]*types.StorageTrace, block.Transactions().Len()),
Codes: make(map[common.Hash]logger.CodeInfo),
ZkTrieTracer: make(map[string]state.ZktrieProofTracer),
StartL1QueueIndex: startL1QueueIndex, StartL1QueueIndex: startL1QueueIndex,
} }
} }
@ -314,15 +315,9 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B
if err != nil { if err != nil {
return fmt.Errorf("failed to create callTracer: %w", err) 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 := &native.MuxTracer{}
tracer.Append("structLogger", structLogger) tracer.Append("structLogger", structLogger)
tracer.Append("callTracer", callTracer) tracer.Append("callTracer", callTracer)
tracer.Append("prestateTracer", prestateTracer)
// Run the transaction with tracing enabled. // Run the transaction with tracing enabled.
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), state, env.chainConfig, vm.Config{Tracer: tracer, NoBaseFee: true}) 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() 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 // merge required proof data
proofAccounts := structLogger.UpdatedAccounts() proofAccounts := structLogger.UpdatedAccounts()
proofAccounts[vmenv.FeeRecipient()] = struct{}{} proofAccounts[vmenv.FeeRecipient()] = struct{}{}
@ -477,10 +481,6 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B
if err != nil { if err != nil {
return fmt.Errorf("failed to get callTracer result: %w", err) 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{ env.ExecutionResults[index] = &types.ExecutionResult{
From: sender, From: sender,
@ -493,7 +493,6 @@ func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.B
ReturnValue: fmt.Sprintf("%x", returnVal), ReturnValue: fmt.Sprintf("%x", returnVal),
StructLogs: logger.FormatLogs(structLogger.StructLogs()), StructLogs: logger.FormatLogs(structLogger.StructLogs()),
CallTrace: callTrace, CallTrace: callTrace,
PrestateTrace: prestateTrace,
} }
env.TxStorageTraces[index] = txStorageTrace env.TxStorageTraces[index] = txStorageTrace
@ -564,24 +563,27 @@ func (env *TraceEnv) fillBlockTrace(block *types.Block) (*types.BlockTrace, erro
CodeSize: statedb.GetCodeSize(env.coinbase), CodeSize: statedb.GetCodeSize(env.coinbase),
}, },
Header: block.Header(), Header: block.Header(),
StorageTrace: env.StorageTrace,
ExecutionResults: env.ExecutionResults, ExecutionResults: env.ExecutionResults,
StorageTrace: env.StorageTrace,
TxStorageTraces: env.TxStorageTraces, TxStorageTraces: env.TxStorageTraces,
Bytecodes: make([]*types.BytecodeTrace, 0, len(env.Codes)),
Transactions: txs, Transactions: txs,
StartL1QueueIndex: env.StartL1QueueIndex, StartL1QueueIndex: env.StartL1QueueIndex,
} }
for i, tx := range block.Transactions() { blockTrace.Bytecodes = append(blockTrace.Bytecodes, &types.BytecodeTrace{
evmTrace := env.ExecutionResults[i] CodeSize: 0,
// Contract is created. KeccakCodeHash: codehash.EmptyKeccakCodeHash,
if tx.To() == nil { PoseidonCodeHash: codehash.EmptyPoseidonCodeHash,
evmTrace.ByteCode = hexutil.Encode(tx.Data()) Code: hexutil.Bytes{},
} else { // contract call be included at this case, specially fallback call's data is empty. })
evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To())) for _, codeInfo := range env.Codes {
// Get tx.to address's code hash. blockTrace.Bytecodes = append(blockTrace.Bytecodes, &types.BytecodeTrace{
codeHash := statedb.GetPoseidonCodeHash(*tx.To()) CodeSize: codeInfo.CodeSize,
evmTrace.PoseidonCodeHash = &codeHash KeccakCodeHash: codeInfo.KeccakCodeHash,
} PoseidonCodeHash: codeInfo.PoseidonCodeHash,
Code: codeInfo.Code,
})
} }
blockTrace.WithdrawTrieRoot = withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, env.state) blockTrace.WithdrawTrieRoot = withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, env.state)