github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/trie/hasher.go (about) 1 // Copyright 2016 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package trie 18 19 import ( 20 "bytes" 21 "hash" 22 "sync" 23 24 "github.com/SmartMeshFoundation/Spectrum/common" 25 "github.com/SmartMeshFoundation/Spectrum/crypto/sha3" 26 "github.com/SmartMeshFoundation/Spectrum/rlp" 27 ) 28 29 // calculator is a utility used by the hasher to calculate the hash value of the tree node. 30 type calculator struct { 31 sha hash.Hash 32 buffer *bytes.Buffer 33 } 34 35 // calculatorPool is a set of temporary calculators that may be individually saved and retrieved. 36 var calculatorPool = sync.Pool{ 37 New: func() interface{} { 38 return &calculator{buffer: new(bytes.Buffer), sha: sha3.NewKeccak256()} 39 }, 40 } 41 42 // hasher hasher is used to calculate the hash value of the whole tree. 43 type hasher struct { 44 cachegen uint16 45 cachelimit uint16 46 threaded bool 47 mu sync.Mutex 48 } 49 50 func newHasher(cachegen, cachelimit uint16) *hasher { 51 h := &hasher{ 52 cachegen: cachegen, 53 cachelimit: cachelimit, 54 } 55 return h 56 } 57 58 // newCalculator retrieves a cleaned calculator from calculator pool. 59 func (h *hasher) newCalculator() *calculator { 60 calculator := calculatorPool.Get().(*calculator) 61 calculator.buffer.Reset() 62 calculator.sha.Reset() 63 return calculator 64 } 65 66 // returnCalculator returns a no longer used calculator to the pool. 67 func (h *hasher) returnCalculator(calculator *calculator) { 68 calculatorPool.Put(calculator) 69 } 70 71 // hash collapses a node down into a hash node, also returning a copy of the 72 // original node initialized with the computed hash to replace the original one. 73 func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { 74 // If we're not storing the node, just hashing, use available cached data 75 if hash, dirty := n.cache(); hash != nil { 76 if db == nil { 77 return hash, n, nil 78 } 79 if n.canUnload(h.cachegen, h.cachelimit) { 80 // Unload the node from cache. All of its subnodes will have a lower or equal 81 // cache generation number. 82 cacheUnloadCounter.Inc(1) 83 return hash, hash, nil 84 } 85 if !dirty { 86 return hash, n, nil 87 } 88 } 89 // Trie not processed yet or needs storage, walk the children 90 collapsed, cached, err := h.hashChildren(n, db) 91 if err != nil { 92 return hashNode{}, n, err 93 } 94 hashed, err := h.store(collapsed, db, force) 95 if err != nil { 96 return hashNode{}, n, err 97 } 98 // Cache the hash of the node for later reuse and remove 99 // the dirty flag in commit mode. It's fine to assign these values directly 100 // without copying the node first because hashChildren copies it. 101 cachedHash, _ := hashed.(hashNode) 102 switch cn := cached.(type) { 103 case *shortNode: 104 cn.flags.hash = cachedHash 105 if db != nil { 106 cn.flags.dirty = false 107 } 108 case *fullNode: 109 cn.flags.hash = cachedHash 110 if db != nil { 111 cn.flags.dirty = false 112 } 113 } 114 return hashed, cached, nil 115 } 116 117 // hashChildren replaces the children of a node with their hashes if the encoded 118 // size of the child is larger than a hash, returning the collapsed node as well 119 // as a replacement for the original node with the child hashes cached in. 120 func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { 121 var err error 122 123 switch n := original.(type) { 124 case *shortNode: 125 // Hash the short node's child, caching the newly hashed subtree 126 collapsed, cached := n.copy(), n.copy() 127 collapsed.Key = hexToCompact(n.Key) 128 cached.Key = common.CopyBytes(n.Key) 129 130 if _, ok := n.Val.(valueNode); !ok { 131 collapsed.Val, cached.Val, err = h.hash(n.Val, db, false) 132 if err != nil { 133 return original, original, err 134 } 135 } 136 if collapsed.Val == nil { 137 collapsed.Val = valueNode(nil) // Ensure that nil children are encoded as empty strings. 138 } 139 return collapsed, cached, nil 140 141 case *fullNode: 142 // Hash the full node's children, caching the newly hashed subtrees 143 collapsed, cached := n.copy(), n.copy() 144 145 // hashChild is a helper to hash a single child, which is called either on the 146 // same thread as the caller or in a goroutine for the toplevel branching. 147 hashChild := func(index int, wg *sync.WaitGroup) { 148 if wg != nil { 149 defer wg.Done() 150 } 151 // Ensure that nil children are encoded as empty strings. 152 if collapsed.Children[index] == nil { 153 collapsed.Children[index] = valueNode(nil) 154 return 155 } 156 // Hash all other children properly 157 var herr error 158 collapsed.Children[index], cached.Children[index], herr = h.hash(n.Children[index], db, false) 159 if herr != nil { 160 h.mu.Lock() // rarely if ever locked, no congenstion 161 err = herr 162 h.mu.Unlock() 163 } 164 } 165 // If we're not running in threaded mode yet, span a goroutine for each child 166 if !h.threaded { 167 // Disable further threading 168 h.threaded = true 169 170 // Hash all the children concurrently 171 var wg sync.WaitGroup 172 for i := 0; i < 16; i++ { 173 wg.Add(1) 174 go hashChild(i, &wg) 175 } 176 wg.Wait() 177 178 // Reenable threading for subsequent hash calls 179 h.threaded = false 180 } else { 181 for i := 0; i < 16; i++ { 182 hashChild(i, nil) 183 } 184 } 185 if err != nil { 186 return original, original, err 187 } 188 cached.Children[16] = n.Children[16] 189 if collapsed.Children[16] == nil { 190 collapsed.Children[16] = valueNode(nil) 191 } 192 return collapsed, cached, nil 193 194 default: 195 // Value and hash nodes don't have children so they're left as were 196 return n, original, nil 197 } 198 } 199 200 func (h *hasher) store(n node, db DatabaseWriter, force bool) (node, error) { 201 // Don't store hashes or empty nodes. 202 if _, isHash := n.(hashNode); n == nil || isHash { 203 return n, nil 204 } 205 calculator := h.newCalculator() 206 defer h.returnCalculator(calculator) 207 208 // Generate the RLP encoding of the node 209 if err := rlp.Encode(calculator.buffer, n); err != nil { 210 panic("encode error: " + err.Error()) 211 } 212 if calculator.buffer.Len() < 32 && !force { 213 return n, nil // Nodes smaller than 32 bytes are stored inside their parent 214 } 215 // Larger nodes are replaced by their hash and stored in the database. 216 hash, _ := n.cache() 217 if hash == nil { 218 calculator.sha.Write(calculator.buffer.Bytes()) 219 hash = hashNode(calculator.sha.Sum(nil)) 220 } 221 if db != nil { 222 // db might be a leveldb batch, which is not safe for concurrent writes 223 h.mu.Lock() 224 err := db.Put(hash, calculator.buffer.Bytes()) 225 h.mu.Unlock() 226 227 return hash, err 228 } 229 return hash, nil 230 }