dump.go 6.71 KB
Newer Older
1
// Copyright 2014 The go-ethereum Authors
2
// This file is part of the go-ethereum library.
3
//
4
// The go-ethereum library is free software: you can redistribute it and/or modify
5 6 7 8
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
9
// The go-ethereum 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 13 14
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
15
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16

obscuren's avatar
obscuren committed
17
package state
obscuren's avatar
obscuren committed
18 19 20 21 22

import (
	"encoding/json"
	"fmt"

obscuren's avatar
obscuren committed
23
	"github.com/ethereum/go-ethereum/common"
24 25
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/log"
26
	"github.com/ethereum/go-ethereum/rlp"
27
	"github.com/ethereum/go-ethereum/trie"
obscuren's avatar
obscuren committed
28 29
)

30 31 32 33 34 35 36 37
// DumpCollector interface which the state trie calls during iteration
type DumpCollector interface {
	// OnRoot is called with the state root
	OnRoot(common.Hash)
	// OnAccount is called once for each account in the trie
	OnAccount(common.Address, DumpAccount)
}

38
// DumpAccount represents an account in the state.
39
type DumpAccount struct {
40 41 42 43 44 45 46 47 48
	Balance   string                 `json:"balance"`
	Nonce     uint64                 `json:"nonce"`
	Root      string                 `json:"root"`
	CodeHash  string                 `json:"codeHash"`
	Code      string                 `json:"code,omitempty"`
	Storage   map[common.Hash]string `json:"storage,omitempty"`
	Address   *common.Address        `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
	SecureKey hexutil.Bytes          `json:"key,omitempty"`     // If we don't have address, we can output the key

obscuren's avatar
obscuren committed
49 50
}

51
// Dump represents the full dump in a collected format, as one large map.
52
type Dump struct {
53 54 55 56
	Root     string                         `json:"root"`
	Accounts map[common.Address]DumpAccount `json:"accounts"`
}

57 58 59 60 61 62 63 64
// OnRoot implements DumpCollector interface
func (d *Dump) OnRoot(root common.Hash) {
	d.Root = fmt.Sprintf("%x", root)
}

// OnAccount implements DumpCollector interface
func (d *Dump) OnAccount(addr common.Address, account DumpAccount) {
	d.Accounts[addr] = account
65
}
66

67 68 69 70 71 72 73
// IteratorDump is an implementation for iterating over data.
type IteratorDump struct {
	Root     string                         `json:"root"`
	Accounts map[common.Address]DumpAccount `json:"accounts"`
	Next     []byte                         `json:"next,omitempty"` // nil if no more accounts
}

74 75
// OnRoot implements DumpCollector interface
func (d *IteratorDump) OnRoot(root common.Hash) {
76
	d.Root = fmt.Sprintf("%x", root)
obscuren's avatar
obscuren committed
77 78
}

79 80
// OnAccount implements DumpCollector interface
func (d *IteratorDump) OnAccount(addr common.Address, account DumpAccount) {
81
	d.Accounts[addr] = account
82
}
83

84 85 86
// iterativeDump is a DumpCollector-implementation which dumps output line-by-line iteratively.
type iterativeDump struct {
	*json.Encoder
87
}
88

89 90
// OnAccount implements DumpCollector interface
func (d iterativeDump) OnAccount(addr common.Address, account DumpAccount) {
91 92 93 94 95 96 97 98 99 100 101 102
	dumpAccount := &DumpAccount{
		Balance:   account.Balance,
		Nonce:     account.Nonce,
		Root:      account.Root,
		CodeHash:  account.CodeHash,
		Code:      account.Code,
		Storage:   account.Storage,
		SecureKey: account.SecureKey,
		Address:   nil,
	}
	if addr != (common.Address{}) {
		dumpAccount.Address = &addr
obscuren's avatar
obscuren committed
103
	}
104
	d.Encode(dumpAccount)
105
}
106

107 108
// OnRoot implements DumpCollector interface
func (d iterativeDump) OnRoot(root common.Hash) {
109
	d.Encode(struct {
110 111 112
		Root common.Hash `json:"root"`
	}{root})
}
obscuren's avatar
obscuren committed
113

114
func (s *StateDB) DumpToCollector(c DumpCollector, excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) (nextKey []byte) {
115
	missingPreimages := 0
116
	c.OnRoot(s.trie.Hash())
117 118 119

	var count int
	it := trie.NewIterator(s.trie.NodeIterator(start))
obscuren's avatar
obscuren committed
120
	for it.Next() {
121 122
		var data Account
		if err := rlp.DecodeBytes(it.Value, &data); err != nil {
Felix Lange's avatar
Felix Lange committed
123 124
			panic(err)
		}
125 126 127 128 129
		account := DumpAccount{
			Balance:  data.Balance.String(),
			Nonce:    data.Nonce,
			Root:     common.Bytes2Hex(data.Root[:]),
			CodeHash: common.Bytes2Hex(data.CodeHash),
Felix Lange's avatar
Felix Lange committed
130
		}
131 132
		addrBytes := s.trie.GetKey(it.Key)
		if addrBytes == nil {
133 134 135 136 137 138 139
			// Preimage missing
			missingPreimages++
			if excludeMissingPreimages {
				continue
			}
			account.SecureKey = it.Key
		}
140 141
		addr := common.BytesToAddress(addrBytes)
		obj := newObject(nil, addr, data)
142
		if !excludeCode {
143
			account.Code = common.Bytes2Hex(obj.Code(s.db))
144 145 146
		}
		if !excludeStorage {
			account.Storage = make(map[common.Hash]string)
147
			storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil))
148
			for storageIt.Next() {
149 150 151 152 153
				_, content, _, err := rlp.Split(storageIt.Value)
				if err != nil {
					log.Error("Failed to decode the value returned by iterator", "error", err)
					continue
				}
154
				account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
155
			}
obscuren's avatar
obscuren committed
156
		}
157
		c.OnAccount(addr, account)
158 159 160 161 162 163 164
		count++
		if maxResults > 0 && count >= maxResults {
			if it.Next() {
				nextKey = it.Key
			}
			break
		}
165 166 167 168
	}
	if missingPreimages > 0 {
		log.Warn("Dump incomplete due to missing preimages", "missing", missingPreimages)
	}
169 170

	return nextKey
171 172 173
}

// RawDump returns the entire state an a single large object
174
func (s *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages bool) Dump {
175 176
	dump := &Dump{
		Accounts: make(map[common.Address]DumpAccount),
obscuren's avatar
obscuren committed
177
	}
178
	s.DumpToCollector(dump, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0)
179
	return *dump
180
}
obscuren's avatar
obscuren committed
181

182
// Dump returns a JSON string representing the entire state as a single json-object
183 184
func (s *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool) []byte {
	dump := s.RawDump(excludeCode, excludeStorage, excludeMissingPreimages)
185
	json, err := json.MarshalIndent(dump, "", "    ")
obscuren's avatar
obscuren committed
186
	if err != nil {
187
		fmt.Println("Dump err", err)
obscuren's avatar
obscuren committed
188
	}
obscuren's avatar
obscuren committed
189
	return json
obscuren's avatar
obscuren committed
190
}
191 192

// IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
193
func (s *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) {
194
	s.DumpToCollector(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0)
195 196 197 198 199 200 201
}

// IteratorDump dumps out a batch of accounts starts with the given start key
func (s *StateDB) IteratorDump(excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) IteratorDump {
	iterator := &IteratorDump{
		Accounts: make(map[common.Address]DumpAccount),
	}
202
	iterator.Next = s.DumpToCollector(iterator, excludeCode, excludeStorage, excludeMissingPreimages, start, maxResults)
203
	return *iterator
204
}