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