github.com/MetalBlockchain/subnet-evm@v0.4.9/eth/tracers/api.go (about) 1 // (c) 2019-2020, 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 2021 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 tracers 28 29 import ( 30 "bytes" 31 "context" 32 "encoding/json" 33 "errors" 34 "fmt" 35 "math/big" 36 "runtime" 37 "sync" 38 "time" 39 40 "github.com/MetalBlockchain/subnet-evm/consensus" 41 "github.com/MetalBlockchain/subnet-evm/core" 42 "github.com/MetalBlockchain/subnet-evm/core/state" 43 "github.com/MetalBlockchain/subnet-evm/core/types" 44 "github.com/MetalBlockchain/subnet-evm/core/vm" 45 "github.com/MetalBlockchain/subnet-evm/eth/tracers/logger" 46 "github.com/MetalBlockchain/subnet-evm/ethdb" 47 "github.com/MetalBlockchain/subnet-evm/internal/ethapi" 48 "github.com/MetalBlockchain/subnet-evm/params" 49 "github.com/MetalBlockchain/subnet-evm/rpc" 50 "github.com/ethereum/go-ethereum/common" 51 "github.com/ethereum/go-ethereum/common/hexutil" 52 "github.com/ethereum/go-ethereum/log" 53 "github.com/ethereum/go-ethereum/rlp" 54 ) 55 56 const ( 57 // defaultTraceTimeout is the amount of time a single transaction can execute 58 // by default before being forcefully aborted. 59 defaultTraceTimeout = 5 * time.Second 60 61 // defaultTraceReexec is the number of blocks the tracer is willing to go back 62 // and reexecute to produce missing historical state necessary to run a specific 63 // trace. 64 defaultTraceReexec = uint64(128) 65 66 // defaultTracechainMemLimit is the size of the triedb, at which traceChain 67 // switches over and tries to use a disk-backed database instead of building 68 // on top of memory. 69 // For non-archive nodes, this limit _will_ be overblown, as disk-backed tries 70 // will only be found every ~15K blocks or so. 71 defaultTracechainMemLimit = common.StorageSize(500 * 1024 * 1024) 72 ) 73 74 // Backend interface provides the common API services (that are provided by 75 // both full and light clients) with access to necessary functions. 76 type Backend interface { 77 HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) 78 HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) 79 BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) 80 BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) 81 BadBlocks() ([]*types.Block, []*core.BadBlockReason) 82 GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) 83 RPCGasCap() uint64 84 ChainConfig() *params.ChainConfig 85 Engine() consensus.Engine 86 ChainDb() ethdb.Database 87 StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (*state.StateDB, error) 88 StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) 89 } 90 91 // API is the collection of tracing APIs exposed over the private debugging endpoint. 92 type API struct { 93 backend Backend 94 } 95 96 // NewAPI creates a new API definition for the tracing methods of the Ethereum service. 97 func NewAPI(backend Backend) *API { 98 return &API{backend: backend} 99 } 100 101 type chainContext struct { 102 api *API 103 ctx context.Context 104 } 105 106 func (context *chainContext) Engine() consensus.Engine { 107 return context.api.backend.Engine() 108 } 109 110 func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header { 111 header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number)) 112 if err != nil { 113 return nil 114 } 115 if header.Hash() == hash { 116 return header 117 } 118 header, err = context.api.backend.HeaderByHash(context.ctx, hash) 119 if err != nil { 120 return nil 121 } 122 return header 123 } 124 125 // chainContext constructs the context reader which is used by the evm for reading 126 // the necessary chain context. 127 func (api *API) chainContext(ctx context.Context) core.ChainContext { 128 return &chainContext{api: api, ctx: ctx} 129 } 130 131 // blockByNumber is the wrapper of the chain access function offered by the backend. 132 // It will return an error if the block is not found. 133 func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 134 block, err := api.backend.BlockByNumber(ctx, number) 135 if err != nil { 136 return nil, err 137 } 138 if block == nil { 139 return nil, fmt.Errorf("block #%d not found", number) 140 } 141 return block, nil 142 } 143 144 // blockByHash is the wrapper of the chain access function offered by the backend. 145 // It will return an error if the block is not found. 146 func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 147 block, err := api.backend.BlockByHash(ctx, hash) 148 if err != nil { 149 return nil, err 150 } 151 if block == nil { 152 return nil, fmt.Errorf("block %s not found", hash.Hex()) 153 } 154 return block, nil 155 } 156 157 // blockByNumberAndHash is the wrapper of the chain access function offered by 158 // the backend. It will return an error if the block is not found. 159 // 160 // Note this function is friendly for the light client which can only retrieve the 161 // historical(before the CHT) header/block by number. 162 func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) { 163 block, err := api.blockByNumber(ctx, number) 164 if err != nil { 165 return nil, err 166 } 167 if block.Hash() == hash { 168 return block, nil 169 } 170 return api.blockByHash(ctx, hash) 171 } 172 173 // TraceConfig holds extra parameters to trace functions. 174 type TraceConfig struct { 175 *logger.Config 176 Tracer *string 177 Timeout *string 178 Reexec *uint64 179 // Config specific to given tracer. Note struct logger 180 // config are historically embedded in main object. 181 TracerConfig json.RawMessage 182 } 183 184 // TraceCallConfig is the config for traceCall API. It holds one more 185 // field to override the state for tracing. 186 type TraceCallConfig struct { 187 TraceConfig 188 StateOverrides *ethapi.StateOverride 189 BlockOverrides *ethapi.BlockOverrides 190 } 191 192 // StdTraceConfig holds extra parameters to standard-json trace functions. 193 type StdTraceConfig struct { 194 logger.Config 195 Reexec *uint64 196 TxHash common.Hash 197 } 198 199 // txTraceResult is the result of a single transaction trace. 200 type txTraceResult struct { 201 Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer 202 Error string `json:"error,omitempty"` // Trace failure produced by the tracer 203 } 204 205 func (t *txTraceResult) String() string { 206 return fmt.Sprintf("result: %s, error: %s", t.Result, t.Error) 207 } 208 209 // blockTraceTask represents a single block trace task when an entire chain is 210 // being traced. 211 type blockTraceTask struct { 212 statedb *state.StateDB // Intermediate state prepped for tracing 213 block *types.Block // Block to trace the transactions from 214 rootref common.Hash // Trie root reference held for this task 215 results []*txTraceResult // Trace results procudes by the task 216 } 217 218 // blockTraceResult represets the results of tracing a single block when an entire 219 // chain is being traced. 220 type blockTraceResult struct { 221 Block hexutil.Uint64 `json:"block"` // Block number corresponding to this trace 222 Hash common.Hash `json:"hash"` // Block hash corresponding to this trace 223 Traces []*txTraceResult `json:"traces"` // Trace results produced by the task 224 } 225 226 // txTraceTask represents a single transaction trace task when an entire block 227 // is being traced. 228 type txTraceTask struct { 229 statedb *state.StateDB // Intermediate state prepped for tracing 230 index int // Transaction offset in the block 231 } 232 233 // TraceChain returns the structured logs created during the execution of EVM 234 // between two blocks (excluding start) and returns them as a JSON object. 235 func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { // Fetch the block interval that we want to trace 236 from, err := api.blockByNumber(ctx, start) 237 if err != nil { 238 return nil, err 239 } 240 to, err := api.blockByNumber(ctx, end) 241 if err != nil { 242 return nil, err 243 } 244 if from.Number().Cmp(to.Number()) >= 0 { 245 return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start) 246 } 247 return api.traceChain(ctx, from, to, config) 248 } 249 250 // traceChain configures a new tracer according to the provided configuration, and 251 // executes all the transactions contained within. The return value will be one item 252 // per transaction, dependent on the requested tracer. 253 func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { 254 // Tracing a chain is a **long** operation, only do with subscriptions 255 notifier, supported := rpc.NotifierFromContext(ctx) 256 if !supported { 257 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 258 } 259 sub := notifier.CreateSubscription() 260 261 // Prepare all the states for tracing. Note this procedure can take very 262 // long time. Timeout mechanism is necessary. 263 reexec := defaultTraceReexec 264 if config != nil && config.Reexec != nil { 265 reexec = *config.Reexec 266 } 267 blocks := int(end.NumberU64() - start.NumberU64()) 268 threads := runtime.NumCPU() 269 if threads > blocks { 270 threads = blocks 271 } 272 var ( 273 pend = new(sync.WaitGroup) 274 tasks = make(chan *blockTraceTask, threads) 275 results = make(chan *blockTraceTask, threads) 276 localctx = context.Background() 277 ) 278 for th := 0; th < threads; th++ { 279 pend.Add(1) 280 go func() { 281 defer pend.Done() 282 283 // Fetch and execute the next block trace tasks 284 for task := range tasks { 285 signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), new(big.Int).SetUint64(task.block.Time())) 286 blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil) 287 // Trace all the transactions contained within 288 for i, tx := range task.block.Transactions() { 289 msg, _ := tx.AsMessage(signer, task.block.BaseFee()) 290 txctx := &Context{ 291 BlockHash: task.block.Hash(), 292 TxIndex: i, 293 TxHash: tx.Hash(), 294 } 295 res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config) 296 if err != nil { 297 task.results[i] = &txTraceResult{Error: err.Error()} 298 log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) 299 break 300 } 301 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 302 task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number())) 303 task.results[i] = &txTraceResult{Result: res} 304 } 305 // Stream the result back to the user or abort on teardown 306 select { 307 case results <- task: 308 case <-notifier.Closed(): 309 return 310 } 311 } 312 }() 313 } 314 // Start a goroutine to feed all the blocks into the tracers 315 var ( 316 begin = time.Now() 317 derefTodo []common.Hash // list of hashes to dereference from the db 318 derefsMu sync.Mutex // mutex for the derefs 319 ) 320 321 go func() { 322 var ( 323 logged time.Time 324 number uint64 325 traced uint64 326 failed error 327 parent common.Hash 328 statedb *state.StateDB 329 ) 330 // Ensure everything is properly cleaned up on any exit path 331 defer func() { 332 close(tasks) 333 pend.Wait() 334 335 switch { 336 case failed != nil: 337 log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed) 338 case number < end.NumberU64(): 339 log.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin)) 340 default: 341 log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin)) 342 } 343 close(results) 344 }() 345 var preferDisk bool 346 // Feed all the blocks both into the tracer, as well as fast process concurrently 347 for number = start.NumberU64(); number < end.NumberU64(); number++ { 348 // Stop tracing if interruption was requested 349 select { 350 case <-notifier.Closed(): 351 return 352 default: 353 } 354 // clean out any derefs 355 derefsMu.Lock() 356 for _, h := range derefTodo { 357 statedb.Database().TrieDB().Dereference(h) 358 } 359 derefTodo = derefTodo[:0] 360 derefsMu.Unlock() 361 362 // Print progress logs if long enough time elapsed 363 if time.Since(logged) > 8*time.Second { 364 logged = time.Now() 365 log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin)) 366 } 367 // Retrieve the parent state to trace on top 368 block, err := api.blockByNumber(localctx, rpc.BlockNumber(number)) 369 if err != nil { 370 failed = err 371 break 372 } 373 // Prepare the statedb for tracing. Don't use the live database for 374 // tracing to avoid persisting state junks into the database. 375 statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false, preferDisk) 376 if err != nil { 377 failed = err 378 break 379 } 380 if trieDb := statedb.Database().TrieDB(); trieDb != nil { 381 // Hold the reference for tracer, will be released at the final stage 382 trieDb.Reference(block.Root(), common.Hash{}) 383 384 // Release the parent state because it's already held by the tracer 385 if parent != (common.Hash{}) { 386 trieDb.Dereference(parent) 387 } 388 // Prefer disk if the trie db memory grows too much 389 s1, s2 := trieDb.Size() 390 if !preferDisk && (s1+s2) > defaultTracechainMemLimit { 391 log.Info("Switching to prefer-disk mode for tracing", "size", s1+s2) 392 preferDisk = true 393 } 394 } 395 parent = block.Root() 396 397 next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1)) 398 if err != nil { 399 failed = err 400 break 401 } 402 // Send the block over to the concurrent tracers (if not in the fast-forward phase) 403 txs := next.Transactions() 404 select { 405 case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}: 406 case <-notifier.Closed(): 407 return 408 } 409 traced += uint64(len(txs)) 410 } 411 }() 412 413 // Keep reading the trace results and stream the to the user 414 go func() { 415 var ( 416 done = make(map[uint64]*blockTraceResult) 417 next = start.NumberU64() + 1 418 ) 419 for res := range results { 420 // Queue up next received result 421 result := &blockTraceResult{ 422 Block: hexutil.Uint64(res.block.NumberU64()), 423 Hash: res.block.Hash(), 424 Traces: res.results, 425 } 426 // Schedule any parent tries held in memory by this task for dereferencing 427 done[uint64(result.Block)] = result 428 derefsMu.Lock() 429 derefTodo = append(derefTodo, res.rootref) 430 derefsMu.Unlock() 431 // Stream completed traces to the user, aborting on the first error 432 for result, ok := done[next]; ok; result, ok = done[next] { 433 if len(result.Traces) > 0 || next == end.NumberU64() { 434 notifier.Notify(sub.ID, result) 435 } 436 delete(done, next) 437 next++ 438 } 439 } 440 }() 441 return sub, nil 442 } 443 444 // TraceBlockByNumber returns the structured logs created during the execution of 445 // EVM and returns them as a JSON object. 446 func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { 447 block, err := api.blockByNumber(ctx, number) 448 if err != nil { 449 return nil, err 450 } 451 return api.traceBlock(ctx, block, config) 452 } 453 454 // TraceBlockByHash returns the structured logs created during the execution of 455 // EVM and returns them as a JSON object. 456 func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { 457 block, err := api.blockByHash(ctx, hash) 458 if err != nil { 459 return nil, err 460 } 461 return api.traceBlock(ctx, block, config) 462 } 463 464 // TraceBlock returns the structured logs created during the execution of EVM 465 // and returns them as a JSON object. 466 func (api *API) TraceBlock(ctx context.Context, blob hexutil.Bytes, config *TraceConfig) ([]*txTraceResult, error) { 467 block := new(types.Block) 468 if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { 469 return nil, fmt.Errorf("could not decode block: %v", err) 470 } 471 return api.traceBlock(ctx, block, config) 472 } 473 474 // TraceBadBlock returns the structured logs created during the execution of 475 // EVM against a block pulled from the pool of bad ones and returns them as a JSON 476 // object. 477 func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { 478 // Search for the bad block corresponding to [hash]. 479 var ( 480 badBlocks, _ = api.backend.BadBlocks() 481 block *types.Block 482 ) 483 for _, badBlock := range badBlocks { 484 if hash == block.Hash() { 485 block = badBlock 486 break 487 } 488 } 489 if block == nil { 490 return nil, fmt.Errorf("bad block %#x not found", hash) 491 } 492 return api.traceBlock(ctx, block, config) 493 } 494 495 // IntermediateRoots executes a block (bad- or canon- or side-), and returns a list 496 // of intermediate roots: the stateroot after each transaction. 497 func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) { 498 block, _ := api.blockByHash(ctx, hash) 499 if block == nil { 500 return nil, fmt.Errorf("block %#x not found", hash) 501 } 502 if block.NumberU64() == 0 { 503 return nil, errors.New("genesis is not traceable") 504 } 505 parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) 506 if err != nil { 507 return nil, err 508 } 509 reexec := defaultTraceReexec 510 if config != nil && config.Reexec != nil { 511 reexec = *config.Reexec 512 } 513 statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) 514 if err != nil { 515 return nil, err 516 } 517 var ( 518 roots []common.Hash 519 signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), new(big.Int).SetUint64(block.Time())) 520 chainConfig = api.backend.ChainConfig() 521 vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 522 deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) 523 ) 524 for i, tx := range block.Transactions() { 525 var ( 526 msg, _ = tx.AsMessage(signer, block.BaseFee()) 527 txContext = core.NewEVMTxContext(msg) 528 vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) 529 ) 530 statedb.Prepare(tx.Hash(), i) 531 if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { 532 log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) 533 // We intentionally don't return the error here: if we do, then the RPC server will not 534 // return the roots. Most likely, the caller already knows that a certain transaction fails to 535 // be included, but still want the intermediate roots that led to that point. 536 // It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be 537 // executable. 538 // N.B: This should never happen while tracing canon blocks, only when tracing bad blocks. 539 return roots, nil 540 } 541 // calling IntermediateRoot will internally call Finalize on the state 542 // so any modifications are written to the trie 543 roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects)) 544 } 545 return roots, nil 546 } 547 548 // traceBlock configures a new tracer according to the provided configuration, and 549 // executes all the transactions contained within. The return value will be one item 550 // per transaction, dependent on the requested tracer. 551 func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { 552 if block.NumberU64() == 0 { 553 return nil, errors.New("genesis is not traceable") 554 } 555 parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) 556 if err != nil { 557 return nil, err 558 } 559 reexec := defaultTraceReexec 560 if config != nil && config.Reexec != nil { 561 reexec = *config.Reexec 562 } 563 statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) 564 if err != nil { 565 return nil, err 566 } 567 // Execute all the transaction contained within the block concurrently 568 var ( 569 signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), new(big.Int).SetUint64(block.Time())) 570 txs = block.Transactions() 571 results = make([]*txTraceResult, len(txs)) 572 573 pend = new(sync.WaitGroup) 574 jobs = make(chan *txTraceTask, len(txs)) 575 ) 576 threads := runtime.NumCPU() 577 if threads > len(txs) { 578 threads = len(txs) 579 } 580 blockHash := block.Hash() 581 for th := 0; th < threads; th++ { 582 pend.Add(1) 583 go func() { 584 defer pend.Done() 585 586 blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 587 // Fetch and execute the next transaction trace tasks 588 for task := range jobs { 589 msg, _ := txs[task.index].AsMessage(signer, block.BaseFee()) 590 txctx := &Context{ 591 BlockHash: blockHash, 592 TxIndex: task.index, 593 TxHash: txs[task.index].Hash(), 594 } 595 res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) 596 if err != nil { 597 results[task.index] = &txTraceResult{Error: err.Error()} 598 continue 599 } 600 results[task.index] = &txTraceResult{Result: res} 601 } 602 }() 603 } 604 // Feed the transactions into the tracers and return 605 var failed error 606 blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 607 for i, tx := range txs { 608 // Send the trace task over for execution 609 jobs <- &txTraceTask{statedb: statedb.Copy(), index: i} 610 611 // Generate the next state snapshot fast without tracing 612 msg, _ := tx.AsMessage(signer, block.BaseFee()) 613 statedb.Prepare(tx.Hash(), i) 614 vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) 615 if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { 616 failed = err 617 break 618 } 619 // Finalize the state so any modifications are written to the trie 620 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 621 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 622 } 623 close(jobs) 624 pend.Wait() 625 626 // If execution failed in between, abort 627 if failed != nil { 628 return nil, failed 629 } 630 return results, nil 631 } 632 633 // TraceTransaction returns the structured logs created during the execution of EVM 634 // and returns them as a JSON object. 635 func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { 636 _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) 637 if err != nil { 638 return nil, err 639 } 640 // It shouldn't happen in practice. 641 if blockNumber == 0 { 642 return nil, errors.New("genesis is not traceable") 643 } 644 reexec := defaultTraceReexec 645 if config != nil && config.Reexec != nil { 646 reexec = *config.Reexec 647 } 648 block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash) 649 if err != nil { 650 return nil, err 651 } 652 msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) 653 if err != nil { 654 return nil, err 655 } 656 txctx := &Context{ 657 BlockHash: blockHash, 658 TxIndex: int(index), 659 TxHash: hash, 660 } 661 return api.traceTx(ctx, msg, txctx, vmctx, statedb, config) 662 } 663 664 // TraceCall lets you trace a given eth_call. It collects the structured logs 665 // created during the execution of EVM if the given transaction was added on 666 // top of the provided block and returns them as a JSON object. 667 func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { 668 // Try to retrieve the specified block 669 var ( 670 err error 671 block *types.Block 672 ) 673 if hash, ok := blockNrOrHash.Hash(); ok { 674 block, err = api.blockByHash(ctx, hash) 675 } else if number, ok := blockNrOrHash.Number(); ok { 676 if number == rpc.PendingBlockNumber { 677 // We don't have access to the miner here. For tracing 'future' transactions, 678 // it can be done with block- and state-overrides instead, which offers 679 // more flexibility and stability than trying to trace on 'pending', since 680 // the contents of 'pending' is unstable and probably not a true representation 681 // of what the next actual block is likely to contain. 682 return nil, errors.New("tracing on top of pending is not supported") 683 } 684 block, err = api.blockByNumber(ctx, number) 685 } else { 686 return nil, errors.New("invalid arguments; neither block nor hash specified") 687 } 688 if err != nil { 689 return nil, err 690 } 691 // try to recompute the state 692 reexec := defaultTraceReexec 693 if config != nil && config.Reexec != nil { 694 reexec = *config.Reexec 695 } 696 statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false) 697 if err != nil { 698 return nil, err 699 } 700 vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 701 // Apply the customization rules if required. 702 if config != nil { 703 if err := config.StateOverrides.Apply(statedb); err != nil { 704 return nil, err 705 } 706 config.BlockOverrides.Apply(&vmctx) 707 } 708 // Execute the trace 709 msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee()) 710 if err != nil { 711 return nil, err 712 } 713 714 var traceConfig *TraceConfig 715 if config != nil { 716 traceConfig = &TraceConfig{ 717 Config: config.Config, 718 Tracer: config.Tracer, 719 Timeout: config.Timeout, 720 Reexec: config.Reexec, 721 } 722 } 723 return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig) 724 } 725 726 // traceTx configures a new tracer according to the provided configuration, and 727 // executes the given message in the provided environment. The return value will 728 // be tracer dependent. 729 func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { 730 var ( 731 tracer Tracer 732 err error 733 timeout = defaultTraceTimeout 734 txContext = core.NewEVMTxContext(message) 735 ) 736 if config == nil { 737 config = &TraceConfig{} 738 } 739 // Default tracer is the struct logger 740 tracer = logger.NewStructLogger(config.Config) 741 if config.Tracer != nil { 742 tracer, err = New(*config.Tracer, txctx, config.TracerConfig) 743 if err != nil { 744 return nil, err 745 } 746 } 747 // Define a meaningful timeout of a single transaction trace 748 if config.Timeout != nil { 749 if timeout, err = time.ParseDuration(*config.Timeout); err != nil { 750 return nil, err 751 } 752 } 753 deadlineCtx, cancel := context.WithTimeout(ctx, timeout) 754 go func() { 755 <-deadlineCtx.Done() 756 if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { 757 tracer.Stop(errors.New("execution timeout")) 758 } 759 }() 760 defer cancel() 761 762 // Run the transaction with tracing enabled. 763 vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) 764 // Call Prepare to clear out the statedb access list 765 statedb.Prepare(txctx.TxHash, txctx.TxIndex) 766 if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())); err != nil { 767 return nil, fmt.Errorf("tracing failed: %w", err) 768 } 769 return tracer.GetResult() 770 } 771 772 // APIs return the collection of RPC services the tracer package offers. 773 func APIs(backend Backend) []rpc.API { 774 // Append all the local APIs and return 775 return []rpc.API{ 776 { 777 Namespace: "debug", 778 Service: NewAPI(backend), 779 Name: "debug-tracer", 780 }, 781 } 782 }