github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/nodes.go (about) 1 // Copyright 2024 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 "bytes" 21 "fmt" 22 "io" 23 "maps" 24 25 "github.com/VictoriaMetrics/fastcache" 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core/rawdb" 28 "github.com/ethereum/go-ethereum/crypto" 29 "github.com/ethereum/go-ethereum/ethdb" 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/rlp" 32 "github.com/ethereum/go-ethereum/trie/trienode" 33 ) 34 35 // nodeSet represents a collection of modified trie nodes resulting from a state 36 // transition, typically corresponding to a block execution. It can also represent 37 // the combined trie node set from several aggregated state transitions. 38 type nodeSet struct { 39 size uint64 // aggregated size of the trie node 40 accountNodes map[string]*trienode.Node // account trie nodes, mapped by path 41 storageNodes map[common.Hash]map[string]*trienode.Node // storage trie nodes, mapped by owner and path 42 } 43 44 // newNodeSet constructs the set with the provided dirty trie nodes. 45 func newNodeSet(nodes map[common.Hash]map[string]*trienode.Node) *nodeSet { 46 // Don't panic for the lazy callers, initialize the nil map instead 47 if nodes == nil { 48 nodes = make(map[common.Hash]map[string]*trienode.Node) 49 } 50 s := &nodeSet{ 51 accountNodes: make(map[string]*trienode.Node), 52 storageNodes: make(map[common.Hash]map[string]*trienode.Node), 53 } 54 for owner, subset := range nodes { 55 if owner == (common.Hash{}) { 56 s.accountNodes = subset 57 } else { 58 s.storageNodes[owner] = subset 59 } 60 } 61 s.computeSize() 62 return s 63 } 64 65 // computeSize calculates the database size of the held trie nodes. 66 func (s *nodeSet) computeSize() { 67 var size uint64 68 for path, n := range s.accountNodes { 69 size += uint64(len(n.Blob) + len(path)) 70 } 71 for _, subset := range s.storageNodes { 72 for path, n := range subset { 73 size += uint64(common.HashLength + len(n.Blob) + len(path)) 74 } 75 } 76 s.size = size 77 } 78 79 // updateSize updates the total cache size by the given delta. 80 func (s *nodeSet) updateSize(delta int64) { 81 size := int64(s.size) + delta 82 if size >= 0 { 83 s.size = uint64(size) 84 return 85 } 86 log.Error("Nodeset size underflow", "prev", common.StorageSize(s.size), "delta", common.StorageSize(delta)) 87 s.size = 0 88 } 89 90 // node retrieves the trie node with node path and its trie identifier. 91 func (s *nodeSet) node(owner common.Hash, path []byte) (*trienode.Node, bool) { 92 // Account trie node 93 if owner == (common.Hash{}) { 94 n, ok := s.accountNodes[string(path)] 95 return n, ok 96 } 97 // Storage trie node 98 subset, ok := s.storageNodes[owner] 99 if !ok { 100 return nil, false 101 } 102 n, ok := subset[string(path)] 103 return n, ok 104 } 105 106 // merge integrates the provided dirty nodes into the set. The provided nodeset 107 // will remain unchanged, as it may still be referenced by other layers. 108 func (s *nodeSet) merge(set *nodeSet) { 109 var ( 110 delta int64 // size difference resulting from node merging 111 overwrite counter // counter of nodes being overwritten 112 ) 113 114 // Merge account nodes 115 for path, n := range set.accountNodes { 116 if orig, exist := s.accountNodes[path]; !exist { 117 delta += int64(len(n.Blob) + len(path)) 118 } else { 119 delta += int64(len(n.Blob) - len(orig.Blob)) 120 overwrite.add(len(orig.Blob) + len(path)) 121 } 122 s.accountNodes[path] = n 123 } 124 125 // Merge storage nodes 126 for owner, subset := range set.storageNodes { 127 current, exist := s.storageNodes[owner] 128 if !exist { 129 for path, n := range subset { 130 delta += int64(common.HashLength + len(n.Blob) + len(path)) 131 } 132 // Perform a shallow copy of the map for the subset instead of claiming it 133 // directly from the provided nodeset to avoid potential concurrent map 134 // read/write issues. The nodes belonging to the original diff layer remain 135 // accessible even after merging. Therefore, ownership of the nodes map 136 // should still belong to the original layer, and any modifications to it 137 // should be prevented. 138 s.storageNodes[owner] = maps.Clone(subset) 139 continue 140 } 141 for path, n := range subset { 142 if orig, exist := current[path]; !exist { 143 delta += int64(common.HashLength + len(n.Blob) + len(path)) 144 } else { 145 delta += int64(len(n.Blob) - len(orig.Blob)) 146 overwrite.add(common.HashLength + len(orig.Blob) + len(path)) 147 } 148 current[path] = n 149 } 150 s.storageNodes[owner] = current 151 } 152 overwrite.report(gcTrieNodeMeter, gcTrieNodeBytesMeter) 153 s.updateSize(delta) 154 } 155 156 // revertTo merges the provided trie nodes into the set. This should reverse the 157 // changes made by the most recent state transition. 158 func (s *nodeSet) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) { 159 var delta int64 160 for owner, subset := range nodes { 161 if owner == (common.Hash{}) { 162 // Account trie nodes 163 for path, n := range subset { 164 orig, ok := s.accountNodes[path] 165 if !ok { 166 blob := rawdb.ReadAccountTrieNode(db, []byte(path)) 167 if bytes.Equal(blob, n.Blob) { 168 continue 169 } 170 panic(fmt.Sprintf("non-existent account node (%v) blob: %v", path, crypto.Keccak256Hash(n.Blob).Hex())) 171 } 172 s.accountNodes[path] = n 173 delta += int64(len(n.Blob)) - int64(len(orig.Blob)) 174 } 175 } else { 176 // Storage trie nodes 177 current, ok := s.storageNodes[owner] 178 if !ok { 179 panic(fmt.Sprintf("non-existent subset (%x)", owner)) 180 } 181 for path, n := range subset { 182 orig, ok := current[path] 183 if !ok { 184 blob := rawdb.ReadStorageTrieNode(db, owner, []byte(path)) 185 if bytes.Equal(blob, n.Blob) { 186 continue 187 } 188 panic(fmt.Sprintf("non-existent storage node (%x %v) blob: %v", owner, path, crypto.Keccak256Hash(n.Blob).Hex())) 189 } 190 current[path] = n 191 delta += int64(len(n.Blob)) - int64(len(orig.Blob)) 192 } 193 } 194 } 195 s.updateSize(delta) 196 } 197 198 // journalNode represents a trie node persisted in the journal. 199 type journalNode struct { 200 Path []byte // Path of the node in the trie 201 Blob []byte // RLP-encoded trie node blob, nil means the node is deleted 202 } 203 204 // journalNodes represents a list trie nodes belong to a single account 205 // or the main account trie. 206 type journalNodes struct { 207 Owner common.Hash 208 Nodes []journalNode 209 } 210 211 // encode serializes the content of trie nodes into the provided writer. 212 func (s *nodeSet) encode(w io.Writer) error { 213 nodes := make([]journalNodes, 0, len(s.storageNodes)+1) 214 215 // Encode account nodes 216 if len(s.accountNodes) > 0 { 217 entry := journalNodes{Owner: common.Hash{}} 218 for path, node := range s.accountNodes { 219 entry.Nodes = append(entry.Nodes, journalNode{ 220 Path: []byte(path), 221 Blob: node.Blob, 222 }) 223 } 224 nodes = append(nodes, entry) 225 } 226 // Encode storage nodes 227 for owner, subset := range s.storageNodes { 228 entry := journalNodes{Owner: owner} 229 for path, node := range subset { 230 entry.Nodes = append(entry.Nodes, journalNode{ 231 Path: []byte(path), 232 Blob: node.Blob, 233 }) 234 } 235 nodes = append(nodes, entry) 236 } 237 return rlp.Encode(w, nodes) 238 } 239 240 // decode deserializes the content from the rlp stream into the nodeset. 241 func (s *nodeSet) decode(r *rlp.Stream) error { 242 var encoded []journalNodes 243 if err := r.Decode(&encoded); err != nil { 244 return fmt.Errorf("load nodes: %v", err) 245 } 246 s.accountNodes = make(map[string]*trienode.Node) 247 s.storageNodes = make(map[common.Hash]map[string]*trienode.Node) 248 249 for _, entry := range encoded { 250 if entry.Owner == (common.Hash{}) { 251 // Account nodes 252 for _, n := range entry.Nodes { 253 if len(n.Blob) > 0 { 254 s.accountNodes[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) 255 } else { 256 s.accountNodes[string(n.Path)] = trienode.NewDeleted() 257 } 258 } 259 } else { 260 // Storage nodes 261 subset := make(map[string]*trienode.Node) 262 for _, n := range entry.Nodes { 263 if len(n.Blob) > 0 { 264 subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) 265 } else { 266 subset[string(n.Path)] = trienode.NewDeleted() 267 } 268 } 269 s.storageNodes[entry.Owner] = subset 270 } 271 } 272 s.computeSize() 273 return nil 274 } 275 276 // write flushes nodes into the provided database batch as a whole. 277 func (s *nodeSet) write(batch ethdb.Batch, clean *fastcache.Cache) int { 278 nodes := make(map[common.Hash]map[string]*trienode.Node) 279 if len(s.accountNodes) > 0 { 280 nodes[common.Hash{}] = s.accountNodes 281 } 282 for owner, subset := range s.storageNodes { 283 nodes[owner] = subset 284 } 285 return writeNodes(batch, nodes, clean) 286 } 287 288 // reset clears all cached trie node data. 289 func (s *nodeSet) reset() { 290 s.accountNodes = make(map[string]*trienode.Node) 291 s.storageNodes = make(map[common.Hash]map[string]*trienode.Node) 292 s.size = 0 293 } 294 295 // dbsize returns the approximate size of db write. 296 func (s *nodeSet) dbsize() int { 297 var m int 298 m += len(s.accountNodes) * len(rawdb.TrieNodeAccountPrefix) // database key prefix 299 for _, nodes := range s.storageNodes { 300 m += len(nodes) * (len(rawdb.TrieNodeStoragePrefix)) // database key prefix 301 } 302 return m + int(s.size) 303 }