github.com/core-coin/go-core/v2@v2.1.9/core/state/snapshot/iterator.go (about) 1 // Copyright 2019 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 snapshot 18 19 import ( 20 "bytes" 21 "fmt" 22 "sort" 23 24 "github.com/core-coin/go-core/v2/xcbdb" 25 26 "github.com/core-coin/go-core/v2/common" 27 "github.com/core-coin/go-core/v2/core/rawdb" 28 ) 29 30 // Iterator is an iterator to step over all the accounts or the specific 31 // storage in a snapshot which may or may not be composed of multiple layers. 32 type Iterator interface { 33 // Next steps the iterator forward one element, returning false if exhausted, 34 // or an error if iteration failed for some reason (e.g. root being iterated 35 // becomes stale and garbage collected). 36 Next() bool 37 38 // Error returns any failure that occurred during iteration, which might have 39 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 40 Error() error 41 42 // Hash returns the hash of the account or storage slot the iterator is 43 // currently at. 44 Hash() common.Hash 45 46 // Release releases associated resources. Release should always succeed and 47 // can be called multiple times without causing error. 48 Release() 49 } 50 51 // AccountIterator is an iterator to step over all the accounts in a snapshot, 52 // which may or may not be composed of multiple layers. 53 type AccountIterator interface { 54 Iterator 55 56 // Account returns the RLP encoded slim account the iterator is currently at. 57 // An error will be returned if the iterator becomes invalid 58 Account() []byte 59 } 60 61 // StorageIterator is an iterator to step over the specific storage in a snapshot, 62 // which may or may not be composed of multiple layers. 63 type StorageIterator interface { 64 Iterator 65 66 // Slot returns the storage slot the iterator is currently at. An error will 67 // be returned if the iterator becomes invalid 68 Slot() []byte 69 } 70 71 // diffAccountIterator is an account iterator that steps over the accounts (both 72 // live and deleted) contained within a single diff layer. Higher order iterators 73 // will use the deleted accounts to skip deeper iterators. 74 type diffAccountIterator struct { 75 // curHash is the current hash the iterator is positioned on. The field is 76 // explicitly tracked since the referenced diff layer might go stale after 77 // the iterator was positioned and we don't want to fail accessing the old 78 // hash as long as the iterator is not touched any more. 79 curHash common.Hash 80 81 layer *diffLayer // Live layer to retrieve values from 82 keys []common.Hash // Keys left in the layer to iterate 83 fail error // Any failures encountered (stale) 84 } 85 86 // AccountIterator creates an account iterator over a single diff layer. 87 func (dl *diffLayer) AccountIterator(seek common.Hash) AccountIterator { 88 // Seek out the requested starting account 89 hashes := dl.AccountList() 90 index := sort.Search(len(hashes), func(i int) bool { 91 return bytes.Compare(seek[:], hashes[i][:]) <= 0 92 }) 93 // Assemble and returned the already seeked iterator 94 return &diffAccountIterator{ 95 layer: dl, 96 keys: hashes[index:], 97 } 98 } 99 100 // Next steps the iterator forward one element, returning false if exhausted. 101 func (it *diffAccountIterator) Next() bool { 102 // If the iterator was already stale, consider it a programmer error. Although 103 // we could just return false here, triggering this path would probably mean 104 // somebody forgot to check for Error, so lets blow up instead of undefined 105 // behavior that's hard to debug. 106 if it.fail != nil { 107 panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) 108 } 109 // Stop iterating if all keys were exhausted 110 if len(it.keys) == 0 { 111 return false 112 } 113 if it.layer.Stale() { 114 it.fail, it.keys = ErrSnapshotStale, nil 115 return false 116 } 117 // Iterator seems to be still alive, retrieve and cache the live hash 118 it.curHash = it.keys[0] 119 // key cached, shift the iterator and notify the user of success 120 it.keys = it.keys[1:] 121 return true 122 } 123 124 // Error returns any failure that occurred during iteration, which might have 125 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 126 func (it *diffAccountIterator) Error() error { 127 return it.fail 128 } 129 130 // Hash returns the hash of the account the iterator is currently at. 131 func (it *diffAccountIterator) Hash() common.Hash { 132 return it.curHash 133 } 134 135 // Account returns the RLP encoded slim account the iterator is currently at. 136 // This method may _fail_, if the underlying layer has been flattened between 137 // the call to Next and Acccount. That type of error will set it.Err. 138 // This method assumes that flattening does not delete elements from 139 // the accountdata mapping (writing nil into it is fine though), and will panic 140 // if elements have been deleted. 141 // 142 // Note the returned account is not a copy, please don't modify it. 143 func (it *diffAccountIterator) Account() []byte { 144 it.layer.lock.RLock() 145 blob, ok := it.layer.accountData[it.curHash] 146 if !ok { 147 if _, ok := it.layer.destructSet[it.curHash]; ok { 148 it.layer.lock.RUnlock() 149 return nil 150 } 151 panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash)) 152 } 153 it.layer.lock.RUnlock() 154 if it.layer.Stale() { 155 it.fail, it.keys = ErrSnapshotStale, nil 156 } 157 return blob 158 } 159 160 // Release is a noop for diff account iterators as there are no held resources. 161 func (it *diffAccountIterator) Release() {} 162 163 // diskAccountIterator is an account iterator that steps over the live accounts 164 // contained within a disk layer. 165 type diskAccountIterator struct { 166 layer *diskLayer 167 it xcbdb.Iterator 168 } 169 170 // AccountIterator creates an account iterator over a disk layer. 171 func (dl *diskLayer) AccountIterator(seek common.Hash) AccountIterator { 172 pos := common.TrimRightZeroes(seek[:]) 173 return &diskAccountIterator{ 174 layer: dl, 175 it: dl.diskdb.NewIterator(rawdb.SnapshotAccountPrefix, pos), 176 } 177 } 178 179 // Next steps the iterator forward one element, returning false if exhausted. 180 func (it *diskAccountIterator) Next() bool { 181 // If the iterator was already exhausted, don't bother 182 if it.it == nil { 183 return false 184 } 185 // Try to advance the iterator and release it if we reached the end 186 for { 187 if !it.it.Next() { 188 it.it.Release() 189 it.it = nil 190 return false 191 } 192 if len(it.it.Key()) == len(rawdb.SnapshotAccountPrefix)+common.HashLength { 193 break 194 } 195 } 196 return true 197 } 198 199 // Error returns any failure that occurred during iteration, which might have 200 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 201 // 202 // A diff layer is immutable after creation content wise and can always be fully 203 // iterated without error, so this method always returns nil. 204 func (it *diskAccountIterator) Error() error { 205 if it.it == nil { 206 return nil // Iterator is exhausted and released 207 } 208 return it.it.Error() 209 } 210 211 // Hash returns the hash of the account the iterator is currently at. 212 func (it *diskAccountIterator) Hash() common.Hash { 213 return common.BytesToHash(it.it.Key()) // The prefix will be truncated 214 } 215 216 // Account returns the RLP encoded slim account the iterator is currently at. 217 func (it *diskAccountIterator) Account() []byte { 218 return it.it.Value() 219 } 220 221 // Release releases the database snapshot held during iteration. 222 func (it *diskAccountIterator) Release() { 223 // The iterator is auto-released on exhaustion, so make sure it's still alive 224 if it.it != nil { 225 it.it.Release() 226 it.it = nil 227 } 228 } 229 230 // diffStorageIterator is a storage iterator that steps over the specific storage 231 // (both live and deleted) contained within a single diff layer. Higher order 232 // iterators will use the deleted slot to skip deeper iterators. 233 type diffStorageIterator struct { 234 // curHash is the current hash the iterator is positioned on. The field is 235 // explicitly tracked since the referenced diff layer might go stale after 236 // the iterator was positioned and we don't want to fail accessing the old 237 // hash as long as the iterator is not touched any more. 238 curHash common.Hash 239 account common.Hash 240 241 layer *diffLayer // Live layer to retrieve values from 242 keys []common.Hash // Keys left in the layer to iterate 243 fail error // Any failures encountered (stale) 244 } 245 246 // StorageIterator creates a storage iterator over a single diff layer. 247 // Execept the storage iterator is returned, there is an additional flag 248 // "destructed" returned. If it's true then it means the whole storage is 249 // destructed in this layer(maybe recreated too), don't bother deeper layer 250 // for storage retrieval. 251 func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) { 252 // Create the storage for this account even it's marked 253 // as destructed. The iterator is for the new one which 254 // just has the same address as the deleted one. 255 hashes, destructed := dl.StorageList(account) 256 index := sort.Search(len(hashes), func(i int) bool { 257 return bytes.Compare(seek[:], hashes[i][:]) <= 0 258 }) 259 // Assemble and returned the already seeked iterator 260 return &diffStorageIterator{ 261 layer: dl, 262 account: account, 263 keys: hashes[index:], 264 }, destructed 265 } 266 267 // Next steps the iterator forward one element, returning false if exhausted. 268 func (it *diffStorageIterator) Next() bool { 269 // If the iterator was already stale, consider it a programmer error. Although 270 // we could just return false here, triggering this path would probably mean 271 // somebody forgot to check for Error, so lets blow up instead of undefined 272 // behavior that's hard to debug. 273 if it.fail != nil { 274 panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) 275 } 276 // Stop iterating if all keys were exhausted 277 if len(it.keys) == 0 { 278 return false 279 } 280 if it.layer.Stale() { 281 it.fail, it.keys = ErrSnapshotStale, nil 282 return false 283 } 284 // Iterator seems to be still alive, retrieve and cache the live hash 285 it.curHash = it.keys[0] 286 // key cached, shift the iterator and notify the user of success 287 it.keys = it.keys[1:] 288 return true 289 } 290 291 // Error returns any failure that occurred during iteration, which might have 292 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 293 func (it *diffStorageIterator) Error() error { 294 return it.fail 295 } 296 297 // Hash returns the hash of the storage slot the iterator is currently at. 298 func (it *diffStorageIterator) Hash() common.Hash { 299 return it.curHash 300 } 301 302 // Slot returns the raw storage slot value the iterator is currently at. 303 // This method may _fail_, if the underlying layer has been flattened between 304 // the call to Next and Value. That type of error will set it.Err. 305 // This method assumes that flattening does not delete elements from 306 // the storage mapping (writing nil into it is fine though), and will panic 307 // if elements have been deleted. 308 // 309 // Note the returned slot is not a copy, please don't modify it. 310 func (it *diffStorageIterator) Slot() []byte { 311 it.layer.lock.RLock() 312 storage, ok := it.layer.storageData[it.account] 313 if !ok { 314 panic(fmt.Sprintf("iterator referenced non-existent account storage: %x", it.account)) 315 } 316 // Storage slot might be nil(deleted), but it must exist 317 blob, ok := storage[it.curHash] 318 if !ok { 319 panic(fmt.Sprintf("iterator referenced non-existent storage slot: %x", it.curHash)) 320 } 321 it.layer.lock.RUnlock() 322 if it.layer.Stale() { 323 it.fail, it.keys = ErrSnapshotStale, nil 324 } 325 return blob 326 } 327 328 // Release is a noop for diff account iterators as there are no held resources. 329 func (it *diffStorageIterator) Release() {} 330 331 // diskStorageIterator is a storage iterator that steps over the live storage 332 // contained within a disk layer. 333 type diskStorageIterator struct { 334 layer *diskLayer 335 account common.Hash 336 it xcbdb.Iterator 337 } 338 339 // StorageIterator creates a storage iterator over a disk layer. 340 // If the whole storage is destructed, then all entries in the disk 341 // layer are deleted already. So the "destructed" flag returned here 342 // is always false. 343 func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) { 344 pos := common.TrimRightZeroes(seek[:]) 345 return &diskStorageIterator{ 346 layer: dl, 347 account: account, 348 it: dl.diskdb.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos), 349 }, false 350 } 351 352 // Next steps the iterator forward one element, returning false if exhausted. 353 func (it *diskStorageIterator) Next() bool { 354 // If the iterator was already exhausted, don't bother 355 if it.it == nil { 356 return false 357 } 358 // Try to advance the iterator and release it if we reached the end 359 for { 360 if !it.it.Next() { 361 it.it.Release() 362 it.it = nil 363 return false 364 } 365 if len(it.it.Key()) == len(rawdb.SnapshotStoragePrefix)+common.HashLength+common.HashLength { 366 break 367 } 368 } 369 return true 370 } 371 372 // Error returns any failure that occurred during iteration, which might have 373 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 374 // 375 // A diff layer is immutable after creation content wise and can always be fully 376 // iterated without error, so this method always returns nil. 377 func (it *diskStorageIterator) Error() error { 378 if it.it == nil { 379 return nil // Iterator is exhausted and released 380 } 381 return it.it.Error() 382 } 383 384 // Hash returns the hash of the storage slot the iterator is currently at. 385 func (it *diskStorageIterator) Hash() common.Hash { 386 return common.BytesToHash(it.it.Key()) // The prefix will be truncated 387 } 388 389 // Slot returns the raw strorage slot content the iterator is currently at. 390 func (it *diskStorageIterator) Slot() []byte { 391 return it.it.Value() 392 } 393 394 // Release releases the database snapshot held during iteration. 395 func (it *diskStorageIterator) Release() { 396 // The iterator is auto-released on exhaustion, so make sure it's still alive 397 if it.it != nil { 398 it.it.Release() 399 it.it = nil 400 } 401 }