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