github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/triedb/pathdb/disklayer.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 "fmt" 21 "sync" 22 23 "github.com/VictoriaMetrics/fastcache" 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/rawdb" 26 "github.com/ethereum/go-ethereum/crypto" 27 "github.com/ethereum/go-ethereum/log" 28 "github.com/ethereum/go-ethereum/trie/trienode" 29 "github.com/ethereum/go-ethereum/trie/triestate" 30 ) 31 32 // diskLayer is a low level persistent layer built on top of a key-value store. 33 type diskLayer struct { 34 root common.Hash // Immutable, root hash to which this layer was made for 35 id uint64 // Immutable, corresponding state id 36 db *Database // Path-based trie database 37 cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs 38 buffer *nodebuffer // Node buffer to aggregate writes 39 stale bool // Signals that the layer became stale (state progressed) 40 lock sync.RWMutex // Lock used to protect stale flag 41 } 42 43 // newDiskLayer creates a new disk layer based on the passing arguments. 44 func newDiskLayer(root common.Hash, id uint64, db *Database, cleans *fastcache.Cache, buffer *nodebuffer) *diskLayer { 45 // Initialize a clean cache if the memory allowance is not zero 46 // or reuse the provided cache if it is not nil (inherited from 47 // the original disk layer). 48 if cleans == nil && db.config.CleanCacheSize != 0 { 49 cleans = fastcache.New(db.config.CleanCacheSize) 50 } 51 return &diskLayer{ 52 root: root, 53 id: id, 54 db: db, 55 cleans: cleans, 56 buffer: buffer, 57 } 58 } 59 60 // rootHash implements the layer interface, returning root hash of corresponding state. 61 func (dl *diskLayer) rootHash() common.Hash { 62 return dl.root 63 } 64 65 // stateID implements the layer interface, returning the state id of disk layer. 66 func (dl *diskLayer) stateID() uint64 { 67 return dl.id 68 } 69 70 // parentLayer implements the layer interface, returning nil as there's no layer 71 // below the disk. 72 func (dl *diskLayer) parentLayer() layer { 73 return nil 74 } 75 76 // isStale return whether this layer has become stale (was flattened across) or if 77 // it's still live. 78 func (dl *diskLayer) isStale() bool { 79 dl.lock.RLock() 80 defer dl.lock.RUnlock() 81 82 return dl.stale 83 } 84 85 // markStale sets the stale flag as true. 86 func (dl *diskLayer) markStale() { 87 dl.lock.Lock() 88 defer dl.lock.Unlock() 89 90 if dl.stale { 91 panic("triedb disk layer is stale") // we've committed into the same base from two children, boom 92 } 93 dl.stale = true 94 } 95 96 // node implements the layer interface, retrieving the trie node with the 97 // provided node info. No error will be returned if the node is not found. 98 func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) { 99 dl.lock.RLock() 100 defer dl.lock.RUnlock() 101 102 if dl.stale { 103 return nil, common.Hash{}, nil, errSnapshotStale 104 } 105 // Try to retrieve the trie node from the not-yet-written 106 // node buffer first. Note the buffer is lock free since 107 // it's impossible to mutate the buffer before tagging the 108 // layer as stale. 109 n, found := dl.buffer.node(owner, path) 110 if found { 111 dirtyHitMeter.Mark(1) 112 dirtyReadMeter.Mark(int64(len(n.Blob))) 113 dirtyNodeHitDepthHist.Update(int64(depth)) 114 return n.Blob, n.Hash, &nodeLoc{loc: locDirtyCache, depth: depth}, nil 115 } 116 dirtyMissMeter.Mark(1) 117 118 // Try to retrieve the trie node from the clean memory cache 119 h := newHasher() 120 defer h.release() 121 122 key := cacheKey(owner, path) 123 if dl.cleans != nil { 124 if blob := dl.cleans.Get(nil, key); len(blob) > 0 { 125 cleanHitMeter.Mark(1) 126 cleanReadMeter.Mark(int64(len(blob))) 127 return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil 128 } 129 cleanMissMeter.Mark(1) 130 } 131 // Try to retrieve the trie node from the disk. 132 var blob []byte 133 if owner == (common.Hash{}) { 134 blob = rawdb.ReadAccountTrieNode(dl.db.diskdb, path) 135 } else { 136 blob = rawdb.ReadStorageTrieNode(dl.db.diskdb, owner, path) 137 } 138 if dl.cleans != nil && len(blob) > 0 { 139 dl.cleans.Set(key, blob) 140 cleanWriteMeter.Mark(int64(len(blob))) 141 } 142 143 return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil 144 } 145 146 // update implements the layer interface, returning a new diff layer on top 147 // with the given state set. 148 func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { 149 return newDiffLayer(dl, root, id, block, nodes, states) 150 } 151 152 // commit merges the given bottom-most diff layer into the node buffer 153 // and returns a newly constructed disk layer. Note the current disk 154 // layer must be tagged as stale first to prevent re-access. 155 func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { 156 dl.lock.Lock() 157 defer dl.lock.Unlock() 158 159 // Construct and store the state history first. If crash happens after storing 160 // the state history but without flushing the corresponding states(journal), 161 // the stored state history will be truncated from head in the next restart. 162 var ( 163 overflow bool 164 oldest uint64 165 ) 166 if dl.db.freezer != nil { 167 err := writeHistory(dl.db.freezer, bottom) 168 if err != nil { 169 return nil, err 170 } 171 // Determine if the persisted history object has exceeded the configured 172 // limitation, set the overflow as true if so. 173 tail, err := dl.db.freezer.Tail() 174 if err != nil { 175 return nil, err 176 } 177 limit := dl.db.config.StateHistory 178 if limit != 0 && bottom.stateID()-tail > limit { 179 overflow = true 180 oldest = bottom.stateID() - limit + 1 // track the id of history **after truncation** 181 } 182 } 183 // Mark the diskLayer as stale before applying any mutations on top. 184 dl.stale = true 185 186 // Store the root->id lookup afterwards. All stored lookups are identified 187 // by the **unique** state root. It's impossible that in the same chain 188 // blocks are not adjacent but have the same root. 189 if dl.id == 0 { 190 rawdb.WriteStateID(dl.db.diskdb, dl.root, 0) 191 } 192 rawdb.WriteStateID(dl.db.diskdb, bottom.rootHash(), bottom.stateID()) 193 194 // Construct a new disk layer by merging the nodes from the provided diff 195 // layer, and flush the content in disk layer if there are too many nodes 196 // cached. The clean cache is inherited from the original disk layer. 197 ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.cleans, dl.buffer.commit(bottom.nodes)) 198 199 // In a unique scenario where the ID of the oldest history object (after tail 200 // truncation) surpasses the persisted state ID, we take the necessary action 201 // of forcibly committing the cached dirty nodes to ensure that the persisted 202 // state ID remains higher. 203 if !force && rawdb.ReadPersistentStateID(dl.db.diskdb) < oldest { 204 force = true 205 } 206 if err := ndl.buffer.flush(ndl.db.diskdb, ndl.cleans, ndl.id, force); err != nil { 207 return nil, err 208 } 209 // To remove outdated history objects from the end, we set the 'tail' parameter 210 // to 'oldest-1' due to the offset between the freezer index and the history ID. 211 if overflow { 212 pruned, err := truncateFromTail(ndl.db.diskdb, ndl.db.freezer, oldest-1) 213 if err != nil { 214 return nil, err 215 } 216 log.Debug("Pruned state history", "items", pruned, "tailid", oldest) 217 } 218 return ndl, nil 219 } 220 221 // revert applies the given state history and return a reverted disk layer. 222 func (dl *diskLayer) revert(h *history, loader triestate.TrieLoader) (*diskLayer, error) { 223 if h.meta.root != dl.rootHash() { 224 return nil, errUnexpectedHistory 225 } 226 if dl.id == 0 { 227 return nil, fmt.Errorf("%w: zero state id", errStateUnrecoverable) 228 } 229 // Apply the reverse state changes upon the current state. This must 230 // be done before holding the lock in order to access state in "this" 231 // layer. 232 nodes, err := triestate.Apply(h.meta.parent, h.meta.root, h.accounts, h.storages, loader) 233 if err != nil { 234 return nil, err 235 } 236 // Mark the diskLayer as stale before applying any mutations on top. 237 dl.lock.Lock() 238 defer dl.lock.Unlock() 239 240 dl.stale = true 241 242 // State change may be applied to node buffer, or the persistent 243 // state, depends on if node buffer is empty or not. If the node 244 // buffer is not empty, it means that the state transition that 245 // needs to be reverted is not yet flushed and cached in node 246 // buffer, otherwise, manipulate persistent state directly. 247 if !dl.buffer.empty() { 248 err := dl.buffer.revert(dl.db.diskdb, nodes) 249 if err != nil { 250 return nil, err 251 } 252 } else { 253 batch := dl.db.diskdb.NewBatch() 254 writeNodes(batch, nodes, dl.cleans) 255 rawdb.WritePersistentStateID(batch, dl.id-1) 256 if err := batch.Write(); err != nil { 257 log.Crit("Failed to write states", "err", err) 258 } 259 } 260 return newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.cleans, dl.buffer), nil 261 } 262 263 // setBufferSize sets the node buffer size to the provided value. 264 func (dl *diskLayer) setBufferSize(size int) error { 265 dl.lock.RLock() 266 defer dl.lock.RUnlock() 267 268 if dl.stale { 269 return errSnapshotStale 270 } 271 return dl.buffer.setSize(size, dl.db.diskdb, dl.cleans, dl.id) 272 } 273 274 // size returns the approximate size of cached nodes in the disk layer. 275 func (dl *diskLayer) size() common.StorageSize { 276 dl.lock.RLock() 277 defer dl.lock.RUnlock() 278 279 if dl.stale { 280 return 0 281 } 282 return common.StorageSize(dl.buffer.size) 283 } 284 285 // resetCache releases the memory held by clean cache to prevent memory leak. 286 func (dl *diskLayer) resetCache() { 287 dl.lock.RLock() 288 defer dl.lock.RUnlock() 289 290 // Stale disk layer loses the ownership of clean cache. 291 if dl.stale { 292 return 293 } 294 if dl.cleans != nil { 295 dl.cleans.Reset() 296 } 297 } 298 299 // hasher is used to compute the sha256 hash of the provided data. 300 type hasher struct{ sha crypto.KeccakState } 301 302 var hasherPool = sync.Pool{ 303 New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} }, 304 } 305 306 func newHasher() *hasher { 307 return hasherPool.Get().(*hasher) 308 } 309 310 func (h *hasher) hash(data []byte) common.Hash { 311 return crypto.HashData(h.sha, data) 312 } 313 314 func (h *hasher) release() { 315 hasherPool.Put(h) 316 }