github.com/theQRL/go-zond@v0.1.1/core/state/snapshot/iterator_fast.go (about) 1 // Copyright 2019 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 snapshot 18 19 import ( 20 "bytes" 21 "fmt" 22 "sort" 23 24 "github.com/theQRL/go-zond/common" 25 "golang.org/x/exp/slices" 26 ) 27 28 // weightedIterator is a iterator with an assigned weight. It is used to prioritise 29 // which account or storage slot is the correct one if multiple iterators find the 30 // same one (modified in multiple consecutive blocks). 31 type weightedIterator struct { 32 it Iterator 33 priority int 34 } 35 36 func (it *weightedIterator) Cmp(other *weightedIterator) int { 37 // Order the iterators primarily by the account hashes 38 hashI := it.it.Hash() 39 hashJ := other.it.Hash() 40 41 switch bytes.Compare(hashI[:], hashJ[:]) { 42 case -1: 43 return -1 44 case 1: 45 return 1 46 } 47 // Same account/storage-slot in multiple layers, split by priority 48 if it.priority < other.priority { 49 return -1 50 } 51 if it.priority > other.priority { 52 return 1 53 } 54 return 0 55 } 56 57 // fastIterator is a more optimized multi-layer iterator which maintains a 58 // direct mapping of all iterators leading down to the bottom layer. 59 type fastIterator struct { 60 tree *Tree // Snapshot tree to reinitialize stale sub-iterators with 61 root common.Hash // Root hash to reinitialize stale sub-iterators through 62 63 curAccount []byte 64 curSlot []byte 65 66 iterators []*weightedIterator 67 initiated bool 68 account bool 69 fail error 70 } 71 72 // newFastIterator creates a new hierarchical account or storage iterator with one 73 // element per diff layer. The returned combo iterator can be used to walk over 74 // the entire snapshot diff stack simultaneously. 75 func newFastIterator(tree *Tree, root common.Hash, account common.Hash, seek common.Hash, accountIterator bool) (*fastIterator, error) { 76 snap := tree.Snapshot(root) 77 if snap == nil { 78 return nil, fmt.Errorf("unknown snapshot: %x", root) 79 } 80 fi := &fastIterator{ 81 tree: tree, 82 root: root, 83 account: accountIterator, 84 } 85 current := snap.(snapshot) 86 for depth := 0; current != nil; depth++ { 87 if accountIterator { 88 fi.iterators = append(fi.iterators, &weightedIterator{ 89 it: current.AccountIterator(seek), 90 priority: depth, 91 }) 92 } else { 93 // If the whole storage is destructed in this layer, don't 94 // bother deeper layer anymore. But we should still keep 95 // the iterator for this layer, since the iterator can contain 96 // some valid slots which belongs to the re-created account. 97 it, destructed := current.StorageIterator(account, seek) 98 fi.iterators = append(fi.iterators, &weightedIterator{ 99 it: it, 100 priority: depth, 101 }) 102 if destructed { 103 break 104 } 105 } 106 current = current.Parent() 107 } 108 fi.init() 109 return fi, nil 110 } 111 112 // init walks over all the iterators and resolves any clashes between them, after 113 // which it prepares the stack for step-by-step iteration. 114 func (fi *fastIterator) init() { 115 // Track which account hashes are iterators positioned on 116 var positioned = make(map[common.Hash]int) 117 118 // Position all iterators and track how many remain live 119 for i := 0; i < len(fi.iterators); i++ { 120 // Retrieve the first element and if it clashes with a previous iterator, 121 // advance either the current one or the old one. Repeat until nothing is 122 // clashing any more. 123 it := fi.iterators[i] 124 for { 125 // If the iterator is exhausted, drop it off the end 126 if !it.it.Next() { 127 it.it.Release() 128 last := len(fi.iterators) - 1 129 130 fi.iterators[i] = fi.iterators[last] 131 fi.iterators[last] = nil 132 fi.iterators = fi.iterators[:last] 133 134 i-- 135 break 136 } 137 // The iterator is still alive, check for collisions with previous ones 138 hash := it.it.Hash() 139 if other, exist := positioned[hash]; !exist { 140 positioned[hash] = i 141 break 142 } else { 143 // Iterators collide, one needs to be progressed, use priority to 144 // determine which. 145 // 146 // This whole else-block can be avoided, if we instead 147 // do an initial priority-sort of the iterators. If we do that, 148 // then we'll only wind up here if a lower-priority (preferred) iterator 149 // has the same value, and then we will always just continue. 150 // However, it costs an extra sort, so it's probably not better 151 if fi.iterators[other].priority < it.priority { 152 // The 'it' should be progressed 153 continue 154 } else { 155 // The 'other' should be progressed, swap them 156 it = fi.iterators[other] 157 fi.iterators[other], fi.iterators[i] = fi.iterators[i], fi.iterators[other] 158 continue 159 } 160 } 161 } 162 } 163 // Re-sort the entire list 164 slices.SortFunc(fi.iterators, func(a, b *weightedIterator) int { return a.Cmp(b) }) 165 fi.initiated = false 166 } 167 168 // Next steps the iterator forward one element, returning false if exhausted. 169 func (fi *fastIterator) Next() bool { 170 if len(fi.iterators) == 0 { 171 return false 172 } 173 if !fi.initiated { 174 // Don't forward first time -- we had to 'Next' once in order to 175 // do the sorting already 176 fi.initiated = true 177 if fi.account { 178 fi.curAccount = fi.iterators[0].it.(AccountIterator).Account() 179 } else { 180 fi.curSlot = fi.iterators[0].it.(StorageIterator).Slot() 181 } 182 if innerErr := fi.iterators[0].it.Error(); innerErr != nil { 183 fi.fail = innerErr 184 return false 185 } 186 if fi.curAccount != nil || fi.curSlot != nil { 187 return true 188 } 189 // Implicit else: we've hit a nil-account or nil-slot, and need to 190 // fall through to the loop below to land on something non-nil 191 } 192 // If an account or a slot is deleted in one of the layers, the key will 193 // still be there, but the actual value will be nil. However, the iterator 194 // should not export nil-values (but instead simply omit the key), so we 195 // need to loop here until we either 196 // - get a non-nil value, 197 // - hit an error, 198 // - or exhaust the iterator 199 for { 200 if !fi.next(0) { 201 return false // exhausted 202 } 203 if fi.account { 204 fi.curAccount = fi.iterators[0].it.(AccountIterator).Account() 205 } else { 206 fi.curSlot = fi.iterators[0].it.(StorageIterator).Slot() 207 } 208 if innerErr := fi.iterators[0].it.Error(); innerErr != nil { 209 fi.fail = innerErr 210 return false // error 211 } 212 if fi.curAccount != nil || fi.curSlot != nil { 213 break // non-nil value found 214 } 215 } 216 return true 217 } 218 219 // next handles the next operation internally and should be invoked when we know 220 // that two elements in the list may have the same value. 221 // 222 // For example, if the iterated hashes become [2,3,5,5,8,9,10], then we should 223 // invoke next(3), which will call Next on elem 3 (the second '5') and will 224 // cascade along the list, applying the same operation if needed. 225 func (fi *fastIterator) next(idx int) bool { 226 // If this particular iterator got exhausted, remove it and return true (the 227 // next one is surely not exhausted yet, otherwise it would have been removed 228 // already). 229 if it := fi.iterators[idx].it; !it.Next() { 230 it.Release() 231 232 fi.iterators = append(fi.iterators[:idx], fi.iterators[idx+1:]...) 233 return len(fi.iterators) > 0 234 } 235 // If there's no one left to cascade into, return 236 if idx == len(fi.iterators)-1 { 237 return true 238 } 239 // We next-ed the iterator at 'idx', now we may have to re-sort that element 240 var ( 241 cur, next = fi.iterators[idx], fi.iterators[idx+1] 242 curHash, nextHash = cur.it.Hash(), next.it.Hash() 243 ) 244 if diff := bytes.Compare(curHash[:], nextHash[:]); diff < 0 { 245 // It is still in correct place 246 return true 247 } else if diff == 0 && cur.priority < next.priority { 248 // So still in correct place, but we need to iterate on the next 249 fi.next(idx + 1) 250 return true 251 } 252 // At this point, the iterator is in the wrong location, but the remaining 253 // list is sorted. Find out where to move the item. 254 clash := -1 255 index := sort.Search(len(fi.iterators), func(n int) bool { 256 // The iterator always advances forward, so anything before the old slot 257 // is known to be behind us, so just skip them altogether. This actually 258 // is an important clause since the sort order got invalidated. 259 if n < idx { 260 return false 261 } 262 if n == len(fi.iterators)-1 { 263 // Can always place an elem last 264 return true 265 } 266 nextHash := fi.iterators[n+1].it.Hash() 267 if diff := bytes.Compare(curHash[:], nextHash[:]); diff < 0 { 268 return true 269 } else if diff > 0 { 270 return false 271 } 272 // The elem we're placing it next to has the same value, 273 // so whichever winds up on n+1 will need further iteration 274 clash = n + 1 275 276 return cur.priority < fi.iterators[n+1].priority 277 }) 278 fi.move(idx, index) 279 if clash != -1 { 280 fi.next(clash) 281 } 282 return true 283 } 284 285 // move advances an iterator to another position in the list. 286 func (fi *fastIterator) move(index, newpos int) { 287 elem := fi.iterators[index] 288 copy(fi.iterators[index:], fi.iterators[index+1:newpos+1]) 289 fi.iterators[newpos] = elem 290 } 291 292 // Error returns any failure that occurred during iteration, which might have 293 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 294 func (fi *fastIterator) Error() error { 295 return fi.fail 296 } 297 298 // Hash returns the current key 299 func (fi *fastIterator) Hash() common.Hash { 300 return fi.iterators[0].it.Hash() 301 } 302 303 // Account returns the current account blob. 304 // Note the returned account is not a copy, please don't modify it. 305 func (fi *fastIterator) Account() []byte { 306 return fi.curAccount 307 } 308 309 // Slot returns the current storage slot. 310 // Note the returned slot is not a copy, please don't modify it. 311 func (fi *fastIterator) Slot() []byte { 312 return fi.curSlot 313 } 314 315 // Release iterates over all the remaining live layer iterators and releases each 316 // of them individually. 317 func (fi *fastIterator) Release() { 318 for _, it := range fi.iterators { 319 it.it.Release() 320 } 321 fi.iterators = nil 322 } 323 324 // Debug is a convenience helper during testing 325 func (fi *fastIterator) Debug() { 326 for _, it := range fi.iterators { 327 fmt.Printf("[p=%v v=%v] ", it.priority, it.it.Hash()[0]) 328 } 329 fmt.Println() 330 } 331 332 // newFastAccountIterator creates a new hierarchical account iterator with one 333 // element per diff layer. The returned combo iterator can be used to walk over 334 // the entire snapshot diff stack simultaneously. 335 func newFastAccountIterator(tree *Tree, root common.Hash, seek common.Hash) (AccountIterator, error) { 336 return newFastIterator(tree, root, common.Hash{}, seek, true) 337 } 338 339 // newFastStorageIterator creates a new hierarchical storage iterator with one 340 // element per diff layer. The returned combo iterator can be used to walk over 341 // the entire snapshot diff stack simultaneously. 342 func newFastStorageIterator(tree *Tree, root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) { 343 return newFastIterator(tree, root, account, seek, false) 344 }