eth, internal/web3ext: tiny polishes in tracers

This commit is contained in:
Péter Szilágyi 2018-12-10 12:32:40 +02:00
parent 42a914a84f
commit 0983d02aa9
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
2 changed files with 97 additions and 77 deletions

View File

@ -66,7 +66,7 @@ type TraceConfig struct {
type StdTraceConfig struct {
*vm.LogConfig
Reexec *uint64
TxHash *common.Hash
TxHash common.Hash
}
// txTraceResult is the result of a single transaction trace.
@ -375,7 +375,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.B
func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
block := api.eth.blockchain.GetBlockByHash(hash)
if block == nil {
return nil, fmt.Errorf("block #%x not found", hash)
return nil, fmt.Errorf("block %#x not found", hash)
}
return api.traceBlock(ctx, block, config)
}
@ -400,37 +400,41 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string,
return api.TraceBlock(ctx, blob, config)
}
// TraceBadBlockByHash returns the structured logs created during the execution of a block
func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, blockHash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
// TraceBadBlockByHash returns the structured logs created during the execution of
// EVM against a block pulled from the pool of bad ones and returns them as a JSON
// object.
func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
blocks := api.eth.blockchain.BadBlocks()
for _, block := range blocks {
if block.Hash() == blockHash {
if block.Hash() == hash {
return api.traceBlock(ctx, block, config)
}
}
return nil, fmt.Errorf("hash not found among bad blocks")
return nil, fmt.Errorf("bad block %#x not found", hash)
}
// StandardTraceBadBlockToFile dumps the standard-json logs to files on the local filesystem,
// and returns a list of files to the caller.
func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, blockHash common.Hash, stdConfig *StdTraceConfig) ([]string, error) {
// StandardTraceBlockToFile dumps the structured logs created during the
// execution of EVM to the local file system and returns a list of files
// to the caller.
func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
block := api.eth.blockchain.GetBlockByHash(hash)
if block == nil {
return nil, fmt.Errorf("block %#x not found", hash)
}
return api.standardTraceBlockToFile(ctx, block, config)
}
// StandardTraceBadBlockToFile dumps the structured logs created during the
// execution of EVM against a block pulled from the pool of bad ones to the
// local file system and returns a list of files to the caller.
func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
blocks := api.eth.blockchain.BadBlocks()
for _, block := range blocks {
if block.Hash() == blockHash {
return api.standardTraceBlockToFile(ctx, block, stdConfig)
if block.Hash() == hash {
return api.standardTraceBlockToFile(ctx, block, config)
}
}
return nil, fmt.Errorf("hash not found among bad blocks")
}
// StandardTraceBlockToFile dumps the standard-json logs to files on the local filesystem,
// and returns a list of files to the caller.
func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, blockHash common.Hash, stdConfig *StdTraceConfig) ([]string, error) {
block := api.eth.blockchain.GetBlockByHash(blockHash)
if block == nil {
return nil, fmt.Errorf("block #%x not found", blockHash)
}
return api.standardTraceBlockToFile(ctx, block, stdConfig)
return nil, fmt.Errorf("bad block %#x not found", hash)
}
// traceBlock configures a new tracer according to the provided configuration, and
@ -443,7 +447,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
}
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, fmt.Errorf("parent %x not found", block.ParentHash())
return nil, fmt.Errorf("parent %#x not found", block.ParentHash())
}
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
@ -514,90 +518,104 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
return results, nil
}
// standardTraceBlockToFile configures a new tracer which uses standard-json output, and
// traces either a full block or an individual transaction. The return value will be one filename
// per transaction traced.
func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, stdConfig *StdTraceConfig) ([]string, error) {
// standardTraceBlockToFile configures a new tracer which uses standard JSON output,
// and traces either a full block or an individual transaction. The return value will
// be one filename per transaction traced.
func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
// If we're tracing a single transaction, make sure it's present
if config != nil && config.TxHash != (common.Hash{}) {
var exists bool
for _, tx := range block.Transactions() {
if exists = (tx.Hash() == config.TxHash); exists {
break
}
}
if !exists {
return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
}
}
// Create the parent state database
if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil {
return nil, err
}
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, fmt.Errorf("parent %x not found", block.ParentHash())
return nil, fmt.Errorf("parent %#x not found", block.ParentHash())
}
var (
signer = types.MakeSigner(api.config, block.Number())
done = false
blockPrefix = fmt.Sprintf("block_0x%x", block.Hash().Bytes()[:4])
usedLogConfig = &vm.LogConfig{Debug: true}
files []string
reExec_val = defaultTraceReexec
txHash *common.Hash
)
if stdConfig != nil {
if stdConfig.Reexec != nil {
reExec_val = *stdConfig.Reexec
}
if stdConfig.LogConfig != nil {
usedLogConfig.DisableMemory = stdConfig.LogConfig.DisableMemory
usedLogConfig.DisableStack = stdConfig.LogConfig.DisableStack
usedLogConfig.DisableStorage = stdConfig.LogConfig.DisableStorage
usedLogConfig.Limit = stdConfig.LogConfig.Limit
}
txHash = stdConfig.TxHash
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
statedb, err := api.computeStateDB(parent, reExec_val)
statedb, err := api.computeStateDB(parent, reexec)
if err != nil {
return nil, err
}
// Retrieve the tracing configurations, or use default values
var (
logConfig vm.LogConfig
txHash common.Hash
)
if config != nil {
if config.LogConfig != nil {
logConfig = *config.LogConfig
}
txHash = config.TxHash
}
logConfig.Debug = true
// Execute transaction, either tracing all or just the requested one
var (
signer = types.MakeSigner(api.config, block.Number())
dumps []string
)
for i, tx := range block.Transactions() {
// Prepare the trasaction for un-traced execution
var (
outfile *os.File
err error
msg, _ = tx.AsMessage(signer)
vmctx = core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
vmConf vm.Config
dump *os.File
err error
)
msg, _ := tx.AsMessage(signer)
vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
vmConf := vm.Config{}
if txHash == nil || bytes.Equal(txHash.Bytes(), tx.Hash().Bytes()) {
prefix := fmt.Sprintf("%v-%d-0x%x-", blockPrefix, i, tx.Hash().Bytes()[:4])
// Open a file to dump trace into
outfile, err = ioutil.TempFile(os.TempDir(), prefix)
// If the transaction needs tracing, swap out the configs
if tx.Hash() == txHash || txHash == (common.Hash{}) {
// Generate a unique temporary file to dump it into
prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
dump, err = ioutil.TempFile(os.TempDir(), prefix)
if err != nil {
return nil, err
}
files = append(files, outfile.Name())
dumps = append(dumps, dump.Name())
// Swap out the noop logger to the standard tracer
vmConf = vm.Config{
Debug: true,
Tracer: vm.NewJSONLogger(usedLogConfig, bufio.NewWriter(outfile)),
Tracer: vm.NewJSONLogger(&logConfig, bufio.NewWriter(dump)),
EnablePreimageRecording: true,
}
if txHash != nil { // Only one tx to trace
done = true
}
}
// Execute the transaction and flush any traces to disk
vmenv := vm.NewEVM(vmctx, statedb, api.config, vmConf)
_, _, _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
if outfile != nil {
outfile.Close()
log.Info("Wrote trace", "file", outfile.Name())
if dump != nil {
dump.Close()
log.Info("Wrote standard trace", "file", dump.Name())
}
if err != nil {
return files, err
return dumps, err
}
// Finalize the state so any modifications are written to the trie
statedb.Finalise(true)
if done {
// If we've traced the transaction we were looking for, abort
if tx.Hash() == txHash {
break
}
}
if txHash != nil && !done {
return nil, fmt.Errorf("transaction hash not found in block")
}
return files, nil
return dumps, nil
}
// computeStateDB retrieves the state database associated with a certain block.
@ -675,7 +693,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
// Retrieve the transaction and assemble its EVM context
tx, blockHash, _, index := rawdb.ReadTransaction(api.eth.ChainDb(), hash)
if tx == nil {
return nil, fmt.Errorf("transaction %x not found", hash)
return nil, fmt.Errorf("transaction %#x not found", hash)
}
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
@ -755,11 +773,11 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
// Create the parent state database
block := api.eth.blockchain.GetBlockByHash(blockHash)
if block == nil {
return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash)
return nil, vm.Context{}, nil, fmt.Errorf("block %#x not found", blockHash)
}
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash())
return nil, vm.Context{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
}
statedb, err := api.computeStateDB(parent, reexec)
if err != nil {
@ -778,10 +796,10 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{})
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
statedb.Finalise(true)
}
return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, blockHash)
}

View File

@ -388,11 +388,13 @@ web3._extend({
name: 'standardTraceBadBlockToFile',
call: 'debug_standardTraceBadBlockToFile',
params: 2,
inputFormatter: [null, null]
}),
new web3._extend.Method({
name: 'standardTraceBlockToFile',
call: 'debug_standardTraceBlockToFile',
params: 2,
inputFormatter: [null, null]
}),
new web3._extend.Method({
name: 'traceBlockByNumber',