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