mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
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:
parent
07fe67a904
commit
05148de519
5 changed files with 90 additions and 178 deletions
|
|
@ -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 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 {
|
type AccountWrapper struct {
|
||||||
Address common.Address `json:"address"`
|
Address common.Address `json:"address"`
|
||||||
Nonce uint64 `json:"nonce"`
|
Nonce uint64 `json:"nonce"`
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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 address’s code
|
// 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 {
|
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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue