github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/eth/tracers/native/prestate.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package native 18 19 import ( 20 "encoding/json" 21 "math/big" 22 "sync/atomic" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/common/hexutil" 27 "github.com/ethereum/go-ethereum/core" 28 "github.com/ethereum/go-ethereum/core/vm" 29 "github.com/ethereum/go-ethereum/crypto" 30 "github.com/ethereum/go-ethereum/eth/tracers" 31 ) 32 33 func init() { 34 register("prestateTracer", newPrestateTracer) 35 } 36 37 type prestate = map[common.Address]*account 38 type account struct { 39 Balance string `json:"balance"` 40 Nonce uint64 `json:"nonce"` 41 Code string `json:"code"` 42 Storage map[common.Hash]common.Hash `json:"storage"` 43 } 44 45 type prestateTracer struct { 46 env *vm.EVM 47 prestate prestate 48 create bool 49 to common.Address 50 interrupt uint32 // Atomic flag to signal execution interruption 51 reason error // Textual reason for the interruption 52 } 53 54 func newPrestateTracer() tracers.Tracer { 55 // First callframe contains tx context info 56 // and is populated on start and end. 57 return &prestateTracer{prestate: prestate{}} 58 } 59 60 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 61 func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 62 t.env = env 63 t.create = create 64 t.to = to 65 66 // Compute intrinsic gas 67 isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber) 68 isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber) 69 intrinsicGas, err := core.IntrinsicGas(input, nil, create, isHomestead, isIstanbul) 70 if err != nil { 71 return 72 } 73 74 t.lookupAccount(from) 75 t.lookupAccount(to) 76 77 // The recipient balance includes the value transferred. 78 toBal := hexutil.MustDecodeBig(t.prestate[to].Balance) 79 toBal = new(big.Int).Sub(toBal, value) 80 t.prestate[to].Balance = hexutil.EncodeBig(toBal) 81 82 // The sender balance is after reducing: value, gasLimit, intrinsicGas. 83 // We need to re-add them to get the pre-tx balance. 84 fromBal := hexutil.MustDecodeBig(t.prestate[from].Balance) 85 gasPrice := env.TxContext.GasPrice 86 consumedGas := new(big.Int).Mul( 87 gasPrice, 88 new(big.Int).Add( 89 new(big.Int).SetUint64(intrinsicGas), 90 new(big.Int).SetUint64(gas), 91 ), 92 ) 93 fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas)) 94 t.prestate[from].Balance = hexutil.EncodeBig(fromBal) 95 t.prestate[from].Nonce-- 96 } 97 98 // CaptureEnd is called after the call finishes to finalize the tracing. 99 func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { 100 if t.create { 101 // Exclude created contract. 102 delete(t.prestate, t.to) 103 } 104 } 105 106 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 107 func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { 108 stack := scope.Stack 109 stackData := stack.Data() 110 stackLen := len(stackData) 111 switch { 112 case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE): 113 slot := common.Hash(stackData[stackLen-1].Bytes32()) 114 t.lookupStorage(scope.Contract.Address(), slot) 115 case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT): 116 addr := common.Address(stackData[stackLen-1].Bytes20()) 117 t.lookupAccount(addr) 118 case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE): 119 addr := common.Address(stackData[stackLen-2].Bytes20()) 120 t.lookupAccount(addr) 121 case op == vm.CREATE: 122 addr := scope.Contract.Address() 123 nonce := t.env.StateDB.GetNonce(addr) 124 t.lookupAccount(crypto.CreateAddress(addr, nonce)) 125 case stackLen >= 4 && op == vm.CREATE2: 126 offset := stackData[stackLen-2] 127 size := stackData[stackLen-3] 128 init := scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) 129 inithash := crypto.Keccak256(init) 130 salt := stackData[stackLen-4] 131 t.lookupAccount(crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), inithash)) 132 } 133 } 134 135 // CaptureFault implements the EVMLogger interface to trace an execution fault. 136 func (t *prestateTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { 137 } 138 139 // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). 140 func (t *prestateTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 141 } 142 143 // CaptureExit is called when EVM exits a scope, even if the scope didn't 144 // execute any code. 145 func (t *prestateTracer) CaptureExit(output []byte, gasUsed uint64, err error) { 146 } 147 148 // GetResult returns the json-encoded nested list of call traces, and any 149 // error arising from the encoding or forceful termination (via `Stop`). 150 func (t *prestateTracer) GetResult() (json.RawMessage, error) { 151 res, err := json.Marshal(t.prestate) 152 if err != nil { 153 return nil, err 154 } 155 return json.RawMessage(res), t.reason 156 } 157 158 // Stop terminates execution of the tracer at the first opportune moment. 159 func (t *prestateTracer) Stop(err error) { 160 t.reason = err 161 atomic.StoreUint32(&t.interrupt, 1) 162 } 163 164 // lookupAccount fetches details of an account and adds it to the prestate 165 // if it doesn't exist there. 166 func (t *prestateTracer) lookupAccount(addr common.Address) { 167 if _, ok := t.prestate[addr]; ok { 168 return 169 } 170 t.prestate[addr] = &account{ 171 Balance: bigToHex(t.env.StateDB.GetBalance(addr)), 172 Nonce: t.env.StateDB.GetNonce(addr), 173 Code: bytesToHex(t.env.StateDB.GetCode(addr)), 174 Storage: make(map[common.Hash]common.Hash), 175 } 176 } 177 178 // lookupStorage fetches the requested storage slot and adds 179 // it to the prestate of the given contract. It assumes `lookupAccount` 180 // has been performed on the contract before. 181 func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) { 182 if _, ok := t.prestate[addr].Storage[key]; ok { 183 return 184 } 185 t.prestate[addr].Storage[key] = t.env.StateDB.GetState(addr, key) 186 }