github.com/calmw/ethereum@v0.1.1/trie/database_wrap.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 trie 18 19 import ( 20 "errors" 21 "runtime" 22 "time" 23 24 "github.com/VictoriaMetrics/fastcache" 25 "github.com/calmw/ethereum/common" 26 "github.com/calmw/ethereum/ethdb" 27 "github.com/calmw/ethereum/log" 28 "github.com/calmw/ethereum/trie/triedb/hashdb" 29 "github.com/calmw/ethereum/trie/trienode" 30 ) 31 32 // Config defines all necessary options for database. 33 type Config struct { 34 Cache int // Memory allowance (MB) to use for caching trie nodes in memory 35 Journal string // Journal of clean cache to survive node restarts 36 Preimages bool // Flag whether the preimage of trie key is recorded 37 } 38 39 // backend defines the methods needed to access/update trie nodes in different 40 // state scheme. 41 type backend interface { 42 // Scheme returns the identifier of used storage scheme. 43 Scheme() string 44 45 // Initialized returns an indicator if the state data is already initialized 46 // according to the state scheme. 47 Initialized(genesisRoot common.Hash) bool 48 49 // Size returns the current storage size of the memory cache in front of the 50 // persistent database layer. 51 Size() common.StorageSize 52 53 // Update performs a state transition by committing dirty nodes contained 54 // in the given set in order to update state from the specified parent to 55 // the specified root. 56 Update(root common.Hash, parent common.Hash, nodes *trienode.MergedNodeSet) error 57 58 // Commit writes all relevant trie nodes belonging to the specified state 59 // to disk. Report specifies whether logs will be displayed in info level. 60 Commit(root common.Hash, report bool) error 61 62 // Close closes the trie database backend and releases all held resources. 63 Close() error 64 } 65 66 // Database is the wrapper of the underlying backend which is shared by different 67 // types of node backend as an entrypoint. It's responsible for all interactions 68 // relevant with trie nodes and node preimages. 69 type Database struct { 70 config *Config // Configuration for trie database 71 diskdb ethdb.Database // Persistent database to store the snapshot 72 cleans *fastcache.Cache // Megabytes permitted using for read caches 73 preimages *preimageStore // The store for caching preimages 74 backend backend // The backend for managing trie nodes 75 } 76 77 // prepare initializes the database with provided configs, but the 78 // database backend is still left as nil. 79 func prepare(diskdb ethdb.Database, config *Config) *Database { 80 var cleans *fastcache.Cache 81 if config != nil && config.Cache > 0 { 82 if config.Journal == "" { 83 cleans = fastcache.New(config.Cache * 1024 * 1024) 84 } else { 85 cleans = fastcache.LoadFromFileOrNew(config.Journal, config.Cache*1024*1024) 86 } 87 } 88 var preimages *preimageStore 89 if config != nil && config.Preimages { 90 preimages = newPreimageStore(diskdb) 91 } 92 return &Database{ 93 config: config, 94 diskdb: diskdb, 95 cleans: cleans, 96 preimages: preimages, 97 } 98 } 99 100 // NewDatabase initializes the trie database with default settings, namely 101 // the legacy hash-based scheme is used by default. 102 func NewDatabase(diskdb ethdb.Database) *Database { 103 return NewDatabaseWithConfig(diskdb, nil) 104 } 105 106 // NewDatabaseWithConfig initializes the trie database with provided configs. 107 // The path-based scheme is not activated yet, always initialized with legacy 108 // hash-based scheme by default. 109 func NewDatabaseWithConfig(diskdb ethdb.Database, config *Config) *Database { 110 db := prepare(diskdb, config) 111 db.backend = hashdb.New(diskdb, db.cleans, mptResolver{}) 112 return db 113 } 114 115 // Reader returns a reader for accessing all trie nodes with provided state root. 116 // Nil is returned in case the state is not available. 117 func (db *Database) Reader(blockRoot common.Hash) Reader { 118 return db.backend.(*hashdb.Database).Reader(blockRoot) 119 } 120 121 // Update performs a state transition by committing dirty nodes contained in the 122 // given set in order to update state from the specified parent to the specified 123 // root. The held pre-images accumulated up to this point will be flushed in case 124 // the size exceeds the threshold. 125 func (db *Database) Update(root common.Hash, parent common.Hash, nodes *trienode.MergedNodeSet) error { 126 if db.preimages != nil { 127 db.preimages.commit(false) 128 } 129 return db.backend.Update(root, parent, nodes) 130 } 131 132 // Commit iterates over all the children of a particular node, writes them out 133 // to disk. As a side effect, all pre-images accumulated up to this point are 134 // also written. 135 func (db *Database) Commit(root common.Hash, report bool) error { 136 if db.preimages != nil { 137 db.preimages.commit(true) 138 } 139 return db.backend.Commit(root, report) 140 } 141 142 // Size returns the storage size of dirty trie nodes in front of the persistent 143 // database and the size of cached preimages. 144 func (db *Database) Size() (common.StorageSize, common.StorageSize) { 145 var ( 146 storages common.StorageSize 147 preimages common.StorageSize 148 ) 149 storages = db.backend.Size() 150 if db.preimages != nil { 151 preimages = db.preimages.size() 152 } 153 return storages, preimages 154 } 155 156 // Initialized returns an indicator if the state data is already initialized 157 // according to the state scheme. 158 func (db *Database) Initialized(genesisRoot common.Hash) bool { 159 return db.backend.Initialized(genesisRoot) 160 } 161 162 // Scheme returns the node scheme used in the database. 163 func (db *Database) Scheme() string { 164 return db.backend.Scheme() 165 } 166 167 // Close flushes the dangling preimages to disk and closes the trie database. 168 // It is meant to be called when closing the blockchain object, so that all 169 // resources held can be released correctly. 170 func (db *Database) Close() error { 171 if db.preimages != nil { 172 db.preimages.commit(true) 173 } 174 return db.backend.Close() 175 } 176 177 // saveCache saves clean state cache to given directory path 178 // using specified CPU cores. 179 func (db *Database) saveCache(dir string, threads int) error { 180 if db.cleans == nil { 181 return nil 182 } 183 log.Info("Writing clean trie cache to disk", "path", dir, "threads", threads) 184 185 start := time.Now() 186 err := db.cleans.SaveToFileConcurrent(dir, threads) 187 if err != nil { 188 log.Error("Failed to persist clean trie cache", "error", err) 189 return err 190 } 191 log.Info("Persisted the clean trie cache", "path", dir, "elapsed", common.PrettyDuration(time.Since(start))) 192 return nil 193 } 194 195 // SaveCache atomically saves fast cache data to the given dir using all 196 // available CPU cores. 197 func (db *Database) SaveCache(dir string) error { 198 return db.saveCache(dir, runtime.GOMAXPROCS(0)) 199 } 200 201 // SaveCachePeriodically atomically saves fast cache data to the given dir with 202 // the specified interval. All dump operation will only use a single CPU core. 203 func (db *Database) SaveCachePeriodically(dir string, interval time.Duration, stopCh <-chan struct{}) { 204 ticker := time.NewTicker(interval) 205 defer ticker.Stop() 206 207 for { 208 select { 209 case <-ticker.C: 210 db.saveCache(dir, 1) 211 case <-stopCh: 212 return 213 } 214 } 215 } 216 217 // Cap iteratively flushes old but still referenced trie nodes until the total 218 // memory usage goes below the given threshold. The held pre-images accumulated 219 // up to this point will be flushed in case the size exceeds the threshold. 220 // 221 // It's only supported by hash-based database and will return an error for others. 222 func (db *Database) Cap(limit common.StorageSize) error { 223 hdb, ok := db.backend.(*hashdb.Database) 224 if !ok { 225 return errors.New("not supported") 226 } 227 if db.preimages != nil { 228 db.preimages.commit(false) 229 } 230 return hdb.Cap(limit) 231 } 232 233 // Reference adds a new reference from a parent node to a child node. This function 234 // is used to add reference between internal trie node and external node(e.g. storage 235 // trie root), all internal trie nodes are referenced together by database itself. 236 // 237 // It's only supported by hash-based database and will return an error for others. 238 func (db *Database) Reference(root common.Hash, parent common.Hash) error { 239 hdb, ok := db.backend.(*hashdb.Database) 240 if !ok { 241 return errors.New("not supported") 242 } 243 hdb.Reference(root, parent) 244 return nil 245 } 246 247 // Dereference removes an existing reference from a root node. It's only 248 // supported by hash-based database and will return an error for others. 249 func (db *Database) Dereference(root common.Hash) error { 250 hdb, ok := db.backend.(*hashdb.Database) 251 if !ok { 252 return errors.New("not supported") 253 } 254 hdb.Dereference(root) 255 return nil 256 } 257 258 // Node retrieves the rlp-encoded node blob with provided node hash. It's 259 // only supported by hash-based database and will return an error for others. 260 // Note, this function should be deprecated once ETH66 is deprecated. 261 func (db *Database) Node(hash common.Hash) ([]byte, error) { 262 hdb, ok := db.backend.(*hashdb.Database) 263 if !ok { 264 return nil, errors.New("not supported") 265 } 266 return hdb.Node(hash) 267 }