has call reverted to snapshot
This commit is contained in:
parent
f241a76f4a
commit
580f02527e
|
@ -17,6 +17,7 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
|
||||
|
@ -536,10 +537,16 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas
|
|||
if leftOverGas != 0 {
|
||||
tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
|
||||
}
|
||||
|
||||
var reverted bool
|
||||
if err != nil {
|
||||
reverted = true
|
||||
}
|
||||
if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) {
|
||||
reverted = false
|
||||
}
|
||||
if isRoot {
|
||||
tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err))
|
||||
tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
|
||||
} else {
|
||||
tracer.CaptureExit(ret, startGas-leftOverGas, VMErrorFromErr(err))
|
||||
tracer.CaptureExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -859,7 +859,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
|||
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
|
||||
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
|
||||
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||
tracer.CaptureExit([]byte{}, 0, nil)
|
||||
tracer.CaptureExit([]byte{}, 0, nil, false)
|
||||
}
|
||||
return nil, errStopToken
|
||||
}
|
||||
|
@ -875,7 +875,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
|
|||
interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address())
|
||||
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
|
||||
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||
tracer.CaptureExit([]byte{}, 0, nil)
|
||||
tracer.CaptureExit([]byte{}, 0, nil, false)
|
||||
}
|
||||
return nil, errStopToken
|
||||
}
|
||||
|
|
|
@ -36,10 +36,18 @@ type EVMLogger interface {
|
|||
CaptureTxEnd(receipt *types.Receipt, err error)
|
||||
// Top call frame
|
||||
CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
|
||||
CaptureEnd(output []byte, gasUsed uint64, err error)
|
||||
// CaptureEnd is invoked when the processing of the top call ends.
|
||||
// See docs for `CaptureExit` for info on the `reverted` parameter.
|
||||
CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool)
|
||||
// Rest of call frames
|
||||
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
|
||||
CaptureExit(output []byte, gasUsed uint64, err error)
|
||||
// CaptureExit is invoked when the processing of a message ends.
|
||||
// `revert` is true when there was an error during the execution.
|
||||
// Exceptionally, before the homestead hardfork a contract creation that
|
||||
// ran out of gas when attempting to persist the code to database did not
|
||||
// count as a call failure and did not cause a revert of the call. This will
|
||||
// be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`.
|
||||
CaptureExit(output []byte, gasUsed uint64, err error, reverted bool)
|
||||
// Opcode level
|
||||
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
||||
CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
|
||||
|
|
|
@ -44,7 +44,7 @@ func (t *NoopTracer) CaptureStart(from common.Address, to common.Address, create
|
|||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||
func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
|
@ -67,7 +67,7 @@ func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
|
|||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
}
|
||||
|
||||
func (*NoopTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {}
|
||||
|
|
|
@ -331,7 +331,7 @@ func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
|||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||
func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
if err != nil {
|
||||
t.ctx["error"] = t.vm.ToValue(err.Error())
|
||||
}
|
||||
|
@ -369,7 +369,7 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad
|
|||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
if !t.traceFrame {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ func runTrace(tracer directory.Tracer, vmctx *vmContext, chaincfg *params.ChainC
|
|||
tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller())
|
||||
tracer.CaptureStart(contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
|
||||
ret, err := env.Interpreter().Run(contract, []byte{}, false)
|
||||
tracer.CaptureEnd(ret, startGas-contract.Gas, err)
|
||||
tracer.CaptureEnd(ret, startGas-contract.Gas, err, true)
|
||||
// Rest gas assumes no refund
|
||||
tracer.CaptureTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil)
|
||||
if err != nil {
|
||||
|
@ -209,7 +209,7 @@ func TestNoStepExec(t *testing.T) {
|
|||
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer})
|
||||
tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{}), common.Address{})
|
||||
tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0))
|
||||
tracer.CaptureEnd(nil, 0, nil)
|
||||
tracer.CaptureEnd(nil, 0, nil, false)
|
||||
ret, err := tracer.GetResult()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -279,7 +279,7 @@ func TestEnterExit(t *testing.T) {
|
|||
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
|
||||
}
|
||||
tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
|
||||
tracer.CaptureExit([]byte{}, 400, nil)
|
||||
tracer.CaptureExit([]byte{}, 400, nil, false)
|
||||
|
||||
have, err := tracer.GetResult()
|
||||
if err != nil {
|
||||
|
|
|
@ -31,7 +31,7 @@ func (p *Printer) CaptureStart(from common.Address, to common.Address, create bo
|
|||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (p *Printer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||
func (p *Printer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
fmt.Printf("CaptureEnd: output=%s, gasUsed=%v, err=%v\n", hexutil.Bytes(output), gasUsed, err)
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ func (p *Printer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Add
|
|||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (p *Printer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
func (p *Printer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
fmt.Printf("CaptureExit: output=%s, gasUsed=%v, err=%v\n", hexutil.Bytes(output), gasUsed, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ func (l *StructLogger) CaptureState(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, err error) {
|
||||
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
l.output = output
|
||||
l.err = err
|
||||
if l.cfg.Debug {
|
||||
|
@ -377,7 +377,7 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
|||
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||
output, gasUsed, err)
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
|
|||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
type endLog struct {
|
||||
Output string `json:"output"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
|
|
|
@ -59,7 +59,8 @@ type callFrame struct {
|
|||
Logs []callLog `json:"logs,omitempty" rlp:"optional"`
|
||||
// Placed at end on purpose. The RLP will be decoded to 0 instead of
|
||||
// nil if there are non-empty elements after in the struct.
|
||||
Value *big.Int `json:"value,omitempty" rlp:"optional"`
|
||||
Value *big.Int `json:"value,omitempty" rlp:"optional"`
|
||||
revertedSnapshot bool
|
||||
}
|
||||
|
||||
func (f callFrame) TypeString() string {
|
||||
|
@ -67,16 +68,17 @@ func (f callFrame) TypeString() string {
|
|||
}
|
||||
|
||||
func (f callFrame) failed() bool {
|
||||
return len(f.Error) > 0
|
||||
return len(f.Error) > 0 && f.revertedSnapshot
|
||||
}
|
||||
|
||||
func (f *callFrame) processOutput(output []byte, err error) {
|
||||
func (f *callFrame) processOutput(output []byte, err error, reverted bool) {
|
||||
output = common.CopyBytes(output)
|
||||
if err == nil {
|
||||
f.Output = output
|
||||
return
|
||||
}
|
||||
f.Error = err.Error()
|
||||
f.revertedSnapshot = reverted
|
||||
if f.Type == vm.CREATE || f.Type == vm.CREATE2 {
|
||||
f.To = nil
|
||||
}
|
||||
|
@ -147,8 +149,8 @@ func (t *callTracer) CaptureStart(from common.Address, to common.Address, create
|
|||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||
t.callstack[0].processOutput(output, err)
|
||||
func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
t.callstack[0].processOutput(output, err, reverted)
|
||||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
|
@ -180,7 +182,7 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
|
|||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
t.depth--
|
||||
if t.config.OnlyTopCall {
|
||||
return
|
||||
|
@ -195,7 +197,7 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
|||
size -= 1
|
||||
|
||||
call.GasUsed = gasUsed
|
||||
call.processOutput(output, err)
|
||||
call.processOutput(output, err, reverted)
|
||||
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
|
||||
}
|
||||
|
||||
|
|
|
@ -151,8 +151,8 @@ func (t *flatCallTracer) CaptureStart(from common.Address, to common.Address, cr
|
|||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||
t.tracer.CaptureEnd(output, gasUsed, err)
|
||||
func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
t.tracer.CaptureEnd(output, gasUsed, err, reverted)
|
||||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
|
@ -178,8 +178,8 @@ func (t *flatCallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to com
|
|||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
t.tracer.CaptureExit(output, gasUsed, err)
|
||||
func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
t.tracer.CaptureExit(output, gasUsed, err, reverted)
|
||||
|
||||
// Parity traces don't include CALL/STATICCALLs to precompiles.
|
||||
// By default we remove them from the callstack.
|
||||
|
|
|
@ -68,9 +68,9 @@ func (t *muxTracer) CaptureStart(from common.Address, to common.Address, create
|
|||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||
func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureEnd(output, gasUsed, err)
|
||||
t.CaptureEnd(output, gasUsed, err, reverted)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,9 +111,9 @@ func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.A
|
|||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureExit(output, gasUsed, err)
|
||||
t.CaptureExit(output, gasUsed, err, reverted)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue