Skip to content

Commit 36bcc24

Browse files
rjl493456442fjl
andauthored
triedb/pathdb: fix journal resolution in pathdb (#32097)
This pull request fixes a flaw in the PBSS state iterator, which could return empty account or storage data. In PBSS, multiple in-memory diff layers and a write buffer are maintained. These layers are persisted to the database and reloaded after node restarts. However, since the state data is encoded using RLP, the distinction between nil and an empty byte slice is lost during the encode/decode process. As a result, invalid state values such as `[]byte{}` can appear in PBSS and ultimately be returned by the state iterator. Checkout https://github.com/ethereum/go-ethereum/blob/master/triedb/pathdb/iterator_fast.go#L270 for more iterator details. It's a long-term existent issue and now be activated since the snapshot integration. The error `err="range contains deletion"` will occur when Geth tries to serve other peers with SNAP protocol request. --------- Co-authored-by: Felix Lange <[email protected]>
1 parent a92f2b8 commit 36bcc24

File tree

2 files changed

+92
-26
lines changed

2 files changed

+92
-26
lines changed

triedb/pathdb/iterator_test.go

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,8 @@ func testAccountIteratorDeletions(t *testing.T, newIterator func(db *Database, r
816816
config := &Config{
817817
NoAsyncGeneration: true,
818818
}
819-
db := New(rawdb.NewMemoryDatabase(), config, false)
819+
memoryDB := rawdb.NewMemoryDatabase()
820+
db := New(memoryDB, config, false)
820821

821822
// Stack three diff layers on top with various overlaps
822823
db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(),
@@ -831,31 +832,55 @@ func testAccountIteratorDeletions(t *testing.T, newIterator func(db *Database, r
831832
db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 3, trienode.NewMergedNodeSet(),
832833
NewStateSetWithOrigin(randomAccountSet("0x33", "0x44", "0x55"), nil, nil, nil, false))
833834

834-
// The output should be 11,33,44,55
835-
it := newIterator(db, common.HexToHash("0x04"), common.Hash{})
836-
// Do a quick check
837-
verifyIterator(t, 4, it, verifyAccount)
838-
it.Release()
839-
840-
// And a more detailed verification that we indeed do not see '0x22'
841-
it = newIterator(db, common.HexToHash("0x04"), common.Hash{})
842-
defer it.Release()
843-
for it.Next() {
844-
hash := it.Hash()
845-
if it.Account() == nil {
846-
t.Errorf("iterator returned nil-value for hash %x", hash)
847-
}
848-
if hash == deleted {
849-
t.Errorf("expected deleted elem %x to not be returned by iterator", deleted)
835+
verify := func() {
836+
// The output should be 11,33,44,55
837+
it := newIterator(db, common.HexToHash("0x04"), common.Hash{})
838+
// Do a quick check
839+
verifyIterator(t, 4, it, verifyAccount)
840+
it.Release()
841+
842+
// And a more detailed verification that we indeed do not see '0x22'
843+
it = newIterator(db, common.HexToHash("0x04"), common.Hash{})
844+
defer it.Release()
845+
for it.Next() {
846+
hash := it.Hash()
847+
if it.Account() == nil {
848+
t.Errorf("iterator returned nil-value for hash %x", hash)
849+
}
850+
if hash == deleted {
851+
t.Errorf("expected deleted elem %x to not be returned by iterator", deleted)
852+
}
850853
}
851854
}
855+
verify()
856+
857+
if err := db.Journal(common.HexToHash("0x04")); err != nil {
858+
t.Fatalf("Failed to journal the database, %v", err)
859+
}
860+
if err := db.Close(); err != nil {
861+
t.Fatalf("Failed to close the database, %v", err)
862+
}
863+
db = New(memoryDB, config, false)
864+
865+
verify()
852866
}
853867

854868
func TestStorageIteratorDeletions(t *testing.T) {
855869
config := &Config{
856870
NoAsyncGeneration: true,
857871
}
858-
db := New(rawdb.NewMemoryDatabase(), config, false)
872+
memoryDB := rawdb.NewMemoryDatabase()
873+
db := New(memoryDB, config, false)
874+
875+
restart := func(head common.Hash) {
876+
if err := db.Journal(head); err != nil {
877+
t.Fatalf("Failed to journal the database, %v", err)
878+
}
879+
if err := db.Close(); err != nil {
880+
t.Fatalf("Failed to close the database, %v", err)
881+
}
882+
db = New(memoryDB, config, false)
883+
}
859884

860885
// Stack three diff layers on top with various overlaps
861886
db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(),
@@ -874,6 +899,19 @@ func TestStorageIteratorDeletions(t *testing.T) {
874899
verifyIterator(t, 3, it, verifyStorage)
875900
it.Release()
876901

902+
// Ensure the iteration result aligns after the database restart
903+
restart(common.HexToHash("0x03"))
904+
905+
// The output should be 02,04,05,06
906+
it, _ = db.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.Hash{})
907+
verifyIterator(t, 4, it, verifyStorage)
908+
it.Release()
909+
910+
// The output should be 04,05,06
911+
it, _ = db.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.HexToHash("0x03"))
912+
verifyIterator(t, 3, it, verifyStorage)
913+
it.Release()
914+
877915
// Destruct the whole storage
878916
accounts := map[common.Hash][]byte{
879917
common.HexToHash("0xaa"): nil,
@@ -885,6 +923,12 @@ func TestStorageIteratorDeletions(t *testing.T) {
885923
verifyIterator(t, 0, it, verifyStorage)
886924
it.Release()
887925

926+
// Ensure the iteration result aligns after the database restart
927+
restart(common.HexToHash("0x04"))
928+
it, _ = db.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{})
929+
verifyIterator(t, 0, it, verifyStorage)
930+
it.Release()
931+
888932
// Re-insert the slots of the same account
889933
db.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), 4, trienode.NewMergedNodeSet(),
890934
NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x07", "0x08", "0x09"}}, nil), nil, nil, false))
@@ -894,6 +938,14 @@ func TestStorageIteratorDeletions(t *testing.T) {
894938
verifyIterator(t, 3, it, verifyStorage)
895939
it.Release()
896940

941+
// Ensure the iteration result aligns after the database restart
942+
restart(common.HexToHash("0x05"))
943+
944+
// The output should be 07,08,09
945+
it, _ = db.StorageIterator(common.HexToHash("0x05"), common.HexToHash("0xaa"), common.Hash{})
946+
verifyIterator(t, 3, it, verifyStorage)
947+
it.Release()
948+
897949
// Destruct the whole storage but re-create the account in the same layer
898950
db.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), 5, trienode.NewMergedNodeSet(),
899951
NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x11", "0x12"}}, [][]string{{"0x07", "0x08", "0x09"}}), nil, nil, false))
@@ -903,6 +955,13 @@ func TestStorageIteratorDeletions(t *testing.T) {
903955
it.Release()
904956

905957
verifyIterator(t, 2, db.tree.get(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage)
958+
959+
// Ensure the iteration result aligns after the database restart
960+
restart(common.HexToHash("0x06"))
961+
it, _ = db.StorageIterator(common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{})
962+
verifyIterator(t, 2, it, verifyStorage) // The output should be 11,12
963+
it.Release()
964+
verifyIterator(t, 2, db.tree.get(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage)
906965
}
907966

908967
// TestStaleIterator tests if the iterator could correctly terminate the iteration

triedb/pathdb/states.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,8 @@ func (s *stateSet) decode(r *rlp.Stream) error {
387387
if err := r.Decode(&dec); err != nil {
388388
return fmt.Errorf("load diff accounts: %v", err)
389389
}
390-
for i := 0; i < len(dec.AddrHashes); i++ {
391-
accountSet[dec.AddrHashes[i]] = dec.Accounts[i]
390+
for i := range dec.AddrHashes {
391+
accountSet[dec.AddrHashes[i]] = empty2nil(dec.Accounts[i])
392392
}
393393
s.accountData = accountSet
394394

@@ -407,8 +407,8 @@ func (s *stateSet) decode(r *rlp.Stream) error {
407407
}
408408
for _, entry := range storages {
409409
storageSet[entry.AddrHash] = make(map[common.Hash][]byte, len(entry.Keys))
410-
for i := 0; i < len(entry.Keys); i++ {
411-
storageSet[entry.AddrHash][entry.Keys[i]] = entry.Vals[i]
410+
for i := range entry.Keys {
411+
storageSet[entry.AddrHash][entry.Keys[i]] = empty2nil(entry.Vals[i])
412412
}
413413
}
414414
s.storageData = storageSet
@@ -550,8 +550,8 @@ func (s *StateSetWithOrigin) decode(r *rlp.Stream) error {
550550
if err := r.Decode(&accounts); err != nil {
551551
return fmt.Errorf("load diff account origin set: %v", err)
552552
}
553-
for i := 0; i < len(accounts.Accounts); i++ {
554-
accountSet[accounts.Addresses[i]] = accounts.Accounts[i]
553+
for i := range accounts.Accounts {
554+
accountSet[accounts.Addresses[i]] = empty2nil(accounts.Accounts[i])
555555
}
556556
s.accountOrigin = accountSet
557557

@@ -570,10 +570,17 @@ func (s *StateSetWithOrigin) decode(r *rlp.Stream) error {
570570
}
571571
for _, storage := range storages {
572572
storageSet[storage.Address] = make(map[common.Hash][]byte)
573-
for i := 0; i < len(storage.Keys); i++ {
574-
storageSet[storage.Address][storage.Keys[i]] = storage.Vals[i]
573+
for i := range storage.Keys {
574+
storageSet[storage.Address][storage.Keys[i]] = empty2nil(storage.Vals[i])
575575
}
576576
}
577577
s.storageOrigin = storageSet
578578
return nil
579579
}
580+
581+
func empty2nil(b []byte) []byte {
582+
if len(b) == 0 {
583+
return nil
584+
}
585+
return b
586+
}

0 commit comments

Comments
 (0)