github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/trie/committer.go (about) 1 // Copyright 2019 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 "errors" 21 "fmt" 22 "sync" 23 24 "golang.org/x/crypto/sha3" 25 26 "github.com/scroll-tech/go-ethereum/common" 27 "github.com/scroll-tech/go-ethereum/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.KeccakState 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.NewLegacyKeccak256().(crypto.KeccakState), 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, int, error) { 78 if db == nil { 79 return nil, 0, errors.New("no db provided") 80 } 81 h, committed, err := c.commit(n, db) 82 if err != nil { 83 return nil, 0, err 84 } 85 return h.(hashNode), committed, 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, int, error) { 90 // if this path is clean, use available cached data 91 hash, dirty := n.cache() 92 if hash != nil && !dirty { 93 return hash, 0, 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 var childCommitted int 104 if _, ok := cn.Val.(*fullNode); ok { 105 childV, committed, err := c.commit(cn.Val, db) 106 if err != nil { 107 return nil, 0, err 108 } 109 collapsed.Val, childCommitted = childV, committed 110 } 111 // The key needs to be copied, since we're delivering it to database 112 collapsed.Key = hexToCompact(cn.Key) 113 hashedNode := c.store(collapsed, db) 114 if hn, ok := hashedNode.(hashNode); ok { 115 return hn, childCommitted + 1, nil 116 } 117 return collapsed, childCommitted, nil 118 case *fullNode: 119 hashedKids, childCommitted, err := c.commitChildren(cn, db) 120 if err != nil { 121 return nil, 0, err 122 } 123 collapsed := cn.copy() 124 collapsed.Children = hashedKids 125 126 hashedNode := c.store(collapsed, db) 127 if hn, ok := hashedNode.(hashNode); ok { 128 return hn, childCommitted + 1, nil 129 } 130 return collapsed, childCommitted, nil 131 case hashNode: 132 return cn, 0, nil 133 default: 134 // nil, valuenode shouldn't be committed 135 panic(fmt.Sprintf("%T: invalid node: %v", n, n)) 136 } 137 } 138 139 // commitChildren commits the children of the given fullnode 140 func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, int, error) { 141 var ( 142 committed int 143 children [17]node 144 ) 145 for i := 0; i < 16; i++ { 146 child := n.Children[i] 147 if child == nil { 148 continue 149 } 150 // If it's the hashed child, save the hash value directly. 151 // Note: it's impossible that the child in range [0, 15] 152 // is a valueNode. 153 if hn, ok := child.(hashNode); ok { 154 children[i] = hn 155 continue 156 } 157 // Commit the child recursively and store the "hashed" value. 158 // Note the returned node can be some embedded nodes, so it's 159 // possible the type is not hashNode. 160 hashed, childCommitted, err := c.commit(child, db) 161 if err != nil { 162 return children, 0, err 163 } 164 children[i] = hashed 165 committed += childCommitted 166 } 167 // For the 17th child, it's possible the type is valuenode. 168 if n.Children[16] != nil { 169 children[16] = n.Children[16] 170 } 171 return children, committed, nil 172 } 173 174 // store hashes the node n and if we have a storage layer specified, it writes 175 // the key/value pair to it and tracks any node->child references as well as any 176 // node->external trie references. 177 func (c *committer) store(n node, db *Database) node { 178 // Larger nodes are replaced by their hash and stored in the database. 179 var ( 180 hash, _ = n.cache() 181 size int 182 ) 183 if hash == nil { 184 // This was not generated - must be a small node stored in the parent. 185 // In theory, we should apply the leafCall here if it's not nil(embedded 186 // node usually contains value). But small value(less than 32bytes) is 187 // not our target. 188 return n 189 } else { 190 // We have the hash already, estimate the RLP encoding-size of the node. 191 // The size is used for mem tracking, does not need to be exact 192 size = estimateSize(n) 193 } 194 // If we're using channel-based leaf-reporting, send to channel. 195 // The leaf channel will be active only when there an active leaf-callback 196 if c.leafCh != nil { 197 c.leafCh <- &leaf{ 198 size: size, 199 hash: common.BytesToHash(hash), 200 node: n, 201 } 202 } else if db != nil { 203 // No leaf-callback used, but there's still a database. Do serial 204 // insertion 205 db.lock.Lock() 206 db.insert(common.BytesToHash(hash), size, n) 207 db.lock.Unlock() 208 } 209 return hash 210 } 211 212 // commitLoop does the actual insert + leaf callback for nodes. 213 func (c *committer) commitLoop(db *Database) { 214 for item := range c.leafCh { 215 var ( 216 hash = item.hash 217 size = item.size 218 n = item.node 219 ) 220 // We are pooling the trie nodes into an intermediate memory cache 221 db.lock.Lock() 222 db.insert(hash, size, n) 223 db.lock.Unlock() 224 225 if c.onleaf != nil { 226 switch n := n.(type) { 227 case *shortNode: 228 if child, ok := n.Val.(valueNode); ok { 229 c.onleaf(nil, nil, child, hash) 230 } 231 case *fullNode: 232 // For children in range [0, 15], it's impossible 233 // to contain valueNode. Only check the 17th child. 234 if n.Children[16] != nil { 235 c.onleaf(nil, nil, n.Children[16].(valueNode), hash) 236 } 237 } 238 } 239 } 240 } 241 242 func (c *committer) makeHashNode(data []byte) hashNode { 243 n := make(hashNode, c.sha.Size()) 244 c.sha.Reset() 245 c.sha.Write(data) 246 c.sha.Read(n) 247 return n 248 } 249 250 // estimateSize estimates the size of an rlp-encoded node, without actually 251 // rlp-encoding it (zero allocs). This method has been experimentally tried, and with a trie 252 // with 1000 leafs, the only errors above 1% are on small shortnodes, where this 253 // method overestimates by 2 or 3 bytes (e.g. 37 instead of 35) 254 func estimateSize(n node) int { 255 switch n := n.(type) { 256 case *shortNode: 257 // A short node contains a compacted key, and a value. 258 return 3 + len(n.Key) + estimateSize(n.Val) 259 case *fullNode: 260 // A full node contains up to 16 hashes (some nils), and a key 261 s := 3 262 for i := 0; i < 16; i++ { 263 if child := n.Children[i]; child != nil { 264 s += estimateSize(child) 265 } else { 266 s++ 267 } 268 } 269 return s 270 case valueNode: 271 return 1 + len(n) 272 case hashNode: 273 return 1 + len(n) 274 default: 275 panic(fmt.Sprintf("node type %T", n)) 276 } 277 }