cmd/evm: statet8n output folder + tx hashes on trace filenames (#21406)

* t8ntool: add output basedir

* t8ntool: add txhash to trace filename

* t8ntool: don't default to '.' basedir, allow absolute paths
This commit is contained in:
Martin Holst Swende 2020-08-19 11:31:13 +02:00 committed by GitHub
parent 560d44479c
commit 7ebc6c43ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 46 additions and 26 deletions

View File

@ -29,6 +29,8 @@ Command line params that has to be supported are
--trace Output full trace logs to files <txhash>.jsonl --trace Output full trace logs to files <txhash>.jsonl
--trace.nomemory Disable full memory dump in traces --trace.nomemory Disable full memory dump in traces
--trace.nostack Disable stack output in traces --trace.nostack Disable stack output in traces
--trace.noreturndata Disable return data output in traces
--output.basedir value Specifies where output files are placed. Will be created if it does not exist. (default: ".")
--output.alloc alloc Determines where to put the alloc of the post-state. --output.alloc alloc Determines where to put the alloc of the post-state.
`stdout` - into the stdout output `stdout` - into the stdout output
`stderr` - into the stderr output `stderr` - into the stderr output
@ -232,13 +234,13 @@ Example where blockhashes are provided:
./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace ./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace
``` ```
``` ```
cat trace-0.jsonl | grep BLOCKHASH -C2 cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2
``` ```
``` ```
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"depth":1,"refund":0,"opName":"PUSH1","error":""} {"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"PUSH1","error":""}
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"depth":1,"refund":0,"opName":"BLOCKHASH","error":""} {"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"depth":1,"refund":0,"opName":"STOP","error":""} {"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"STOP","error":""}
{"output":"","gasUsed":"0x17","time":155861} {"output":"","gasUsed":"0x17","time":112885}
``` ```
In this example, the caller has not provided the required blockhash: In this example, the caller has not provided the required blockhash:
@ -254,9 +256,9 @@ Error code: 4
Another thing that can be done, is to chain invocations: Another thing that can be done, is to chain invocations:
``` ```
./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json ./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json
INFO [06-29|11:52:04.934] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low" INFO [08-03|15:25:15.168] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
INFO [06-29|11:52:04.936] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low" INFO [08-03|15:25:15.169] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
INFO [06-29|11:52:04.936] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low" INFO [08-03|15:25:15.169] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
``` ```
What happened here, is that we first applied two identical transactions, so the second one was rejected. What happened here, is that we first applied two identical transactions, so the second one was rejected.

View File

@ -81,7 +81,7 @@ type stEnvMarshaling struct {
// Apply applies a set of transactions to a pre-state // Apply applies a set of transactions to a pre-state
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
txs types.Transactions, miningReward int64, txs types.Transactions, miningReward int64,
getTracerFn func(txIndex int) (tracer vm.Tracer, err error)) (*state.StateDB, *ExecutionResult, error) { getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error)) (*state.StateDB, *ExecutionResult, error) {
// Capture errors for BLOCKHASH operation, if we haven't been supplied the // Capture errors for BLOCKHASH operation, if we haven't been supplied the
// required blockhashes // required blockhashes
@ -135,7 +135,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
rejectedTxs = append(rejectedTxs, i) rejectedTxs = append(rejectedTxs, i)
continue continue
} }
tracer, err := getTracerFn(txIndex) tracer, err := getTracerFn(txIndex, tx.Hash())
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -42,6 +42,11 @@ var (
Name: "trace.noreturndata", Name: "trace.noreturndata",
Usage: "Disable return data output in traces", Usage: "Disable return data output in traces",
} }
OutputBasedir = cli.StringFlag{
Name: "output.basedir",
Usage: "Specifies where output files are placed. Will be created if it does not exist.",
Value: "",
}
OutputAllocFlag = cli.StringFlag{ OutputAllocFlag = cli.StringFlag{
Name: "output.alloc", Name: "output.alloc",
Usage: "Determines where to put the `alloc` of the post-state.\n" + Usage: "Determines where to put the `alloc` of the post-state.\n" +

View File

@ -22,6 +22,7 @@ import (
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"os" "os"
"path"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -77,9 +78,20 @@ func Main(ctx *cli.Context) error {
var ( var (
err error err error
tracer vm.Tracer tracer vm.Tracer
baseDir = ""
) )
var getTracer func(txIndex int) (vm.Tracer, error) var getTracer func(txIndex int, txHash common.Hash) (vm.Tracer, error)
// If user specified a basedir, make sure it exists
if ctx.IsSet(OutputBasedir.Name) {
if base := ctx.String(OutputBasedir.Name); len(base) > 0 {
err := os.MkdirAll(base, 0755) // //rw-r--r--
if err != nil {
return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
}
baseDir = base
}
}
if ctx.Bool(TraceFlag.Name) { if ctx.Bool(TraceFlag.Name) {
// Configure the EVM logger // Configure the EVM logger
logConfig := &vm.LogConfig{ logConfig := &vm.LogConfig{
@ -95,11 +107,11 @@ func Main(ctx *cli.Context) error {
prevFile.Close() prevFile.Close()
} }
}() }()
getTracer = func(txIndex int) (vm.Tracer, error) { getTracer = func(txIndex int, txHash common.Hash) (vm.Tracer, error) {
if prevFile != nil { if prevFile != nil {
prevFile.Close() prevFile.Close()
} }
traceFile, err := os.Create(fmt.Sprintf("trace-%d.jsonl", txIndex)) traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
if err != nil { if err != nil {
return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
} }
@ -107,7 +119,7 @@ func Main(ctx *cli.Context) error {
return vm.NewJSONLogger(logConfig, traceFile), nil return vm.NewJSONLogger(logConfig, traceFile), nil
} }
} else { } else {
getTracer = func(txIndex int) (tracer vm.Tracer, err error) { getTracer = func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error) {
return nil, nil return nil, nil
} }
} }
@ -197,7 +209,7 @@ func Main(ctx *cli.Context) error {
//postAlloc := state.DumpGenesisFormat(false, false, false) //postAlloc := state.DumpGenesisFormat(false, false, false)
collector := make(Alloc) collector := make(Alloc)
state.DumpToCollector(collector, false, false, false, nil, -1) state.DumpToCollector(collector, false, false, false, nil, -1)
return dispatchOutput(ctx, result, collector) return dispatchOutput(ctx, baseDir, result, collector)
} }
@ -224,12 +236,12 @@ func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
} }
// saveFile marshalls the object to the given file // saveFile marshalls the object to the given file
func saveFile(filename string, data interface{}) error { func saveFile(baseDir, filename string, data interface{}) error {
b, err := json.MarshalIndent(data, "", " ") b, err := json.MarshalIndent(data, "", " ")
if err != nil { if err != nil {
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
} }
if err = ioutil.WriteFile(filename, b, 0644); err != nil { if err = ioutil.WriteFile(path.Join(baseDir, filename), b, 0644); err != nil {
return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err)) return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
} }
return nil return nil
@ -237,26 +249,26 @@ func saveFile(filename string, data interface{}) error {
// dispatchOutput writes the output data to either stderr or stdout, or to the specified // dispatchOutput writes the output data to either stderr or stdout, or to the specified
// files // files
func dispatchOutput(ctx *cli.Context, result *ExecutionResult, alloc Alloc) error { func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc) error {
stdOutObject := make(map[string]interface{}) stdOutObject := make(map[string]interface{})
stdErrObject := make(map[string]interface{}) stdErrObject := make(map[string]interface{})
dispatch := func(fName, name string, obj interface{}) error { dispatch := func(baseDir, fName, name string, obj interface{}) error {
switch fName { switch fName {
case "stdout": case "stdout":
stdOutObject[name] = obj stdOutObject[name] = obj
case "stderr": case "stderr":
stdErrObject[name] = obj stdErrObject[name] = obj
default: // save to file default: // save to file
if err := saveFile(fName, obj); err != nil { if err := saveFile(baseDir, fName, obj); err != nil {
return err return err
} }
} }
return nil return nil
} }
if err := dispatch(ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil { if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
return err return err
} }
if err := dispatch(ctx.String(OutputResultFlag.Name), "result", result); err != nil { if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
return err return err
} }
if len(stdOutObject) > 0 { if len(stdOutObject) > 0 {

View File

@ -146,6 +146,7 @@ var stateTransitionCommand = cli.Command{
t8ntool.TraceDisableMemoryFlag, t8ntool.TraceDisableMemoryFlag,
t8ntool.TraceDisableStackFlag, t8ntool.TraceDisableStackFlag,
t8ntool.TraceDisableReturnDataFlag, t8ntool.TraceDisableReturnDataFlag,
t8ntool.OutputBasedir,
t8ntool.OutputAllocFlag, t8ntool.OutputAllocFlag,
t8ntool.OutputResultFlag, t8ntool.OutputResultFlag,
t8ntool.InputAllocFlag, t8ntool.InputAllocFlag,

View File

@ -155,10 +155,10 @@ echo "Example where blockhashes are provided: "
cmd="./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace" cmd="./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace"
tick && echo $cmd && tick tick && echo $cmd && tick
$cmd 2>&1 >/dev/null $cmd 2>&1 >/dev/null
cmd="cat trace-0.jsonl | grep BLOCKHASH -C2" cmd="cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2"
tick && echo $cmd && tick tick && echo $cmd && tick
echo "$ticks" echo "$ticks"
cat trace-0.jsonl | grep BLOCKHASH -C2 cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2
echo "$ticks" echo "$ticks"
echo "" echo ""