Skip to content

Commit 4883ce4

Browse files
committed
core/state, cmd/geth: Streaming json output for command
1 parent 3ec1b9a commit 4883ce4

File tree

4 files changed

+99
-8
lines changed

4 files changed

+99
-8
lines changed

cmd/geth/chaincmd.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ Remove blockchain and state databases`,
135135
utils.DataDirFlag,
136136
utils.CacheFlag,
137137
utils.LightModeFlag,
138+
utils.IterativeOutputFlag,
138139
},
139140
Category: "BLOCKCHAIN COMMANDS",
140141
Description: `
@@ -386,7 +387,11 @@ func dump(ctx *cli.Context) error {
386387
if err != nil {
387388
utils.Fatalf("could not create new state: %v", err)
388389
}
389-
fmt.Printf("%s\n", state.Dump())
390+
if ctx.GlobalIsSet(utils.IterativeOutputFlag.Name) {
391+
state.IterativeDump()
392+
} else {
393+
fmt.Printf("%s\n", state.Dump())
394+
}
390395
}
391396
}
392397
chainDb.Close()

cmd/geth/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ var (
144144
utils.WhisperMaxMessageSizeFlag,
145145
utils.WhisperMinPOWFlag,
146146
}
147+
148+
dumpFlags = []cli.Flag{
149+
utils.IterativeOutputFlag,
150+
}
147151
)
148152

149153
func init() {
@@ -184,6 +188,7 @@ func init() {
184188
app.Flags = append(app.Flags, consoleFlags...)
185189
app.Flags = append(app.Flags, debug.Flags...)
186190
app.Flags = append(app.Flags, whisperFlags...)
191+
app.Flags = append(app.Flags, dumpFlags...)
187192

188193
app.Before = func(ctx *cli.Context) error {
189194
runtime.GOMAXPROCS(runtime.NumCPU())

cmd/utils/flags.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ var (
164164
Name: "light",
165165
Usage: "Enable light client mode",
166166
}
167+
IterativeOutputFlag = cli.BoolFlag{
168+
Name: "iterative",
169+
Usage: "Print streaming json iteratively as json objects, delimited by lines",
170+
}
167171
defaultSyncMode = eth.DefaultConfig.SyncMode
168172
SyncModeFlag = TextMarshalerFlag{
169173
Name: "syncmode",

core/state/dump.go

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ import (
2323
"github.com/ethereum/go-ethereum/common"
2424
"github.com/ethereum/go-ethereum/rlp"
2525
"github.com/ethereum/go-ethereum/trie"
26+
"io"
27+
"os"
2628
)
2729

30+
// DumpAccount represents an account in the state
2831
type DumpAccount struct {
2932
Balance string `json:"balance"`
3033
Nonce uint64 `json:"nonce"`
@@ -34,16 +37,76 @@ type DumpAccount struct {
3437
Storage map[string]string `json:"storage"`
3538
}
3639

40+
// For output in a collected format, as one large map
3741
type Dump struct {
3842
Root string `json:"root"`
3943
Accounts map[string]DumpAccount `json:"accounts"`
4044
}
4145

42-
func (self *StateDB) RawDump() Dump {
43-
dump := Dump{
44-
Root: fmt.Sprintf("%x", self.trie.Hash()),
46+
// DumpAccountFull is the same as DumpAccount but also with address, for standalone printing
47+
type DumpAccountFull struct {
48+
Address string `json:"address"`
49+
Balance string `json:"balance"`
50+
Nonce uint64 `json:"nonce"`
51+
Root string `json:"root"`
52+
CodeHash string `json:"codeHash"`
53+
Code string `json:"code"`
54+
Storage map[string]string `json:"storage"`
55+
}
56+
57+
// For line-by-line json output
58+
type IterativeDump struct {
59+
encoder *json.Encoder
60+
}
61+
62+
// Collector interface which the state trie calls during iteration
63+
type collector interface {
64+
onRoot(common.Hash)
65+
onAccount(string, DumpAccount)
66+
}
67+
68+
func newCollectingDump() *Dump {
69+
return &Dump{
4570
Accounts: make(map[string]DumpAccount),
4671
}
72+
}
73+
74+
func newIterativeDump(w io.Writer) *IterativeDump {
75+
return &IterativeDump{
76+
encoder: json.NewEncoder(w),
77+
}
78+
}
79+
80+
func (self *Dump) onRoot(root common.Hash) {
81+
self.Root = fmt.Sprintf("%x", root)
82+
}
83+
84+
func (self *Dump) onAccount(addr string, account DumpAccount) {
85+
self.Accounts[addr] = account
86+
}
87+
88+
func (self *IterativeDump) onAccount(addr string, account DumpAccount) {
89+
self.encoder.Encode(&DumpAccountFull{
90+
addr,
91+
account.Balance,
92+
account.Nonce,
93+
account.Root,
94+
account.CodeHash,
95+
account.Code,
96+
account.Storage,
97+
})
98+
}
99+
func (self *IterativeDump) onRoot(root common.Hash) {
100+
self.encoder.Encode(struct {
101+
Root string `json:"root"`
102+
}{
103+
common.Bytes2Hex(root.Bytes()),
104+
})
105+
}
106+
107+
func (self *StateDB) performDump(c collector) {
108+
109+
c.onRoot(self.trie.Hash())
47110

48111
it := trie.NewIterator(self.trie.NodeIterator(nil))
49112
for it.Next() {
@@ -66,16 +129,30 @@ func (self *StateDB) RawDump() Dump {
66129
for storageIt.Next() {
67130
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
68131
}
69-
dump.Accounts[common.Bytes2Hex(addr)] = account
132+
c.onAccount(common.Bytes2Hex(addr), account)
70133
}
71-
return dump
72134
}
73135

136+
// RawDump returns the entire state an a single large object
137+
func (self *StateDB) RawDump() Dump {
138+
139+
dump := newCollectingDump()
140+
self.performDump(dump)
141+
return *dump
142+
}
143+
144+
// Dump returns a JSON string representing the entire state as a single json-object
74145
func (self *StateDB) Dump() []byte {
75-
json, err := json.MarshalIndent(self.RawDump(), "", " ")
146+
dump := newCollectingDump()
147+
self.performDump(dump)
148+
json, err := json.MarshalIndent(dump, "", " ")
76149
if err != nil {
77150
fmt.Println("dump err", err)
78151
}
79-
80152
return json
81153
}
154+
155+
// IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
156+
func (self *StateDB) IterativeDump() {
157+
self.performDump(newIterativeDump(os.Stdout))
158+
}

0 commit comments

Comments
 (0)