github.com/core-coin/go-core/v2@v2.1.9/trie/hasher.go (about) 1 // Copyright 2016 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 "sync" 21 22 "golang.org/x/crypto/sha3" 23 24 "github.com/core-coin/go-core/v2/crypto" 25 "github.com/core-coin/go-core/v2/rlp" 26 ) 27 28 type sliceBuffer []byte 29 30 func (b *sliceBuffer) Write(data []byte) (n int, err error) { 31 *b = append(*b, data...) 32 return len(data), nil 33 } 34 35 func (b *sliceBuffer) Reset() { 36 *b = (*b)[:0] 37 } 38 39 // hasher is a type used for the trie Hash operation. A hasher has some 40 // internal preallocated temp space 41 type hasher struct { 42 sha crypto.SHA3State 43 tmp sliceBuffer 44 parallel bool // Whether to use paralallel threads when hashing 45 } 46 47 // hasherPool holds pureHashers 48 var hasherPool = sync.Pool{ 49 New: func() interface{} { 50 return &hasher{ 51 tmp: make(sliceBuffer, 0, 550), // cap is as large as a full fullNode. 52 sha: sha3.New256().(crypto.SHA3State), 53 } 54 }, 55 } 56 57 func newHasher(parallel bool) *hasher { 58 h := hasherPool.Get().(*hasher) 59 h.parallel = parallel 60 return h 61 } 62 63 func returnHasherToPool(h *hasher) { 64 hasherPool.Put(h) 65 } 66 67 // hash collapses a node down into a hash node, also returning a copy of the 68 // original node initialized with the computed hash to replace the original one. 69 func (h *hasher) hash(n node, force bool) (hashed node, cached node) { 70 // Return the cached hash if it's available 71 if hash, _ := n.cache(); hash != nil { 72 return hash, n 73 } 74 // Trie not processed yet, walk the children 75 switch n := n.(type) { 76 case *shortNode: 77 collapsed, cached := h.hashShortNodeChildren(n) 78 hashed := h.shortnodeToHash(collapsed, force) 79 // We need to retain the possibly _not_ hashed node, in case it was too 80 // small to be hashed 81 if hn, ok := hashed.(hashNode); ok { 82 cached.flags.hash = hn 83 } else { 84 cached.flags.hash = nil 85 } 86 return hashed, cached 87 case *fullNode: 88 collapsed, cached := h.hashFullNodeChildren(n) 89 hashed = h.fullnodeToHash(collapsed, force) 90 if hn, ok := hashed.(hashNode); ok { 91 cached.flags.hash = hn 92 } else { 93 cached.flags.hash = nil 94 } 95 return hashed, cached 96 default: 97 // Value and hash nodes don't have children so they're left as were 98 return n, n 99 } 100 } 101 102 // hashShortNodeChildren collapses the short node. The returned collapsed node 103 // holds a live reference to the Key, and must not be modified. 104 // The cached 105 func (h *hasher) hashShortNodeChildren(n *shortNode) (collapsed, cached *shortNode) { 106 // Hash the short node's child, caching the newly hashed subtree 107 collapsed, cached = n.copy(), n.copy() 108 // Previously, we did copy this one. We don't seem to need to actually 109 // do that, since we don't overwrite/reuse keys 110 //cached.Key = common.CopyBytes(n.Key) 111 collapsed.Key = hexToCompact(n.Key) 112 // Unless the child is a valuenode or hashnode, hash it 113 switch n.Val.(type) { 114 case *fullNode, *shortNode: 115 collapsed.Val, cached.Val = h.hash(n.Val, false) 116 } 117 return collapsed, cached 118 } 119 120 func (h *hasher) hashFullNodeChildren(n *fullNode) (collapsed *fullNode, cached *fullNode) { 121 // Hash the full node's children, caching the newly hashed subtrees 122 cached = n.copy() 123 collapsed = n.copy() 124 if h.parallel { 125 var wg sync.WaitGroup 126 wg.Add(16) 127 for i := 0; i < 16; i++ { 128 go func(i int) { 129 hasher := newHasher(false) 130 if child := n.Children[i]; child != nil { 131 collapsed.Children[i], cached.Children[i] = hasher.hash(child, false) 132 } else { 133 collapsed.Children[i] = nilValueNode 134 } 135 returnHasherToPool(hasher) 136 wg.Done() 137 }(i) 138 } 139 wg.Wait() 140 } else { 141 for i := 0; i < 16; i++ { 142 if child := n.Children[i]; child != nil { 143 collapsed.Children[i], cached.Children[i] = h.hash(child, false) 144 } else { 145 collapsed.Children[i] = nilValueNode 146 } 147 } 148 } 149 return collapsed, cached 150 } 151 152 // shortnodeToHash creates a hashNode from a shortNode. The supplied shortnode 153 // should have hex-type Key, which will be converted (without modification) 154 // into compact form for RLP encoding. 155 // If the rlp data is smaller than 32 bytes, `nil` is returned. 156 func (h *hasher) shortnodeToHash(n *shortNode, force bool) node { 157 h.tmp.Reset() 158 if err := rlp.Encode(&h.tmp, n); err != nil { 159 panic("encode error: " + err.Error()) 160 } 161 162 if len(h.tmp) < 32 && !force { 163 return n // Nodes smaller than 32 bytes are stored inside their parent 164 } 165 return h.hashData(h.tmp) 166 } 167 168 // shortnodeToHash is used to creates a hashNode from a set of hashNodes, (which 169 // may contain nil values) 170 func (h *hasher) fullnodeToHash(n *fullNode, force bool) node { 171 h.tmp.Reset() 172 // Generate the RLP encoding of the node 173 if err := n.EncodeRLP(&h.tmp); err != nil { 174 panic("encode error: " + err.Error()) 175 } 176 177 if len(h.tmp) < 32 && !force { 178 return n // Nodes smaller than 32 bytes are stored inside their parent 179 } 180 return h.hashData(h.tmp) 181 } 182 183 // hashData hashes the provided data 184 func (h *hasher) hashData(data []byte) hashNode { 185 n := make(hashNode, 32) 186 h.sha.Reset() 187 h.sha.Write(data) 188 h.sha.Read(n) 189 return n 190 } 191 192 // proofHash is used to construct trie proofs, and returns the 'collapsed' 193 // node (for later RLP encoding) aswell as the hashed node -- unless the 194 // node is smaller than 32 bytes, in which case it will be returned as is. 195 // This method does not do anything on value- or hash-nodes. 196 func (h *hasher) proofHash(original node) (collapsed, hashed node) { 197 switch n := original.(type) { 198 case *shortNode: 199 sn, _ := h.hashShortNodeChildren(n) 200 return sn, h.shortnodeToHash(sn, false) 201 case *fullNode: 202 fn, _ := h.hashFullNodeChildren(n) 203 return fn, h.fullnodeToHash(fn, false) 204 default: 205 // Value and hash nodes don't have children so they're left as were 206 return n, n 207 } 208 }