github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/core/vm/logger.go (about) 1 // Copyright 2015 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 vm 18 19 import ( 20 "bytes" 21 "encoding/hex" 22 "fmt" 23 "io" 24 "math/big" 25 "strings" 26 "time" 27 28 "github.com/holiman/uint256" 29 30 "github.com/scroll-tech/go-ethereum/common" 31 "github.com/scroll-tech/go-ethereum/common/hexutil" 32 "github.com/scroll-tech/go-ethereum/common/math" 33 "github.com/scroll-tech/go-ethereum/core/types" 34 "github.com/scroll-tech/go-ethereum/crypto" 35 "github.com/scroll-tech/go-ethereum/log" 36 "github.com/scroll-tech/go-ethereum/params" 37 ) 38 39 // Storage represents a contract's storage. 40 type Storage map[common.Hash]common.Hash 41 42 // Copy duplicates the current storage. 43 func (s Storage) Copy() Storage { 44 cpy := make(Storage) 45 for key, value := range s { 46 cpy[key] = value 47 } 48 return cpy 49 } 50 51 // LogConfig are the configuration options for structured logger the EVM 52 type LogConfig struct { 53 EnableMemory bool // enable memory capture 54 DisableStack bool // disable stack capture 55 DisableStorage bool // disable storage capture 56 EnableReturnData bool // enable return data capture 57 Debug bool // print output during capture end 58 Limit int // maximum length of output, but zero means unlimited 59 // Chain overrides, can be used to execute a trace using future fork rules 60 Overrides *params.ChainConfig `json:"overrides,omitempty"` 61 } 62 63 //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go 64 65 // StructLog is emitted to the EVM each cycle and lists information about the current internal state 66 // prior to the execution of the statement. 67 type StructLog struct { 68 Pc uint64 `json:"pc"` 69 Op OpCode `json:"op"` 70 Gas uint64 `json:"gas"` 71 GasCost uint64 `json:"gasCost"` 72 Memory bytes.Buffer `json:"memory"` 73 MemorySize int `json:"memSize"` 74 Stack []uint256.Int `json:"stack"` 75 ReturnData bytes.Buffer `json:"returnData"` 76 Storage map[common.Hash]common.Hash `json:"-"` 77 Depth int `json:"depth"` 78 RefundCounter uint64 `json:"refund"` 79 ExtraData *types.ExtraData `json:"extraData"` 80 Err error `json:"-"` 81 } 82 83 func NewStructlog(pc uint64, op OpCode, gas, cost uint64, depth int, err error) *StructLog { 84 return &StructLog{ 85 Pc: pc, 86 Op: op, 87 Gas: gas, 88 GasCost: cost, 89 Depth: depth, 90 Err: err, 91 } 92 } 93 94 func (s *StructLog) clean() { 95 s.Memory.Reset() 96 s.Stack = s.Stack[:0] 97 s.ReturnData.Reset() 98 s.Storage = nil 99 s.ExtraData = nil 100 s.Err = nil 101 } 102 103 func (s *StructLog) getOrInitExtraData() *types.ExtraData { 104 if s.ExtraData == nil { 105 s.ExtraData = &types.ExtraData{} 106 } 107 return s.ExtraData 108 } 109 110 // overrides for gencodec 111 type structLogMarshaling struct { 112 Gas math.HexOrDecimal64 113 GasCost math.HexOrDecimal64 114 Memory hexutil.Bytes 115 ReturnData hexutil.Bytes 116 OpName string `json:"opName"` // adds call to OpName() in MarshalJSON 117 ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON 118 } 119 120 // OpName formats the operand name in a human-readable format. 121 func (s *StructLog) OpName() string { 122 return s.Op.String() 123 } 124 125 // ErrorString formats the log's error as a string. 126 func (s *StructLog) ErrorString() string { 127 if s.Err != nil { 128 return s.Err.Error() 129 } 130 return "" 131 } 132 133 // EVMLogger is used to collect execution traces from an EVM transaction 134 // execution. CaptureState is called for each step of the VM with the 135 // current VM state. 136 // Note that reference types are actual VM data structures; make copies 137 // if you need to retain them beyond the current call. 138 type EVMLogger interface { 139 CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) 140 CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) 141 CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) 142 CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) 143 CaptureExit(output []byte, gasUsed uint64, err error) 144 CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) 145 CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) 146 } 147 148 // StructLogger is an EVM state logger and implements EVMLogger. 149 // 150 // StructLogger can capture state based on the given Log configuration and also keeps 151 // a track record of modified storage which is used in reporting snapshots of the 152 // contract their storage. 153 type StructLogger struct { 154 cfg LogConfig 155 env *EVM 156 157 statesAffected map[common.Address]struct{} 158 storage map[common.Address]Storage 159 createdAccount *types.AccountWrapper 160 161 callStackLogInd []int 162 logs []*StructLog 163 output []byte 164 err error 165 } 166 167 // NewStructLogger returns a new logger 168 func NewStructLogger(cfg *LogConfig) *StructLogger { 169 logger := &StructLogger{ 170 storage: make(map[common.Address]Storage), 171 statesAffected: make(map[common.Address]struct{}), 172 } 173 if cfg != nil { 174 logger.cfg = *cfg 175 } 176 177 return logger 178 } 179 180 // Reset clears the data held by the logger. 181 func (l *StructLogger) Reset() { 182 l.storage = make(map[common.Address]Storage) 183 l.statesAffected = make(map[common.Address]struct{}) 184 l.output = make([]byte, 0) 185 l.logs = l.logs[:0] 186 l.callStackLogInd = nil 187 l.err = nil 188 l.createdAccount = nil 189 } 190 191 // CaptureStart implements the EVMLogger interface to initialize the tracing operation. 192 func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, isCreate bool, input []byte, gas uint64, value *big.Int) { 193 l.env = env 194 195 if isCreate { 196 // notice codeHash is set AFTER CreateTx has exited, so here codeHash is still empty 197 l.createdAccount = &types.AccountWrapper{ 198 Address: to, 199 // nonce is 1 after EIP158, so we query it from stateDb 200 Nonce: env.StateDB.GetNonce(to), 201 Balance: (*hexutil.Big)(value), 202 } 203 } 204 205 l.statesAffected[from] = struct{}{} 206 l.statesAffected[to] = struct{}{} 207 } 208 209 // CaptureState logs a new structured log message and pushes it out to the environment 210 // 211 // CaptureState also tracks SLOAD/SSTORE ops to track storage change. 212 func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, opErr error) { 213 memory := scope.Memory 214 stack := scope.Stack 215 contract := scope.Contract 216 // create a struct log. 217 structLog := NewStructlog(pc, op, gas, cost, depth, opErr) 218 219 // check if already accumulated the specified number of logs 220 if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { 221 return 222 } 223 // Copy a snapshot of the current memory state to a new buffer 224 if l.cfg.EnableMemory { 225 structLog.Memory.Write(memory.Data()) 226 structLog.MemorySize = memory.Len() 227 } 228 // Copy a snapshot of the current stack state to a new buffer 229 if !l.cfg.DisableStack { 230 structLog.Stack = append(structLog.Stack, stack.Data()...) 231 } 232 var ( 233 recordStorageDetail bool 234 storageKey common.Hash 235 storageValue common.Hash 236 ) 237 if !l.cfg.DisableStorage { 238 if op == SLOAD && stack.len() >= 1 { 239 recordStorageDetail = true 240 storageKey = stack.data[stack.len()-1].Bytes32() 241 storageValue = l.env.StateDB.GetState(contract.Address(), storageKey) 242 } else if op == SSTORE && stack.len() >= 2 { 243 recordStorageDetail = true 244 storageKey = stack.data[stack.len()-1].Bytes32() 245 storageValue = stack.data[stack.len()-2].Bytes32() 246 } 247 } 248 if recordStorageDetail { 249 contractAddress := contract.Address() 250 if l.storage[contractAddress] == nil { 251 l.storage[contractAddress] = make(Storage) 252 } 253 l.storage[contractAddress][storageKey] = storageValue 254 structLog.Storage = l.storage[contractAddress].Copy() 255 256 if err := traceStorage(l, scope, structLog.getOrInitExtraData()); err != nil { 257 log.Error("Failed to trace data", "opcode", op.String(), "err", err) 258 } 259 } 260 if l.cfg.EnableReturnData { 261 structLog.ReturnData.Write(rData) 262 } 263 execFuncList, ok := OpcodeExecs[op] 264 if ok { 265 // execute trace func list. 266 for _, exec := range execFuncList { 267 if err := exec(l, scope, structLog.getOrInitExtraData()); err != nil { 268 log.Error("Failed to trace data", "opcode", op.String(), "err", err) 269 } 270 } 271 } 272 // for each "calling" op, pick the caller's state 273 switch op { 274 case CALL, CALLCODE, STATICCALL, DELEGATECALL, CREATE, CREATE2: 275 extraData := structLog.getOrInitExtraData() 276 extraData.Caller = append(extraData.Caller, getWrappedAccountForAddr(l, scope.Contract.Address())) 277 } 278 279 // in reality it is impossible for CREATE to trigger ErrContractAddressCollision 280 if op == CREATE2 && opErr == nil { 281 _ = stack.data[stack.len()-1] // value 282 offset := stack.data[stack.len()-2] 283 size := stack.data[stack.len()-3] 284 salt := stack.data[stack.len()-4] 285 // `CaptureState` is called **before** memory resizing 286 // So sometimes we need to auto pad 0. 287 code := getData(scope.Memory.Data(), offset.Uint64(), size.Uint64()) 288 289 codeAndHash := &codeAndHash{code: code} 290 291 address := crypto.CreateAddress2(contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) 292 293 contractHash := l.env.StateDB.GetKeccakCodeHash(address) 294 if l.env.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyKeccakCodeHash) { 295 extraData := structLog.getOrInitExtraData() 296 wrappedStatus := getWrappedAccountForAddr(l, address) 297 extraData.StateList = append(extraData.StateList, wrappedStatus) 298 l.statesAffected[address] = struct{}{} 299 } 300 } 301 302 structLog.RefundCounter = l.env.StateDB.GetRefund() 303 l.logs = append(l.logs, structLog) 304 } 305 306 func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { 307 } 308 309 // CaptureFault implements the EVMLogger interface to trace an execution fault 310 // while running an opcode. 311 func (l *StructLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { 312 } 313 314 // CaptureEnd is called after the call finishes to finalize the tracing. 315 func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { 316 l.output = output 317 if err != nil { 318 l.err = err 319 } 320 if l.cfg.Debug { 321 fmt.Printf("0x%x\n", output) 322 if err != nil { 323 fmt.Printf(" error: %v\n", err) 324 } 325 } 326 } 327 328 func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 329 // the last logged op should be CALL/STATICCALL/CALLCODE/CREATE/CREATE2 330 lastLogPos := len(l.logs) - 1 331 log.Debug("mark call stack", "pos", lastLogPos, "op", l.logs[lastLogPos].Op) 332 l.callStackLogInd = append(l.callStackLogInd, lastLogPos) 333 // sanity check 334 if len(l.callStackLogInd) != l.env.depth { 335 panic("unexpected evm depth in capture enter") 336 } 337 l.statesAffected[to] = struct{}{} 338 theLog := l.logs[lastLogPos] 339 theLog.getOrInitExtraData() 340 // handling additional updating for CALL/STATICCALL/CALLCODE/CREATE/CREATE2 only 341 // append extraData part for the log, capture the account status (the nonce / balance has been updated in capture enter) 342 wrappedStatus := getWrappedAccountForAddr(l, to) 343 theLog.ExtraData.StateList = append(theLog.ExtraData.StateList, wrappedStatus) 344 // finally we update the caller's status (it is possible that nonce and balance being updated) 345 if len(theLog.ExtraData.Caller) == 1 { 346 theLog.ExtraData.Caller = append(theLog.ExtraData.Caller, getWrappedAccountForAddr(l, from)) 347 } 348 } 349 350 // CaptureExit phase, a CREATE has its target address's code being set and queryable 351 func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) { 352 stackH := len(l.callStackLogInd) 353 if stackH == 0 { 354 panic("unexpected capture exit occur") 355 } 356 357 theLogPos := l.callStackLogInd[stackH-1] 358 l.callStackLogInd = l.callStackLogInd[:stackH-1] 359 theLog := l.logs[theLogPos] 360 // update "forecast" data 361 if err != nil { 362 theLog.ExtraData.CallFailed = true 363 } 364 365 // handling updating for CREATE only 366 switch theLog.Op { 367 case CREATE, CREATE2: 368 // append extraData part for the log whose op is CREATE(2), capture the account status (the codehash would be updated in capture exit) 369 dataLen := len(theLog.ExtraData.StateList) 370 if dataLen == 0 { 371 panic("unexpected data capture for target op") 372 } 373 374 lastAccData := theLog.ExtraData.StateList[dataLen-1] 375 wrappedStatus := getWrappedAccountForAddr(l, lastAccData.Address) 376 theLog.ExtraData.StateList = append(theLog.ExtraData.StateList, wrappedStatus) 377 code := getCodeForAddr(l, lastAccData.Address) 378 theLog.ExtraData.CodeList = append(theLog.ExtraData.CodeList, hexutil.Encode(code)) 379 default: 380 //do nothing for other op code 381 return 382 } 383 384 } 385 386 // UpdatedAccounts is used to collect all "touched" accounts 387 func (l *StructLogger) UpdatedAccounts() map[common.Address]struct{} { 388 return l.statesAffected 389 } 390 391 // UpdatedStorages is used to collect all "touched" storage slots 392 func (l *StructLogger) UpdatedStorages() map[common.Address]Storage { 393 return l.storage 394 } 395 396 // CreatedAccount return the account data in case it is a create tx 397 func (l *StructLogger) CreatedAccount() *types.AccountWrapper { return l.createdAccount } 398 399 // StructLogs returns the captured log entries. 400 func (l *StructLogger) StructLogs() []*StructLog { return l.logs } 401 402 // Error returns the VM error captured by the trace. 403 func (l *StructLogger) Error() error { return l.err } 404 405 // Output returns the VM return value captured by the trace. 406 func (l *StructLogger) Output() []byte { return l.output } 407 408 // WriteTrace writes a formatted trace to the given writer 409 func WriteTrace(writer io.Writer, logs []*StructLog) { 410 for _, log := range logs { 411 fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost) 412 if log.Err != nil { 413 fmt.Fprintf(writer, " ERROR: %v", log.Err) 414 } 415 fmt.Fprintln(writer) 416 417 if len(log.Stack) > 0 { 418 fmt.Fprintln(writer, "Stack:") 419 for i := len(log.Stack) - 1; i >= 0; i-- { 420 fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex()) 421 } 422 } 423 if log.Memory.Len() > 0 { 424 fmt.Fprintln(writer, "Memory:") 425 fmt.Fprint(writer, hex.Dump(log.Memory.Bytes())) 426 } 427 if len(log.Storage) > 0 { 428 fmt.Fprintln(writer, "Storage:") 429 for h, item := range log.Storage { 430 fmt.Fprintf(writer, "%x: %x\n", h, item) 431 } 432 } 433 if log.ReturnData.Len() > 0 { 434 fmt.Fprintln(writer, "ReturnData:") 435 fmt.Fprint(writer, hex.Dump(log.ReturnData.Bytes())) 436 } 437 fmt.Fprintln(writer) 438 } 439 } 440 441 // WriteLogs writes vm logs in a readable format to the given writer 442 func WriteLogs(writer io.Writer, logs []*types.Log) { 443 for _, log := range logs { 444 fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex) 445 446 for i, topic := range log.Topics { 447 fmt.Fprintf(writer, "%08d %x\n", i, topic) 448 } 449 450 fmt.Fprint(writer, hex.Dump(log.Data)) 451 fmt.Fprintln(writer) 452 } 453 } 454 455 type mdLogger struct { 456 out io.Writer 457 cfg *LogConfig 458 env *EVM 459 } 460 461 // NewMarkdownLogger creates a logger which outputs information in a format adapted 462 // for human readability, and is also a valid markdown table 463 func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger { 464 l := &mdLogger{out: writer, cfg: cfg} 465 if l.cfg == nil { 466 l.cfg = &LogConfig{} 467 } 468 return l 469 } 470 471 func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 472 t.env = env 473 if !create { 474 fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", 475 from.String(), to.String(), 476 input, gas, value) 477 } else { 478 fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", 479 from.String(), to.String(), 480 input, gas, value) 481 } 482 483 fmt.Fprintf(t.out, ` 484 | Pc | Op | Cost | Stack | RStack | Refund | 485 |-------|-------------|------|-----------|-----------|---------| 486 `) 487 } 488 489 // CaptureState also tracks SLOAD/SSTORE ops to track storage change. 490 func (t *mdLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { 491 stack := scope.Stack 492 fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) 493 494 if !t.cfg.DisableStack { 495 // format stack 496 var a []string 497 for _, elem := range stack.data { 498 a = append(a, elem.Hex()) 499 } 500 b := fmt.Sprintf("[%v]", strings.Join(a, ",")) 501 fmt.Fprintf(t.out, "%10v |", b) 502 } 503 fmt.Fprintf(t.out, "%10v |", t.env.StateDB.GetRefund()) 504 fmt.Fprintln(t.out, "") 505 if err != nil { 506 fmt.Fprintf(t.out, "Error: %v\n", err) 507 } 508 } 509 510 // CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change. 511 func (t *mdLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { 512 } 513 514 func (t *mdLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { 515 fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) 516 } 517 518 func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) { 519 fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n", 520 output, gasUsed, err) 521 } 522 523 func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 524 } 525 526 func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} 527 528 // FormatLogs formats EVM returned structured logs for json output 529 func FormatLogs(logs []*StructLog) []*types.StructLogRes { 530 formatted := make([]*types.StructLogRes, 0, len(logs)) 531 532 for _, trace := range logs { 533 logRes := types.NewStructLogResBasic(trace.Pc, trace.Op.String(), trace.Gas, trace.GasCost, trace.Depth, trace.RefundCounter, trace.Err) 534 for _, stackValue := range trace.Stack { 535 logRes.Stack = append(logRes.Stack, stackValue.Hex()) 536 } 537 for i := 0; i+32 <= trace.Memory.Len(); i += 32 { 538 logRes.Memory = append(logRes.Memory, common.Bytes2Hex(trace.Memory.Bytes()[i:i+32])) 539 } 540 if len(trace.Storage) != 0 { 541 storage := make(map[string]string) 542 for i, storageValue := range trace.Storage { 543 storage[i.Hex()] = storageValue.Hex() 544 } 545 logRes.Storage = storage 546 } 547 logRes.ExtraData = trace.ExtraData 548 549 formatted = append(formatted, logRes) 550 } 551 return formatted 552 }