|
|
|
@ -18,6 +18,7 @@ package main |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"encoding/json" |
|
|
|
|
"errors" |
|
|
|
|
"fmt" |
|
|
|
|
"os" |
|
|
|
|
"runtime" |
|
|
|
@ -27,12 +28,16 @@ import ( |
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/cmd/utils" |
|
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil" |
|
|
|
|
"github.com/ethereum/go-ethereum/core" |
|
|
|
|
"github.com/ethereum/go-ethereum/core/rawdb" |
|
|
|
|
"github.com/ethereum/go-ethereum/core/state" |
|
|
|
|
"github.com/ethereum/go-ethereum/core/types" |
|
|
|
|
"github.com/ethereum/go-ethereum/crypto" |
|
|
|
|
"github.com/ethereum/go-ethereum/ethdb" |
|
|
|
|
"github.com/ethereum/go-ethereum/log" |
|
|
|
|
"github.com/ethereum/go-ethereum/metrics" |
|
|
|
|
"github.com/ethereum/go-ethereum/node" |
|
|
|
|
"gopkg.in/urfave/cli.v1" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
@ -152,20 +157,21 @@ The export-preimages command export hash preimages to an RLP encoded stream`, |
|
|
|
|
Action: utils.MigrateFlags(dump), |
|
|
|
|
Name: "dump", |
|
|
|
|
Usage: "Dump a specific block from storage", |
|
|
|
|
ArgsUsage: "[<blockHash> | <blockNum>]...", |
|
|
|
|
ArgsUsage: "[? <blockHash> | <blockNum>]", |
|
|
|
|
Flags: []cli.Flag{ |
|
|
|
|
utils.DataDirFlag, |
|
|
|
|
utils.CacheFlag, |
|
|
|
|
utils.SyncModeFlag, |
|
|
|
|
utils.IterativeOutputFlag, |
|
|
|
|
utils.ExcludeCodeFlag, |
|
|
|
|
utils.ExcludeStorageFlag, |
|
|
|
|
utils.IncludeIncompletesFlag, |
|
|
|
|
utils.StartKeyFlag, |
|
|
|
|
utils.DumpLimitFlag, |
|
|
|
|
}, |
|
|
|
|
Category: "BLOCKCHAIN COMMANDS", |
|
|
|
|
Description: ` |
|
|
|
|
The arguments are interpreted as block numbers or hashes. |
|
|
|
|
Use "ethereum dump 0" to dump the genesis block.`, |
|
|
|
|
This command dumps out the state for a given block (or latest, if none provided). |
|
|
|
|
`, |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
|
|
|
|
@ -373,47 +379,85 @@ func exportPreimages(ctx *cli.Context) error { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func dump(ctx *cli.Context) error { |
|
|
|
|
stack, _ := makeConfigNode(ctx) |
|
|
|
|
defer stack.Close() |
|
|
|
|
|
|
|
|
|
func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) { |
|
|
|
|
db := utils.MakeChainDatabase(ctx, stack, true) |
|
|
|
|
for _, arg := range ctx.Args() { |
|
|
|
|
var header *types.Header |
|
|
|
|
if ctx.NArg() > 1 { |
|
|
|
|
return nil, nil, common.Hash{}, fmt.Errorf("expected 1 argument (number or hash), got %d", ctx.NArg()) |
|
|
|
|
} |
|
|
|
|
if ctx.NArg() == 1 { |
|
|
|
|
arg := ctx.Args().First() |
|
|
|
|
if hashish(arg) { |
|
|
|
|
hash := common.HexToHash(arg) |
|
|
|
|
number := rawdb.ReadHeaderNumber(db, hash) |
|
|
|
|
if number != nil { |
|
|
|
|
if number := rawdb.ReadHeaderNumber(db, hash); number != nil { |
|
|
|
|
header = rawdb.ReadHeader(db, hash, *number) |
|
|
|
|
} else { |
|
|
|
|
return nil, nil, common.Hash{}, fmt.Errorf("block %x not found", hash) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
number, _ := strconv.Atoi(arg) |
|
|
|
|
hash := rawdb.ReadCanonicalHash(db, uint64(number)) |
|
|
|
|
if hash != (common.Hash{}) { |
|
|
|
|
number, err := strconv.Atoi(arg) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, nil, common.Hash{}, err |
|
|
|
|
} |
|
|
|
|
if hash := rawdb.ReadCanonicalHash(db, uint64(number)); hash != (common.Hash{}) { |
|
|
|
|
header = rawdb.ReadHeader(db, hash, uint64(number)) |
|
|
|
|
} else { |
|
|
|
|
return nil, nil, common.Hash{}, fmt.Errorf("header for block %d not found", number) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if header == nil { |
|
|
|
|
fmt.Println("{}") |
|
|
|
|
utils.Fatalf("block not found") |
|
|
|
|
} else { |
|
|
|
|
state, err := state.New(header.Root, state.NewDatabase(db), nil) |
|
|
|
|
// Use latest
|
|
|
|
|
header = rawdb.ReadHeadHeader(db) |
|
|
|
|
} |
|
|
|
|
if header == nil { |
|
|
|
|
return nil, nil, common.Hash{}, errors.New("no head block found") |
|
|
|
|
} |
|
|
|
|
startArg := common.FromHex(ctx.String(utils.StartKeyFlag.Name)) |
|
|
|
|
var start common.Hash |
|
|
|
|
switch len(startArg) { |
|
|
|
|
case 0: // common.Hash
|
|
|
|
|
case 32: |
|
|
|
|
start = common.BytesToHash(startArg) |
|
|
|
|
case 20: |
|
|
|
|
start = crypto.Keccak256Hash(startArg) |
|
|
|
|
log.Info("Converting start-address to hash", "address", common.BytesToAddress(startArg), "hash", start.Hex()) |
|
|
|
|
default: |
|
|
|
|
return nil, nil, common.Hash{}, fmt.Errorf("invalid start argument: %x. 20 or 32 hex-encoded bytes required", startArg) |
|
|
|
|
} |
|
|
|
|
var conf = &state.DumpConfig{ |
|
|
|
|
SkipCode: ctx.Bool(utils.ExcludeCodeFlag.Name), |
|
|
|
|
SkipStorage: ctx.Bool(utils.ExcludeStorageFlag.Name), |
|
|
|
|
OnlyWithAddresses: !ctx.Bool(utils.IncludeIncompletesFlag.Name), |
|
|
|
|
Start: start.Bytes(), |
|
|
|
|
Max: ctx.Uint64(utils.DumpLimitFlag.Name), |
|
|
|
|
} |
|
|
|
|
log.Info("State dump configured", "block", header.Number, "hash", header.Hash().Hex(), |
|
|
|
|
"skipcode", conf.SkipCode, "skipstorage", conf.SkipStorage, |
|
|
|
|
"start", hexutil.Encode(conf.Start), "limit", conf.Max) |
|
|
|
|
return conf, db, header.Root, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func dump(ctx *cli.Context) error { |
|
|
|
|
stack, _ := makeConfigNode(ctx) |
|
|
|
|
defer stack.Close() |
|
|
|
|
|
|
|
|
|
conf, db, root, err := parseDumpConfig(ctx, stack) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
state, err := state.New(root, state.NewDatabase(db), nil) |
|
|
|
|
if err != nil { |
|
|
|
|
utils.Fatalf("could not create new state: %v", err) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
excludeCode := ctx.Bool(utils.ExcludeCodeFlag.Name) |
|
|
|
|
excludeStorage := ctx.Bool(utils.ExcludeStorageFlag.Name) |
|
|
|
|
includeMissing := ctx.Bool(utils.IncludeIncompletesFlag.Name) |
|
|
|
|
if ctx.Bool(utils.IterativeOutputFlag.Name) { |
|
|
|
|
state.IterativeDump(excludeCode, excludeStorage, !includeMissing, json.NewEncoder(os.Stdout)) |
|
|
|
|
state.IterativeDump(conf, json.NewEncoder(os.Stdout)) |
|
|
|
|
} else { |
|
|
|
|
if includeMissing { |
|
|
|
|
fmt.Printf("If you want to include accounts with missing preimages, you need iterative output, since" + |
|
|
|
|
if conf.OnlyWithAddresses { |
|
|
|
|
fmt.Fprintf(os.Stderr, "If you want to include accounts with missing preimages, you need iterative output, since"+ |
|
|
|
|
" otherwise the accounts will overwrite each other in the resulting mapping.") |
|
|
|
|
return fmt.Errorf("incompatible options") |
|
|
|
|
} |
|
|
|
|
fmt.Printf("%v %s\n", includeMissing, state.Dump(excludeCode, excludeStorage, false)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
fmt.Println(string(state.Dump(conf))) |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|