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