github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/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 "bytes" 21 "encoding/json" 22 "math/big" 23 "sync/atomic" 24 25 "github.com/tirogen/go-ethereum/common" 26 "github.com/tirogen/go-ethereum/common/hexutil" 27 "github.com/tirogen/go-ethereum/core/vm" 28 "github.com/tirogen/go-ethereum/crypto" 29 "github.com/tirogen/go-ethereum/eth/tracers" 30 ) 31 32 //go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go 33 34 func init() { 35 register("prestateTracer", newPrestateTracer) 36 } 37 38 type state = map[common.Address]*account 39 40 type account struct { 41 Balance *big.Int `json:"balance,omitempty"` 42 Code []byte `json:"code,omitempty"` 43 Nonce uint64 `json:"nonce,omitempty"` 44 Storage map[common.Hash]common.Hash `json:"storage,omitempty"` 45 } 46 47 func (a *account) exists() bool { 48 return a.Nonce > 0 || len(a.Code) > 0 || len(a.Storage) > 0 || (a.Balance != nil && a.Balance.Sign() != 0) 49 } 50 51 type accountMarshaling struct { 52 Balance *hexutil.Big 53 Code hexutil.Bytes 54 } 55 56 type prestateTracer struct { 57 noopTracer 58 env *vm.EVM 59 pre state 60 post state 61 create bool 62 to common.Address 63 gasLimit uint64 // Amount of gas bought for the whole tx 64 config prestateTracerConfig 65 interrupt uint32 // Atomic flag to signal execution interruption 66 reason error // Textual reason for the interruption 67 created map[common.Address]bool 68 deleted map[common.Address]bool 69 } 70 71 type prestateTracerConfig struct { 72 DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications 73 } 74 75 func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { 76 var config prestateTracerConfig 77 if cfg != nil { 78 if err := json.Unmarshal(cfg, &config); err != nil { 79 return nil, err 80 } 81 } 82 return &prestateTracer{ 83 pre: state{}, 84 post: state{}, 85 config: config, 86 created: make(map[common.Address]bool), 87 deleted: make(map[common.Address]bool), 88 }, nil 89 } 90 91 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 92 func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 93 t.env = env 94 t.create = create 95 t.to = to 96 97 t.lookupAccount(from) 98 t.lookupAccount(to) 99 t.lookupAccount(env.Context.Coinbase) 100 101 // The recipient balance includes the value transferred. 102 toBal := new(big.Int).Sub(t.pre[to].Balance, value) 103 t.pre[to].Balance = toBal 104 105 // The sender balance is after reducing: value and gasLimit. 106 // We need to re-add them to get the pre-tx balance. 107 fromBal := new(big.Int).Set(t.pre[from].Balance) 108 gasPrice := env.TxContext.GasPrice 109 consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit)) 110 fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas)) 111 t.pre[from].Balance = fromBal 112 t.pre[from].Nonce-- 113 114 if create && t.config.DiffMode { 115 t.created[to] = true 116 } 117 } 118 119 // CaptureEnd is called after the call finishes to finalize the tracing. 120 func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { 121 if t.config.DiffMode { 122 return 123 } 124 125 if t.create { 126 // Keep existing account prior to contract creation at that address 127 if s := t.pre[t.to]; s != nil && !s.exists() { 128 // Exclude newly created contract. 129 delete(t.pre, t.to) 130 } 131 } 132 } 133 134 // CaptureState implements the EVMLogger interface to trace a single step of VM execution. 135 func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { 136 stack := scope.Stack 137 stackData := stack.Data() 138 stackLen := len(stackData) 139 caller := scope.Contract.Address() 140 switch { 141 case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE): 142 slot := common.Hash(stackData[stackLen-1].Bytes32()) 143 t.lookupStorage(caller, slot) 144 case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT): 145 addr := common.Address(stackData[stackLen-1].Bytes20()) 146 t.lookupAccount(addr) 147 if op == vm.SELFDESTRUCT { 148 t.deleted[caller] = true 149 } 150 case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE): 151 addr := common.Address(stackData[stackLen-2].Bytes20()) 152 t.lookupAccount(addr) 153 case op == vm.CREATE: 154 nonce := t.env.StateDB.GetNonce(caller) 155 addr := crypto.CreateAddress(caller, nonce) 156 t.lookupAccount(addr) 157 t.created[addr] = true 158 case stackLen >= 4 && op == vm.CREATE2: 159 offset := stackData[stackLen-2] 160 size := stackData[stackLen-3] 161 init := scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) 162 inithash := crypto.Keccak256(init) 163 salt := stackData[stackLen-4] 164 addr := crypto.CreateAddress2(caller, salt.Bytes32(), inithash) 165 t.lookupAccount(addr) 166 t.created[addr] = true 167 } 168 } 169 170 func (t *prestateTracer) CaptureTxStart(gasLimit uint64) { 171 t.gasLimit = gasLimit 172 } 173 174 func (t *prestateTracer) CaptureTxEnd(restGas uint64) { 175 if !t.config.DiffMode { 176 return 177 } 178 179 for addr, state := range t.pre { 180 // The deleted account's state is pruned from `post` but kept in `pre` 181 if _, ok := t.deleted[addr]; ok { 182 continue 183 } 184 modified := false 185 postAccount := &account{Storage: make(map[common.Hash]common.Hash)} 186 newBalance := t.env.StateDB.GetBalance(addr) 187 newNonce := t.env.StateDB.GetNonce(addr) 188 newCode := t.env.StateDB.GetCode(addr) 189 190 if newBalance.Cmp(t.pre[addr].Balance) != 0 { 191 modified = true 192 postAccount.Balance = newBalance 193 } 194 if newNonce != t.pre[addr].Nonce { 195 modified = true 196 postAccount.Nonce = newNonce 197 } 198 if !bytes.Equal(newCode, t.pre[addr].Code) { 199 modified = true 200 postAccount.Code = newCode 201 } 202 203 for key, val := range state.Storage { 204 // don't include the empty slot 205 if val == (common.Hash{}) { 206 delete(t.pre[addr].Storage, key) 207 } 208 209 newVal := t.env.StateDB.GetState(addr, key) 210 if val == newVal { 211 // Omit unchanged slots 212 delete(t.pre[addr].Storage, key) 213 } else { 214 modified = true 215 if newVal != (common.Hash{}) { 216 postAccount.Storage[key] = newVal 217 } 218 } 219 } 220 221 if modified { 222 t.post[addr] = postAccount 223 } else { 224 // if state is not modified, then no need to include into the pre state 225 delete(t.pre, addr) 226 } 227 } 228 // the new created contracts' prestate were empty, so delete them 229 for a := range t.created { 230 // the created contract maybe exists in statedb before the creating tx 231 if s := t.pre[a]; s != nil && !s.exists() { 232 delete(t.pre, a) 233 } 234 } 235 } 236 237 // GetResult returns the json-encoded nested list of call traces, and any 238 // error arising from the encoding or forceful termination (via `Stop`). 239 func (t *prestateTracer) GetResult() (json.RawMessage, error) { 240 var res []byte 241 var err error 242 if t.config.DiffMode { 243 res, err = json.Marshal(struct { 244 Post state `json:"post"` 245 Pre state `json:"pre"` 246 }{t.post, t.pre}) 247 } else { 248 res, err = json.Marshal(t.pre) 249 } 250 if err != nil { 251 return nil, err 252 } 253 return json.RawMessage(res), t.reason 254 } 255 256 // Stop terminates execution of the tracer at the first opportune moment. 257 func (t *prestateTracer) Stop(err error) { 258 t.reason = err 259 atomic.StoreUint32(&t.interrupt, 1) 260 } 261 262 // lookupAccount fetches details of an account and adds it to the prestate 263 // if it doesn't exist there. 264 func (t *prestateTracer) lookupAccount(addr common.Address) { 265 if _, ok := t.pre[addr]; ok { 266 return 267 } 268 269 t.pre[addr] = &account{ 270 Balance: t.env.StateDB.GetBalance(addr), 271 Nonce: t.env.StateDB.GetNonce(addr), 272 Code: t.env.StateDB.GetCode(addr), 273 Storage: make(map[common.Hash]common.Hash), 274 } 275 } 276 277 // lookupStorage fetches the requested storage slot and adds 278 // it to the prestate of the given contract. It assumes `lookupAccount` 279 // has been performed on the contract before. 280 func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) { 281 if _, ok := t.pre[addr].Storage[key]; ok { 282 return 283 } 284 t.pre[addr].Storage[key] = t.env.StateDB.GetState(addr, key) 285 }