mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 09:23:48 +00:00
commit
0c55428f52
2 changed files with 132 additions and 2 deletions
|
|
@ -102,12 +102,22 @@ func (s *StructLog) ErrorString() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type wrappedLog struct {
|
||||||
|
parent *wrappedLog
|
||||||
|
error error
|
||||||
|
log StructLog
|
||||||
|
children []*wrappedLog
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
// a track record of modified storage which is used in reporting snapshots of the
|
// a track record of modified storage which is used in reporting snapshots of the
|
||||||
// contract their storage.
|
// contract their storage.
|
||||||
type StructLogger struct {
|
type StructLogger struct {
|
||||||
|
current *wrappedLog
|
||||||
|
depth int
|
||||||
|
|
||||||
cfg Config
|
cfg Config
|
||||||
env *tracing.VMContext
|
env *tracing.VMContext
|
||||||
|
|
||||||
|
|
@ -165,6 +175,36 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
|
||||||
op := vm.OpCode(opcode)
|
op := vm.OpCode(opcode)
|
||||||
memory := scope.MemoryData()
|
memory := scope.MemoryData()
|
||||||
stack := scope.StackData()
|
stack := scope.StackData()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// Copy a snapshot of the current memory state to a new buffer
|
// Copy a snapshot of the current memory state to a new buffer
|
||||||
var mem []byte
|
var mem []byte
|
||||||
if l.cfg.EnableMemory {
|
if l.cfg.EnableMemory {
|
||||||
|
|
@ -212,7 +252,7 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
|
||||||
}
|
}
|
||||||
// create a new snapshot of the EVM.
|
// create a new snapshot of the EVM.
|
||||||
log := StructLog{pc, op, gas, cost, mem, len(memory), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
|
log := StructLog{pc, op, gas, cost, mem, len(memory), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
|
||||||
l.logs = append(l.logs, log)
|
l.current.log = log
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnExit is called a call frame finishes processing.
|
// OnExit is called a call frame finishes processing.
|
||||||
|
|
@ -220,6 +260,18 @@ func (l *StructLogger) OnExit(depth int, output []byte, gasUsed uint64, err erro
|
||||||
if depth != 0 {
|
if depth != 0 {
|
||||||
return
|
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.output = output
|
||||||
l.err = err
|
l.err = err
|
||||||
if l.cfg.Debug {
|
if l.cfg.Debug {
|
||||||
|
|
@ -258,6 +310,8 @@ func (l *StructLogger) Stop(err error) {
|
||||||
|
|
||||||
func (l *StructLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
func (l *StructLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
l.env = env
|
l.env = env
|
||||||
|
l.depth = 0
|
||||||
|
l.current = &wrappedLog{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) {
|
func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
|
|
@ -273,8 +327,19 @@ 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.
|
// StructLogs returns the captured log entries.
|
||||||
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
func (l *StructLogger) StructLogs() []StructLog { return l.current.getLogs() }
|
||||||
|
|
||||||
// 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 }
|
||||||
|
|
|
||||||
|
|
@ -1363,6 +1363,71 @@ func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs,
|
||||||
return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, api.b.RPCGasCap())
|
return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, 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 .
|
// RPCMarshalHeader converts the given header to the RPC output .
|
||||||
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
||||||
result := map[string]interface{}{
|
result := map[string]interface{}{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue