From 46d8bc473e9c07cb827129c4776bade967b92b28 Mon Sep 17 00:00:00 2001 From: CMajeri Date: Wed, 28 Jul 2021 15:08:25 +0200 Subject: [PATCH 1/5] Enables fast tracing for CALL family --- core/vm/logger.go | 70 ++++++++++++++++++++++++++++++++++++++++-- internal/ethapi/api.go | 6 +++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index 900a5e58543a..f9579d93b429 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -110,12 +110,22 @@ type Tracer interface { CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) } +type wrappedLog struct { + parent *wrappedLog + error error + log StructLog + children []*wrappedLog +} + // StructLogger is an EVM state logger and implements Tracer. // // 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 // contract their storage. type StructLogger struct { + current *wrappedLog + depth int + cfg LogConfig storage map[common.Address]Storage @@ -145,12 +155,45 @@ func (l *StructLogger) Reset() { // CaptureStart implements the Tracer interface to initialize the tracing operation. func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + l.depth = 0 + l.current = &wrappedLog{} +} + +func errToStr(err error) string { + if err == nil { + return "" + } + return err.Error() } // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { + for ; l.depth > depth-1; l.depth-- { + l.current = l.current.parent + } + if err != nil { + if l.current.error != nil { + l.current.error=fmt.Errorf("%s:%s", l.current.error.Error(), err.Error()) + } + l.current.error = err + } + switch op { + case CALL,DELEGATECALL,STATICCALL,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 REVERT: + l.current.error = ErrExecutionReverted + return + default: + return + } memory := scope.Memory stack := scope.Stack contract := scope.Contract @@ -205,7 +248,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui } // create a new snapshot of the EVM. log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.StateDB.GetRefund(), err} - l.logs = append(l.logs, log) + l.current.log = log } // CaptureFault implements the Tracer interface to trace an execution fault @@ -215,6 +258,16 @@ func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost ui // CaptureEnd is called after the call finishes to finalize the tracing. func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { + for ; l.depth > 1; l.depth-- { + l.current = l.current.parent + } + l.current.log = StructLog{ + Op: CALL, + GasCost: gasUsed, + ReturnData: output, + Depth: 0, + Err: err, + } l.output = output l.err = err if l.cfg.Debug { @@ -225,8 +278,21 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration } } +// 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.logs } +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 } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1af98e1071da..d1068afc4a4b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1142,13 +1142,17 @@ type StructLogRes struct { func FormatLogs(logs []vm.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: trace.ErrorString(), + Error: errString, } if trace.Stack != nil { stack := make([]string, len(trace.Stack)) From 0b450bcb6d150c2508898c301f81cc9820897ee9 Mon Sep 17 00:00:00 2001 From: CMajeri Date: Wed, 28 Jul 2021 15:21:38 +0200 Subject: [PATCH 2/5] Removes useless code --- core/vm/logger.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index f9579d93b429..0ca35a6fa477 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -159,13 +159,6 @@ func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Add l.current = &wrappedLog{} } -func errToStr(err error) string { - if err == nil { - return "" - } - return err.Error() -} - // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. @@ -174,9 +167,6 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui l.current = l.current.parent } if err != nil { - if l.current.error != nil { - l.current.error=fmt.Errorf("%s:%s", l.current.error.Error(), err.Error()) - } l.current.error = err } switch op { From ff52ea4c2c606b492602dd06d79314f885e99d46 Mon Sep 17 00:00:00 2001 From: Bastien Giegel Date: Mon, 7 Feb 2022 11:13:14 +0100 Subject: [PATCH 3/5] =?UTF-8?q?apply=20log=20wrapper=20chervin=E2=80=99s?= =?UTF-8?q?=20patch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eth/tracers/logger/logger.go | 60 ++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 8461935822d8..c54d294877c3 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -99,12 +99,22 @@ func (s *StructLog) ErrorString() string { return "" } +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 // a track record of modified storage which is used in reporting snapshots of the // contract their storage. type StructLogger struct { + current *wrappedLog + depth int + cfg Config env *vm.EVM @@ -136,12 +146,36 @@ func (l *StructLogger) Reset() { // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { l.env = env + l.depth = 0 + l.current = &wrappedLog{} } // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + for ; l.depth > depth-1; l.depth-- { + 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 + } + memory := scope.Memory stack := scope.Stack contract := scope.Contract @@ -198,7 +232,7 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s } // create a new snapshot of the EVM. log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err} - l.logs = append(l.logs, log) + l.current.log = log } // CaptureFault implements the EVMLogger interface to trace an execution fault @@ -208,6 +242,17 @@ func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, s // CaptureEnd is called after the call finishes to finalize the tracing. func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { + 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 if l.cfg.Debug { @@ -223,8 +268,19 @@ func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to commo func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, 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.logs } +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 } From 4c6d40391b86574a6dc38034deeb7860cfdfae6e Mon Sep 17 00:00:00 2001 From: CMajeri Date: Fri, 27 May 2022 17:22:29 +0200 Subject: [PATCH 4/5] Detect silent failures (no REVERT or error message) --- eth/tracers/logger/logger.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index c54d294877c3..a4c6c21ee59b 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -154,7 +154,14 @@ func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common. // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - for ; l.depth > depth-1; l.depth-- { + stack := scope.Stack + for i := l.depth - (depth - 1); l.depth > depth-1; l.depth, i = l.depth-1, i-1 { + if l.current.error == nil { + switch stack.Data()[len(stack.Data())-i].Bytes32()[31] { + case 0x00: + l.current.error = fmt.Errorf("call failed") + } + } l.current = l.current.parent } if err != nil { @@ -177,7 +184,6 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s } memory := scope.Memory - stack := scope.Stack contract := scope.Contract // check if already accumulated the specified number of logs if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { From a29939938bbc506900665c40903baf8c34524cfd Mon Sep 17 00:00:00 2001 From: CMajeri Date: Tue, 21 Jun 2022 09:36:16 +0200 Subject: [PATCH 5/5] Simplify loop --- eth/tracers/logger/logger.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 0f1f89320cb9..78b279efd725 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -174,7 +174,8 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s memory := scope.Memory stack := scope.Stack contract := scope.Contract - for i := l.depth - (depth - 1); l.depth > depth-1; l.depth, i = l.depth-1, i-1 { + for ; l.depth > depth-1; l.depth = l.depth - 1 { + i := l.depth - (depth - 1) if l.current.error == nil { switch stack.Data()[len(stack.Data())-i].Bytes32()[31] { case 0x00: