Merge branch 'master' into release_v1.15.2

This commit is contained in:
Andriy 2025-02-21 09:08:15 +01:00
commit 15b46f238d
No known key found for this signature in database
GPG key ID: 5EDD552B172A91EB
2 changed files with 134 additions and 0 deletions

View file

@ -195,6 +195,13 @@ func (s *StructLog) toLegacyJSON() json.RawMessage {
return element
}
type wrappedLog struct {
parent *wrappedLog
error error
log StructLog
children []*wrappedLog
}
// StructLogger is an EVM state logger and implements EVMLogger.
//
// StructLogger can capture state based on the given Log configuration and also keeps
@ -204,6 +211,9 @@ func (s *StructLog) toLegacyJSON() json.RawMessage {
// A StructLogger can either yield it's output immediately (streaming) or store for
// later output.
type StructLogger struct {
current *wrappedLog
depth int
cfg Config
env *tracing.VMContext
@ -274,7 +284,38 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
stack = scope.StackData()
stackLen = len(stack)
)
for ; l.depth > depth-1; l.depth = l.depth - 1 {
i := l.depth - (depth - 1)
if l.current.error == nil {
switch stack[len(stack)-i].Bytes32()[31] {
case 0x00:
l.current.error = fmt.Errorf("call failed")
}
}
l.current = l.current.parent
}
if err != nil {
l.current.error = err
}
switch op {
case vm.CALL, vm.DELEGATECALL, vm.STATICCALL, vm.CALLCODE:
l.depth = l.depth + 1
wl := &wrappedLog{
parent: l.current,
error: l.current.error,
}
l.current.children = append(l.current.children, wl)
l.current = wl
case vm.REVERT:
l.current.error = vm.ErrExecutionReverted
return
default:
return
}
log := StructLog{pc, op, gas, cost, nil, len(memory), nil, nil, nil, depth, l.env.StateDB.GetRefund(), err}
l.current.log = log
if l.cfg.EnableMemory {
log.Memory = memory
}
@ -331,6 +372,18 @@ func (l *StructLogger) OnExit(depth int, output []byte, gasUsed uint64, err erro
if l.skip {
return
}
for ; l.depth > 1; l.depth-- {
l.current = l.current.parent
}
l.current.log = StructLog{
Op: vm.CALL,
GasCost: gasUsed,
ReturnData: output,
Depth: 0,
Err: err,
}
l.output = output
l.err = err
// TODO @holiman, should we output the per-scope output?
@ -370,6 +423,8 @@ func (l *StructLogger) Stop(err error) {
func (l *StructLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
l.env = env
l.depth = 0
l.current = &wrappedLog{}
}
func (l *StructLogger) OnSystemCallStart(env *tracing.VMContext) {
l.skip = true
@ -392,6 +447,20 @@ func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) {
}
}
// Depth first append for all children (stack max depth is 1024)
func (l *wrappedLog) getLogs() []StructLog {
var logs []StructLog
l.log.Err = l.error
logs = append(logs, l.log)
for _, child := range l.children {
logs = append(logs, child.getLogs()...)
}
return logs
}
// StructLogs returns the captured log entries.
func (l *StructLogger) StructLogs() []StructLog { return l.current.getLogs() }
// Error returns the VM error captured by the trace.
func (l *StructLogger) Error() error { return l.err }

View file

@ -864,6 +864,71 @@ func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs,
return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, blockOverrides, api.b.RPCGasCap())
}
// 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
type ExecutionResult struct {
Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue string `json:"returnValue"`
StructLogs []StructLogRes `json:"structLogs"`
}
// StructLogRes stores a structured log emitted by the EVM while replaying a
// transaction in debug mode
type StructLogRes struct {
Pc uint64 `json:"pc"`
Op string `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Depth int `json:"depth"`
Error string `json:"error,omitempty"`
Stack *[]string `json:"stack,omitempty"`
Memory *[]string `json:"memory,omitempty"`
Storage *map[string]string `json:"storage,omitempty"`
}
// FormatLogs formats EVM returned structured logs for json output
func FormatLogs(logs []logger.StructLog) []StructLogRes {
formatted := make([]StructLogRes, len(logs))
for index, trace := range logs {
var errString string
if trace.Err != nil {
errString = trace.Err.Error()
}
formatted[index] = StructLogRes{
Pc: trace.Pc,
Op: trace.Op.String(),
Gas: trace.Gas,
GasCost: trace.GasCost,
Depth: trace.Depth,
Error: errString,
}
if trace.Stack != nil {
stack := make([]string, len(trace.Stack))
for i, stackValue := range trace.Stack {
stack[i] = stackValue.Hex()
}
formatted[index].Stack = &stack
}
if trace.Memory != nil {
memory := make([]string, 0, (len(trace.Memory)+31)/32)
for i := 0; i+32 <= len(trace.Memory); i += 32 {
memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
}
formatted[index].Memory = &memory
}
if trace.Storage != nil {
storage := make(map[string]string)
for i, storageValue := range trace.Storage {
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
}
formatted[index].Storage = &storage
}
}
return formatted
}
// RPCMarshalHeader converts the given header to the RPC output .
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
result := map[string]interface{}{