github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/eth/tracers/api.go (about) 1 // Copyright 2021 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 tracers 18 19 import ( 20 "bufio" 21 "bytes" 22 "context" 23 "errors" 24 "fmt" 25 "io/ioutil" 26 "math/big" 27 "os" 28 "runtime" 29 "sync" 30 "time" 31 32 "github.com/fff-chain/go-fff/common" 33 "github.com/fff-chain/go-fff/common/gopool" 34 "github.com/fff-chain/go-fff/common/hexutil" 35 "github.com/fff-chain/go-fff/consensus" 36 "github.com/fff-chain/go-fff/core" 37 "github.com/fff-chain/go-fff/core/rawdb" 38 "github.com/fff-chain/go-fff/core/state" 39 "github.com/fff-chain/go-fff/core/types" 40 "github.com/fff-chain/go-fff/core/vm" 41 "github.com/fff-chain/go-fff/ethdb" 42 "github.com/fff-chain/go-fff/internal/ethapi" 43 "github.com/fff-chain/go-fff/log" 44 "github.com/fff-chain/go-fff/params" 45 "github.com/fff-chain/go-fff/rlp" 46 "github.com/fff-chain/go-fff/rpc" 47 ) 48 49 const ( 50 // defaultTraceTimeout is the amount of time a single transaction can execute 51 // by default before being forcefully aborted. 52 defaultTraceTimeout = 5 * time.Second 53 54 // defaultTraceReexec is the number of blocks the tracer is willing to go back 55 // and reexecute to produce missing historical state necessary to run a specific 56 // trace. 57 defaultTraceReexec = uint64(128) 58 59 // defaultTracechainMemLimit is the size of the triedb, at which traceChain 60 // switches over and tries to use a disk-backed database instead of building 61 // on top of memory. 62 // For non-archive nodes, this limit _will_ be overblown, as disk-backed tries 63 // will only be found every ~15K blocks or so. 64 defaultTracechainMemLimit = common.StorageSize(500 * 1024 * 1024) 65 ) 66 67 // Backend interface provides the common API services (that are provided by 68 // both full and light clients) with access to necessary functions. 69 type Backend interface { 70 HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) 71 HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) 72 BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) 73 BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) 74 GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) 75 RPCGasCap() uint64 76 ChainConfig() *params.ChainConfig 77 Engine() consensus.Engine 78 ChainDb() ethdb.Database 79 // StateAtBlock returns the state corresponding to the stateroot of the block. 80 // N.B: For executing transactions on block N, the required stateRoot is block N-1, 81 // so this method should be called with the parent. 82 StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error) 83 StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) 84 } 85 86 // API is the collection of tracing APIs exposed over the private debugging endpoint. 87 type API struct { 88 backend Backend 89 } 90 91 // NewAPI creates a new API definition for the tracing methods of the Ethereum service. 92 func NewAPI(backend Backend) *API { 93 return &API{backend: backend} 94 } 95 96 type chainContext struct { 97 api *API 98 ctx context.Context 99 } 100 101 func (context *chainContext) Engine() consensus.Engine { 102 return context.api.backend.Engine() 103 } 104 105 func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header { 106 header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number)) 107 if err != nil { 108 return nil 109 } 110 if header.Hash() == hash { 111 return header 112 } 113 header, err = context.api.backend.HeaderByHash(context.ctx, hash) 114 if err != nil { 115 return nil 116 } 117 return header 118 } 119 120 // chainContext construts the context reader which is used by the evm for reading 121 // the necessary chain context. 122 func (api *API) chainContext(ctx context.Context) core.ChainContext { 123 return &chainContext{api: api, ctx: ctx} 124 } 125 126 // blockByNumber is the wrapper of the chain access function offered by the backend. 127 // It will return an error if the block is not found. 128 func (api *API) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 129 block, err := api.backend.BlockByNumber(ctx, number) 130 if err != nil { 131 return nil, err 132 } 133 if block == nil { 134 return nil, fmt.Errorf("block #%d not found", number) 135 } 136 return block, nil 137 } 138 139 // blockByHash is the wrapper of the chain access function offered by the backend. 140 // It will return an error if the block is not found. 141 func (api *API) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { 142 block, err := api.backend.BlockByHash(ctx, hash) 143 if err != nil { 144 return nil, err 145 } 146 if block == nil { 147 return nil, fmt.Errorf("block %s not found", hash.Hex()) 148 } 149 return block, nil 150 } 151 152 // blockByNumberAndHash is the wrapper of the chain access function offered by 153 // the backend. It will return an error if the block is not found. 154 // 155 // Note this function is friendly for the light client which can only retrieve the 156 // historical(before the CHT) header/block by number. 157 func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) { 158 block, err := api.blockByNumber(ctx, number) 159 if err != nil { 160 return nil, err 161 } 162 if block.Hash() == hash { 163 return block, nil 164 } 165 return api.blockByHash(ctx, hash) 166 } 167 168 // TraceConfig holds extra parameters to trace functions. 169 type TraceConfig struct { 170 *vm.LogConfig 171 Tracer *string 172 Timeout *string 173 Reexec *uint64 174 } 175 176 // TraceCallConfig is the config for traceCall API. It holds one more 177 // field to override the state for tracing. 178 type TraceCallConfig struct { 179 *vm.LogConfig 180 Tracer *string 181 Timeout *string 182 Reexec *uint64 183 StateOverrides *ethapi.StateOverride 184 } 185 186 // StdTraceConfig holds extra parameters to standard-json trace functions. 187 type StdTraceConfig struct { 188 vm.LogConfig 189 Reexec *uint64 190 TxHash common.Hash 191 } 192 193 // txTraceResult is the result of a single transaction trace. 194 type txTraceResult struct { 195 Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer 196 Error string `json:"error,omitempty"` // Trace failure produced by the tracer 197 } 198 199 // blockTraceTask represents a single block trace task when an entire chain is 200 // being traced. 201 type blockTraceTask struct { 202 statedb *state.StateDB // Intermediate state prepped for tracing 203 block *types.Block // Block to trace the transactions from 204 rootref common.Hash // Trie root reference held for this task 205 results []*txTraceResult // Trace results procudes by the task 206 } 207 208 // blockTraceResult represets the results of tracing a single block when an entire 209 // chain is being traced. 210 type blockTraceResult struct { 211 Block hexutil.Uint64 `json:"block"` // Block number corresponding to this trace 212 Hash common.Hash `json:"hash"` // Block hash corresponding to this trace 213 Traces []*txTraceResult `json:"traces"` // Trace results produced by the task 214 } 215 216 // txTraceTask represents a single transaction trace task when an entire block 217 // is being traced. 218 type txTraceTask struct { 219 statedb *state.StateDB // Intermediate state prepped for tracing 220 index int // Transaction offset in the block 221 } 222 223 // TraceChain returns the structured logs created during the execution of EVM 224 // between two blocks (excluding start) and returns them as a JSON object. 225 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 226 from, err := api.blockByNumber(ctx, start) 227 if err != nil { 228 return nil, err 229 } 230 to, err := api.blockByNumber(ctx, end) 231 if err != nil { 232 return nil, err 233 } 234 if from.Number().Cmp(to.Number()) >= 0 { 235 return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start) 236 } 237 return api.traceChain(ctx, from, to, config) 238 } 239 240 // traceChain configures a new tracer according to the provided configuration, and 241 // executes all the transactions contained within. The return value will be one item 242 // per transaction, dependent on the requested tracer. 243 func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { 244 // Tracing a chain is a **long** operation, only do with subscriptions 245 notifier, supported := rpc.NotifierFromContext(ctx) 246 if !supported { 247 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 248 } 249 sub := notifier.CreateSubscription() 250 251 // Prepare all the states for tracing. Note this procedure can take very 252 // long time. Timeout mechanism is necessary. 253 reexec := defaultTraceReexec 254 if config != nil && config.Reexec != nil { 255 reexec = *config.Reexec 256 } 257 blocks := int(end.NumberU64() - start.NumberU64()) 258 threads := runtime.NumCPU() 259 if threads > blocks { 260 threads = blocks 261 } 262 var ( 263 pend = new(sync.WaitGroup) 264 tasks = make(chan *blockTraceTask, threads) 265 results = make(chan *blockTraceTask, threads) 266 localctx = context.Background() 267 ) 268 for th := 0; th < threads; th++ { 269 pend.Add(1) 270 gopool.Submit(func() { 271 defer pend.Done() 272 273 // Fetch and execute the next block trace tasks 274 for task := range tasks { 275 signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number()) 276 blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil) 277 // Trace all the transactions contained within 278 for i, tx := range task.block.Transactions() { 279 msg, _ := tx.AsMessage(signer) 280 txctx := &Context{ 281 BlockHash: task.block.Hash(), 282 TxIndex: i, 283 TxHash: tx.Hash(), 284 } 285 res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config) 286 if err != nil { 287 task.results[i] = &txTraceResult{Error: err.Error()} 288 log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) 289 break 290 } 291 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 292 task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number())) 293 task.results[i] = &txTraceResult{Result: res} 294 } 295 // Stream the result back to the user or abort on teardown 296 select { 297 case results <- task: 298 case <-notifier.Closed(): 299 return 300 } 301 } 302 }) 303 } 304 // Start a goroutine to feed all the blocks into the tracers 305 var ( 306 begin = time.Now() 307 derefTodo []common.Hash // list of hashes to dereference from the db 308 derefsMu sync.Mutex // mutex for the derefs 309 ) 310 311 gopool.Submit(func() { 312 var ( 313 logged time.Time 314 number uint64 315 traced uint64 316 failed error 317 parent common.Hash 318 statedb *state.StateDB 319 ) 320 // Ensure everything is properly cleaned up on any exit path 321 defer func() { 322 close(tasks) 323 pend.Wait() 324 325 switch { 326 case failed != nil: 327 log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed) 328 case number < end.NumberU64(): 329 log.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin)) 330 default: 331 log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin)) 332 } 333 close(results) 334 }() 335 var preferDisk bool 336 // Feed all the blocks both into the tracer, as well as fast process concurrently 337 for number = start.NumberU64(); number < end.NumberU64(); number++ { 338 // Stop tracing if interruption was requested 339 select { 340 case <-notifier.Closed(): 341 return 342 default: 343 } 344 // clean out any derefs 345 derefsMu.Lock() 346 for _, h := range derefTodo { 347 statedb.Database().TrieDB().Dereference(h) 348 } 349 derefTodo = derefTodo[:0] 350 derefsMu.Unlock() 351 352 // Print progress logs if long enough time elapsed 353 if time.Since(logged) > 8*time.Second { 354 logged = time.Now() 355 log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin)) 356 } 357 // Retrieve the parent state to trace on top 358 block, err := api.blockByNumber(localctx, rpc.BlockNumber(number)) 359 if err != nil { 360 failed = err 361 break 362 } 363 // Prepare the statedb for tracing. Don't use the live database for 364 // tracing to avoid persisting state junks into the database. 365 statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false, preferDisk) 366 if err != nil { 367 failed = err 368 break 369 } 370 if trieDb := statedb.Database().TrieDB(); trieDb != nil { 371 // Hold the reference for tracer, will be released at the final stage 372 trieDb.Reference(block.Root(), common.Hash{}) 373 374 // Release the parent state because it's already held by the tracer 375 if parent != (common.Hash{}) { 376 trieDb.Dereference(parent) 377 } 378 // Prefer disk if the trie db memory grows too much 379 s1, s2 := trieDb.Size() 380 if !preferDisk && (s1+s2) > defaultTracechainMemLimit { 381 log.Info("Switching to prefer-disk mode for tracing", "size", s1+s2) 382 preferDisk = true 383 } 384 } 385 parent = block.Root() 386 387 next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1)) 388 if err != nil { 389 failed = err 390 break 391 } 392 // Send the block over to the concurrent tracers (if not in the fast-forward phase) 393 txs := next.Transactions() 394 select { 395 case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: next, rootref: block.Root(), results: make([]*txTraceResult, len(txs))}: 396 case <-notifier.Closed(): 397 return 398 } 399 traced += uint64(len(txs)) 400 } 401 }) 402 403 // Keep reading the trace results and stream the to the user 404 gopool.Submit(func() { 405 var ( 406 done = make(map[uint64]*blockTraceResult) 407 next = start.NumberU64() + 1 408 ) 409 for res := range results { 410 // Queue up next received result 411 result := &blockTraceResult{ 412 Block: hexutil.Uint64(res.block.NumberU64()), 413 Hash: res.block.Hash(), 414 Traces: res.results, 415 } 416 // Schedule any parent tries held in memory by this task for dereferencing 417 done[uint64(result.Block)] = result 418 derefsMu.Lock() 419 derefTodo = append(derefTodo, res.rootref) 420 derefsMu.Unlock() 421 // Stream completed traces to the user, aborting on the first error 422 for result, ok := done[next]; ok; result, ok = done[next] { 423 if len(result.Traces) > 0 || next == end.NumberU64() { 424 notifier.Notify(sub.ID, result) 425 } 426 delete(done, next) 427 next++ 428 } 429 } 430 }) 431 return sub, nil 432 } 433 434 // TraceBlockByNumber returns the structured logs created during the execution of 435 // EVM and returns them as a JSON object. 436 func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { 437 block, err := api.blockByNumber(ctx, number) 438 if err != nil { 439 return nil, err 440 } 441 return api.traceBlock(ctx, block, config) 442 } 443 444 // TraceBlockByHash returns the structured logs created during the execution of 445 // EVM and returns them as a JSON object. 446 func (api *API) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { 447 block, err := api.blockByHash(ctx, hash) 448 if err != nil { 449 return nil, err 450 } 451 return api.traceBlock(ctx, block, config) 452 } 453 454 // TraceBlock returns the structured logs created during the execution of EVM 455 // and returns them as a JSON object. 456 func (api *API) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { 457 block := new(types.Block) 458 if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { 459 return nil, fmt.Errorf("could not decode block: %v", err) 460 } 461 return api.traceBlock(ctx, block, config) 462 } 463 464 // TraceBlockFromFile returns the structured logs created during the execution of 465 // EVM and returns them as a JSON object. 466 func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { 467 blob, err := ioutil.ReadFile(file) 468 if err != nil { 469 return nil, fmt.Errorf("could not read file: %v", err) 470 } 471 return api.TraceBlock(ctx, blob, 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 block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash) 479 if block == nil { 480 return nil, fmt.Errorf("bad block %#x not found", hash) 481 } 482 return api.traceBlock(ctx, block, config) 483 } 484 485 // StandardTraceBlockToFile dumps the structured logs created during the 486 // execution of EVM to the local file system and returns a list of files 487 // to the caller. 488 func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { 489 block, err := api.blockByHash(ctx, hash) 490 if err != nil { 491 return nil, err 492 } 493 return api.standardTraceBlockToFile(ctx, block, config) 494 } 495 496 // IntermediateRoots executes a block (bad- or canon- or side-), and returns a list 497 // of intermediate roots: the stateroot after each transaction. 498 func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) { 499 block, _ := api.blockByHash(ctx, hash) 500 if block == nil { 501 // Check in the bad blocks 502 block = rawdb.ReadBadBlock(api.backend.ChainDb(), hash) 503 } 504 if block == nil { 505 return nil, fmt.Errorf("block %#x not found", hash) 506 } 507 if block.NumberU64() == 0 { 508 return nil, errors.New("genesis is not traceable") 509 } 510 parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) 511 if err != nil { 512 return nil, err 513 } 514 reexec := defaultTraceReexec 515 if config != nil && config.Reexec != nil { 516 reexec = *config.Reexec 517 } 518 statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) 519 if err != nil { 520 return nil, err 521 } 522 var ( 523 roots []common.Hash 524 signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) 525 chainConfig = api.backend.ChainConfig() 526 vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 527 deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) 528 ) 529 for i, tx := range block.Transactions() { 530 var ( 531 msg, _ = tx.AsMessage(signer) 532 txContext = core.NewEVMTxContext(msg) 533 vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) 534 ) 535 536 if posa, ok := api.backend.Engine().(consensus.PoSA); ok { 537 if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem { 538 balance := statedb.GetBalance(consensus.SystemAddress) 539 if balance.Cmp(common.Big0) > 0 { 540 statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) 541 statedb.AddBalance(vmctx.Coinbase, balance) 542 } 543 } 544 } 545 546 statedb.Prepare(tx.Hash(), block.Hash(), i) 547 if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { 548 log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) 549 // We intentionally don't return the error here: if we do, then the RPC server will not 550 // return the roots. Most likely, the caller already knows that a certain transaction fails to 551 // be included, but still want the intermediate roots that led to that point. 552 // It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be 553 // executable. 554 // N.B: This should never happen while tracing canon blocks, only when tracing bad blocks. 555 return roots, nil 556 } 557 // calling IntermediateRoot will internally call Finalize on the state 558 // so any modifications are written to the trie 559 root := statedb.IntermediateRoot(deleteEmptyObjects) 560 561 roots = append(roots, root) 562 } 563 return roots, nil 564 } 565 566 // StandardTraceBadBlockToFile dumps the structured logs created during the 567 // execution of EVM against a block pulled from the pool of bad ones to the 568 // local file system and returns a list of files to the caller. 569 func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { 570 block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash) 571 if block == nil { 572 return nil, fmt.Errorf("bad block %#x not found", hash) 573 } 574 return api.standardTraceBlockToFile(ctx, block, config) 575 } 576 577 // traceBlock configures a new tracer according to the provided configuration, and 578 // executes all the transactions contained within. The return value will be one item 579 // per transaction, dependent on the requestd tracer. 580 func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { 581 if block.NumberU64() == 0 { 582 return nil, errors.New("genesis is not traceable") 583 } 584 parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) 585 if err != nil { 586 return nil, err 587 } 588 reexec := defaultTraceReexec 589 if config != nil && config.Reexec != nil { 590 reexec = *config.Reexec 591 } 592 statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) 593 if err != nil { 594 return nil, err 595 } 596 // Execute all the transaction contained within the block concurrently 597 var ( 598 signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) 599 txs = block.Transactions() 600 results = make([]*txTraceResult, len(txs)) 601 602 pend = new(sync.WaitGroup) 603 jobs = make(chan *txTraceTask, len(txs)) 604 ) 605 threads := runtime.NumCPU() 606 if threads > len(txs) { 607 threads = len(txs) 608 } 609 blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 610 blockHash := block.Hash() 611 for th := 0; th < threads; th++ { 612 blockCtx := blockCtx 613 614 pend.Add(1) 615 gopool.Submit(func() { 616 defer pend.Done() 617 // Fetch and execute the next transaction trace tasks 618 for task := range jobs { 619 msg, _ := txs[task.index].AsMessage(signer) 620 txctx := &Context{ 621 BlockHash: blockHash, 622 TxIndex: task.index, 623 TxHash: txs[task.index].Hash(), 624 } 625 res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) 626 if err != nil { 627 results[task.index] = &txTraceResult{Error: err.Error()} 628 continue 629 } 630 results[task.index] = &txTraceResult{Result: res} 631 } 632 }) 633 } 634 // Feed the transactions into the tracers and return 635 var failed error 636 blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 637 for i, tx := range txs { 638 // Send the trace task over for execution 639 jobs <- &txTraceTask{statedb: statedb.Copy(), index: i} 640 641 // Generate the next state snapshot fast without tracing 642 msg, _ := tx.AsMessage(signer) 643 644 if posa, ok := api.backend.Engine().(consensus.PoSA); ok { 645 if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem { 646 balance := statedb.GetBalance(consensus.SystemAddress) 647 if balance.Cmp(common.Big0) > 0 { 648 statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) 649 statedb.AddBalance(block.Header().Coinbase, balance) 650 } 651 } 652 } 653 654 statedb.Prepare(tx.Hash(), block.Hash(), i) 655 vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) 656 if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { 657 failed = err 658 break 659 } 660 // Finalize the state so any modifications are written to the trie 661 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 662 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 663 } 664 close(jobs) 665 pend.Wait() 666 667 // If execution failed in between, abort 668 if failed != nil { 669 return nil, failed 670 } 671 return results, nil 672 } 673 674 // standardTraceBlockToFile configures a new tracer which uses standard JSON output, 675 // and traces either a full block or an individual transaction. The return value will 676 // be one filename per transaction traced. 677 func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { 678 // If we're tracing a single transaction, make sure it's present 679 if config != nil && config.TxHash != (common.Hash{}) { 680 if !containsTx(block, config.TxHash) { 681 return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash) 682 } 683 } 684 if block.NumberU64() == 0 { 685 return nil, errors.New("genesis is not traceable") 686 } 687 parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) 688 if err != nil { 689 return nil, err 690 } 691 reexec := defaultTraceReexec 692 if config != nil && config.Reexec != nil { 693 reexec = *config.Reexec 694 } 695 statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) 696 if err != nil { 697 return nil, err 698 } 699 // Retrieve the tracing configurations, or use default values 700 var ( 701 logConfig vm.LogConfig 702 txHash common.Hash 703 ) 704 if config != nil { 705 logConfig = config.LogConfig 706 txHash = config.TxHash 707 } 708 logConfig.Debug = true 709 710 // Execute transaction, either tracing all or just the requested one 711 var ( 712 dumps []string 713 signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) 714 chainConfig = api.backend.ChainConfig() 715 vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 716 canon = true 717 ) 718 // Check if there are any overrides: the caller may wish to enable a future 719 // fork when executing this block. Note, such overrides are only applicable to the 720 // actual specified block, not any preceding blocks that we have to go through 721 // in order to obtain the state. 722 // Therefore, it's perfectly valid to specify `"futureForkBlock": 0`, to enable `futureFork` 723 724 if config != nil && config.Overrides != nil { 725 // Copy the config, to not screw up the main config 726 // Note: the Clique-part is _not_ deep copied 727 chainConfigCopy := new(params.ChainConfig) 728 *chainConfigCopy = *chainConfig 729 chainConfig = chainConfigCopy 730 if berlin := config.LogConfig.Overrides.BerlinBlock; berlin != nil { 731 chainConfig.BerlinBlock = berlin 732 canon = false 733 } 734 } 735 for i, tx := range block.Transactions() { 736 // Prepare the trasaction for un-traced execution 737 var ( 738 msg, _ = tx.AsMessage(signer) 739 txContext = core.NewEVMTxContext(msg) 740 vmConf vm.Config 741 dump *os.File 742 writer *bufio.Writer 743 err error 744 ) 745 // If the transaction needs tracing, swap out the configs 746 if tx.Hash() == txHash || txHash == (common.Hash{}) { 747 // Generate a unique temporary file to dump it into 748 prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4]) 749 if !canon { 750 prefix = fmt.Sprintf("%valt-", prefix) 751 } 752 dump, err = ioutil.TempFile(os.TempDir(), prefix) 753 if err != nil { 754 return nil, err 755 } 756 dumps = append(dumps, dump.Name()) 757 758 // Swap out the noop logger to the standard tracer 759 writer = bufio.NewWriter(dump) 760 vmConf = vm.Config{ 761 Debug: true, 762 Tracer: vm.NewJSONLogger(&logConfig, writer), 763 EnablePreimageRecording: true, 764 } 765 } 766 // Execute the transaction and flush any traces to disk 767 vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) 768 if posa, ok := api.backend.Engine().(consensus.PoSA); ok { 769 if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem { 770 balance := statedb.GetBalance(consensus.SystemAddress) 771 if balance.Cmp(common.Big0) > 0 { 772 statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) 773 statedb.AddBalance(vmctx.Coinbase, balance) 774 } 775 } 776 } 777 statedb.Prepare(tx.Hash(), block.Hash(), i) 778 _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) 779 if writer != nil { 780 writer.Flush() 781 } 782 if dump != nil { 783 dump.Close() 784 log.Info("Wrote standard trace", "file", dump.Name()) 785 } 786 if err != nil { 787 return dumps, err 788 } 789 // Finalize the state so any modifications are written to the trie 790 // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect 791 statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) 792 793 // If we've traced the transaction we were looking for, abort 794 if tx.Hash() == txHash { 795 break 796 } 797 } 798 return dumps, nil 799 } 800 801 // containsTx reports whether the transaction with a certain hash 802 // is contained within the specified block. 803 func containsTx(block *types.Block, hash common.Hash) bool { 804 for _, tx := range block.Transactions() { 805 if tx.Hash() == hash { 806 return true 807 } 808 } 809 return false 810 } 811 812 // TraceTransaction returns the structured logs created during the execution of EVM 813 // and returns them as a JSON object. 814 func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { 815 _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) 816 if err != nil { 817 return nil, err 818 } 819 // It shouldn't happen in practice. 820 if blockNumber == 0 { 821 return nil, errors.New("genesis is not traceable") 822 } 823 reexec := defaultTraceReexec 824 if config != nil && config.Reexec != nil { 825 reexec = *config.Reexec 826 } 827 block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash) 828 if err != nil { 829 return nil, err 830 } 831 msg, vmctx, statedb, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) 832 if err != nil { 833 return nil, err 834 } 835 txctx := &Context{ 836 BlockHash: blockHash, 837 TxIndex: int(index), 838 TxHash: hash, 839 } 840 return api.traceTx(ctx, msg, txctx, vmctx, statedb, config) 841 } 842 843 // TraceCall lets you trace a given eth_call. It collects the structured logs 844 // created during the execution of EVM if the given transaction was added on 845 // top of the provided block and returns them as a JSON object. 846 // You can provide -2 as a block number to trace on top of the pending block. 847 func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { 848 // Try to retrieve the specified block 849 var ( 850 err error 851 block *types.Block 852 ) 853 if hash, ok := blockNrOrHash.Hash(); ok { 854 block, err = api.blockByHash(ctx, hash) 855 } else if number, ok := blockNrOrHash.Number(); ok { 856 block, err = api.blockByNumber(ctx, number) 857 } else { 858 return nil, errors.New("invalid arguments; neither block nor hash specified") 859 } 860 if err != nil { 861 return nil, err 862 } 863 // try to recompute the state 864 reexec := defaultTraceReexec 865 if config != nil && config.Reexec != nil { 866 reexec = *config.Reexec 867 } 868 statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false) 869 if err != nil { 870 return nil, err 871 } 872 // Apply the customized state rules if required. 873 if config != nil { 874 if err := config.StateOverrides.Apply(statedb); err != nil { 875 return nil, err 876 } 877 } 878 // Execute the trace 879 msg := args.ToMessage(api.backend.RPCGasCap()) 880 vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) 881 882 var traceConfig *TraceConfig 883 if config != nil { 884 traceConfig = &TraceConfig{ 885 LogConfig: config.LogConfig, 886 Tracer: config.Tracer, 887 Timeout: config.Timeout, 888 Reexec: config.Reexec, 889 } 890 } 891 return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig) 892 } 893 894 // traceTx configures a new tracer according to the provided configuration, and 895 // executes the given message in the provided environment. The return value will 896 // be tracer dependent. 897 func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { 898 // Assemble the structured logger or the JavaScript tracer 899 var ( 900 tracer vm.EVMLogger 901 err error 902 txContext = core.NewEVMTxContext(message) 903 ) 904 switch { 905 case config == nil: 906 tracer = vm.NewStructLogger(nil) 907 case config.Tracer != nil: 908 // Define a meaningful timeout of a single transaction trace 909 timeout := defaultTraceTimeout 910 if config.Timeout != nil { 911 if timeout, err = time.ParseDuration(*config.Timeout); err != nil { 912 return nil, err 913 } 914 } 915 if t, err := New(*config.Tracer, txctx); err != nil { 916 return nil, err 917 } else { 918 deadlineCtx, cancel := context.WithTimeout(ctx, timeout) 919 go func() { 920 <-deadlineCtx.Done() 921 if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { 922 t.Stop(errors.New("execution timeout")) 923 } 924 }() 925 defer cancel() 926 tracer = t 927 } 928 default: 929 tracer = vm.NewStructLogger(config.LogConfig) 930 } 931 // Run the transaction with tracing enabled. 932 vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer}) 933 934 if posa, ok := api.backend.Engine().(consensus.PoSA); ok && message.From() == vmctx.Coinbase && 935 posa.IsSystemContract(message.To()) && message.GasPrice().Cmp(big.NewInt(0)) == 0 { 936 balance := statedb.GetBalance(consensus.SystemAddress) 937 if balance.Cmp(common.Big0) > 0 { 938 statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) 939 statedb.AddBalance(vmctx.Coinbase, balance) 940 } 941 } 942 943 // Call Prepare to clear out the statedb access list 944 statedb.Prepare(txctx.TxHash, txctx.BlockHash, txctx.TxIndex) 945 946 result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) 947 if err != nil { 948 return nil, fmt.Errorf("tracing failed: %w", err) 949 } 950 951 // Depending on the tracer type, format and return the output. 952 switch tracer := tracer.(type) { 953 case *vm.StructLogger: 954 // If the result contains a revert reason, return it. 955 returnVal := fmt.Sprintf("%x", result.Return()) 956 if len(result.Revert()) > 0 { 957 returnVal = fmt.Sprintf("%x", result.Revert()) 958 } 959 return ðapi.ExecutionResult{ 960 Gas: result.UsedGas, 961 Failed: result.Failed(), 962 ReturnValue: returnVal, 963 StructLogs: ethapi.FormatLogs(tracer.StructLogs()), 964 }, nil 965 966 case Tracer: 967 return tracer.GetResult() 968 969 default: 970 panic(fmt.Sprintf("bad tracer type %T", tracer)) 971 } 972 } 973 974 // APIs return the collection of RPC services the tracer package offers. 975 func APIs(backend Backend) []rpc.API { 976 // Append all the local APIs and return 977 return []rpc.API{ 978 { 979 Namespace: "debug", 980 Version: "1.0", 981 Service: NewAPI(backend), 982 Public: false, 983 }, 984 } 985 }