github.com/alanchchen/go-ethereum@v1.6.6-0.20170601190819-6171d01b1195/trie/sync.go (about) 1 // Copyright 2015 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 23 "github.com/ethereum/go-ethereum/common" 24 "gopkg.in/karalabe/cookiejar.v2/collections/prque" 25 ) 26 27 // ErrNotRequested is returned by the trie sync when it's requested to process a 28 // node it did not request. 29 var ErrNotRequested = errors.New("not requested") 30 31 // request represents a scheduled or already in-flight state retrieval request. 32 type request struct { 33 hash common.Hash // Hash of the node data content to retrieve 34 data []byte // Data content of the node, cached until all subtrees complete 35 raw bool // Whether this is a raw entry (code) or a trie node 36 37 parents []*request // Parent state nodes referencing this entry (notify all upon completion) 38 depth int // Depth level within the trie the node is located to prioritise DFS 39 deps int // Number of dependencies before allowed to commit this node 40 41 callback TrieSyncLeafCallback // Callback to invoke if a leaf node it reached on this branch 42 } 43 44 // SyncResult is a simple list to return missing nodes along with their request 45 // hashes. 46 type SyncResult struct { 47 Hash common.Hash // Hash of the originally unknown trie node 48 Data []byte // Data content of the retrieved node 49 } 50 51 // TrieSyncLeafCallback is a callback type invoked when a trie sync reaches a 52 // leaf node. It's used by state syncing to check if the leaf node requires some 53 // further data syncing. 54 type TrieSyncLeafCallback func(leaf []byte, parent common.Hash) error 55 56 // TrieSync is the main state trie synchronisation scheduler, which provides yet 57 // unknown trie hashes to retrieve, accepts node data associated with said hashes 58 // and reconstructs the trie step by step until all is done. 59 type TrieSync struct { 60 database DatabaseReader 61 requests map[common.Hash]*request // Pending requests pertaining to a key hash 62 queue *prque.Prque // Priority queue with the pending requests 63 } 64 65 // NewTrieSync creates a new trie data download scheduler. 66 func NewTrieSync(root common.Hash, database DatabaseReader, callback TrieSyncLeafCallback) *TrieSync { 67 ts := &TrieSync{ 68 database: database, 69 requests: make(map[common.Hash]*request), 70 queue: prque.New(), 71 } 72 ts.AddSubTrie(root, 0, common.Hash{}, callback) 73 return ts 74 } 75 76 // AddSubTrie registers a new trie to the sync code, rooted at the designated parent. 77 func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callback TrieSyncLeafCallback) { 78 // Short circuit if the trie is empty or already known 79 if root == emptyRoot { 80 return 81 } 82 key := root.Bytes() 83 blob, _ := s.database.Get(key) 84 if local, err := decodeNode(key, blob, 0); local != nil && err == nil { 85 return 86 } 87 // Assemble the new sub-trie sync request 88 req := &request{ 89 hash: root, 90 depth: depth, 91 callback: callback, 92 } 93 // If this sub-trie has a designated parent, link them together 94 if parent != (common.Hash{}) { 95 ancestor := s.requests[parent] 96 if ancestor == nil { 97 panic(fmt.Sprintf("sub-trie ancestor not found: %x", parent)) 98 } 99 ancestor.deps++ 100 req.parents = append(req.parents, ancestor) 101 } 102 s.schedule(req) 103 } 104 105 // AddRawEntry schedules the direct retrieval of a state entry that should not be 106 // interpreted as a trie node, but rather accepted and stored into the database 107 // as is. This method's goal is to support misc state metadata retrievals (e.g. 108 // contract code). 109 func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) { 110 // Short circuit if the entry is empty or already known 111 if hash == emptyState { 112 return 113 } 114 if blob, _ := s.database.Get(hash.Bytes()); blob != nil { 115 return 116 } 117 // Assemble the new sub-trie sync request 118 req := &request{ 119 hash: hash, 120 raw: true, 121 depth: depth, 122 } 123 // If this sub-trie has a designated parent, link them together 124 if parent != (common.Hash{}) { 125 ancestor := s.requests[parent] 126 if ancestor == nil { 127 panic(fmt.Sprintf("raw-entry ancestor not found: %x", parent)) 128 } 129 ancestor.deps++ 130 req.parents = append(req.parents, ancestor) 131 } 132 s.schedule(req) 133 } 134 135 // Missing retrieves the known missing nodes from the trie for retrieval. 136 func (s *TrieSync) Missing(max int) []common.Hash { 137 requests := []common.Hash{} 138 for !s.queue.Empty() && (max == 0 || len(requests) < max) { 139 requests = append(requests, s.queue.PopItem().(common.Hash)) 140 } 141 return requests 142 } 143 144 // Process injects a batch of retrieved trie nodes data, returning if something 145 // was committed to the database and also the index of an entry if processing of 146 // it failed. 147 func (s *TrieSync) Process(results []SyncResult, dbw DatabaseWriter) (bool, int, error) { 148 committed := false 149 150 for i, item := range results { 151 // If the item was not requested, bail out 152 request := s.requests[item.Hash] 153 if request == nil { 154 return committed, i, ErrNotRequested 155 } 156 // If the item is a raw entry request, commit directly 157 if request.raw { 158 request.data = item.Data 159 s.commit(request, dbw) 160 committed = true 161 continue 162 } 163 // Decode the node data content and update the request 164 node, err := decodeNode(item.Hash[:], item.Data, 0) 165 if err != nil { 166 return committed, i, err 167 } 168 request.data = item.Data 169 170 // Create and schedule a request for all the children nodes 171 requests, err := s.children(request, node) 172 if err != nil { 173 return committed, i, err 174 } 175 if len(requests) == 0 && request.deps == 0 { 176 s.commit(request, dbw) 177 committed = true 178 continue 179 } 180 request.deps += len(requests) 181 for _, child := range requests { 182 s.schedule(child) 183 } 184 } 185 return committed, 0, nil 186 } 187 188 // Pending returns the number of state entries currently pending for download. 189 func (s *TrieSync) Pending() int { 190 return len(s.requests) 191 } 192 193 // schedule inserts a new state retrieval request into the fetch queue. If there 194 // is already a pending request for this node, the new request will be discarded 195 // and only a parent reference added to the old one. 196 func (s *TrieSync) schedule(req *request) { 197 // If we're already requesting this node, add a new reference and stop 198 if old, ok := s.requests[req.hash]; ok { 199 old.parents = append(old.parents, req.parents...) 200 return 201 } 202 // Schedule the request for future retrieval 203 s.queue.Push(req.hash, float32(req.depth)) 204 s.requests[req.hash] = req 205 } 206 207 // children retrieves all the missing children of a state trie entry for future 208 // retrieval scheduling. 209 func (s *TrieSync) children(req *request, object node) ([]*request, error) { 210 // Gather all the children of the node, irrelevant whether known or not 211 type child struct { 212 node node 213 depth int 214 } 215 children := []child{} 216 217 switch node := (object).(type) { 218 case *shortNode: 219 children = []child{{ 220 node: node.Val, 221 depth: req.depth + len(node.Key), 222 }} 223 case *fullNode: 224 for i := 0; i < 17; i++ { 225 if node.Children[i] != nil { 226 children = append(children, child{ 227 node: node.Children[i], 228 depth: req.depth + 1, 229 }) 230 } 231 } 232 default: 233 panic(fmt.Sprintf("unknown node: %+v", node)) 234 } 235 // Iterate over the children, and request all unknown ones 236 requests := make([]*request, 0, len(children)) 237 for _, child := range children { 238 // Notify any external watcher of a new key/value node 239 if req.callback != nil { 240 if node, ok := (child.node).(valueNode); ok { 241 if err := req.callback(node, req.hash); err != nil { 242 return nil, err 243 } 244 } 245 } 246 // If the child references another node, resolve or schedule 247 if node, ok := (child.node).(hashNode); ok { 248 // Try to resolve the node from the local database 249 blob, _ := s.database.Get(node) 250 if local, err := decodeNode(node[:], blob, 0); local != nil && err == nil { 251 continue 252 } 253 // Locally unknown node, schedule for retrieval 254 requests = append(requests, &request{ 255 hash: common.BytesToHash(node), 256 parents: []*request{req}, 257 depth: child.depth, 258 callback: req.callback, 259 }) 260 } 261 } 262 return requests, nil 263 } 264 265 // commit finalizes a retrieval request and stores it into the database. If any 266 // of the referencing parent requests complete due to this commit, they are also 267 // committed themselves. 268 func (s *TrieSync) commit(req *request, dbw DatabaseWriter) (err error) { 269 // Write the node content to disk 270 if err := dbw.Put(req.hash[:], req.data); err != nil { 271 return err 272 } 273 delete(s.requests, req.hash) 274 275 // Check all parents for completion 276 for _, parent := range req.parents { 277 parent.deps-- 278 if parent.deps == 0 { 279 if err := s.commit(parent, dbw); err != nil { 280 return err 281 } 282 } 283 } 284 return nil 285 }