github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/core/state/snapshot/disklayer.go (about) 1 // Copyright 2019 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 snapshot 18 19 import ( 20 "bytes" 21 "sync" 22 23 "github.com/VictoriaMetrics/fastcache" 24 "github.com/tirogen/go-ethereum/common" 25 "github.com/tirogen/go-ethereum/core/rawdb" 26 "github.com/tirogen/go-ethereum/ethdb" 27 "github.com/tirogen/go-ethereum/rlp" 28 "github.com/tirogen/go-ethereum/trie" 29 ) 30 31 // diskLayer is a low level persistent snapshot built on top of a key-value store. 32 type diskLayer struct { 33 diskdb ethdb.KeyValueStore // Key-value store containing the base snapshot 34 triedb *trie.Database // Trie node cache for reconstruction purposes 35 cache *fastcache.Cache // Cache to avoid hitting the disk for direct access 36 37 root common.Hash // Root hash of the base snapshot 38 stale bool // Signals that the layer became stale (state progressed) 39 40 genMarker []byte // Marker for the state that's indexed during initial layer generation 41 genPending chan struct{} // Notification channel when generation is done (test synchronicity) 42 genAbort chan chan *generatorStats // Notification channel to abort generating the snapshot in this layer 43 44 lock sync.RWMutex 45 } 46 47 // Root returns root hash for which this snapshot was made. 48 func (dl *diskLayer) Root() common.Hash { 49 return dl.root 50 } 51 52 // Parent always returns nil as there's no layer below the disk. 53 func (dl *diskLayer) Parent() snapshot { 54 return nil 55 } 56 57 // Stale return whether this layer has become stale (was flattened across) or if 58 // it's still live. 59 func (dl *diskLayer) Stale() bool { 60 dl.lock.RLock() 61 defer dl.lock.RUnlock() 62 63 return dl.stale 64 } 65 66 // Account directly retrieves the account associated with a particular hash in 67 // the snapshot slim data format. 68 func (dl *diskLayer) Account(hash common.Hash) (*Account, error) { 69 data, err := dl.AccountRLP(hash) 70 if err != nil { 71 return nil, err 72 } 73 if len(data) == 0 { // can be both nil and []byte{} 74 return nil, nil 75 } 76 account := new(Account) 77 if err := rlp.DecodeBytes(data, account); err != nil { 78 panic(err) 79 } 80 return account, nil 81 } 82 83 // AccountRLP directly retrieves the account RLP associated with a particular 84 // hash in the snapshot slim data format. 85 func (dl *diskLayer) AccountRLP(hash common.Hash) ([]byte, error) { 86 dl.lock.RLock() 87 defer dl.lock.RUnlock() 88 89 // If the layer was flattened into, consider it invalid (any live reference to 90 // the original should be marked as unusable). 91 if dl.stale { 92 return nil, ErrSnapshotStale 93 } 94 // If the layer is being generated, ensure the requested hash has already been 95 // covered by the generator. 96 if dl.genMarker != nil && bytes.Compare(hash[:], dl.genMarker) > 0 { 97 return nil, ErrNotCoveredYet 98 } 99 // If we're in the disk layer, all diff layers missed 100 snapshotDirtyAccountMissMeter.Mark(1) 101 102 // Try to retrieve the account from the memory cache 103 if blob, found := dl.cache.HasGet(nil, hash[:]); found { 104 snapshotCleanAccountHitMeter.Mark(1) 105 snapshotCleanAccountReadMeter.Mark(int64(len(blob))) 106 return blob, nil 107 } 108 // Cache doesn't contain account, pull from disk and cache for later 109 blob := rawdb.ReadAccountSnapshot(dl.diskdb, hash) 110 dl.cache.Set(hash[:], blob) 111 112 snapshotCleanAccountMissMeter.Mark(1) 113 if n := len(blob); n > 0 { 114 snapshotCleanAccountWriteMeter.Mark(int64(n)) 115 } else { 116 snapshotCleanAccountInexMeter.Mark(1) 117 } 118 return blob, nil 119 } 120 121 // Storage directly retrieves the storage data associated with a particular hash, 122 // within a particular account. 123 func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, error) { 124 dl.lock.RLock() 125 defer dl.lock.RUnlock() 126 127 // If the layer was flattened into, consider it invalid (any live reference to 128 // the original should be marked as unusable). 129 if dl.stale { 130 return nil, ErrSnapshotStale 131 } 132 key := append(accountHash[:], storageHash[:]...) 133 134 // If the layer is being generated, ensure the requested hash has already been 135 // covered by the generator. 136 if dl.genMarker != nil && bytes.Compare(key, dl.genMarker) > 0 { 137 return nil, ErrNotCoveredYet 138 } 139 // If we're in the disk layer, all diff layers missed 140 snapshotDirtyStorageMissMeter.Mark(1) 141 142 // Try to retrieve the storage slot from the memory cache 143 if blob, found := dl.cache.HasGet(nil, key); found { 144 snapshotCleanStorageHitMeter.Mark(1) 145 snapshotCleanStorageReadMeter.Mark(int64(len(blob))) 146 return blob, nil 147 } 148 // Cache doesn't contain storage slot, pull from disk and cache for later 149 blob := rawdb.ReadStorageSnapshot(dl.diskdb, accountHash, storageHash) 150 dl.cache.Set(key, blob) 151 152 snapshotCleanStorageMissMeter.Mark(1) 153 if n := len(blob); n > 0 { 154 snapshotCleanStorageWriteMeter.Mark(int64(n)) 155 } else { 156 snapshotCleanStorageInexMeter.Mark(1) 157 } 158 return blob, nil 159 } 160 161 // Update creates a new layer on top of the existing snapshot diff tree with 162 // the specified data items. Note, the maps are retained by the method to avoid 163 // copying everything. 164 func (dl *diskLayer) Update(blockHash common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer { 165 return newDiffLayer(dl, blockHash, destructs, accounts, storage) 166 }