github.com/MetalBlockchain/subnet-evm@v0.4.9/trie/committer.go (about) 1 // (c) 2020-2021, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2020 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package trie 28 29 import ( 30 "fmt" 31 32 "github.com/ethereum/go-ethereum/common" 33 ) 34 35 // leaf represents a trie leaf node 36 type leaf struct { 37 blob []byte // raw blob of leaf 38 parent common.Hash // the hash of parent node 39 } 40 41 // committer is the tool used for the trie Commit operation. The committer will 42 // capture all dirty nodes during the commit process and keep them cached in 43 // insertion order. 44 type committer struct { 45 nodes *NodeSet 46 collectLeaf bool 47 } 48 49 // newCommitter creates a new committer or picks one from the pool. 50 func newCommitter(owner common.Hash, collectLeaf bool) *committer { 51 return &committer{ 52 nodes: NewNodeSet(owner), 53 collectLeaf: collectLeaf, 54 } 55 } 56 57 // Commit collapses a node down into a hash node and inserts it into the database 58 func (c *committer) Commit(n node) (hashNode, *NodeSet, error) { 59 h, err := c.commit(nil, n) 60 if err != nil { 61 return nil, nil, err 62 } 63 return h.(hashNode), c.nodes, nil 64 } 65 66 // commit collapses a node down into a hash node and inserts it into the database 67 func (c *committer) commit(path []byte, n node) (node, error) { 68 // if this path is clean, use available cached data 69 hash, dirty := n.cache() 70 if hash != nil && !dirty { 71 return hash, nil 72 } 73 // Commit children, then parent, and remove the dirty flag. 74 switch cn := n.(type) { 75 case *shortNode: 76 // Commit child 77 collapsed := cn.copy() 78 79 // If the child is fullNode, recursively commit, 80 // otherwise it can only be hashNode or valueNode. 81 if _, ok := cn.Val.(*fullNode); ok { 82 childV, err := c.commit(append(path, cn.Key...), cn.Val) 83 if err != nil { 84 return nil, err 85 } 86 collapsed.Val = childV 87 } 88 // The key needs to be copied, since we're delivering it to database 89 collapsed.Key = hexToCompact(cn.Key) 90 hashedNode := c.store(path, collapsed) 91 if hn, ok := hashedNode.(hashNode); ok { 92 return hn, nil 93 } 94 return collapsed, nil 95 case *fullNode: 96 hashedKids, err := c.commitChildren(path, cn) 97 if err != nil { 98 return nil, err 99 } 100 collapsed := cn.copy() 101 collapsed.Children = hashedKids 102 103 hashedNode := c.store(path, collapsed) 104 if hn, ok := hashedNode.(hashNode); ok { 105 return hn, nil 106 } 107 return collapsed, nil 108 case hashNode: 109 return cn, nil 110 default: 111 // nil, valuenode shouldn't be committed 112 panic(fmt.Sprintf("%T: invalid node: %v", n, n)) 113 } 114 } 115 116 // commitChildren commits the children of the given fullnode 117 func (c *committer) commitChildren(path []byte, n *fullNode) ([17]node, error) { 118 var children [17]node 119 for i := 0; i < 16; i++ { 120 child := n.Children[i] 121 if child == nil { 122 continue 123 } 124 // If it's the hashed child, save the hash value directly. 125 // Note: it's impossible that the child in range [0, 15] 126 // is a valueNode. 127 if hn, ok := child.(hashNode); ok { 128 children[i] = hn 129 continue 130 } 131 // Commit the child recursively and store the "hashed" value. 132 // Note the returned node can be some embedded nodes, so it's 133 // possible the type is not hashNode. 134 hashed, err := c.commit(append(path, byte(i)), child) 135 if err != nil { 136 return children, err 137 } 138 children[i] = hashed 139 } 140 // For the 17th child, it's possible the type is valuenode. 141 if n.Children[16] != nil { 142 children[16] = n.Children[16] 143 } 144 return children, nil 145 } 146 147 // store hashes the node n and if we have a storage layer specified, it writes 148 // the key/value pair to it and tracks any node->child references as well as any 149 // node->external trie references. 150 func (c *committer) store(path []byte, n node) node { 151 // Larger nodes are replaced by their hash and stored in the database. 152 var hash, _ = n.cache() 153 // This was not generated - must be a small node stored in the parent. 154 // In theory, we should check if the node is leaf here (embedded node 155 // usually is leaf node). But small value(less than 32bytes) is not 156 // our target(leaves in account trie only). 157 if hash == nil { 158 return n 159 } 160 // We have the hash already, estimate the RLP encoding-size of the node. 161 // The size is used for mem tracking, does not need to be exact 162 var ( 163 size = estimateSize(n) 164 nhash = common.BytesToHash(hash) 165 mnode = &memoryNode{ 166 hash: nhash, 167 node: simplifyNode(n), 168 size: uint16(size), 169 } 170 ) 171 // Collect the dirty node to nodeset for return. 172 c.nodes.add(string(path), mnode) 173 174 // Collect the corresponding leaf node if it's required. We don't check 175 // full node since it's impossible to store value in fullNode. The key 176 // length of leaves should be exactly same. 177 if c.collectLeaf { 178 if sn, ok := n.(*shortNode); ok { 179 if val, ok := sn.Val.(valueNode); ok { 180 c.nodes.addLeaf(&leaf{blob: val, parent: nhash}) 181 } 182 } 183 } 184 return hash 185 } 186 187 // estimateSize estimates the size of an rlp-encoded node, without actually 188 // rlp-encoding it (zero allocs). This method has been experimentally tried, and with a trie 189 // with 1000 leaves, the only errors above 1% are on small shortnodes, where this 190 // method overestimates by 2 or 3 bytes (e.g. 37 instead of 35) 191 func estimateSize(n node) int { 192 switch n := n.(type) { 193 case *shortNode: 194 // A short node contains a compacted key, and a value. 195 return 3 + len(n.Key) + estimateSize(n.Val) 196 case *fullNode: 197 // A full node contains up to 16 hashes (some nils), and a key 198 s := 3 199 for i := 0; i < 16; i++ { 200 if child := n.Children[i]; child != nil { 201 s += estimateSize(child) 202 } else { 203 s++ 204 } 205 } 206 return s 207 case valueNode: 208 return 1 + len(n) 209 case hashNode: 210 return 1 + len(n) 211 default: 212 panic(fmt.Sprintf("node type %T", n)) 213 } 214 }