Skip to content

Commit 43c278c

Browse files
authored
core/state: disable snapshot iteration if it's not fully constructed (#21682)
* core/state/snapshot: add diskRoot function * core/state/snapshot: disable iteration if the snapshot is generating * core/state/snapshot: simplify the function * core/state: panic for undefined layer
1 parent 18145ad commit 43c278c

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

core/state/snapshot/snapshot.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ var (
8686
// range of accounts covered.
8787
ErrNotCoveredYet = errors.New("not covered yet")
8888

89+
// ErrNotConstructed is returned if the callers want to iterate the snapshot
90+
// while the generation is not finished yet.
91+
ErrNotConstructed = errors.New("snapshot is not constructed")
92+
8993
// errSnapshotCycle is returned if a snapshot is attempted to be inserted
9094
// that forms a cycle in the snapshot tree.
9195
errSnapshotCycle = errors.New("snapshot cycle")
@@ -609,11 +613,61 @@ func (t *Tree) Rebuild(root common.Hash) {
609613
// AccountIterator creates a new account iterator for the specified root hash and
610614
// seeks to a starting account hash.
611615
func (t *Tree) AccountIterator(root common.Hash, seek common.Hash) (AccountIterator, error) {
616+
ok, err := t.generating()
617+
if err != nil {
618+
return nil, err
619+
}
620+
if ok {
621+
return nil, ErrNotConstructed
622+
}
612623
return newFastAccountIterator(t, root, seek)
613624
}
614625

615626
// StorageIterator creates a new storage iterator for the specified root hash and
616627
// account. The iterator will be move to the specific start position.
617628
func (t *Tree) StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) {
629+
ok, err := t.generating()
630+
if err != nil {
631+
return nil, err
632+
}
633+
if ok {
634+
return nil, ErrNotConstructed
635+
}
618636
return newFastStorageIterator(t, root, account, seek)
619637
}
638+
639+
// disklayer is an internal helper function to return the disk layer.
640+
// The lock of snapTree is assumed to be held already.
641+
func (t *Tree) disklayer() *diskLayer {
642+
var snap snapshot
643+
for _, s := range t.layers {
644+
snap = s
645+
break
646+
}
647+
if snap == nil {
648+
return nil
649+
}
650+
switch layer := snap.(type) {
651+
case *diskLayer:
652+
return layer
653+
case *diffLayer:
654+
return layer.origin
655+
default:
656+
panic(fmt.Sprintf("%T: undefined layer", snap))
657+
}
658+
}
659+
660+
// generating is an internal helper function which reports whether the snapshot
661+
// is still under the construction.
662+
func (t *Tree) generating() (bool, error) {
663+
t.lock.Lock()
664+
defer t.lock.Unlock()
665+
666+
layer := t.disklayer()
667+
if layer == nil {
668+
return false, errors.New("disk layer is missing")
669+
}
670+
layer.lock.RLock()
671+
defer layer.lock.RUnlock()
672+
return layer.genMarker != nil, nil
673+
}

0 commit comments

Comments
 (0)