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