Skip to content

Commit b5afdec

Browse files
committed
core: remove block numbers from snapshots to make a pure state tree
1 parent 707c052 commit b5afdec

File tree

11 files changed

+175
-165
lines changed

11 files changed

+175
-165
lines changed

core/blockchain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
301301
}
302302
// Load any existing snapshot, regenerating it if loading failed
303303
head := bc.CurrentBlock()
304-
if bc.snaps, err = snapshot.New(bc.db, "snapshot.rlp", head.NumberU64(), head.Root()); err != nil {
304+
if bc.snaps, err = snapshot.New(bc.db, "snapshot.rlp", head.Root()); err != nil {
305305
return nil, err
306306
}
307307
// Take ownership of this particular state

core/rawdb/accessors_snapshot.go

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,36 @@
1717
package rawdb
1818

1919
import (
20-
"encoding/binary"
21-
2220
"github.com/ethereum/go-ethereum/common"
2321
"github.com/ethereum/go-ethereum/ethdb"
2422
"github.com/ethereum/go-ethereum/log"
2523
)
2624

27-
// ReadSnapshotBlock retrieves the number and root of the block whose state is
28-
// contained in the persisted snapshot.
29-
func ReadSnapshotBlock(db ethdb.KeyValueReader) (uint64, common.Hash) {
30-
data, _ := db.Get(snapshotBlockKey)
31-
if len(data) != 8+common.HashLength {
32-
return 0, common.Hash{}
25+
// ReadSnapshotRoot retrieves the root of the block whose state is contained in
26+
// the persisted snapshot.
27+
func ReadSnapshotRoot(db ethdb.KeyValueReader) common.Hash {
28+
data, _ := db.Get(snapshotRootKey)
29+
if len(data) != common.HashLength {
30+
return common.Hash{}
3331
}
34-
return binary.BigEndian.Uint64(data[:8]), common.BytesToHash(data[8:])
32+
return common.BytesToHash(data)
3533
}
3634

37-
// WriteSnapshotBlock stores the number and root of the block whose state is
38-
// contained in the persisted snapshot.
39-
func WriteSnapshotBlock(db ethdb.KeyValueWriter, number uint64, root common.Hash) {
40-
if err := db.Put(snapshotBlockKey, append(encodeBlockNumber(number), root.Bytes()...)); err != nil {
41-
log.Crit("Failed to store snapsnot block's number and root", "err", err)
35+
// WriteSnapshotRoot stores the root of the block whose state is contained in
36+
// the persisted snapshot.
37+
func WriteSnapshotRoot(db ethdb.KeyValueWriter, root common.Hash) {
38+
if err := db.Put(snapshotRootKey, root[:]); err != nil {
39+
log.Crit("Failed to store snapshot root", "err", err)
4240
}
4341
}
4442

45-
// DeleteSnapshotBlock deletes the number and hash of the block whose state is
46-
// contained in the persisted snapshot. Since snapshots are not immutable, this
47-
// method can be used during updates, so a crash or failure will mark the entire
48-
// snapshot invalid.
49-
func DeleteSnapshotBlock(db ethdb.KeyValueWriter) {
50-
if err := db.Delete(snapshotBlockKey); err != nil {
51-
log.Crit("Failed to remove snapsnot block's number and hash", "err", err)
43+
// DeleteSnapshotRoot deletes the hash of the block whose state is contained in
44+
// the persisted snapshot. Since snapshots are not immutable, this method can
45+
// be used during updates, so a crash or failure will mark the entire snapshot
46+
// invalid.
47+
func DeleteSnapshotRoot(db ethdb.KeyValueWriter) {
48+
if err := db.Delete(snapshotRootKey); err != nil {
49+
log.Crit("Failed to remove snapshot root", "err", err)
5250
}
5351
}
5452

core/rawdb/schema.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ var (
4141
// fastTrieProgressKey tracks the number of trie entries imported during fast sync.
4242
fastTrieProgressKey = []byte("TrieSync")
4343

44-
// snapshotBlockKey tracks the number and hash of the last snapshot.
45-
snapshotBlockKey = []byte("SnapshotBlock")
44+
// snapshotRootKey tracks the number and hash of the last snapshot.
45+
snapshotRootKey = []byte("SnapshotRoot")
4646

4747
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
4848
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header

core/state/snapshot/difflayer.go

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ type diffLayer struct {
3636
parent snapshot // Parent snapshot modified by this one, never nil
3737
memory uint64 // Approximate guess as to how much memory we use
3838

39-
number uint64 // Block number to which this snapshot diff belongs to
40-
root common.Hash // Root hash to which this snapshot diff belongs to
41-
stale bool // Signals that the layer became stale (state progressed)
39+
root common.Hash // Root hash to which this snapshot diff belongs to
40+
stale bool // Signals that the layer became stale (state progressed)
4241

4342
accountList []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil
4443
accountData map[common.Hash][]byte // Keyed accounts for direct retrival (nil means deleted)
@@ -50,11 +49,10 @@ type diffLayer struct {
5049

5150
// newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low
5251
// level persistent database or a hierarchical diff already.
53-
func newDiffLayer(parent snapshot, number uint64, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
52+
func newDiffLayer(parent snapshot, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
5453
// Create the new layer with some pre-allocated data segments
5554
dl := &diffLayer{
5655
parent: parent,
57-
number: number,
5856
root: root,
5957
accountData: accounts,
6058
storageData: storage,
@@ -63,7 +61,6 @@ func newDiffLayer(parent snapshot, number uint64, root common.Hash, accounts map
6361
for _, data := range accounts {
6462
dl.memory += uint64(len(data))
6563
}
66-
6764
// Fill the storage hashes and sort them for the iterator
6865
dl.storageList = make(map[common.Hash][]common.Hash)
6966

@@ -93,9 +90,27 @@ func newDiffLayer(parent snapshot, number uint64, root common.Hash, accounts map
9390
return dl
9491
}
9592

96-
// Info returns the block number and root hash for which this snapshot was made.
97-
func (dl *diffLayer) Info() (uint64, common.Hash) {
98-
return dl.number, dl.root
93+
// Root returns the root hash for which this snapshot was made.
94+
func (dl *diffLayer) Root() common.Hash {
95+
return dl.root
96+
}
97+
98+
// Stale return whether this layer has become stale (was flattened across) or if
99+
// it's still live.
100+
func (dl *diffLayer) Stale() bool {
101+
// If the parent is stale, mark this layer as stale too
102+
if stale := dl.parent.Stale(); stale {
103+
dl.lock.Lock()
104+
defer dl.lock.Unlock()
105+
106+
dl.stale = true
107+
return true
108+
}
109+
// Parent not stale, return only whether we are
110+
dl.lock.RLock()
111+
defer dl.lock.RUnlock()
112+
113+
return dl.stale
99114
}
100115

101116
// Account directly retrieves the account associated with a particular hash in
@@ -164,7 +179,7 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro
164179
// Update creates a new layer on top of the existing snapshot diff tree with
165180
// the specified data items.
166181
func (dl *diffLayer) Update(blockRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
167-
return newDiffLayer(dl, dl.number+1, blockRoot, accounts, storage)
182+
return newDiffLayer(dl, blockRoot, accounts, storage)
168183
}
169184

170185
// flatten pushes all data from this point downwards, flattening everything into
@@ -213,7 +228,6 @@ func (dl *diffLayer) flatten() snapshot {
213228
// Return the combo parent
214229
return &diffLayer{
215230
parent: parent.parent,
216-
number: dl.number,
217231
root: dl.root,
218232
storageList: parent.storageList,
219233
storageData: parent.storageData,

core/state/snapshot/difflayer_journal.go

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,12 @@ type journalStorage struct {
4343
// diff and verifying that it can be linked to the requested parent.
4444
func loadDiffLayer(parent snapshot, r *rlp.Stream) (snapshot, error) {
4545
// Read the next diff journal entry
46-
var (
47-
number uint64
48-
root common.Hash
49-
)
50-
if err := r.Decode(&number); err != nil {
46+
var root common.Hash
47+
if err := r.Decode(&root); err != nil {
5148
// The first read may fail with EOF, marking the end of the journal
5249
if err == io.EOF {
5350
return parent, nil
5451
}
55-
return nil, fmt.Errorf("load diff number: %v", err)
56-
}
57-
if err := r.Decode(&root); err != nil {
5852
return nil, fmt.Errorf("load diff root: %v", err)
5953
}
6054
var accounts []journalAccount
@@ -77,13 +71,7 @@ func loadDiffLayer(parent snapshot, r *rlp.Stream) (snapshot, error) {
7771
}
7872
storageData[entry.Hash] = slots
7973
}
80-
// Validate the block number to avoid state corruption
81-
if parent, ok := parent.(*diffLayer); ok {
82-
if number != parent.number+1 {
83-
return nil, fmt.Errorf("snapshot chain broken: block #%d after #%d", number, parent.number)
84-
}
85-
}
86-
return loadDiffLayer(newDiffLayer(parent, number, root, accountData, storageData), r)
74+
return loadDiffLayer(newDiffLayer(parent, root, accountData, storageData), r)
8775
}
8876

8977
// journal is the internal version of Journal that also returns the journal file
@@ -113,13 +101,8 @@ func (dl *diffLayer) journal() (io.WriteCloser, error) {
113101
writer.Close()
114102
return nil, ErrSnapshotStale
115103
}
116-
buf := bufio.NewWriter(writer)
117104
// Everything below was journalled, persist this layer too
118-
if err := rlp.Encode(buf, dl.number); err != nil {
119-
buf.Flush()
120-
writer.Close()
121-
return nil, err
122-
}
105+
buf := bufio.NewWriter(writer)
123106
if err := rlp.Encode(buf, dl.root); err != nil {
124107
buf.Flush()
125108
writer.Close()

core/state/snapshot/difflayer_test.go

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ func TestMergeBasics(t *testing.T) {
6161
}
6262
}
6363
// Add some (identical) layers on top
64-
parent := newDiffLayer(emptyLayer{}, 1, common.Hash{}, accounts, storage)
65-
child := newDiffLayer(parent, 1, common.Hash{}, accounts, storage)
66-
child = newDiffLayer(child, 1, common.Hash{}, accounts, storage)
67-
child = newDiffLayer(child, 1, common.Hash{}, accounts, storage)
68-
child = newDiffLayer(child, 1, common.Hash{}, accounts, storage)
64+
parent := newDiffLayer(emptyLayer{}, common.Hash{}, accounts, storage)
65+
child := newDiffLayer(parent, common.Hash{}, accounts, storage)
66+
child = newDiffLayer(child, common.Hash{}, accounts, storage)
67+
child = newDiffLayer(child, common.Hash{}, accounts, storage)
68+
child = newDiffLayer(child, common.Hash{}, accounts, storage)
6969
// And flatten
7070
merged := (child.flatten()).(*diffLayer)
7171

@@ -122,7 +122,7 @@ func TestMergeDelete(t *testing.T) {
122122
}
123123

124124
// Add some flip-flopping layers on top
125-
parent := newDiffLayer(emptyLayer{}, 1, common.Hash{}, flip(), storage)
125+
parent := newDiffLayer(emptyLayer{}, common.Hash{}, flip(), storage)
126126
child := parent.Update(common.Hash{}, flop(), storage)
127127
child = child.Update(common.Hash{}, flip(), storage)
128128
child = child.Update(common.Hash{}, flop(), storage)
@@ -139,10 +139,6 @@ func TestMergeDelete(t *testing.T) {
139139
// And flatten
140140
merged := (child.flatten()).(*diffLayer)
141141

142-
// check number
143-
if got, exp := merged.number, child.number; got != exp {
144-
t.Errorf("merged layer: wrong number - exp %d got %d", exp, got)
145-
}
146142
if data, _ := merged.Account(h1); data == nil {
147143
t.Errorf("merged layer: expected %x to be non-nil", h1)
148144
}
@@ -169,7 +165,7 @@ func TestInsertAndMerge(t *testing.T) {
169165
{
170166
var accounts = make(map[common.Hash][]byte)
171167
var storage = make(map[common.Hash]map[common.Hash][]byte)
172-
parent = newDiffLayer(emptyLayer{}, 1, common.Hash{}, accounts, storage)
168+
parent = newDiffLayer(emptyLayer{}, common.Hash{}, accounts, storage)
173169
}
174170
{
175171
var accounts = make(map[common.Hash][]byte)
@@ -178,7 +174,7 @@ func TestInsertAndMerge(t *testing.T) {
178174
accstorage := make(map[common.Hash][]byte)
179175
storage[acc] = accstorage
180176
storage[acc][slot] = []byte{0x01}
181-
child = newDiffLayer(parent, 2, common.Hash{}, accounts, storage)
177+
child = newDiffLayer(parent, common.Hash{}, accounts, storage)
182178
}
183179
// And flatten
184180
merged := (child.flatten()).(*diffLayer)
@@ -200,11 +196,12 @@ func (emptyLayer) Journal() error {
200196
panic("implement me")
201197
}
202198

203-
func (emptyLayer) Info() (uint64, common.Hash) {
204-
return 0, common.Hash{}
199+
func (emptyLayer) Stale() bool {
200+
panic("implement me")
205201
}
206-
func (emptyLayer) Number() uint64 {
207-
return 0
202+
203+
func (emptyLayer) Root() common.Hash {
204+
return common.Hash{}
208205
}
209206

210207
func (emptyLayer) Account(hash common.Hash) (*Account, error) {
@@ -227,19 +224,15 @@ func (emptyLayer) Storage(accountHash, storageHash common.Hash) ([]byte, error)
227224
// BenchmarkSearch-6 500000 3723 ns/op (10k per layer, only top-level RLock()
228225
func BenchmarkSearch(b *testing.B) {
229226
// First, we set up 128 diff layers, with 1K items each
230-
231-
blocknum := uint64(0)
232227
fill := func(parent snapshot) *diffLayer {
233228
accounts := make(map[common.Hash][]byte)
234229
storage := make(map[common.Hash]map[common.Hash][]byte)
235230

236231
for i := 0; i < 10000; i++ {
237232
accounts[randomHash()] = randomAccount()
238233
}
239-
blocknum++
240-
return newDiffLayer(parent, blocknum, common.Hash{}, accounts, storage)
234+
return newDiffLayer(parent, common.Hash{}, accounts, storage)
241235
}
242-
243236
var layer snapshot
244237
layer = emptyLayer{}
245238
for i := 0; i < 128; i++ {
@@ -261,8 +254,6 @@ func BenchmarkSearch(b *testing.B) {
261254
// BenchmarkSearchSlot-6 100000 14551 ns/op (when checking parent number using atomic)
262255
func BenchmarkSearchSlot(b *testing.B) {
263256
// First, we set up 128 diff layers, with 1K items each
264-
265-
blocknum := uint64(0)
266257
accountKey := common.Hash{}
267258
storageKey := common.HexToHash("0x1337")
268259
accountRLP := randomAccount()
@@ -278,16 +269,13 @@ func BenchmarkSearchSlot(b *testing.B) {
278269
accStorage[randomHash()] = value
279270
storage[accountKey] = accStorage
280271
}
281-
blocknum++
282-
return newDiffLayer(parent, blocknum, common.Hash{}, accounts, storage)
272+
return newDiffLayer(parent, common.Hash{}, accounts, storage)
283273
}
284-
285274
var layer snapshot
286275
layer = emptyLayer{}
287276
for i := 0; i < 128; i++ {
288277
layer = fill(layer)
289278
}
290-
291279
b.ResetTimer()
292280
for i := 0; i < b.N; i++ {
293281
layer.Storage(accountKey, storageKey)
@@ -300,7 +288,7 @@ func BenchmarkSearchSlot(b *testing.B) {
300288
// Without sorting and tracking accountlist
301289
// BenchmarkFlatten-6 300 5511511 ns/op
302290
func BenchmarkFlatten(b *testing.B) {
303-
fill := func(parent snapshot, blocknum int) *diffLayer {
291+
fill := func(parent snapshot) *diffLayer {
304292
accounts := make(map[common.Hash][]byte)
305293
storage := make(map[common.Hash]map[common.Hash][]byte)
306294

@@ -317,7 +305,7 @@ func BenchmarkFlatten(b *testing.B) {
317305
}
318306
storage[accountKey] = accStorage
319307
}
320-
return newDiffLayer(parent, uint64(blocknum), common.Hash{}, accounts, storage)
308+
return newDiffLayer(parent, common.Hash{}, accounts, storage)
321309
}
322310

323311
b.ResetTimer()
@@ -327,7 +315,7 @@ func BenchmarkFlatten(b *testing.B) {
327315
var layer snapshot
328316
layer = emptyLayer{}
329317
for i := 1; i < 128; i++ {
330-
layer = fill(layer, i)
318+
layer = fill(layer)
331319
}
332320
b.StartTimer()
333321

@@ -336,7 +324,6 @@ func BenchmarkFlatten(b *testing.B) {
336324
if !ok {
337325
break
338326
}
339-
340327
layer = dl.flatten()
341328
}
342329
b.StopTimer()
@@ -351,7 +338,7 @@ func BenchmarkFlatten(b *testing.B) {
351338
// BenchmarkJournal-6 1 1471373923 ns/ops
352339
// BenchmarkJournal-6 1 1208083335 ns/op // bufio writer
353340
func BenchmarkJournal(b *testing.B) {
354-
fill := func(parent snapshot, blocknum int) *diffLayer {
341+
fill := func(parent snapshot) *diffLayer {
355342
accounts := make(map[common.Hash][]byte)
356343
storage := make(map[common.Hash]map[common.Hash][]byte)
357344

@@ -368,15 +355,14 @@ func BenchmarkJournal(b *testing.B) {
368355
}
369356
storage[accountKey] = accStorage
370357
}
371-
return newDiffLayer(parent, uint64(blocknum), common.Hash{}, accounts, storage)
358+
return newDiffLayer(parent, common.Hash{}, accounts, storage)
372359
}
373-
374360
var layer snapshot
375361
layer = &diskLayer{
376362
journal: path.Join(os.TempDir(), "difflayer_journal.tmp"),
377363
}
378364
for i := 1; i < 128; i++ {
379-
layer = fill(layer, i)
365+
layer = fill(layer)
380366
}
381367
b.ResetTimer()
382368

0 commit comments

Comments
 (0)