github.com/ethereum/go-ethereum@v1.16.1/trie/committer.go (about) 1 // Copyright 2020 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 "fmt" 21 "sync" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/trie/trienode" 25 ) 26 27 // committer is the tool used for the trie Commit operation. The committer will 28 // capture all dirty nodes during the commit process and keep them cached in 29 // insertion order. 30 type committer struct { 31 nodes *trienode.NodeSet 32 tracer *tracer 33 collectLeaf bool 34 } 35 36 // newCommitter creates a new committer or picks one from the pool. 37 func newCommitter(nodeset *trienode.NodeSet, tracer *tracer, collectLeaf bool) *committer { 38 return &committer{ 39 nodes: nodeset, 40 tracer: tracer, 41 collectLeaf: collectLeaf, 42 } 43 } 44 45 // Commit collapses a node down into a hash node. 46 func (c *committer) Commit(n node, parallel bool) hashNode { 47 return c.commit(nil, n, parallel).(hashNode) 48 } 49 50 // commit collapses a node down into a hash node and returns it. 51 func (c *committer) commit(path []byte, n node, parallel bool) node { 52 // if this path is clean, use available cached data 53 hash, dirty := n.cache() 54 if hash != nil && !dirty { 55 return hash 56 } 57 // Commit children, then parent, and remove the dirty flag. 58 switch cn := n.(type) { 59 case *shortNode: 60 // If the child is fullNode, recursively commit, 61 // otherwise it can only be hashNode or valueNode. 62 if _, ok := cn.Val.(*fullNode); ok { 63 cn.Val = c.commit(append(path, cn.Key...), cn.Val, false) 64 } 65 // The key needs to be copied, since we're adding it to the 66 // modified nodeset. 67 cn.Key = hexToCompact(cn.Key) 68 hashedNode := c.store(path, cn) 69 if hn, ok := hashedNode.(hashNode); ok { 70 return hn 71 } 72 return cn 73 case *fullNode: 74 c.commitChildren(path, cn, parallel) 75 hashedNode := c.store(path, cn) 76 if hn, ok := hashedNode.(hashNode); ok { 77 return hn 78 } 79 return cn 80 case hashNode: 81 return cn 82 default: 83 // nil, valuenode shouldn't be committed 84 panic(fmt.Sprintf("%T: invalid node: %v", n, n)) 85 } 86 } 87 88 // commitChildren commits the children of the given fullnode 89 func (c *committer) commitChildren(path []byte, n *fullNode, parallel bool) { 90 var ( 91 wg sync.WaitGroup 92 nodesMu sync.Mutex 93 ) 94 for i := 0; i < 16; i++ { 95 child := n.Children[i] 96 if child == nil { 97 continue 98 } 99 // If it's the hashed child, save the hash value directly. 100 // Note: it's impossible that the child in range [0, 15] 101 // is a valueNode. 102 if _, ok := child.(hashNode); ok { 103 continue 104 } 105 // Commit the child recursively and store the "hashed" value. 106 // Note the returned node can be some embedded nodes, so it's 107 // possible the type is not hashNode. 108 if !parallel { 109 n.Children[i] = c.commit(append(path, byte(i)), child, false) 110 } else { 111 wg.Add(1) 112 go func(index int) { 113 p := append(path, byte(index)) 114 childSet := trienode.NewNodeSet(c.nodes.Owner) 115 childCommitter := newCommitter(childSet, c.tracer, c.collectLeaf) 116 n.Children[index] = childCommitter.commit(p, child, false) 117 nodesMu.Lock() 118 c.nodes.MergeSet(childSet) 119 nodesMu.Unlock() 120 wg.Done() 121 }(i) 122 } 123 } 124 if parallel { 125 wg.Wait() 126 } 127 } 128 129 // store hashes the node n and adds it to the modified nodeset. If leaf collection 130 // is enabled, leaf nodes will be tracked in the modified nodeset as well. 131 func (c *committer) store(path []byte, n node) node { 132 // Larger nodes are replaced by their hash and stored in the database. 133 var hash, _ = n.cache() 134 135 // This was not generated - must be a small node stored in the parent. 136 // In theory, we should check if the node is leaf here (embedded node 137 // usually is leaf node). But small value (less than 32bytes) is not 138 // our target (leaves in account trie only). 139 if hash == nil { 140 // The node is embedded in its parent, in other words, this node 141 // will not be stored in the database independently, mark it as 142 // deleted only if the node was existent in database before. 143 _, ok := c.tracer.accessList[string(path)] 144 if ok { 145 c.nodes.AddNode(path, trienode.NewDeleted()) 146 } 147 return n 148 } 149 // Collect the dirty node to nodeset for return. 150 nhash := common.BytesToHash(hash) 151 c.nodes.AddNode(path, trienode.New(nhash, nodeToBytes(n))) 152 153 // Collect the corresponding leaf node if it's required. We don't check 154 // full node since it's impossible to store value in fullNode. The key 155 // length of leaves should be exactly same. 156 if c.collectLeaf { 157 if sn, ok := n.(*shortNode); ok { 158 if val, ok := sn.Val.(valueNode); ok { 159 c.nodes.AddLeaf(nhash, val) 160 } 161 } 162 } 163 return hash 164 } 165 166 // ForGatherChildren decodes the provided node and traverses the children inside. 167 func ForGatherChildren(node []byte, onChild func(common.Hash)) { 168 forGatherChildren(mustDecodeNodeUnsafe(nil, node), onChild) 169 } 170 171 // forGatherChildren traverses the node hierarchy and invokes the callback 172 // for all the hashnode children. 173 func forGatherChildren(n node, onChild func(hash common.Hash)) { 174 switch n := n.(type) { 175 case *shortNode: 176 forGatherChildren(n.Val, onChild) 177 case *fullNode: 178 for i := 0; i < 16; i++ { 179 forGatherChildren(n.Children[i], onChild) 180 } 181 case hashNode: 182 onChild(common.BytesToHash(n)) 183 case valueNode, nil: 184 default: 185 panic(fmt.Sprintf("unknown node type: %T", n)) 186 } 187 }