github.com/MetalBlockchain/subnet-evm@v0.4.9/eth/tracers/native/revertreason.go (about) 1 // (c) 2022, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2022 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package native 28 29 import ( 30 "bytes" 31 "encoding/json" 32 "math/big" 33 "sync/atomic" 34 "time" 35 36 "github.com/MetalBlockchain/subnet-evm/accounts/abi" 37 "github.com/MetalBlockchain/subnet-evm/core/vm" 38 "github.com/MetalBlockchain/subnet-evm/eth/tracers" 39 "github.com/MetalBlockchain/subnet-evm/vmerrs" 40 "github.com/ethereum/go-ethereum/common" 41 "github.com/ethereum/go-ethereum/crypto" 42 ) 43 44 func init() { 45 register("revertReasonTracer", newRevertReasonTracer) 46 } 47 48 var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4] 49 50 // revertReasonTracer is a go implementation of the Tracer interface which 51 // track the error message or revert reason return by the contract. 52 type revertReasonTracer struct { 53 env *vm.EVM 54 revertReason string // The revert reason return from the tx, if tx success, empty string return 55 interrupt uint32 // Atomic flag to signal execution interruption 56 reason error // Textual reason for the interruption 57 } 58 59 // newRevertReasonTracer returns a new revert reason tracer. 60 func newRevertReasonTracer(_ *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { 61 return &revertReasonTracer{}, nil 62 } 63 64 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 65 func (t *revertReasonTracer) CaptureStart(env *vm.EVM, _ common.Address, _ common.Address, _ bool, _ []byte, _ uint64, _ *big.Int) { 66 t.env = env 67 } 68 69 // CaptureEnd is called after the call finishes to finalize the tracing. 70 func (t *revertReasonTracer) CaptureEnd(output []byte, _ uint64, _ time.Duration, err error) { 71 if err != nil { 72 if err == vmerrs.ErrExecutionReverted && len(output) > 4 && bytes.Equal(output[:4], revertSelector) { 73 errMsg, _ := abi.UnpackRevert(output) 74 t.revertReason = err.Error() + ": " + errMsg 75 } else { 76 t.revertReason = err.Error() 77 } 78 } 79 } 80 81 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 82 func (t *revertReasonTracer) CaptureState(_ uint64, _ vm.OpCode, _, _ uint64, _ *vm.ScopeContext, _ []byte, _ int, _ error) { 83 } 84 85 // CaptureFault implements the EVMLogger interface to trace an execution fault. 86 func (t *revertReasonTracer) CaptureFault(_ uint64, _ vm.OpCode, _, _ uint64, _ *vm.ScopeContext, _ int, _ error) { 87 } 88 89 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 90 func (t *revertReasonTracer) CaptureEnter(_ vm.OpCode, _ common.Address, _ common.Address, _ []byte, _ uint64, _ *big.Int) { 91 // Skip if tracing was interrupted 92 if atomic.LoadUint32(&t.interrupt) > 0 { 93 t.env.Cancel() 94 return 95 } 96 } 97 98 // CaptureExit is called when EVM exits a scope, even if the scope didn't 99 // execute any code. 100 func (t *revertReasonTracer) CaptureExit(_ []byte, _ uint64, _ error) {} 101 102 func (t *revertReasonTracer) CaptureTxStart(_ uint64) {} 103 104 func (t *revertReasonTracer) CaptureTxEnd(_ uint64) {} 105 106 // GetResult returns an error message json object. 107 func (t *revertReasonTracer) GetResult() (json.RawMessage, error) { 108 res, err := json.Marshal(t.revertReason) 109 if err != nil { 110 return nil, err 111 } 112 return res, t.reason 113 } 114 115 // Stop terminates execution of the tracer at the first opportune moment. 116 func (t *revertReasonTracer) Stop(err error) { 117 t.reason = err 118 atomic.StoreUint32(&t.interrupt, 1) 119 }