github.com/core-coin/go-core/v2@v2.1.9/trie/committer.go (about) 1 // Copyright 2020 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package trie 18 19 import ( 20 "errors" 21 "fmt" 22 "sync" 23 24 "golang.org/x/crypto/sha3" 25 26 "github.com/core-coin/go-core/v2/common" 27 "github.com/core-coin/go-core/v2/crypto" 28 ) 29 30 // leafChanSize is the size of the leafCh. It's a pretty arbitrary number, to allow 31 // some parallelism but not incur too much memory overhead. 32 const leafChanSize = 200 33 34 // leaf represents a trie leaf value 35 type leaf struct { 36 size int // size of the rlp data (estimate) 37 hash common.Hash // hash of rlp data 38 node node // the node to commit 39 } 40 41 // committer is a type used for the trie Commit operation. A committer has some 42 // internal preallocated temp space, and also a callback that is invoked when 43 // leaves are committed. The leafs are passed through the `leafCh`, to allow 44 // some level of parallelism. 45 // By 'some level' of parallelism, it's still the case that all leaves will be 46 // processed sequentially - onleaf will never be called in parallel or out of order. 47 type committer struct { 48 tmp sliceBuffer 49 sha crypto.SHA3State 50 51 onleaf LeafCallback 52 leafCh chan *leaf 53 } 54 55 // committers live in a global sync.Pool 56 var committerPool = sync.Pool{ 57 New: func() interface{} { 58 return &committer{ 59 tmp: make(sliceBuffer, 0, 550), // cap is as large as a full fullNode. 60 sha: sha3.New256().(crypto.SHA3State), 61 } 62 }, 63 } 64 65 // newCommitter creates a new committer or picks one from the pool. 66 func newCommitter() *committer { 67 return committerPool.Get().(*committer) 68 } 69 70 func returnCommitterToPool(h *committer) { 71 h.onleaf = nil 72 h.leafCh = nil 73 committerPool.Put(h) 74 } 75 76 // commit collapses a node down into a hash node and inserts it into the database 77 func (c *committer) Commit(n node, db *Database) (hashNode, error) { 78 if db == nil { 79 return nil, errors.New("no db provided") 80 } 81 h, err := c.commit(n, db) 82 if err != nil { 83 return nil, err 84 } 85 return h.(hashNode), nil 86 } 87 88 // commit collapses a node down into a hash node and inserts it into the database 89 func (c *committer) commit(n node, db *Database) (node, error) { 90 // if this path is clean, use available cached data 91 hash, dirty := n.cache() 92 if hash != nil && !dirty { 93 return hash, nil 94 } 95 // Commit children, then parent, and remove remove the dirty flag. 96 switch cn := n.(type) { 97 case *shortNode: 98 // Commit child 99 collapsed := cn.copy() 100 101 // If the child is fullnode, recursively commit. 102 // Otherwise it can only be hashNode or valueNode. 103 if _, ok := cn.Val.(*fullNode); ok { 104 childV, err := c.commit(cn.Val, db) 105 if err != nil { 106 return nil, err 107 } 108 collapsed.Val = childV 109 } 110 // The key needs to be copied, since we're delivering it to database 111 collapsed.Key = hexToCompact(cn.Key) 112 hashedNode := c.store(collapsed, db) 113 if hn, ok := hashedNode.(hashNode); ok { 114 return hn, nil 115 } 116 return collapsed, nil 117 case *fullNode: 118 hashedKids, err := c.commitChildren(cn, db) 119 if err != nil { 120 return nil, err 121 } 122 collapsed := cn.copy() 123 collapsed.Children = hashedKids 124 125 hashedNode := c.store(collapsed, db) 126 if hn, ok := hashedNode.(hashNode); ok { 127 return hn, nil 128 } 129 return collapsed, nil 130 case hashNode: 131 return cn, nil 132 default: 133 // nil, valuenode shouldn't be committed 134 panic(fmt.Sprintf("%T: invalid node: %v", n, n)) 135 } 136 } 137 138 // commitChildren commits the children of the given fullnode 139 func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, error) { 140 var children [17]node 141 for i := 0; i < 16; i++ { 142 child := n.Children[i] 143 if child == nil { 144 continue 145 } 146 // If it's the hashed child, save the hash value directly. 147 // Note: it's impossible that the child in range [0, 15] 148 // is a valuenode. 149 if hn, ok := child.(hashNode); ok { 150 children[i] = hn 151 continue 152 } 153 // Commit the child recursively and store the "hashed" value. 154 // Note the returned node can be some embedded nodes, so it's 155 // possible the type is not hashnode. 156 hashed, err := c.commit(child, db) 157 if err != nil { 158 return children, err 159 } 160 children[i] = hashed 161 } 162 // For the 17th child, it's possible the type is valuenode. 163 if n.Children[16] != nil { 164 children[16] = n.Children[16] 165 } 166 return children, nil 167 } 168 169 // store hashes the node n and if we have a storage layer specified, it writes 170 // the key/value pair to it and tracks any node->child references as well as any 171 // node->external trie references. 172 func (c *committer) store(n node, db *Database) node { 173 // Larger nodes are replaced by their hash and stored in the database. 174 var ( 175 hash, _ = n.cache() 176 size int 177 ) 178 if hash == nil { 179 // This was not generated - must be a small node stored in the parent. 180 // In theory we should apply the leafCall here if it's not nil(embedded 181 // node usually contains value). But small value(less than 32bytes) is 182 // not our target. 183 return n 184 } else { 185 // We have the hash already, estimate the RLP encoding-size of the node. 186 // The size is used for mem tracking, does not need to be exact 187 size = estimateSize(n) 188 } 189 // If we're using channel-based leaf-reporting, send to channel. 190 // The leaf channel will be active only when there an active leaf-callback 191 if c.leafCh != nil { 192 c.leafCh <- &leaf{ 193 size: size, 194 hash: common.BytesToHash(hash), 195 node: n, 196 } 197 } else if db != nil { 198 // No leaf-callback used, but there's still a database. Do serial 199 // insertion 200 db.lock.Lock() 201 db.insert(common.BytesToHash(hash), size, n) 202 db.lock.Unlock() 203 } 204 return hash 205 } 206 207 // commitLoop does the actual insert + leaf callback for nodes. 208 func (c *committer) commitLoop(db *Database) { 209 for item := range c.leafCh { 210 var ( 211 hash = item.hash 212 size = item.size 213 n = item.node 214 ) 215 // We are pooling the trie nodes into an intermediate memory cache 216 db.lock.Lock() 217 db.insert(hash, size, n) 218 db.lock.Unlock() 219 220 if c.onleaf != nil { 221 switch n := n.(type) { 222 case *shortNode: 223 if child, ok := n.Val.(valueNode); ok { 224 c.onleaf(nil, child, hash) 225 } 226 case *fullNode: 227 // For children in range [0, 15], it's impossible 228 // to contain valuenode. Only check the 17th child. 229 if n.Children[16] != nil { 230 c.onleaf(nil, n.Children[16].(valueNode), hash) 231 } 232 } 233 } 234 } 235 } 236 237 func (c *committer) makeHashNode(data []byte) hashNode { 238 n := make(hashNode, c.sha.Size()) 239 c.sha.Reset() 240 c.sha.Write(data) 241 c.sha.Read(n) 242 return n 243 } 244 245 // estimateSize estimates the size of an rlp-encoded node, without actually 246 // rlp-encoding it (zero allocs). This method has been experimentally tried, and with a trie 247 // with 1000 leafs, the only errors above 1% are on small shortnodes, where this 248 // method overestimates by 2 or 3 bytes (e.g. 37 instead of 35) 249 func estimateSize(n node) int { 250 switch n := n.(type) { 251 case *shortNode: 252 // A short node contains a compacted key, and a value. 253 return 3 + len(n.Key) + estimateSize(n.Val) 254 case *fullNode: 255 // A full node contains up to 16 hashes (some nils), and a key 256 s := 3 257 for i := 0; i < 16; i++ { 258 if child := n.Children[i]; child != nil { 259 s += estimateSize(child) 260 } else { 261 s++ 262 } 263 } 264 return s 265 case valueNode: 266 return 1 + len(n) 267 case hashNode: 268 return 1 + len(n) 269 default: 270 panic(fmt.Sprintf("node type %T", n)) 271 } 272 }