github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/journal.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 pathdb 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "io" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core/rawdb" 28 "github.com/ethereum/go-ethereum/core/types" 29 "github.com/ethereum/go-ethereum/ethdb" 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/rlp" 32 ) 33 34 var ( 35 errMissJournal = errors.New("journal not found") 36 errMissVersion = errors.New("version not found") 37 errUnexpectedVersion = errors.New("unexpected journal version") 38 errMissDiskRoot = errors.New("disk layer root not found") 39 errUnmatchedJournal = errors.New("unmatched journal") 40 ) 41 42 // journalVersion ensures that an incompatible journal is detected and discarded. 43 // 44 // Changelog: 45 // 46 // - Version 0: initial version 47 // - Version 1: storage.Incomplete field is removed 48 // - Version 2: add post-modification state values 49 // - Version 3: a flag has been added to indicate whether the storage slot key is the raw key or a hash 50 const journalVersion uint64 = 3 51 52 // loadJournal tries to parse the layer journal from the disk. 53 func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { 54 journal := rawdb.ReadTrieJournal(db.diskdb) 55 if len(journal) == 0 { 56 return nil, errMissJournal 57 } 58 r := rlp.NewStream(bytes.NewReader(journal), 0) 59 60 // Firstly, resolve the first element as the journal version 61 version, err := r.Uint64() 62 if err != nil { 63 return nil, errMissVersion 64 } 65 if version != journalVersion { 66 return nil, fmt.Errorf("%w want %d got %d", errUnexpectedVersion, journalVersion, version) 67 } 68 // Secondly, resolve the disk layer root, ensure it's continuous 69 // with disk layer. Note now we can ensure it's the layer journal 70 // correct version, so we expect everything can be resolved properly. 71 var root common.Hash 72 if err := r.Decode(&root); err != nil { 73 return nil, errMissDiskRoot 74 } 75 // The journal is not matched with persistent state, discard them. 76 // It can happen that geth crashes without persisting the journal. 77 if !bytes.Equal(root.Bytes(), diskRoot.Bytes()) { 78 return nil, fmt.Errorf("%w want %x got %x", errUnmatchedJournal, root, diskRoot) 79 } 80 // Load the disk layer from the journal 81 base, err := db.loadDiskLayer(r) 82 if err != nil { 83 return nil, err 84 } 85 // Load all the diff layers from the journal 86 head, err := db.loadDiffLayer(base, r) 87 if err != nil { 88 return nil, err 89 } 90 log.Debug("Loaded layer journal", "diskroot", diskRoot, "diffhead", head.rootHash()) 91 return head, nil 92 } 93 94 // journalGenerator is a disk layer entry containing the generator progress marker. 95 type journalGenerator struct { 96 // Indicator that whether the database was in progress of being wiped. 97 // It's deprecated but keep it here for backward compatibility. 98 Wiping bool 99 100 Done bool // Whether the generator finished creating the snapshot 101 Marker []byte 102 Accounts uint64 103 Slots uint64 104 Storage uint64 105 } 106 107 // loadGenerator loads the state generation progress marker from the database. 108 func loadGenerator(db ethdb.KeyValueReader, hash nodeHasher) (*journalGenerator, common.Hash, error) { 109 trieRoot, err := hash(rawdb.ReadAccountTrieNode(db, nil)) 110 if err != nil { 111 return nil, common.Hash{}, err 112 } 113 // State generation progress marker is lost, rebuild it 114 blob := rawdb.ReadSnapshotGenerator(db) 115 if len(blob) == 0 { 116 log.Info("State snapshot generator is not found") 117 return nil, trieRoot, nil 118 } 119 // State generation progress marker is not compatible, rebuild it 120 var generator journalGenerator 121 if err := rlp.DecodeBytes(blob, &generator); err != nil { 122 log.Info("State snapshot generator is not compatible") 123 return nil, trieRoot, nil 124 } 125 // The state snapshot is inconsistent with the trie data and must 126 // be rebuilt. 127 // 128 // Note: The SnapshotRoot and SnapshotGenerator are always consistent 129 // with each other, both in the legacy state snapshot and the path database. 130 // Therefore, if the SnapshotRoot does not match the trie root, 131 // the entire generator is considered stale and must be discarded. 132 stateRoot := rawdb.ReadSnapshotRoot(db) 133 if trieRoot != stateRoot { 134 log.Info("State snapshot is not consistent", "trie", trieRoot, "state", stateRoot) 135 return nil, trieRoot, nil 136 } 137 // Slice null-ness is lost after rlp decoding, reset it back to empty 138 if !generator.Done && generator.Marker == nil { 139 generator.Marker = []byte{} 140 } 141 return &generator, trieRoot, nil 142 } 143 144 // loadLayers loads a pre-existing state layer backed by a key-value store. 145 func (db *Database) loadLayers() layer { 146 // Retrieve the root node of persistent state. 147 root, err := db.hasher(rawdb.ReadAccountTrieNode(db.diskdb, nil)) 148 if err != nil { 149 log.Crit("Failed to compute node hash", "err", err) 150 } 151 // Load the layers by resolving the journal 152 head, err := db.loadJournal(root) 153 if err == nil { 154 return head 155 } 156 // journal is not matched(or missing) with the persistent state, discard 157 // it. Display log for discarding journal, but try to avoid showing 158 // useless information when the db is created from scratch. 159 if !(root == types.EmptyRootHash && errors.Is(err, errMissJournal)) { 160 log.Info("Failed to load journal, discard it", "err", err) 161 } 162 // Return single layer with persistent state. 163 return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0), nil) 164 } 165 166 // loadDiskLayer reads the binary blob from the layer journal, reconstructing 167 // a new disk layer on it. 168 func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { 169 // Resolve disk layer root 170 var root common.Hash 171 if err := r.Decode(&root); err != nil { 172 return nil, fmt.Errorf("load disk root: %v", err) 173 } 174 // Resolve the state id of disk layer, it can be different 175 // with the persistent id tracked in disk, the id distance 176 // is the number of transitions aggregated in disk layer. 177 var id uint64 178 if err := r.Decode(&id); err != nil { 179 return nil, fmt.Errorf("load state id: %v", err) 180 } 181 stored := rawdb.ReadPersistentStateID(db.diskdb) 182 if stored > id { 183 return nil, fmt.Errorf("invalid state id: stored %d resolved %d", stored, id) 184 } 185 // Resolve nodes cached in aggregated buffer 186 var nodes nodeSet 187 if err := nodes.decode(r); err != nil { 188 return nil, err 189 } 190 // Resolve flat state sets in aggregated buffer 191 var states stateSet 192 if err := states.decode(r); err != nil { 193 return nil, err 194 } 195 return newDiskLayer(root, id, db, nil, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored), nil), nil 196 } 197 198 // loadDiffLayer reads the next sections of a layer journal, reconstructing a new 199 // diff and verifying that it can be linked to the requested parent. 200 func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) { 201 // Read the next diff journal entry 202 var root common.Hash 203 if err := r.Decode(&root); err != nil { 204 // The first read may fail with EOF, marking the end of the journal 205 if err == io.EOF { 206 return parent, nil 207 } 208 return nil, fmt.Errorf("load diff root: %v", err) 209 } 210 var block uint64 211 if err := r.Decode(&block); err != nil { 212 return nil, fmt.Errorf("load block number: %v", err) 213 } 214 // Read in-memory trie nodes from journal 215 var nodes nodeSet 216 if err := nodes.decode(r); err != nil { 217 return nil, err 218 } 219 // Read flat states set (with original value attached) from journal 220 var stateSet StateSetWithOrigin 221 if err := stateSet.decode(r); err != nil { 222 return nil, err 223 } 224 return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, &nodes, &stateSet), r) 225 } 226 227 // journal implements the layer interface, marshaling the un-flushed trie nodes 228 // along with layer meta data into provided byte buffer. 229 func (dl *diskLayer) journal(w io.Writer) error { 230 dl.lock.RLock() 231 defer dl.lock.RUnlock() 232 233 // Ensure the layer didn't get stale 234 if dl.stale { 235 return errSnapshotStale 236 } 237 // Step one, write the disk root into the journal. 238 if err := rlp.Encode(w, dl.root); err != nil { 239 return err 240 } 241 // Step two, write the corresponding state id into the journal 242 if err := rlp.Encode(w, dl.id); err != nil { 243 return err 244 } 245 // Step three, write the accumulated trie nodes into the journal 246 if err := dl.buffer.nodes.encode(w); err != nil { 247 return err 248 } 249 // Step four, write the accumulated flat states into the journal 250 if err := dl.buffer.states.encode(w); err != nil { 251 return err 252 } 253 log.Debug("Journaled pathdb disk layer", "root", dl.root) 254 return nil 255 } 256 257 // journal implements the layer interface, writing the memory layer contents 258 // into a buffer to be stored in the database as the layer journal. 259 func (dl *diffLayer) journal(w io.Writer) error { 260 dl.lock.RLock() 261 defer dl.lock.RUnlock() 262 263 // journal the parent first 264 if err := dl.parent.journal(w); err != nil { 265 return err 266 } 267 // Everything below was journaled, persist this layer too 268 if err := rlp.Encode(w, dl.root); err != nil { 269 return err 270 } 271 if err := rlp.Encode(w, dl.block); err != nil { 272 return err 273 } 274 // Write the accumulated trie nodes into buffer 275 if err := dl.nodes.encode(w); err != nil { 276 return err 277 } 278 // Write the associated flat state set into buffer 279 if err := dl.states.encode(w); err != nil { 280 return err 281 } 282 log.Debug("Journaled pathdb diff layer", "root", dl.root, "parent", dl.parent.rootHash(), "id", dl.stateID(), "block", dl.block) 283 return nil 284 } 285 286 // Journal commits an entire diff hierarchy to disk into a single journal entry. 287 // This is meant to be used during shutdown to persist the layer without 288 // flattening everything down (bad for reorgs). And this function will mark the 289 // database as read-only to prevent all following mutation to disk. 290 // 291 // The supplied root must be a valid trie hash value. 292 func (db *Database) Journal(root common.Hash) error { 293 // Retrieve the head layer to journal from. 294 l := db.tree.get(root) 295 if l == nil { 296 return fmt.Errorf("triedb layer [%#x] missing", root) 297 } 298 disk := db.tree.bottom() 299 if l, ok := l.(*diffLayer); ok { 300 log.Info("Persisting dirty state to disk", "head", l.block, "root", root, "layers", l.id-disk.id+disk.buffer.layers) 301 } else { // disk layer only on noop runs (likely) or deep reorgs (unlikely) 302 log.Info("Persisting dirty state to disk", "root", root, "layers", disk.buffer.layers) 303 } 304 // Block until the background flushing is finished and terminate 305 // the potential active state generator. 306 if err := disk.terminate(); err != nil { 307 return err 308 } 309 start := time.Now() 310 311 // Run the journaling 312 db.lock.Lock() 313 defer db.lock.Unlock() 314 315 // Short circuit if the database is in read only mode. 316 if db.readOnly { 317 return errDatabaseReadOnly 318 } 319 // Firstly write out the metadata of journal 320 journal := new(bytes.Buffer) 321 if err := rlp.Encode(journal, journalVersion); err != nil { 322 return err 323 } 324 // Secondly write out the state root in disk, ensure all layers 325 // on top are continuous with disk. 326 diskRoot, err := db.hasher(rawdb.ReadAccountTrieNode(db.diskdb, nil)) 327 if err != nil { 328 return err 329 } 330 if err := rlp.Encode(journal, diskRoot); err != nil { 331 return err 332 } 333 // Finally write out the journal of each layer in reverse order. 334 if err := l.journal(journal); err != nil { 335 return err 336 } 337 // Store the journal into the database and return 338 rawdb.WriteTrieJournal(db.diskdb, journal.Bytes()) 339 340 // Set the db in read only mode to reject all following mutations 341 db.readOnly = true 342 log.Info("Persisted dirty state to disk", "size", common.StorageSize(journal.Len()), "elapsed", common.PrettyDuration(time.Since(start))) 343 return nil 344 }