github.com/theQRL/go-zond@v0.1.1/trie/triedb/pathdb/database.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 "errors" 21 "fmt" 22 "io" 23 "sync" 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/zonddb" 30 "github.com/theQRL/go-zond/log" 31 "github.com/theQRL/go-zond/params" 32 "github.com/theQRL/go-zond/trie/trienode" 33 "github.com/theQRL/go-zond/trie/triestate" 34 ) 35 36 const ( 37 // maxDiffLayers is the maximum diff layers allowed in the layer tree. 38 maxDiffLayers = 128 39 40 // defaultCleanSize is the default memory allowance of clean cache. 41 defaultCleanSize = 16 * 1024 * 1024 42 43 // maxBufferSize is the maximum memory allowance of node buffer. 44 // Too large nodebuffer will cause the system to pause for a long 45 // time when write happens. Also, the largest batch that pebble can 46 // support is 4GB, node will panic if batch size exceeds this limit. 47 maxBufferSize = 256 * 1024 * 1024 48 49 // DefaultBufferSize is the default memory allowance of node buffer 50 // that aggregates the writes from above until it's flushed into the 51 // disk. It's meant to be used once the initial sync is finished. 52 // Do not increase the buffer size arbitrarily, otherwise the system 53 // pause time will increase when the database writes happen. 54 DefaultBufferSize = 64 * 1024 * 1024 55 ) 56 57 // layer is the interface implemented by all state layers which includes some 58 // public methods and some additional methods for internal usage. 59 type layer interface { 60 // Node retrieves the trie node with the node info. An error will be returned 61 // if the read operation exits abnormally. For example, if the layer is already 62 // stale, or the associated state is regarded as corrupted. Notably, no error 63 // will be returned if the requested node is not found in database. 64 Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) 65 66 // rootHash returns the root hash for which this layer was made. 67 rootHash() common.Hash 68 69 // stateID returns the associated state id of layer. 70 stateID() uint64 71 72 // parentLayer returns the subsequent layer of it, or nil if the disk was reached. 73 parentLayer() layer 74 75 // update creates a new layer on top of the existing layer diff tree with 76 // the provided dirty trie nodes along with the state change set. 77 // 78 // Note, the maps are retained by the method to avoid copying everything. 79 update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer 80 81 // journal commits an entire diff hierarchy to disk into a single journal entry. 82 // This is meant to be used during shutdown to persist the layer without 83 // flattening everything down (bad for reorgs). 84 journal(w io.Writer) error 85 } 86 87 // Config contains the settings for database. 88 type Config struct { 89 StateHistory uint64 // Number of recent blocks to maintain state history for 90 CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes 91 DirtyCacheSize int // Maximum memory allowance (in bytes) for caching dirty nodes 92 ReadOnly bool // Flag whether the database is opened in read only mode. 93 } 94 95 // sanitize checks the provided user configurations and changes anything that's 96 // unreasonable or unworkable. 97 func (c *Config) sanitize() *Config { 98 conf := *c 99 if conf.DirtyCacheSize > maxBufferSize { 100 log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.DirtyCacheSize), "updated", common.StorageSize(maxBufferSize)) 101 conf.DirtyCacheSize = maxBufferSize 102 } 103 return &conf 104 } 105 106 // Defaults contains default settings for Ethereum mainnet. 107 var Defaults = &Config{ 108 StateHistory: params.FullImmutabilityThreshold, 109 CleanCacheSize: defaultCleanSize, 110 DirtyCacheSize: DefaultBufferSize, 111 } 112 113 // ReadOnly is the config in order to open database in read only mode. 114 var ReadOnly = &Config{ReadOnly: true} 115 116 // Database is a multiple-layered structure for maintaining in-memory trie nodes. 117 // It consists of one persistent base layer backed by a key-value store, on top 118 // of which arbitrarily many in-memory diff layers are stacked. The memory diffs 119 // can form a tree with branching, but the disk layer is singleton and common to 120 // all. If a reorg goes deeper than the disk layer, a batch of reverse diffs can 121 // be applied to rollback. The deepest reorg that can be handled depends on the 122 // amount of state histories tracked in the disk. 123 // 124 // At most one readable and writable database can be opened at the same time in 125 // the whole system which ensures that only one database writer can operate disk 126 // state. Unexpected open operations can cause the system to panic. 127 type Database struct { 128 // readOnly is the flag whether the mutation is allowed to be applied. 129 // It will be set automatically when the database is journaled during 130 // the shutdown to reject all following unexpected mutations. 131 readOnly bool // Indicator if database is opened in read only mode 132 bufferSize int // Memory allowance (in bytes) for caching dirty nodes 133 config *Config // Configuration for database 134 diskdb zonddb.Database // Persistent storage for matured trie nodes 135 tree *layerTree // The group for all known layers 136 freezer *rawdb.ResettableFreezer // Freezer for storing trie histories, nil possible in tests 137 lock sync.RWMutex // Lock to prevent mutations from happening at the same time 138 } 139 140 // New attempts to load an already existing layer from a persistent key-value 141 // store (with a number of memory layers from a journal). If the journal is not 142 // matched with the base persistent layer, all the recorded diff layers are discarded. 143 func New(diskdb zonddb.Database, config *Config) *Database { 144 if config == nil { 145 config = Defaults 146 } 147 config = config.sanitize() 148 149 db := &Database{ 150 readOnly: config.ReadOnly, 151 bufferSize: config.DirtyCacheSize, 152 config: config, 153 diskdb: diskdb, 154 } 155 // Construct the layer tree by resolving the in-disk singleton state 156 // and in-memory layer journal. 157 db.tree = newLayerTree(db.loadLayers()) 158 159 // Open the freezer for state history if the passed database contains an 160 // ancient store. Otherwise, all the relevant functionalities are disabled. 161 // 162 // Because the freezer can only be opened once at the same time, this 163 // mechanism also ensures that at most one **non-readOnly** database 164 // is opened at the same time to prevent accidental mutation. 165 if ancient, err := diskdb.AncientDatadir(); err == nil && ancient != "" && !db.readOnly { 166 freezer, err := rawdb.NewStateFreezer(ancient, false) 167 if err != nil { 168 log.Crit("Failed to open state history freezer", "err", err) 169 } 170 db.freezer = freezer 171 172 // Truncate the extra state histories above in freezer in case 173 // it's not aligned with the disk layer. 174 pruned, err := truncateFromHead(db.diskdb, freezer, db.tree.bottom().stateID()) 175 if err != nil { 176 log.Crit("Failed to truncate extra state histories", "err", err) 177 } 178 if pruned != 0 { 179 log.Warn("Truncated extra state histories", "number", pruned) 180 } 181 } 182 log.Warn("Path-based state scheme is an experimental feature") 183 return db 184 } 185 186 // Reader retrieves a layer belonging to the given state root. 187 func (db *Database) Reader(root common.Hash) (layer, error) { 188 l := db.tree.get(root) 189 if l == nil { 190 return nil, fmt.Errorf("state %#x is not available", root) 191 } 192 return l, nil 193 } 194 195 // Update adds a new layer into the tree, if that can be linked to an existing 196 // old parent. It is disallowed to insert a disk layer (the origin of all). Apart 197 // from that this function will flatten the extra diff layers at bottom into disk 198 // to only keep 128 diff layers in memory by default. 199 // 200 // The passed in maps(nodes, states) will be retained to avoid copying everything. 201 // Therefore, these maps must not be changed afterwards. 202 func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { 203 // Hold the lock to prevent concurrent mutations. 204 db.lock.Lock() 205 defer db.lock.Unlock() 206 207 // Short circuit if the database is in read only mode. 208 if db.readOnly { 209 return errSnapshotReadOnly 210 } 211 if err := db.tree.add(root, parentRoot, block, nodes, states); err != nil { 212 return err 213 } 214 // Keep 128 diff layers in the memory, persistent layer is 129th. 215 // - head layer is paired with HEAD state 216 // - head-1 layer is paired with HEAD-1 state 217 // - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state 218 // - head-128 layer(disk layer) is paired with HEAD-128 state 219 return db.tree.cap(root, maxDiffLayers) 220 } 221 222 // Commit traverses downwards the layer tree from a specified layer with the 223 // provided state root and all the layers below are flattened downwards. It 224 // can be used alone and mostly for test purposes. 225 func (db *Database) Commit(root common.Hash, report bool) error { 226 // Hold the lock to prevent concurrent mutations. 227 db.lock.Lock() 228 defer db.lock.Unlock() 229 230 // Short circuit if the database is in read only mode. 231 if db.readOnly { 232 return errSnapshotReadOnly 233 } 234 return db.tree.cap(root, 0) 235 } 236 237 // Reset rebuilds the database with the specified state as the base. 238 // 239 // - if target state is empty, clear the stored state and all layers on top 240 // - if target state is non-empty, ensure the stored state matches with it 241 // and clear all other layers on top. 242 func (db *Database) Reset(root common.Hash) error { 243 db.lock.Lock() 244 defer db.lock.Unlock() 245 246 // Short circuit if the database is in read only mode. 247 if db.readOnly { 248 return errSnapshotReadOnly 249 } 250 batch := db.diskdb.NewBatch() 251 root = types.TrieRootHash(root) 252 if root == types.EmptyRootHash { 253 // Empty state is requested as the target, nuke out 254 // the root node and leave all others as dangling. 255 rawdb.DeleteAccountTrieNode(batch, nil) 256 } else { 257 // Ensure the requested state is existent before any 258 // action is applied. 259 _, hash := rawdb.ReadAccountTrieNode(db.diskdb, nil) 260 if hash != root { 261 return fmt.Errorf("state is mismatched, local: %x, target: %x", hash, root) 262 } 263 } 264 // Mark the disk layer as stale before applying any mutation. 265 db.tree.bottom().markStale() 266 267 // Drop the stale state journal in persistent database and 268 // reset the persistent state id back to zero. 269 rawdb.DeleteTrieJournal(batch) 270 rawdb.WritePersistentStateID(batch, 0) 271 if err := batch.Write(); err != nil { 272 return err 273 } 274 // Clean up all state histories in freezer. Theoretically 275 // all root->id mappings should be removed as well. Since 276 // mappings can be huge and might take a while to clear 277 // them, just leave them in disk and wait for overwriting. 278 if db.freezer != nil { 279 if err := db.freezer.Reset(); err != nil { 280 return err 281 } 282 } 283 // Re-construct a new disk layer backed by persistent state 284 // with **empty clean cache and node buffer**. 285 dl := newDiskLayer(root, 0, db, nil, newNodeBuffer(db.bufferSize, nil, 0)) 286 db.tree.reset(dl) 287 log.Info("Rebuilt trie database", "root", root) 288 return nil 289 } 290 291 // Recover rollbacks the database to a specified historical point. 292 // The state is supported as the rollback destination only if it's 293 // canonical state and the corresponding trie histories are existent. 294 func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error { 295 db.lock.Lock() 296 defer db.lock.Unlock() 297 298 // Short circuit if rollback operation is not supported. 299 if db.readOnly || db.freezer == nil { 300 return errors.New("state rollback is non-supported") 301 } 302 // Short circuit if the target state is not recoverable. 303 root = types.TrieRootHash(root) 304 if !db.Recoverable(root) { 305 return errStateUnrecoverable 306 } 307 // Apply the state histories upon the disk layer in order. 308 var ( 309 start = time.Now() 310 dl = db.tree.bottom() 311 ) 312 for dl.rootHash() != root { 313 h, err := readHistory(db.freezer, dl.stateID()) 314 if err != nil { 315 return err 316 } 317 dl, err = dl.revert(h, loader) 318 if err != nil { 319 return err 320 } 321 // reset layer with newly created disk layer. It must be 322 // done after each revert operation, otherwise the new 323 // disk layer won't be accessible from outside. 324 db.tree.reset(dl) 325 } 326 rawdb.DeleteTrieJournal(db.diskdb) 327 _, err := truncateFromHead(db.diskdb, db.freezer, dl.stateID()) 328 if err != nil { 329 return err 330 } 331 log.Debug("Recovered state", "root", root, "elapsed", common.PrettyDuration(time.Since(start))) 332 return nil 333 } 334 335 // Recoverable returns the indicator if the specified state is recoverable. 336 func (db *Database) Recoverable(root common.Hash) bool { 337 // Ensure the requested state is a known state. 338 root = types.TrieRootHash(root) 339 id := rawdb.ReadStateID(db.diskdb, root) 340 if id == nil { 341 return false 342 } 343 // Recoverable state must below the disk layer. The recoverable 344 // state only refers the state that is currently not available, 345 // but can be restored by applying state history. 346 dl := db.tree.bottom() 347 if *id >= dl.stateID() { 348 return false 349 } 350 // Ensure the requested state is a canonical state and all state 351 // histories in range [id+1, disklayer.ID] are present and complete. 352 parent := root 353 return checkHistories(db.freezer, *id+1, dl.stateID()-*id, func(m *meta) error { 354 if m.parent != parent { 355 return errors.New("unexpected state history") 356 } 357 if len(m.incomplete) > 0 { 358 return errors.New("incomplete state history") 359 } 360 parent = m.root 361 return nil 362 }) == nil 363 } 364 365 // Close closes the trie database and the held freezer. 366 func (db *Database) Close() error { 367 db.lock.Lock() 368 defer db.lock.Unlock() 369 370 // Set the database to read-only mode to prevent all 371 // following mutations. 372 db.readOnly = true 373 374 // Release the memory held by clean cache. 375 db.tree.bottom().resetCache() 376 377 // Close the attached state history freezer. 378 if db.freezer == nil { 379 return nil 380 } 381 return db.freezer.Close() 382 } 383 384 // Size returns the current storage size of the memory cache in front of the 385 // persistent database layer. 386 func (db *Database) Size() (diffs common.StorageSize, nodes common.StorageSize) { 387 db.tree.forEach(func(layer layer) { 388 if diff, ok := layer.(*diffLayer); ok { 389 diffs += common.StorageSize(diff.memory) 390 } 391 if disk, ok := layer.(*diskLayer); ok { 392 nodes += disk.size() 393 } 394 }) 395 return diffs, nodes 396 } 397 398 // Initialized returns an indicator if the state data is already 399 // initialized in path-based scheme. 400 func (db *Database) Initialized(genesisRoot common.Hash) bool { 401 var inited bool 402 db.tree.forEach(func(layer layer) { 403 if layer.rootHash() != types.EmptyRootHash { 404 inited = true 405 } 406 }) 407 return inited 408 } 409 410 // SetBufferSize sets the node buffer size to the provided value(in bytes). 411 func (db *Database) SetBufferSize(size int) error { 412 db.lock.Lock() 413 defer db.lock.Unlock() 414 415 if size > maxBufferSize { 416 log.Info("Capped node buffer size", "provided", common.StorageSize(size), "adjusted", common.StorageSize(maxBufferSize)) 417 size = maxBufferSize 418 } 419 db.bufferSize = size 420 return db.tree.bottom().setBufferSize(db.bufferSize) 421 } 422 423 // Scheme returns the node scheme used in the database. 424 func (db *Database) Scheme() string { 425 return rawdb.PathScheme 426 }