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