github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/trie/sync.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package trie 13 14 import ( 15 "errors" 16 "fmt" 17 18 "github.com/Sberex/go-sberex/common" 19 "github.com/Sberex/go-sberex/ethdb" 20 "gopkg.in/karalabe/cookiejar.v2/collections/prque" 21 ) 22 23 // ErrNotRequested is returned by the trie sync when it's requested to process a 24 // node it did not request. 25 var ErrNotRequested = errors.New("not requested") 26 27 // ErrAlreadyProcessed is returned by the trie sync when it's requested to process a 28 // node it already processed previously. 29 var ErrAlreadyProcessed = errors.New("already processed") 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 LeafCallback // 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 // syncMemBatch is an in-memory buffer of successfully downloaded but not yet 52 // persisted data items. 53 type syncMemBatch struct { 54 batch map[common.Hash][]byte // In-memory membatch of recently completed items 55 order []common.Hash // Order of completion to prevent out-of-order data loss 56 } 57 58 // newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes. 59 func newSyncMemBatch() *syncMemBatch { 60 return &syncMemBatch{ 61 batch: make(map[common.Hash][]byte), 62 order: make([]common.Hash, 0, 256), 63 } 64 } 65 66 // TrieSync is the main state trie synchronisation scheduler, which provides yet 67 // unknown trie hashes to retrieve, accepts node data associated with said hashes 68 // and reconstructs the trie step by step until all is done. 69 type TrieSync struct { 70 database DatabaseReader // Persistent database to check for existing entries 71 membatch *syncMemBatch // Memory buffer to avoid frequest database writes 72 requests map[common.Hash]*request // Pending requests pertaining to a key hash 73 queue *prque.Prque // Priority queue with the pending requests 74 } 75 76 // NewTrieSync creates a new trie data download scheduler. 77 func NewTrieSync(root common.Hash, database DatabaseReader, callback LeafCallback) *TrieSync { 78 ts := &TrieSync{ 79 database: database, 80 membatch: newSyncMemBatch(), 81 requests: make(map[common.Hash]*request), 82 queue: prque.New(), 83 } 84 ts.AddSubTrie(root, 0, common.Hash{}, callback) 85 return ts 86 } 87 88 // AddSubTrie registers a new trie to the sync code, rooted at the designated parent. 89 func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callback LeafCallback) { 90 // Short circuit if the trie is empty or already known 91 if root == emptyRoot { 92 return 93 } 94 if _, ok := s.membatch.batch[root]; ok { 95 return 96 } 97 key := root.Bytes() 98 blob, _ := s.database.Get(key) 99 if local, err := decodeNode(key, blob, 0); local != nil && err == nil { 100 return 101 } 102 // Assemble the new sub-trie sync request 103 req := &request{ 104 hash: root, 105 depth: depth, 106 callback: callback, 107 } 108 // If this sub-trie has a designated parent, link them together 109 if parent != (common.Hash{}) { 110 ancestor := s.requests[parent] 111 if ancestor == nil { 112 panic(fmt.Sprintf("sub-trie ancestor not found: %x", parent)) 113 } 114 ancestor.deps++ 115 req.parents = append(req.parents, ancestor) 116 } 117 s.schedule(req) 118 } 119 120 // AddRawEntry schedules the direct retrieval of a state entry that should not be 121 // interpreted as a trie node, but rather accepted and stored into the database 122 // as is. This method's goal is to support misc state metadata retrievals (e.g. 123 // contract code). 124 func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) { 125 // Short circuit if the entry is empty or already known 126 if hash == emptyState { 127 return 128 } 129 if _, ok := s.membatch.batch[hash]; ok { 130 return 131 } 132 if ok, _ := s.database.Has(hash.Bytes()); ok { 133 return 134 } 135 // Assemble the new sub-trie sync request 136 req := &request{ 137 hash: hash, 138 raw: true, 139 depth: depth, 140 } 141 // If this sub-trie has a designated parent, link them together 142 if parent != (common.Hash{}) { 143 ancestor := s.requests[parent] 144 if ancestor == nil { 145 panic(fmt.Sprintf("raw-entry ancestor not found: %x", parent)) 146 } 147 ancestor.deps++ 148 req.parents = append(req.parents, ancestor) 149 } 150 s.schedule(req) 151 } 152 153 // Missing retrieves the known missing nodes from the trie for retrieval. 154 func (s *TrieSync) Missing(max int) []common.Hash { 155 requests := []common.Hash{} 156 for !s.queue.Empty() && (max == 0 || len(requests) < max) { 157 requests = append(requests, s.queue.PopItem().(common.Hash)) 158 } 159 return requests 160 } 161 162 // Process injects a batch of retrieved trie nodes data, returning if something 163 // was committed to the database and also the index of an entry if processing of 164 // it failed. 165 func (s *TrieSync) Process(results []SyncResult) (bool, int, error) { 166 committed := false 167 168 for i, item := range results { 169 // If the item was not requested, bail out 170 request := s.requests[item.Hash] 171 if request == nil { 172 return committed, i, ErrNotRequested 173 } 174 if request.data != nil { 175 return committed, i, ErrAlreadyProcessed 176 } 177 // If the item is a raw entry request, commit directly 178 if request.raw { 179 request.data = item.Data 180 s.commit(request) 181 committed = true 182 continue 183 } 184 // Decode the node data content and update the request 185 node, err := decodeNode(item.Hash[:], item.Data, 0) 186 if err != nil { 187 return committed, i, err 188 } 189 request.data = item.Data 190 191 // Create and schedule a request for all the children nodes 192 requests, err := s.children(request, node) 193 if err != nil { 194 return committed, i, err 195 } 196 if len(requests) == 0 && request.deps == 0 { 197 s.commit(request) 198 committed = true 199 continue 200 } 201 request.deps += len(requests) 202 for _, child := range requests { 203 s.schedule(child) 204 } 205 } 206 return committed, 0, nil 207 } 208 209 // Commit flushes the data stored in the internal membatch out to persistent 210 // storage, returning th enumber of items written and any occurred error. 211 func (s *TrieSync) Commit(dbw ethdb.Putter) (int, error) { 212 // Dump the membatch into a database dbw 213 for i, key := range s.membatch.order { 214 if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil { 215 return i, err 216 } 217 } 218 written := len(s.membatch.order) 219 220 // Drop the membatch data and return 221 s.membatch = newSyncMemBatch() 222 return written, nil 223 } 224 225 // Pending returns the number of state entries currently pending for download. 226 func (s *TrieSync) Pending() int { 227 return len(s.requests) 228 } 229 230 // schedule inserts a new state retrieval request into the fetch queue. If there 231 // is already a pending request for this node, the new request will be discarded 232 // and only a parent reference added to the old one. 233 func (s *TrieSync) schedule(req *request) { 234 // If we're already requesting this node, add a new reference and stop 235 if old, ok := s.requests[req.hash]; ok { 236 old.parents = append(old.parents, req.parents...) 237 return 238 } 239 // Schedule the request for future retrieval 240 s.queue.Push(req.hash, float32(req.depth)) 241 s.requests[req.hash] = req 242 } 243 244 // children retrieves all the missing children of a state trie entry for future 245 // retrieval scheduling. 246 func (s *TrieSync) children(req *request, object node) ([]*request, error) { 247 // Gather all the children of the node, irrelevant whether known or not 248 type child struct { 249 node node 250 depth int 251 } 252 children := []child{} 253 254 switch node := (object).(type) { 255 case *shortNode: 256 children = []child{{ 257 node: node.Val, 258 depth: req.depth + len(node.Key), 259 }} 260 case *fullNode: 261 for i := 0; i < 17; i++ { 262 if node.Children[i] != nil { 263 children = append(children, child{ 264 node: node.Children[i], 265 depth: req.depth + 1, 266 }) 267 } 268 } 269 default: 270 panic(fmt.Sprintf("unknown node: %+v", node)) 271 } 272 // Iterate over the children, and request all unknown ones 273 requests := make([]*request, 0, len(children)) 274 for _, child := range children { 275 // Notify any external watcher of a new key/value node 276 if req.callback != nil { 277 if node, ok := (child.node).(valueNode); ok { 278 if err := req.callback(node, req.hash); err != nil { 279 return nil, err 280 } 281 } 282 } 283 // If the child references another node, resolve or schedule 284 if node, ok := (child.node).(hashNode); ok { 285 // Try to resolve the node from the local database 286 hash := common.BytesToHash(node) 287 if _, ok := s.membatch.batch[hash]; ok { 288 continue 289 } 290 if ok, _ := s.database.Has(node); ok { 291 continue 292 } 293 // Locally unknown node, schedule for retrieval 294 requests = append(requests, &request{ 295 hash: hash, 296 parents: []*request{req}, 297 depth: child.depth, 298 callback: req.callback, 299 }) 300 } 301 } 302 return requests, nil 303 } 304 305 // commit finalizes a retrieval request and stores it into the membatch. If any 306 // of the referencing parent requests complete due to this commit, they are also 307 // committed themselves. 308 func (s *TrieSync) commit(req *request) (err error) { 309 // Write the node content to the membatch 310 s.membatch.batch[req.hash] = req.data 311 s.membatch.order = append(s.membatch.order, req.hash) 312 313 delete(s.requests, req.hash) 314 315 // Check all parents for completion 316 for _, parent := range req.parents { 317 parent.deps-- 318 if parent.deps == 0 { 319 if err := s.commit(parent); err != nil { 320 return err 321 } 322 } 323 } 324 return nil 325 }