github.com/MetalBlockchain/subnet-evm@v0.4.9/eth/tracers/native/prestate.go (about) 1 // (c) 2020-2021, 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 "encoding/json" 31 "math/big" 32 "sync/atomic" 33 "time" 34 35 "github.com/MetalBlockchain/subnet-evm/core/vm" 36 "github.com/MetalBlockchain/subnet-evm/eth/tracers" 37 "github.com/ethereum/go-ethereum/common" 38 "github.com/ethereum/go-ethereum/common/hexutil" 39 "github.com/ethereum/go-ethereum/crypto" 40 ) 41 42 func init() { 43 register("prestateTracer", newPrestateTracer) 44 } 45 46 type prestate = map[common.Address]*account 47 type account struct { 48 Balance string `json:"balance"` 49 Nonce uint64 `json:"nonce"` 50 Code string `json:"code"` 51 Storage map[common.Hash]common.Hash `json:"storage"` 52 } 53 54 type prestateTracer struct { 55 env *vm.EVM 56 prestate prestate 57 create bool 58 to common.Address 59 gasLimit uint64 // Amount of gas bought for the whole tx 60 interrupt uint32 // Atomic flag to signal execution interruption 61 reason error // Textual reason for the interruption 62 } 63 64 func newPrestateTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { 65 // First callframe contains tx context info 66 // and is populated on start and end. 67 return &prestateTracer{prestate: prestate{}}, nil 68 } 69 70 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 71 func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 72 t.env = env 73 t.create = create 74 t.to = to 75 76 t.lookupAccount(from) 77 t.lookupAccount(to) 78 79 // The recipient balance includes the value transferred. 80 toBal := hexutil.MustDecodeBig(t.prestate[to].Balance) 81 toBal = new(big.Int).Sub(toBal, value) 82 t.prestate[to].Balance = hexutil.EncodeBig(toBal) 83 84 // The sender balance is after reducing: value and gasLimit. 85 // We need to re-add them to get the pre-tx balance. 86 fromBal := hexutil.MustDecodeBig(t.prestate[from].Balance) 87 gasPrice := env.TxContext.GasPrice 88 consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit)) 89 fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas)) 90 t.prestate[from].Balance = hexutil.EncodeBig(fromBal) 91 t.prestate[from].Nonce-- 92 } 93 94 // CaptureEnd is called after the call finishes to finalize the tracing. 95 func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { 96 if t.create { 97 // Exclude created contract. 98 delete(t.prestate, t.to) 99 } 100 } 101 102 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 103 func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { 104 stack := scope.Stack 105 stackData := stack.Data() 106 stackLen := len(stackData) 107 switch { 108 case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE): 109 slot := common.Hash(stackData[stackLen-1].Bytes32()) 110 t.lookupStorage(scope.Contract.Address(), slot) 111 case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT): 112 addr := common.Address(stackData[stackLen-1].Bytes20()) 113 t.lookupAccount(addr) 114 case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE): 115 addr := common.Address(stackData[stackLen-2].Bytes20()) 116 t.lookupAccount(addr) 117 case op == vm.CREATE: 118 addr := scope.Contract.Address() 119 nonce := t.env.StateDB.GetNonce(addr) 120 t.lookupAccount(crypto.CreateAddress(addr, nonce)) 121 case stackLen >= 4 && op == vm.CREATE2: 122 offset := stackData[stackLen-2] 123 size := stackData[stackLen-3] 124 init := scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) 125 inithash := crypto.Keccak256(init) 126 salt := stackData[stackLen-4] 127 t.lookupAccount(crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), inithash)) 128 } 129 } 130 131 // CaptureFault implements the EVMLogger interface to trace an execution fault. 132 func (t *prestateTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { 133 } 134 135 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 136 func (t *prestateTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 137 } 138 139 func (t *prestateTracer) CaptureTxStart(gasLimit uint64) { 140 t.gasLimit = gasLimit 141 } 142 143 func (t *prestateTracer) CaptureTxEnd(restGas uint64) {} 144 145 // CaptureExit is called when EVM exits a scope, even if the scope didn't 146 // execute any code. 147 func (t *prestateTracer) CaptureExit(output []byte, gasUsed uint64, err error) { 148 } 149 150 // GetResult returns the json-encoded nested list of call traces, and any 151 // error arising from the encoding or forceful termination (via `Stop`). 152 func (t *prestateTracer) GetResult() (json.RawMessage, error) { 153 res, err := json.Marshal(t.prestate) 154 if err != nil { 155 return nil, err 156 } 157 return json.RawMessage(res), t.reason 158 } 159 160 // Stop terminates execution of the tracer at the first opportune moment. 161 func (t *prestateTracer) Stop(err error) { 162 t.reason = err 163 atomic.StoreUint32(&t.interrupt, 1) 164 } 165 166 // lookupAccount fetches details of an account and adds it to the prestate 167 // if it doesn't exist there. 168 func (t *prestateTracer) lookupAccount(addr common.Address) { 169 if _, ok := t.prestate[addr]; ok { 170 return 171 } 172 t.prestate[addr] = &account{ 173 Balance: bigToHex(t.env.StateDB.GetBalance(addr)), 174 Nonce: t.env.StateDB.GetNonce(addr), 175 Code: bytesToHex(t.env.StateDB.GetCode(addr)), 176 Storage: make(map[common.Hash]common.Hash), 177 } 178 } 179 180 // lookupStorage fetches the requested storage slot and adds 181 // it to the prestate of the given contract. It assumes `lookupAccount` 182 // has been performed on the contract before. 183 func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) { 184 if _, ok := t.prestate[addr].Storage[key]; ok { 185 return 186 } 187 t.prestate[addr].Storage[key] = t.env.StateDB.GetState(addr, key) 188 }