github.com/carter-ya/go-ethereum@v0.0.0-20230628080049-d2309be3983b/trie/committer.go (about) 1 // Copyright 2020 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 trie 18 19 import ( 20 "fmt" 21 22 "github.com/ethereum/go-ethereum/common" 23 ) 24 25 // leaf represents a trie leaf node 26 type leaf struct { 27 blob []byte // raw blob of leaf 28 parent common.Hash // the hash of parent node 29 } 30 31 // committer is the tool used for the trie Commit operation. The committer will 32 // capture all dirty nodes during the commit process and keep them cached in 33 // insertion order. 34 type committer struct { 35 nodes *NodeSet 36 tracer *tracer 37 collectLeaf bool 38 } 39 40 // newCommitter creates a new committer or picks one from the pool. 41 func newCommitter(owner common.Hash, tracer *tracer, collectLeaf bool) *committer { 42 return &committer{ 43 nodes: NewNodeSet(owner), 44 tracer: tracer, 45 collectLeaf: collectLeaf, 46 } 47 } 48 49 // Commit collapses a node down into a hash node and returns it along with 50 // the modified nodeset. 51 func (c *committer) Commit(n node) (hashNode, *NodeSet, error) { 52 h, err := c.commit(nil, n) 53 if err != nil { 54 return nil, nil, err 55 } 56 // Some nodes can be deleted from trie which can't be captured by committer 57 // itself. Iterate all deleted nodes tracked by tracer and marked them as 58 // deleted only if they are present in database previously. 59 for _, path := range c.tracer.deleteList() { 60 // There are a few possibilities for this scenario(the node is deleted 61 // but not present in database previously), for example the node was 62 // embedded in the parent and now deleted from the trie. In this case 63 // it's noop from database's perspective. 64 val := c.tracer.getPrev(path) 65 if len(val) == 0 { 66 continue 67 } 68 c.nodes.markDeleted(path, val) 69 } 70 return h.(hashNode), c.nodes, nil 71 } 72 73 // commit collapses a node down into a hash node and returns it. 74 func (c *committer) commit(path []byte, n node) (node, error) { 75 // if this path is clean, use available cached data 76 hash, dirty := n.cache() 77 if hash != nil && !dirty { 78 return hash, nil 79 } 80 // Commit children, then parent, and remove the dirty flag. 81 switch cn := n.(type) { 82 case *shortNode: 83 // Commit child 84 collapsed := cn.copy() 85 86 // If the child is fullNode, recursively commit, 87 // otherwise it can only be hashNode or valueNode. 88 if _, ok := cn.Val.(*fullNode); ok { 89 childV, err := c.commit(append(path, cn.Key...), cn.Val) 90 if err != nil { 91 return nil, err 92 } 93 collapsed.Val = childV 94 } 95 // The key needs to be copied, since we're adding it to the 96 // modified nodeset. 97 collapsed.Key = hexToCompact(cn.Key) 98 hashedNode := c.store(path, collapsed) 99 if hn, ok := hashedNode.(hashNode); ok { 100 return hn, nil 101 } 102 // The short node now is embedded in its parent. Mark the node as 103 // deleted if it's present in database previously. It's equivalent 104 // as deletion from database's perspective. 105 if prev := c.tracer.getPrev(path); len(prev) != 0 { 106 c.nodes.markDeleted(path, prev) 107 } 108 return collapsed, nil 109 case *fullNode: 110 hashedKids, err := c.commitChildren(path, cn) 111 if err != nil { 112 return nil, err 113 } 114 collapsed := cn.copy() 115 collapsed.Children = hashedKids 116 117 hashedNode := c.store(path, collapsed) 118 if hn, ok := hashedNode.(hashNode); ok { 119 return hn, nil 120 } 121 // The full node now is embedded in its parent. Mark the node as 122 // deleted if it's present in database previously. It's equivalent 123 // as deletion from database's perspective. 124 if prev := c.tracer.getPrev(path); len(prev) != 0 { 125 c.nodes.markDeleted(path, prev) 126 } 127 return collapsed, nil 128 case hashNode: 129 return cn, nil 130 default: 131 // nil, valuenode shouldn't be committed 132 panic(fmt.Sprintf("%T: invalid node: %v", n, n)) 133 } 134 } 135 136 // commitChildren commits the children of the given fullnode 137 func (c *committer) commitChildren(path []byte, n *fullNode) ([17]node, error) { 138 var children [17]node 139 for i := 0; i < 16; i++ { 140 child := n.Children[i] 141 if child == nil { 142 continue 143 } 144 // If it's the hashed child, save the hash value directly. 145 // Note: it's impossible that the child in range [0, 15] 146 // is a valueNode. 147 if hn, ok := child.(hashNode); ok { 148 children[i] = hn 149 continue 150 } 151 // Commit the child recursively and store the "hashed" value. 152 // Note the returned node can be some embedded nodes, so it's 153 // possible the type is not hashNode. 154 hashed, err := c.commit(append(path, byte(i)), child) 155 if err != nil { 156 return children, err 157 } 158 children[i] = hashed 159 } 160 // For the 17th child, it's possible the type is valuenode. 161 if n.Children[16] != nil { 162 children[16] = n.Children[16] 163 } 164 return children, nil 165 } 166 167 // store hashes the node n and adds it to the modified nodeset. If leaf collection 168 // is enabled, leaf nodes will be tracked in the modified nodeset as well. 169 func (c *committer) store(path []byte, n node) node { 170 // Larger nodes are replaced by their hash and stored in the database. 171 var hash, _ = n.cache() 172 173 // This was not generated - must be a small node stored in the parent. 174 // In theory, we should check if the node is leaf here (embedded node 175 // usually is leaf node). But small value (less than 32bytes) is not 176 // our target (leaves in account trie only). 177 if hash == nil { 178 return n 179 } 180 // We have the hash already, estimate the RLP encoding-size of the node. 181 // The size is used for mem tracking, does not need to be exact 182 var ( 183 size = estimateSize(n) 184 nhash = common.BytesToHash(hash) 185 mnode = &memoryNode{ 186 hash: nhash, 187 node: simplifyNode(n), 188 size: uint16(size), 189 } 190 ) 191 // Collect the dirty node to nodeset for return. 192 c.nodes.markUpdated(path, mnode, c.tracer.getPrev(path)) 193 194 // Collect the corresponding leaf node if it's required. We don't check 195 // full node since it's impossible to store value in fullNode. The key 196 // length of leaves should be exactly same. 197 if c.collectLeaf { 198 if sn, ok := n.(*shortNode); ok { 199 if val, ok := sn.Val.(valueNode); ok { 200 c.nodes.addLeaf(&leaf{blob: val, parent: nhash}) 201 } 202 } 203 } 204 return hash 205 } 206 207 // estimateSize estimates the size of an rlp-encoded node, without actually 208 // rlp-encoding it (zero allocs). This method has been experimentally tried, and with a trie 209 // with 1000 leaves, the only errors above 1% are on small shortnodes, where this 210 // method overestimates by 2 or 3 bytes (e.g. 37 instead of 35) 211 func estimateSize(n node) int { 212 switch n := n.(type) { 213 case *shortNode: 214 // A short node contains a compacted key, and a value. 215 return 3 + len(n.Key) + estimateSize(n.Val) 216 case *fullNode: 217 // A full node contains up to 16 hashes (some nils), and a key 218 s := 3 219 for i := 0; i < 16; i++ { 220 if child := n.Children[i]; child != nil { 221 s += estimateSize(child) 222 } else { 223 s++ 224 } 225 } 226 return s 227 case valueNode: 228 return 1 + len(n) 229 case hashNode: 230 return 1 + len(n) 231 default: 232 panic(fmt.Sprintf("node type %T", n)) 233 } 234 }