github.com/theQRL/go-zond@v0.1.1/trie/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/theQRL/go-zond/common" 24 "github.com/theQRL/go-zond/log" 25 "github.com/theQRL/go-zond/trie/trienode" 26 "github.com/theQRL/go-zond/trie/triestate" 27 ) 28 29 // diffLayer represents a collection of modifications made to the in-memory tries 30 // along with associated state changes after running a block on top. 31 // 32 // The goal of a diff layer is to act as a journal, tracking recent modifications 33 // made to the state, that have not yet graduated into a semi-immutable state. 34 type diffLayer struct { 35 // Immutables 36 root common.Hash // Root hash to which this layer diff belongs to 37 id uint64 // Corresponding state id 38 block uint64 // Associated block number 39 nodes map[common.Hash]map[string]*trienode.Node // Cached trie nodes indexed by owner and path 40 states *triestate.Set // Associated state change set for building history 41 memory uint64 // Approximate guess as to how much memory we use 42 43 parent layer // Parent layer modified by this one, never nil, **can be changed** 44 lock sync.RWMutex // Lock used to protect parent 45 } 46 47 // newDiffLayer creates a new diff layer on top of an existing layer. 48 func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { 49 var ( 50 size int64 51 count int 52 ) 53 dl := &diffLayer{ 54 root: root, 55 id: id, 56 block: block, 57 nodes: nodes, 58 states: states, 59 parent: parent, 60 } 61 for _, subset := range nodes { 62 for path, n := range subset { 63 dl.memory += uint64(n.Size() + len(path)) 64 size += int64(len(n.Blob) + len(path)) 65 } 66 count += len(subset) 67 } 68 if states != nil { 69 dl.memory += uint64(states.Size()) 70 } 71 dirtyWriteMeter.Mark(size) 72 diffLayerNodesMeter.Mark(int64(count)) 73 diffLayerBytesMeter.Mark(int64(dl.memory)) 74 log.Debug("Created new diff layer", "id", id, "block", block, "nodes", count, "size", common.StorageSize(dl.memory)) 75 return dl 76 } 77 78 // rootHash implements the layer interface, returning the root hash of 79 // corresponding state. 80 func (dl *diffLayer) rootHash() common.Hash { 81 return dl.root 82 } 83 84 // stateID implements the layer interface, returning the state id of the layer. 85 func (dl *diffLayer) stateID() uint64 { 86 return dl.id 87 } 88 89 // parentLayer implements the layer interface, returning the subsequent 90 // layer of the diff layer. 91 func (dl *diffLayer) parentLayer() layer { 92 dl.lock.RLock() 93 defer dl.lock.RUnlock() 94 95 return dl.parent 96 } 97 98 // node retrieves the node with provided node information. It's the internal 99 // version of Node function with additional accessed layer tracked. No error 100 // will be returned if node is not found. 101 func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, error) { 102 // Hold the lock, ensure the parent won't be changed during the 103 // state accessing. 104 dl.lock.RLock() 105 defer dl.lock.RUnlock() 106 107 // If the trie node is known locally, return it 108 subset, ok := dl.nodes[owner] 109 if ok { 110 n, ok := subset[string(path)] 111 if ok { 112 // If the trie node is not hash matched, or marked as removed, 113 // bubble up an error here. It shouldn't happen at all. 114 if n.Hash != hash { 115 dirtyFalseMeter.Mark(1) 116 log.Error("Unexpected trie node in diff layer", "owner", owner, "path", path, "expect", hash, "got", n.Hash) 117 return nil, newUnexpectedNodeError("diff", hash, n.Hash, owner, path) 118 } 119 dirtyHitMeter.Mark(1) 120 dirtyNodeHitDepthHist.Update(int64(depth)) 121 dirtyReadMeter.Mark(int64(len(n.Blob))) 122 return n.Blob, nil 123 } 124 } 125 // Trie node unknown to this layer, resolve from parent 126 if diff, ok := dl.parent.(*diffLayer); ok { 127 return diff.node(owner, path, hash, depth+1) 128 } 129 // Failed to resolve through diff layers, fallback to disk layer 130 return dl.parent.Node(owner, path, hash) 131 } 132 133 // Node implements the layer interface, retrieving the trie node blob with the 134 // provided node information. No error will be returned if the node is not found. 135 func (dl *diffLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { 136 return dl.node(owner, path, hash, 0) 137 } 138 139 // update implements the layer interface, creating a new layer on top of the 140 // existing layer tree with the specified data items. 141 func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { 142 return newDiffLayer(dl, root, id, block, nodes, states) 143 } 144 145 // persist flushes the diff layer and all its parent layers to disk layer. 146 func (dl *diffLayer) persist(force bool) (layer, error) { 147 if parent, ok := dl.parentLayer().(*diffLayer); ok { 148 // Hold the lock to prevent any read operation until the new 149 // parent is linked correctly. 150 dl.lock.Lock() 151 152 // The merging of diff layers starts at the bottom-most layer, 153 // therefore we recurse down here, flattening on the way up 154 // (diffToDisk). 155 result, err := parent.persist(force) 156 if err != nil { 157 dl.lock.Unlock() 158 return nil, err 159 } 160 dl.parent = result 161 dl.lock.Unlock() 162 } 163 return diffToDisk(dl, force) 164 } 165 166 // diffToDisk merges a bottom-most diff into the persistent disk layer underneath 167 // it. The method will panic if called onto a non-bottom-most diff layer. 168 func diffToDisk(layer *diffLayer, force bool) (layer, error) { 169 disk, ok := layer.parentLayer().(*diskLayer) 170 if !ok { 171 panic(fmt.Sprintf("unknown layer type: %T", layer.parentLayer())) 172 } 173 return disk.commit(layer, force) 174 }