github.com/smalaichami/go-bowhead@v0.0.0-20180311002552-16302db95eaa/aht/api_tracer.go (about) 1 // Copyright 2017 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 eth 18 19 import ( 20 "bytes" 21 "context" 22 "errors" 23 "fmt" 24 "io/ioutil" 25 "runtime" 26 "sync" 27 "time" 28 29 "github.com/smalaichami/go-bowhead/common" 30 "github.com/smalaichami/go-bowhead/common/hexutil" 31 "github.com/smalaichami/go-bowhead/core" 32 "github.com/smalaichami/go-bowhead/core/state" 33 "github.com/smalaichami/go-bowhead/core/types" 34 "github.com/smalaichami/go-bowhead/core/vm" 35 "github.com/smalaichami/go-bowhead/aht/tracers" 36 "github.com/smalaichami/go-bowhead/internal/ethapi" 37 "github.com/smalaichami/go-bowhead/log" 38 "github.com/smalaichami/go-bowhead/rlp" 39 "github.com/smalaichami/go-bowhead/rpc" 40 "github.com/smalaichami/go-bowhead/trie" 41 ) 42 43 const ( 44 // defaultTraceTimeout is the amount of time a single transaction can execute 45 // by default before being forcefully aborted. 46 defaultTraceTimeout = 5 * time.Second 47 48 // defaultTraceReexec is the number of blocks the tracer is willing to go back 49 // and reexecute to produce missing historical state necessary to run a specific 50 // trace. 51 defaultTraceReexec = uint64(128) 52 ) 53 54 // TraceConfig holds extra parameters to trace functions. 55 type TraceConfig struct { 56 *vm.LogConfig 57 Tracer *string 58 Timeout *string 59 Reexec *uint64 60 } 61 62 // txTraceResult is the result of a single transaction trace. 63 type txTraceResult struct { 64 Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer 65 Error string `json:"error,omitempty"` // Trace failure produced by the tracer 66 } 67 68 // blockTraceTask represents a single block trace task when an entire chain is 69 // being traced. 70 type blockTraceTask struct { 71 statedb *state.StateDB // Intermediate state prepped for tracing 72 block *types.Block // Block to trace the transactions from 73 rootref common.Hash // Trie root reference held for this task 74 results []*txTraceResult // Trace results procudes by the task 75 } 76 77 // blockTraceResult represets the results of tracing a single block when an entire 78 // chain is being traced. 79 type blockTraceResult struct { 80 Block hexutil.Uint64 `json:"block"` // Block number corresponding to this trace 81 Hash common.Hash `json:"hash"` // Block hash corresponding to this trace 82 Traces []*txTraceResult `json:"traces"` // Trace results produced by the task 83 } 84 85 // txTraceTask represents a single transaction trace task when an entire block 86 // is being traced. 87 type txTraceTask struct { 88 statedb *state.StateDB // Intermediate state prepped for tracing 89 index int // Transaction offset in the block 90 } 91 92 // TraceChain returns the structured logs created during the execution of EVM 93 // between two blocks (excluding start) and returns them as a JSON object. 94 func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { 95 // Fetch the block interval that we want to trace 96 var from, to *types.Block 97 98 switch start { 99 case rpc.PendingBlockNumber: 100 from = api.eth.miner.PendingBlock() 101 case rpc.LatestBlockNumber: 102 from = api.eth.blockchain.CurrentBlock() 103 default: 104 from = api.eth.blockchain.GetBlockByNumber(uint64(start)) 105 } 106 switch end { 107 case rpc.PendingBlockNumber: 108 to = api.eth.miner.PendingBlock() 109 case rpc.LatestBlockNumber: 110 to = api.eth.blockchain.CurrentBlock() 111 default: 112 to = api.eth.blockchain.GetBlockByNumber(uint64(end)) 113 } 114 // Trace the chain if we've found all our blocks 115 if from == nil { 116 return nil, fmt.Errorf("starting block #%d not found", start) 117 } 118 if to == nil { 119 return nil, fmt.Errorf("end block #%d not found", end) 120 } 121 return api.traceChain(ctx, from, to, config) 122 } 123 124 // traceChain configures a new tracer according to the provided configuration, and 125 // executes all the transactions contained within. The return value will be one item 126 // per transaction, dependent on the requestd tracer. 127 func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { 128 // Tracing a chain is a **long** operation, only do with subscriptions 129 notifier, supported := rpc.NotifierFromContext(ctx) 130 if !supported { 131 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 132 } 133 sub := notifier.CreateSubscription() 134 135 // Ensure we have a valid starting state before doing any work 136 origin := start.NumberU64() 137 database := state.NewDatabase(api.eth.ChainDb()) 138 139 if number := start.NumberU64(); number > 0 { 140 start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1) 141 if start == nil { 142 return nil, fmt.Errorf("parent block #%d not found", number-1) 143 } 144 } 145 statedb, err := state.New(start.Root(), database) 146 if err != nil { 147 // If the starting state is missing, allow some number of blocks to be reexecuted 148 reexec := defaultTraceReexec 149 if config != nil && config.Reexec != nil { 150 reexec = *config.Reexec 151 } 152 // Find the most recent block that has the state available 153 for i := uint64(0); i < reexec; i++ { 154 start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1) 155 if start == nil { 156 break 157 } 158 if statedb, err = state.New(start.Root(), database); err == nil { 159 break 160 } 161 } 162 // If we still don't have the state available, bail out 163 if err != nil { 164 switch err.(type) { 165 case *trie.MissingNodeError: 166 return nil, errors.New("required historical state unavailable") 167 default: 168 return nil, err 169 } 170 } 171 } 172 // Execute all the transaction contained within the chain concurrently for each block 173 blocks := int(end.NumberU64() - origin) 174 175 threads := runtime.NumCPU() 176 if threads > blocks { 177 threads = blocks 178 } 179 var ( 180 pend = new(sync.WaitGroup) 181 tasks = make(chan *blockTraceTask, threads) 182 results = make(chan *blockTraceTask, threads) 183 ) 184 for th := 0; th < threads; th++ { 185 pend.Add(1) 186 go func() { 187 defer pend.Done() 188 189 // Fetch and execute the next block trace tasks 190 for task := range tasks { 191 signer := types.MakeSigner(api.config, task.block.Number()) 192 193 // Trace all the transactions contained within 194 for i, tx := range task.block.Transactions() { 195 msg, _ := tx.AsMessage(signer) 196 vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil) 197 198 res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config) 199 if err != nil { 200 task.results[i] = &txTraceResult{Error: err.Error()} 201 log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) 202 break 203 } 204 task.statedb.DeleteSuicides() 205 task.results[i] = &txTraceResult{Result: res} 206 } 207 // Stream the result back to the user or abort on teardown 208 select { 209 case results <- task: 210 case <-notifier.Closed(): 211 return 212 } 213 } 214 }() 215 } 216 // Start a goroutine to feed all the blocks into the tracers 217 begin := time.Now() 218 219 go func() { 220 var ( 221 logged time.Time 222 number uint64 223 traced uint64 224 failed error 225 proot common.Hash 226 ) 227 // Ensure everything is properly cleaned up on any exit path 228 defer func() { 229 close(tasks) 230 pend.Wait() 231 232 switch { 233 case failed != nil: 234 log.Warn("Chain tracing failed", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin), "err", failed) 235 case number < end.NumberU64(): 236 log.Warn("Chain tracing aborted", "start", start.NumberU64(), "end", end.NumberU64(), "abort", number, "transactions", traced, "elapsed", time.Since(begin)) 237 default: 238 log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin)) 239 } 240 close(results) 241 }() 242 // Feed all the blocks both into the tracer, as well as fast process concurrently 243 for number = start.NumberU64() + 1; number <= end.NumberU64(); number++ { 244 // Stop tracing if interruption was requested 245 select { 246 case <-notifier.Closed(): 247 return 248 default: 249 } 250 // Print progress logs if long enough time elapsed 251 if time.Since(logged) > 8*time.Second { 252 if number > origin { 253 log.Info("Tracing chain segment", "start", origin, "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin), "memory", database.TrieDB().Size()) 254 } else { 255 log.Info("Preparing state for chain trace", "block", number, "start", origin, "elapsed", time.Since(begin)) 256 } 257 logged = time.Now() 258 } 259 // Retrieve the next block to trace 260 block := api.eth.blockchain.GetBlockByNumber(number) 261 if block == nil { 262 failed = fmt.Errorf("block #%d not found", number) 263 break 264 } 265 // Send the block over to the concurrent tracers (if not in the fast-forward phase) 266 if number > origin { 267 txs := block.Transactions() 268 269 select { 270 case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: block, rootref: proot, results: make([]*txTraceResult, len(txs))}: 271 case <-notifier.Closed(): 272 return 273 } 274 traced += uint64(len(txs)) 275 } 276 // Generate the next state snapshot fast without tracing 277 _, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{}) 278 if err != nil { 279 failed = err 280 break 281 } 282 // Finalize the state so any modifications are written to the trie 283 root, err := statedb.Commit(true) 284 if err != nil { 285 failed = err 286 break 287 } 288 if err := statedb.Reset(root); err != nil { 289 failed = err 290 break 291 } 292 // Reference the trie twice, once for us, once for the trancer 293 database.TrieDB().Reference(root, common.Hash{}) 294 if number >= origin { 295 database.TrieDB().Reference(root, common.Hash{}) 296 } 297 // Dereference all past tries we ourselves are done working with 298 database.TrieDB().Dereference(proot, common.Hash{}) 299 proot = root 300 } 301 }() 302 303 // Keep reading the trace results and stream the to the user 304 go func() { 305 var ( 306 done = make(map[uint64]*blockTraceResult) 307 next = origin + 1 308 ) 309 for res := range results { 310 // Queue up next received result 311 result := &blockTraceResult{ 312 Block: hexutil.Uint64(res.block.NumberU64()), 313 Hash: res.block.Hash(), 314 Traces: res.results, 315 } 316 done[uint64(result.Block)] = result 317 318 // Dereference any paret tries held in memory by this task 319 database.TrieDB().Dereference(res.rootref, common.Hash{}) 320 321 // Stream completed traces to the user, aborting on the first error 322 for result, ok := done[next]; ok; result, ok = done[next] { 323 if len(result.Traces) > 0 || next == end.NumberU64() { 324 notifier.Notify(sub.ID, result) 325 } 326 delete(done, next) 327 next++ 328 } 329 } 330 }() 331 return sub, nil 332 } 333 334 // TraceBlockByNumber returns the structured logs created during the execution of 335 // EVM and returns them as a JSON object. 336 func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { 337 // Fetch the block that we want to trace 338 var block *types.Block 339 340 switch number { 341 case rpc.PendingBlockNumber: 342 block = api.eth.miner.PendingBlock() 343 case rpc.LatestBlockNumber: 344 block = api.eth.blockchain.CurrentBlock() 345 default: 346 block = api.eth.blockchain.GetBlockByNumber(uint64(number)) 347 } 348 // Trace the block if it was found 349 if block == nil { 350 return nil, fmt.Errorf("block #%d not found", number) 351 } 352 return api.traceBlock(ctx, block, config) 353 } 354 355 // TraceBlockByHash returns the structured logs created during the execution of 356 // EVM and returns them as a JSON object. 357 func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { 358 block := api.eth.blockchain.GetBlockByHash(hash) 359 if block == nil { 360 return nil, fmt.Errorf("block #%x not found", hash) 361 } 362 return api.traceBlock(ctx, block, config) 363 } 364 365 // TraceBlock returns the structured logs created during the execution of EVM 366 // and returns them as a JSON object. 367 func (api *PrivateDebugAPI) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { 368 block := new(types.Block) 369 if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { 370 return nil, fmt.Errorf("could not decode block: %v", err) 371 } 372 return api.traceBlock(ctx, block, config) 373 } 374 375 // TraceBlockFromFile returns the structured logs created during the execution of 376 // EVM and returns them as a JSON object. 377 func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { 378 blob, err := ioutil.ReadFile(file) 379 if err != nil { 380 return nil, fmt.Errorf("could not read file: %v", err) 381 } 382 return api.TraceBlock(ctx, blob, config) 383 } 384 385 // traceBlock configures a new tracer according to the provided configuration, and 386 // executes all the transactions contained within. The return value will be one item 387 // per transaction, dependent on the requestd tracer. 388 func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { 389 // Create the parent state database 390 if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil { 391 return nil, err 392 } 393 parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) 394 if parent == nil { 395 return nil, fmt.Errorf("parent %x not found", block.ParentHash()) 396 } 397 reexec := defaultTraceReexec 398 if config != nil && config.Reexec != nil { 399 reexec = *config.Reexec 400 } 401 statedb, err := api.computeStateDB(parent, reexec) 402 if err != nil { 403 return nil, err 404 } 405 // Execute all the transaction contained within the block concurrently 406 var ( 407 signer = types.MakeSigner(api.config, block.Number()) 408 409 txs = block.Transactions() 410 results = make([]*txTraceResult, len(txs)) 411 412 pend = new(sync.WaitGroup) 413 jobs = make(chan *txTraceTask, len(txs)) 414 ) 415 threads := runtime.NumCPU() 416 if threads > len(txs) { 417 threads = len(txs) 418 } 419 for th := 0; th < threads; th++ { 420 pend.Add(1) 421 go func() { 422 defer pend.Done() 423 424 // Fetch and execute the next transaction trace tasks 425 for task := range jobs { 426 msg, _ := txs[task.index].AsMessage(signer) 427 vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) 428 429 res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config) 430 if err != nil { 431 results[task.index] = &txTraceResult{Error: err.Error()} 432 continue 433 } 434 results[task.index] = &txTraceResult{Result: res} 435 } 436 }() 437 } 438 // Feed the transactions into the tracers and return 439 var failed error 440 for i, tx := range txs { 441 // Send the trace task over for execution 442 jobs <- &txTraceTask{statedb: statedb.Copy(), index: i} 443 444 // Generate the next state snapshot fast without tracing 445 msg, _ := tx.AsMessage(signer) 446 vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) 447 448 vmenv := vm.NewEVM(vmctx, statedb, api.config, vm.Config{}) 449 if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { 450 failed = err 451 break 452 } 453 // Finalize the state so any modifications are written to the trie 454 statedb.Finalise(true) 455 } 456 close(jobs) 457 pend.Wait() 458 459 // If execution failed in between, abort 460 if failed != nil { 461 return nil, failed 462 } 463 return results, nil 464 } 465 466 // computeStateDB retrieves the state database associated with a certain block. 467 // If no state is locally available for the given block, a number of blocks are 468 // attempted to be reexecuted to generate the desired state. 469 func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*state.StateDB, error) { 470 // If we have the state fully available, use that 471 statedb, err := api.eth.blockchain.StateAt(block.Root()) 472 if err == nil { 473 return statedb, nil 474 } 475 // Otherwise try to reexec blocks until we find a state or reach our limit 476 origin := block.NumberU64() 477 database := state.NewDatabase(api.eth.ChainDb()) 478 479 for i := uint64(0); i < reexec; i++ { 480 block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) 481 if block == nil { 482 break 483 } 484 if statedb, err = state.New(block.Root(), database); err == nil { 485 break 486 } 487 } 488 if err != nil { 489 switch err.(type) { 490 case *trie.MissingNodeError: 491 return nil, errors.New("required historical state unavailable") 492 default: 493 return nil, err 494 } 495 } 496 // State was available at historical point, regenerate 497 var ( 498 start = time.Now() 499 logged time.Time 500 proot common.Hash 501 ) 502 for block.NumberU64() < origin { 503 // Print progress logs if long enough time elapsed 504 if time.Since(logged) > 8*time.Second { 505 log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "elapsed", time.Since(start)) 506 logged = time.Now() 507 } 508 // Retrieve the next block to regenerate and process it 509 if block = api.eth.blockchain.GetBlockByNumber(block.NumberU64() + 1); block == nil { 510 return nil, fmt.Errorf("block #%d not found", block.NumberU64()+1) 511 } 512 _, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{}) 513 if err != nil { 514 return nil, err 515 } 516 // Finalize the state so any modifications are written to the trie 517 root, err := statedb.Commit(true) 518 if err != nil { 519 return nil, err 520 } 521 if err := statedb.Reset(root); err != nil { 522 return nil, err 523 } 524 database.TrieDB().Reference(root, common.Hash{}) 525 database.TrieDB().Dereference(proot, common.Hash{}) 526 proot = root 527 } 528 log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "size", database.TrieDB().Size()) 529 return statedb, nil 530 } 531 532 // TraceTransaction returns the structured logs created during the execution of EVM 533 // and returns them as a JSON object. 534 func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { 535 // Retrieve the transaction and assemble its EVM context 536 tx, blockHash, _, index := core.GetTransaction(api.eth.ChainDb(), hash) 537 if tx == nil { 538 return nil, fmt.Errorf("transaction %x not found", hash) 539 } 540 reexec := defaultTraceReexec 541 if config != nil && config.Reexec != nil { 542 reexec = *config.Reexec 543 } 544 msg, vmctx, statedb, err := api.computeTxEnv(blockHash, int(index), reexec) 545 if err != nil { 546 return nil, err 547 } 548 // Trace the transaction and return 549 return api.traceTx(ctx, msg, vmctx, statedb, config) 550 } 551 552 // traceTx configures a new tracer according to the provided configuration, and 553 // executes the given message in the provided environment. The return value will 554 // be tracer dependent. 555 func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { 556 // Assemble the structured logger or the JavaScript tracer 557 var ( 558 tracer vm.Tracer 559 err error 560 ) 561 switch { 562 case config != nil && config.Tracer != nil: 563 // Define a meaningful timeout of a single transaction trace 564 timeout := defaultTraceTimeout 565 if config.Timeout != nil { 566 if timeout, err = time.ParseDuration(*config.Timeout); err != nil { 567 return nil, err 568 } 569 } 570 // Constuct the JavaScript tracer to execute with 571 if tracer, err = tracers.New(*config.Tracer); err != nil { 572 return nil, err 573 } 574 // Handle timeouts and RPC cancellations 575 deadlineCtx, cancel := context.WithTimeout(ctx, timeout) 576 go func() { 577 <-deadlineCtx.Done() 578 tracer.(*tracers.Tracer).Stop(errors.New("execution timeout")) 579 }() 580 defer cancel() 581 582 case config == nil: 583 tracer = vm.NewStructLogger(nil) 584 585 default: 586 tracer = vm.NewStructLogger(config.LogConfig) 587 } 588 // Run the transaction with tracing enabled. 589 vmenv := vm.NewEVM(vmctx, statedb, api.config, vm.Config{Debug: true, Tracer: tracer}) 590 591 ret, gas, failed, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) 592 if err != nil { 593 return nil, fmt.Errorf("tracing failed: %v", err) 594 } 595 // Depending on the tracer type, format and return the output 596 switch tracer := tracer.(type) { 597 case *vm.StructLogger: 598 return ðapi.ExecutionResult{ 599 Gas: gas, 600 Failed: failed, 601 ReturnValue: fmt.Sprintf("%x", ret), 602 StructLogs: ethapi.FormatLogs(tracer.StructLogs()), 603 }, nil 604 605 case *tracers.Tracer: 606 return tracer.GetResult() 607 608 default: 609 panic(fmt.Sprintf("bad tracer type %T", tracer)) 610 } 611 } 612 613 // computeTxEnv returns the execution environment of a certain transaction. 614 func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) { 615 // Create the parent state database 616 block := api.eth.blockchain.GetBlockByHash(blockHash) 617 if block == nil { 618 return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash) 619 } 620 parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) 621 if parent == nil { 622 return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash()) 623 } 624 statedb, err := api.computeStateDB(parent, reexec) 625 if err != nil { 626 return nil, vm.Context{}, nil, err 627 } 628 // Recompute transactions up to the target index. 629 signer := types.MakeSigner(api.config, block.Number()) 630 631 for idx, tx := range block.Transactions() { 632 // Assemble the transaction call message and return if the requested offset 633 msg, _ := tx.AsMessage(signer) 634 context := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) 635 if idx == txIndex { 636 return msg, context, statedb, nil 637 } 638 // Not yet the searched for transaction, execute on top of the current state 639 vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{}) 640 if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { 641 return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) 642 } 643 statedb.DeleteSuicides() 644 } 645 return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash) 646 }