github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/difflayer.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/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/log" 25 ) 26 27 // diffLayer represents a collection of modifications made to the in-memory tries 28 // along with associated state changes after running a block on top. 29 // 30 // The purpose of a diff layer is to serve as a journal, recording recent state modifications 31 // that have not yet been committed to a more stable or semi-permanent state. 32 type diffLayer struct { 33 // Immutables 34 root common.Hash // Root hash to which this layer diff belongs to 35 id uint64 // Corresponding state id 36 block uint64 // Associated block number 37 nodes *nodeSet // Cached trie nodes indexed by owner and path 38 states *StateSetWithOrigin // Associated state changes along with origin value 39 40 parent layer // Parent layer modified by this one, never nil, **can be changed** 41 lock sync.RWMutex // Lock used to protect parent 42 } 43 44 // newDiffLayer creates a new diff layer on top of an existing layer. 45 func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { 46 dl := &diffLayer{ 47 root: root, 48 id: id, 49 block: block, 50 parent: parent, 51 nodes: nodes, 52 states: states, 53 } 54 dirtyNodeWriteMeter.Mark(int64(nodes.size)) 55 dirtyStateWriteMeter.Mark(int64(states.size)) 56 log.Debug("Created new diff layer", "id", id, "block", block, "nodesize", common.StorageSize(nodes.size), "statesize", common.StorageSize(states.size)) 57 return dl 58 } 59 60 // rootHash implements the layer interface, returning the root hash of 61 // corresponding state. 62 func (dl *diffLayer) rootHash() common.Hash { 63 return dl.root 64 } 65 66 // stateID implements the layer interface, returning the state id of the layer. 67 func (dl *diffLayer) stateID() uint64 { 68 return dl.id 69 } 70 71 // parentLayer implements the layer interface, returning the subsequent 72 // layer of the diff layer. 73 func (dl *diffLayer) parentLayer() layer { 74 dl.lock.RLock() 75 defer dl.lock.RUnlock() 76 77 return dl.parent 78 } 79 80 // node implements the layer interface, retrieving the trie node blob with the 81 // provided node information. No error will be returned if the node is not found. 82 func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) { 83 // Hold the lock, ensure the parent won't be changed during the 84 // state accessing. 85 dl.lock.RLock() 86 defer dl.lock.RUnlock() 87 88 // If the trie node is known locally, return it 89 n, ok := dl.nodes.node(owner, path) 90 if ok { 91 dirtyNodeHitMeter.Mark(1) 92 dirtyNodeHitDepthHist.Update(int64(depth)) 93 dirtyNodeReadMeter.Mark(int64(len(n.Blob))) 94 return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil 95 } 96 // Trie node unknown to this layer, resolve from parent 97 return dl.parent.node(owner, path, depth+1) 98 } 99 100 // account directly retrieves the account RLP associated with a particular 101 // hash in the slim data format. 102 // 103 // Note the returned account is not a copy, please don't modify it. 104 func (dl *diffLayer) account(hash common.Hash, depth int) ([]byte, error) { 105 // Hold the lock, ensure the parent won't be changed during the 106 // state accessing. 107 dl.lock.RLock() 108 defer dl.lock.RUnlock() 109 110 if blob, found := dl.states.account(hash); found { 111 dirtyStateHitMeter.Mark(1) 112 dirtyStateHitDepthHist.Update(int64(depth)) 113 dirtyStateReadMeter.Mark(int64(len(blob))) 114 115 if len(blob) == 0 { 116 stateAccountInexMeter.Mark(1) 117 } else { 118 stateAccountExistMeter.Mark(1) 119 } 120 return blob, nil 121 } 122 // Account is unknown to this layer, resolve from parent 123 return dl.parent.account(hash, depth+1) 124 } 125 126 // storage directly retrieves the storage data associated with a particular hash, 127 // within a particular account. 128 // 129 // Note the returned storage slot is not a copy, please don't modify it. 130 func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([]byte, error) { 131 // Hold the lock, ensure the parent won't be changed during the 132 // state accessing. 133 dl.lock.RLock() 134 defer dl.lock.RUnlock() 135 136 if blob, found := dl.states.storage(accountHash, storageHash); found { 137 dirtyStateHitMeter.Mark(1) 138 dirtyStateHitDepthHist.Update(int64(depth)) 139 dirtyStateReadMeter.Mark(int64(len(blob))) 140 141 if len(blob) == 0 { 142 stateStorageInexMeter.Mark(1) 143 } else { 144 stateStorageExistMeter.Mark(1) 145 } 146 return blob, nil 147 } 148 // storage slot is unknown to this layer, resolve from parent 149 return dl.parent.storage(accountHash, storageHash, depth+1) 150 } 151 152 // update implements the layer interface, creating a new layer on top of the 153 // existing layer tree with the specified data items. 154 func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { 155 return newDiffLayer(dl, root, id, block, nodes, states) 156 } 157 158 // persist flushes the diff layer and all its parent layers to disk layer. 159 func (dl *diffLayer) persist(force bool) (*diskLayer, error) { 160 if parent, ok := dl.parentLayer().(*diffLayer); ok { 161 // Hold the lock to prevent any read operation until the new 162 // parent is linked correctly. 163 dl.lock.Lock() 164 165 // The merging of diff layers starts at the bottom-most layer, 166 // therefore we recurse down here, flattening on the way up 167 // (diffToDisk). 168 result, err := parent.persist(force) 169 if err != nil { 170 dl.lock.Unlock() 171 return nil, err 172 } 173 dl.parent = result 174 dl.lock.Unlock() 175 } 176 return diffToDisk(dl, force) 177 } 178 179 // size returns the approximate memory size occupied by this diff layer. 180 func (dl *diffLayer) size() uint64 { 181 return dl.nodes.size + dl.states.size 182 } 183 184 // diffToDisk merges a bottom-most diff into the persistent disk layer underneath 185 // it. The method will panic if called onto a non-bottom-most diff layer. 186 func diffToDisk(layer *diffLayer, force bool) (*diskLayer, error) { 187 disk, ok := layer.parentLayer().(*diskLayer) 188 if !ok { 189 panic(fmt.Sprintf("unknown layer type: %T", layer.parentLayer())) 190 } 191 return disk.commit(layer, force) 192 }