github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/core/state/snapshot/dangling.go (about) 1 // Copyright 2022 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 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 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 // GNU Lesser General Public License for more details. 13 // 14 // 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 17 package snapshot 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "io" 24 "time" 25 26 "github.com/electroneum/electroneum-sc/common" 27 "github.com/electroneum/electroneum-sc/core/rawdb" 28 "github.com/electroneum/electroneum-sc/ethdb" 29 "github.com/electroneum/electroneum-sc/log" 30 "github.com/electroneum/electroneum-sc/rlp" 31 ) 32 33 // CheckDanglingStorage iterates the snap storage data, and verifies that all 34 // storage also has corresponding account data. 35 func CheckDanglingStorage(chaindb ethdb.KeyValueStore) error { 36 if err := checkDanglingDiskStorage(chaindb); err != nil { 37 return err 38 } 39 return checkDanglingMemStorage(chaindb) 40 } 41 42 // checkDanglingDiskStorage checks if there is any 'dangling' storage data in the 43 // disk-backed snapshot layer. 44 func checkDanglingDiskStorage(chaindb ethdb.KeyValueStore) error { 45 var ( 46 lastReport = time.Now() 47 start = time.Now() 48 lastKey []byte 49 it = rawdb.NewKeyLengthIterator(chaindb.NewIterator(rawdb.SnapshotStoragePrefix, nil), 1+2*common.HashLength) 50 ) 51 log.Info("Checking dangling snapshot disk storage") 52 53 defer it.Release() 54 for it.Next() { 55 k := it.Key() 56 accKey := k[1:33] 57 if bytes.Equal(accKey, lastKey) { 58 // No need to look up for every slot 59 continue 60 } 61 lastKey = common.CopyBytes(accKey) 62 if time.Since(lastReport) > time.Second*8 { 63 log.Info("Iterating snap storage", "at", fmt.Sprintf("%#x", accKey), "elapsed", common.PrettyDuration(time.Since(start))) 64 lastReport = time.Now() 65 } 66 if data := rawdb.ReadAccountSnapshot(chaindb, common.BytesToHash(accKey)); len(data) == 0 { 67 log.Warn("Dangling storage - missing account", "account", fmt.Sprintf("%#x", accKey), "storagekey", fmt.Sprintf("%#x", k)) 68 return fmt.Errorf("dangling snapshot storage account %#x", accKey) 69 } 70 } 71 log.Info("Verified the snapshot disk storage", "time", common.PrettyDuration(time.Since(start)), "err", it.Error()) 72 return nil 73 } 74 75 // checkDanglingMemStorage checks if there is any 'dangling' storage in the journalled 76 // snapshot difflayers. 77 func checkDanglingMemStorage(db ethdb.KeyValueStore) error { 78 var ( 79 start = time.Now() 80 journal = rawdb.ReadSnapshotJournal(db) 81 ) 82 if len(journal) == 0 { 83 log.Warn("Loaded snapshot journal", "diffs", "missing") 84 return nil 85 } 86 r := rlp.NewStream(bytes.NewReader(journal), 0) 87 // Firstly, resolve the first element as the journal version 88 version, err := r.Uint64() 89 if err != nil { 90 log.Warn("Failed to resolve the journal version", "error", err) 91 return nil 92 } 93 if version != journalVersion { 94 log.Warn("Discarded the snapshot journal with wrong version", "required", journalVersion, "got", version) 95 return nil 96 } 97 // Secondly, resolve the disk layer root, ensure it's continuous 98 // with disk layer. Note now we can ensure it's the snapshot journal 99 // correct version, so we expect everything can be resolved properly. 100 var root common.Hash 101 if err := r.Decode(&root); err != nil { 102 return errors.New("missing disk layer root") 103 } 104 // The diff journal is not matched with disk, discard them. 105 // It can happen that Geth crashes without persisting the latest 106 // diff journal. 107 // Load all the snapshot diffs from the journal 108 if err := checkDanglingJournalStorage(r); err != nil { 109 return err 110 } 111 log.Info("Verified the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start))) 112 return nil 113 } 114 115 // loadDiffLayer reads the next sections of a snapshot journal, reconstructing a new 116 // diff and verifying that it can be linked to the requested parent. 117 func checkDanglingJournalStorage(r *rlp.Stream) error { 118 for { 119 // Read the next diff journal entry 120 var root common.Hash 121 if err := r.Decode(&root); err != nil { 122 // The first read may fail with EOF, marking the end of the journal 123 if err == io.EOF { 124 return nil 125 } 126 return fmt.Errorf("load diff root: %v", err) 127 } 128 var destructs []journalDestruct 129 if err := r.Decode(&destructs); err != nil { 130 return fmt.Errorf("load diff destructs: %v", err) 131 } 132 var accounts []journalAccount 133 if err := r.Decode(&accounts); err != nil { 134 return fmt.Errorf("load diff accounts: %v", err) 135 } 136 accountData := make(map[common.Hash][]byte) 137 for _, entry := range accounts { 138 if len(entry.Blob) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that 139 accountData[entry.Hash] = entry.Blob 140 } else { 141 accountData[entry.Hash] = nil 142 } 143 } 144 var storage []journalStorage 145 if err := r.Decode(&storage); err != nil { 146 return fmt.Errorf("load diff storage: %v", err) 147 } 148 for _, entry := range storage { 149 if _, ok := accountData[entry.Hash]; !ok { 150 log.Error("Dangling storage - missing account", "account", fmt.Sprintf("%#x", entry.Hash), "root", root) 151 return fmt.Errorf("dangling journal snapshot storage account %#x", entry.Hash) 152 } 153 } 154 } 155 }