github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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/crypto" 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/rlp" 32 "github.com/ethereum/go-ethereum/trie/trienode" 33 "github.com/ethereum/go-ethereum/trie/triestate" 34 ) 35 36 var ( 37 errMissJournal = errors.New("journal not found") 38 errMissVersion = errors.New("version not found") 39 errUnexpectedVersion = errors.New("unexpected journal version") 40 errMissDiskRoot = errors.New("disk layer root not found") 41 errUnmatchedJournal = errors.New("unmatched journal") 42 ) 43 44 // journalVersion ensures that an incompatible journal is detected and discarded. 45 // 46 // Changelog: 47 // 48 // - Version 0: initial version 49 // - Version 1: storage.Incomplete field is removed 50 const journalVersion uint64 = 1 51 52 // journalNode represents a trie node persisted in the journal. 53 type journalNode struct { 54 Path []byte // Path of the node in the trie 55 Blob []byte // RLP-encoded trie node blob, nil means the node is deleted 56 } 57 58 // journalNodes represents a list trie nodes belong to a single account 59 // or the main account trie. 60 type journalNodes struct { 61 Owner common.Hash 62 Nodes []journalNode 63 } 64 65 // journalAccounts represents a list accounts belong to the layer. 66 type journalAccounts struct { 67 Addresses []common.Address 68 Accounts [][]byte 69 } 70 71 // journalStorage represents a list of storage slots belong to an account. 72 type journalStorage struct { 73 Account common.Address 74 Hashes []common.Hash 75 Slots [][]byte 76 } 77 78 // loadJournal tries to parse the layer journal from the disk. 79 func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { 80 journal := rawdb.ReadTrieJournal(db.diskdb) 81 if len(journal) == 0 { 82 return nil, errMissJournal 83 } 84 r := rlp.NewStream(bytes.NewReader(journal), 0) 85 86 // Firstly, resolve the first element as the journal version 87 version, err := r.Uint64() 88 if err != nil { 89 return nil, errMissVersion 90 } 91 if version != journalVersion { 92 return nil, fmt.Errorf("%w want %d got %d", errUnexpectedVersion, journalVersion, version) 93 } 94 // Secondly, resolve the disk layer root, ensure it's continuous 95 // with disk layer. Note now we can ensure it's the layer journal 96 // correct version, so we expect everything can be resolved properly. 97 var root common.Hash 98 if err := r.Decode(&root); err != nil { 99 return nil, errMissDiskRoot 100 } 101 // The journal is not matched with persistent state, discard them. 102 // It can happen that geth crashes without persisting the journal. 103 if !bytes.Equal(root.Bytes(), diskRoot.Bytes()) { 104 return nil, fmt.Errorf("%w want %x got %x", errUnmatchedJournal, root, diskRoot) 105 } 106 // Load the disk layer from the journal 107 base, err := db.loadDiskLayer(r) 108 if err != nil { 109 return nil, err 110 } 111 // Load all the diff layers from the journal 112 head, err := db.loadDiffLayer(base, r) 113 if err != nil { 114 return nil, err 115 } 116 log.Debug("Loaded layer journal", "diskroot", diskRoot, "diffhead", head.rootHash()) 117 return head, nil 118 } 119 120 // loadLayers loads a pre-existing state layer backed by a key-value store. 121 func (db *Database) loadLayers() layer { 122 // Retrieve the root node of persistent state. 123 var root = types.EmptyRootHash 124 if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 { 125 root = crypto.Keccak256Hash(blob) 126 } 127 // Load the layers by resolving the journal 128 head, err := db.loadJournal(root) 129 if err == nil { 130 return head 131 } 132 // journal is not matched(or missing) with the persistent state, discard 133 // it. Display log for discarding journal, but try to avoid showing 134 // useless information when the db is created from scratch. 135 if !(root == types.EmptyRootHash && errors.Is(err, errMissJournal)) { 136 log.Info("Failed to load journal, discard it", "err", err) 137 } 138 // Return single layer with persistent state. 139 return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newNodeBuffer(db.bufferSize, nil, 0)) 140 } 141 142 // loadDiskLayer reads the binary blob from the layer journal, reconstructing 143 // a new disk layer on it. 144 func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { 145 // Resolve disk layer root 146 var root common.Hash 147 if err := r.Decode(&root); err != nil { 148 return nil, fmt.Errorf("load disk root: %v", err) 149 } 150 // Resolve the state id of disk layer, it can be different 151 // with the persistent id tracked in disk, the id distance 152 // is the number of transitions aggregated in disk layer. 153 var id uint64 154 if err := r.Decode(&id); err != nil { 155 return nil, fmt.Errorf("load state id: %v", err) 156 } 157 stored := rawdb.ReadPersistentStateID(db.diskdb) 158 if stored > id { 159 return nil, fmt.Errorf("invalid state id: stored %d resolved %d", stored, id) 160 } 161 // Resolve nodes cached in node buffer 162 var encoded []journalNodes 163 if err := r.Decode(&encoded); err != nil { 164 return nil, fmt.Errorf("load disk nodes: %v", err) 165 } 166 nodes := make(map[common.Hash]map[string]*trienode.Node) 167 for _, entry := range encoded { 168 subset := make(map[string]*trienode.Node) 169 for _, n := range entry.Nodes { 170 if len(n.Blob) > 0 { 171 subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) 172 } else { 173 subset[string(n.Path)] = trienode.NewDeleted() 174 } 175 } 176 nodes[entry.Owner] = subset 177 } 178 // Calculate the internal state transitions by id difference. 179 base := newDiskLayer(root, id, db, nil, newNodeBuffer(db.bufferSize, nodes, id-stored)) 180 return base, nil 181 } 182 183 // loadDiffLayer reads the next sections of a layer journal, reconstructing a new 184 // diff and verifying that it can be linked to the requested parent. 185 func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) { 186 // Read the next diff journal entry 187 var root common.Hash 188 if err := r.Decode(&root); err != nil { 189 // The first read may fail with EOF, marking the end of the journal 190 if err == io.EOF { 191 return parent, nil 192 } 193 return nil, fmt.Errorf("load diff root: %v", err) 194 } 195 var block uint64 196 if err := r.Decode(&block); err != nil { 197 return nil, fmt.Errorf("load block number: %v", err) 198 } 199 // Read in-memory trie nodes from journal 200 var encoded []journalNodes 201 if err := r.Decode(&encoded); err != nil { 202 return nil, fmt.Errorf("load diff nodes: %v", err) 203 } 204 nodes := make(map[common.Hash]map[string]*trienode.Node) 205 for _, entry := range encoded { 206 subset := make(map[string]*trienode.Node) 207 for _, n := range entry.Nodes { 208 if len(n.Blob) > 0 { 209 subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) 210 } else { 211 subset[string(n.Path)] = trienode.NewDeleted() 212 } 213 } 214 nodes[entry.Owner] = subset 215 } 216 // Read state changes from journal 217 var ( 218 jaccounts journalAccounts 219 jstorages []journalStorage 220 accounts = make(map[common.Address][]byte) 221 storages = make(map[common.Address]map[common.Hash][]byte) 222 ) 223 if err := r.Decode(&jaccounts); err != nil { 224 return nil, fmt.Errorf("load diff accounts: %v", err) 225 } 226 for i, addr := range jaccounts.Addresses { 227 accounts[addr] = jaccounts.Accounts[i] 228 } 229 if err := r.Decode(&jstorages); err != nil { 230 return nil, fmt.Errorf("load diff storages: %v", err) 231 } 232 for _, entry := range jstorages { 233 set := make(map[common.Hash][]byte) 234 for i, h := range entry.Hashes { 235 if len(entry.Slots[i]) > 0 { 236 set[h] = entry.Slots[i] 237 } else { 238 set[h] = nil 239 } 240 } 241 storages[entry.Account] = set 242 } 243 return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, nodes, triestate.New(accounts, storages)), r) 244 } 245 246 // journal implements the layer interface, marshaling the un-flushed trie nodes 247 // along with layer meta data into provided byte buffer. 248 func (dl *diskLayer) journal(w io.Writer) error { 249 dl.lock.RLock() 250 defer dl.lock.RUnlock() 251 252 // Ensure the layer didn't get stale 253 if dl.stale { 254 return errSnapshotStale 255 } 256 // Step one, write the disk root into the journal. 257 if err := rlp.Encode(w, dl.root); err != nil { 258 return err 259 } 260 // Step two, write the corresponding state id into the journal 261 if err := rlp.Encode(w, dl.id); err != nil { 262 return err 263 } 264 // Step three, write all unwritten nodes into the journal 265 nodes := make([]journalNodes, 0, len(dl.buffer.nodes)) 266 for owner, subset := range dl.buffer.nodes { 267 entry := journalNodes{Owner: owner} 268 for path, node := range subset { 269 entry.Nodes = append(entry.Nodes, journalNode{Path: []byte(path), Blob: node.Blob}) 270 } 271 nodes = append(nodes, entry) 272 } 273 if err := rlp.Encode(w, nodes); err != nil { 274 return err 275 } 276 log.Debug("Journaled pathdb disk layer", "root", dl.root, "nodes", len(dl.buffer.nodes)) 277 return nil 278 } 279 280 // journal implements the layer interface, writing the memory layer contents 281 // into a buffer to be stored in the database as the layer journal. 282 func (dl *diffLayer) journal(w io.Writer) error { 283 dl.lock.RLock() 284 defer dl.lock.RUnlock() 285 286 // journal the parent first 287 if err := dl.parent.journal(w); err != nil { 288 return err 289 } 290 // Everything below was journaled, persist this layer too 291 if err := rlp.Encode(w, dl.root); err != nil { 292 return err 293 } 294 if err := rlp.Encode(w, dl.block); err != nil { 295 return err 296 } 297 // Write the accumulated trie nodes into buffer 298 nodes := make([]journalNodes, 0, len(dl.nodes)) 299 for owner, subset := range dl.nodes { 300 entry := journalNodes{Owner: owner} 301 for path, node := range subset { 302 entry.Nodes = append(entry.Nodes, journalNode{Path: []byte(path), Blob: node.Blob}) 303 } 304 nodes = append(nodes, entry) 305 } 306 if err := rlp.Encode(w, nodes); err != nil { 307 return err 308 } 309 // Write the accumulated state changes into buffer 310 var jacct journalAccounts 311 for addr, account := range dl.states.Accounts { 312 jacct.Addresses = append(jacct.Addresses, addr) 313 jacct.Accounts = append(jacct.Accounts, account) 314 } 315 if err := rlp.Encode(w, jacct); err != nil { 316 return err 317 } 318 storage := make([]journalStorage, 0, len(dl.states.Storages)) 319 for addr, slots := range dl.states.Storages { 320 entry := journalStorage{Account: addr} 321 for slotHash, slot := range slots { 322 entry.Hashes = append(entry.Hashes, slotHash) 323 entry.Slots = append(entry.Slots, slot) 324 } 325 storage = append(storage, entry) 326 } 327 if err := rlp.Encode(w, storage); err != nil { 328 return err 329 } 330 log.Debug("Journaled pathdb diff layer", "root", dl.root, "parent", dl.parent.rootHash(), "id", dl.stateID(), "block", dl.block, "nodes", len(dl.nodes)) 331 return nil 332 } 333 334 // Journal commits an entire diff hierarchy to disk into a single journal entry. 335 // This is meant to be used during shutdown to persist the layer without 336 // flattening everything down (bad for reorgs). And this function will mark the 337 // database as read-only to prevent all following mutation to disk. 338 func (db *Database) Journal(root common.Hash) error { 339 // Retrieve the head layer to journal from. 340 l := db.tree.get(root) 341 if l == nil { 342 return fmt.Errorf("triedb layer [%#x] missing", root) 343 } 344 disk := db.tree.bottom() 345 if l, ok := l.(*diffLayer); ok { 346 log.Info("Persisting dirty state to disk", "head", l.block, "root", root, "layers", l.id-disk.id+disk.buffer.layers) 347 } else { // disk layer only on noop runs (likely) or deep reorgs (unlikely) 348 log.Info("Persisting dirty state to disk", "root", root, "layers", disk.buffer.layers) 349 } 350 start := time.Now() 351 352 // Run the journaling 353 db.lock.Lock() 354 defer db.lock.Unlock() 355 356 // Short circuit if the database is in read only mode. 357 if db.readOnly { 358 return errDatabaseReadOnly 359 } 360 // Firstly write out the metadata of journal 361 journal := new(bytes.Buffer) 362 if err := rlp.Encode(journal, journalVersion); err != nil { 363 return err 364 } 365 // Secondly write out the state root in disk, ensure all layers 366 // on top are continuous with disk. 367 diskRoot := types.EmptyRootHash 368 if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 { 369 diskRoot = crypto.Keccak256Hash(blob) 370 } 371 if err := rlp.Encode(journal, diskRoot); err != nil { 372 return err 373 } 374 // Finally write out the journal of each layer in reverse order. 375 if err := l.journal(journal); err != nil { 376 return err 377 } 378 // Store the journal into the database and return 379 rawdb.WriteTrieJournal(db.diskdb, journal.Bytes()) 380 381 // Set the db in read only mode to reject all following mutations 382 db.readOnly = true 383 log.Info("Persisted dirty state to disk", "size", common.StorageSize(journal.Len()), "elapsed", common.PrettyDuration(time.Since(start))) 384 return nil 385 }