mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
core/vm, eth/tracers: simplify ethcall response type, encode early
This commit is contained in:
parent
966b62dbcb
commit
220c250db8
2 changed files with 92 additions and 80 deletions
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/asm"
|
"github.com/ethereum/go-ethereum/core/asm"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/core/vm/program"
|
"github.com/ethereum/go-ethereum/core/vm/program"
|
||||||
|
|
@ -670,17 +671,23 @@ func TestColdAccountAccessCost(t *testing.T) {
|
||||||
want: 7600,
|
want: 7600,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
tracer := logger.NewStructLogger(nil)
|
var step = 0
|
||||||
|
var have = uint64(0)
|
||||||
Execute(tc.code, nil, &Config{
|
Execute(tc.code, nil, &Config{
|
||||||
EVMConfig: vm.Config{
|
EVMConfig: vm.Config{
|
||||||
Tracer: tracer.Hooks(),
|
Tracer: &tracing.Hooks{
|
||||||
|
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
|
// Uncomment to investigate failures:
|
||||||
|
//t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost)
|
||||||
|
if step == tc.step {
|
||||||
|
have = cost
|
||||||
|
}
|
||||||
|
step++
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
have := tracer.StructLogs()[tc.step].GasCost
|
|
||||||
if want := tc.want; have != want {
|
if want := tc.want; have != want {
|
||||||
for ii, op := range tracer.StructLogs() {
|
|
||||||
t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost)
|
|
||||||
}
|
|
||||||
t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
|
t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"maps"
|
"maps"
|
||||||
"math/big"
|
"math/big"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
|
@ -124,6 +123,69 @@ func (log *StructLog) WriteTo(writer io.Writer) {
|
||||||
fmt.Fprintln(writer)
|
fmt.Fprintln(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
ReturnData string `json:"returnData,omitempty"`
|
||||||
|
Memory *[]string `json:"memory,omitempty"`
|
||||||
|
Storage *map[string]string `json:"storage,omitempty"`
|
||||||
|
RefundCounter uint64 `json:"refund,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// toLegacyJSON converts the structlog to json-encoded legacy form (StructLogRes).
|
||||||
|
//
|
||||||
|
// The differences between the structlog json and the 'legacy' json are:
|
||||||
|
// - Op. Legacy uses string (e.g. "SSTORE"), non-legacy uses a byte.
|
||||||
|
// - non-legacy has an 'opName' field containing the op name.
|
||||||
|
// - gas, gasCost: legacy uses integers, non-legacy hex-strings
|
||||||
|
// - memory: legacy uses a list of 64-char strings, each representing 32-byte chunks
|
||||||
|
// of evm memory. Non-legacy just uses a string of hexdata, no chunking.
|
||||||
|
// - storage: legacy has a storage-field.
|
||||||
|
func (log *StructLog) toLegacyJSON() json.RawMessage {
|
||||||
|
msg := StructLogRes{
|
||||||
|
Pc: log.Pc,
|
||||||
|
Op: log.Op.String(),
|
||||||
|
Gas: log.Gas,
|
||||||
|
GasCost: log.GasCost,
|
||||||
|
Depth: log.Depth,
|
||||||
|
Error: log.ErrorString(),
|
||||||
|
RefundCounter: log.RefundCounter,
|
||||||
|
}
|
||||||
|
if log.Stack != nil {
|
||||||
|
stack := make([]string, len(log.Stack))
|
||||||
|
for i, stackValue := range log.Stack {
|
||||||
|
stack[i] = stackValue.Hex()
|
||||||
|
}
|
||||||
|
msg.Stack = &stack
|
||||||
|
}
|
||||||
|
if len(log.ReturnData) > 0 {
|
||||||
|
msg.ReturnData = hexutil.Bytes(log.ReturnData).String()
|
||||||
|
}
|
||||||
|
if log.Memory != nil {
|
||||||
|
memory := make([]string, 0, (len(log.Memory)+31)/32)
|
||||||
|
for i := 0; i+32 <= len(log.Memory); i += 32 {
|
||||||
|
memory = append(memory, fmt.Sprintf("%x", log.Memory[i:i+32]))
|
||||||
|
}
|
||||||
|
msg.Memory = &memory
|
||||||
|
}
|
||||||
|
if log.Storage != nil {
|
||||||
|
storage := make(map[string]string)
|
||||||
|
for i, storageValue := range log.Storage {
|
||||||
|
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
|
||||||
|
}
|
||||||
|
msg.Storage = &storage
|
||||||
|
}
|
||||||
|
element, _ := json.Marshal(msg)
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -137,12 +199,13 @@ type StructLogger struct {
|
||||||
env *tracing.VMContext
|
env *tracing.VMContext
|
||||||
|
|
||||||
storage map[common.Address]Storage
|
storage map[common.Address]Storage
|
||||||
logs []StructLog
|
|
||||||
output []byte
|
output []byte
|
||||||
err error
|
err error
|
||||||
usedGas uint64
|
usedGas uint64
|
||||||
|
|
||||||
writer io.Writer // If set, the logger will stream instead of store logs
|
writer io.Writer // If set, the logger will stream instead of store logs
|
||||||
|
logs []json.RawMessage // buffer of json-encoded logs
|
||||||
|
resultSize int
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -191,8 +254,8 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
|
||||||
if l.interrupt.Load() {
|
if l.interrupt.Load() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// check if already accumulated the specified number of logs
|
// check if already accumulated the size of the response.
|
||||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
if l.cfg.Limit != 0 && l.resultSize > l.cfg.Limit {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
|
@ -242,11 +305,9 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
|
||||||
log.Storage = storage
|
log.Storage = storage
|
||||||
// create a log
|
// create a log
|
||||||
if l.writer == nil {
|
if l.writer == nil {
|
||||||
// Non-streaming, need to copy slices.
|
entry := log.toLegacyJSON()
|
||||||
log.Memory = slices.Clone(log.Memory)
|
l.resultSize += len(entry)
|
||||||
log.Stack = slices.Clone(log.Stack)
|
l.logs = append(l.logs, entry)
|
||||||
log.ReturnData = slices.Clone(rData)
|
|
||||||
l.logs = append(l.logs, log)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.WriteTo(l.writer)
|
log.WriteTo(l.writer)
|
||||||
|
|
@ -283,7 +344,7 @@ func (l *StructLogger) GetResult() (json.RawMessage, error) {
|
||||||
Gas: l.usedGas,
|
Gas: l.usedGas,
|
||||||
Failed: failed,
|
Failed: failed,
|
||||||
ReturnValue: returnVal,
|
ReturnValue: returnVal,
|
||||||
StructLogs: formatLogs(l.StructLogs()),
|
StructLogs: l.jsonLogs(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,8 +371,9 @@ func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StructLogs returns the captured log entries.
|
func (l *StructLogger) jsonLogs() []json.RawMessage {
|
||||||
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
return l.logs
|
||||||
|
}
|
||||||
|
|
||||||
// Error returns the VM error captured by the trace.
|
// Error returns the VM error captured by the trace.
|
||||||
func (l *StructLogger) Error() error { return l.err }
|
func (l *StructLogger) Error() error { return l.err }
|
||||||
|
|
@ -423,65 +485,8 @@ func (t *mdLogger) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.O
|
||||||
// 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
|
||||||
type ExecutionResult struct {
|
type ExecutionResult struct {
|
||||||
Gas uint64 `json:"gas"`
|
Gas uint64 `json:"gas"`
|
||||||
Failed bool `json:"failed"`
|
Failed bool `json:"failed"`
|
||||||
ReturnValue string `json:"returnValue"`
|
ReturnValue string `json:"returnValue"`
|
||||||
StructLogs []StructLogRes `json:"structLogs"`
|
StructLogs []json.RawMessage `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"`
|
|
||||||
ReturnData string `json:"returnData,omitempty"`
|
|
||||||
Memory *[]string `json:"memory,omitempty"`
|
|
||||||
Storage *map[string]string `json:"storage,omitempty"`
|
|
||||||
RefundCounter uint64 `json:"refund,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatLogs formats EVM returned structured logs for json output
|
|
||||||
func formatLogs(logs []StructLog) []StructLogRes {
|
|
||||||
formatted := make([]StructLogRes, len(logs))
|
|
||||||
for index, trace := range logs {
|
|
||||||
formatted[index] = StructLogRes{
|
|
||||||
Pc: trace.Pc,
|
|
||||||
Op: trace.Op.String(),
|
|
||||||
Gas: trace.Gas,
|
|
||||||
GasCost: trace.GasCost,
|
|
||||||
Depth: trace.Depth,
|
|
||||||
Error: trace.ErrorString(),
|
|
||||||
RefundCounter: trace.RefundCounter,
|
|
||||||
}
|
|
||||||
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 len(trace.ReturnData) > 0 {
|
|
||||||
formatted[index].ReturnData = hexutil.Bytes(trace.ReturnData).String()
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue