github.com/ethereum/go-ethereum@v1.16.1/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/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/rawdb" 26 "github.com/ethereum/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 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 Account. 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 panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash)) 148 } 149 it.layer.lock.RUnlock() 150 if it.layer.Stale() { 151 it.fail, it.keys = ErrSnapshotStale, nil 152 } 153 return blob 154 } 155 156 // Release is a noop for diff account iterators as there are no held resources. 157 func (it *diffAccountIterator) Release() {} 158 159 // diskAccountIterator is an account iterator that steps over the live accounts 160 // contained within a disk layer. 161 type diskAccountIterator struct { 162 layer *diskLayer 163 it ethdb.Iterator 164 } 165 166 // AccountIterator creates an account iterator over a disk layer. 167 func (dl *diskLayer) AccountIterator(seek common.Hash) AccountIterator { 168 pos := common.TrimRightZeroes(seek[:]) 169 return &diskAccountIterator{ 170 layer: dl, 171 it: dl.diskdb.NewIterator(rawdb.SnapshotAccountPrefix, pos), 172 } 173 } 174 175 // Next steps the iterator forward one element, returning false if exhausted. 176 func (it *diskAccountIterator) Next() bool { 177 // If the iterator was already exhausted, don't bother 178 if it.it == nil { 179 return false 180 } 181 // Try to advance the iterator and release it if we reached the end 182 for { 183 if !it.it.Next() { 184 it.it.Release() 185 it.it = nil 186 return false 187 } 188 if len(it.it.Key()) == len(rawdb.SnapshotAccountPrefix)+common.HashLength { 189 break 190 } 191 } 192 return true 193 } 194 195 // Error returns any failure that occurred during iteration, which might have 196 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 197 // 198 // A diff layer is immutable after creation content wise and can always be fully 199 // iterated without error, so this method always returns nil. 200 func (it *diskAccountIterator) Error() error { 201 if it.it == nil { 202 return nil // Iterator is exhausted and released 203 } 204 return it.it.Error() 205 } 206 207 // Hash returns the hash of the account the iterator is currently at. 208 func (it *diskAccountIterator) Hash() common.Hash { 209 return common.BytesToHash(it.it.Key()) // The prefix will be truncated 210 } 211 212 // Account returns the RLP encoded slim account the iterator is currently at. 213 func (it *diskAccountIterator) Account() []byte { 214 return it.it.Value() 215 } 216 217 // Release releases the database snapshot held during iteration. 218 func (it *diskAccountIterator) Release() { 219 // The iterator is auto-released on exhaustion, so make sure it's still alive 220 if it.it != nil { 221 it.it.Release() 222 it.it = nil 223 } 224 } 225 226 // diffStorageIterator is a storage iterator that steps over the specific storage 227 // (both live and deleted) contained within a single diff layer. Higher order 228 // iterators will use the deleted slot to skip deeper iterators. 229 type diffStorageIterator struct { 230 // curHash is the current hash the iterator is positioned on. The field is 231 // explicitly tracked since the referenced diff layer might go stale after 232 // the iterator was positioned and we don't want to fail accessing the old 233 // hash as long as the iterator is not touched any more. 234 curHash common.Hash 235 account common.Hash 236 237 layer *diffLayer // Live layer to retrieve values from 238 keys []common.Hash // Keys left in the layer to iterate 239 fail error // Any failures encountered (stale) 240 } 241 242 // StorageIterator creates a storage iterator over a single diff layer. 243 // Except the storage iterator is returned, there is an additional flag 244 // "destructed" returned. If it's true then it means the whole storage is 245 // destructed in this layer(maybe recreated too), don't bother deeper layer 246 // for storage retrieval. 247 func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) StorageIterator { 248 // Create the storage for this account even it's marked 249 // as destructed. The iterator is for the new one which 250 // just has the same address as the deleted one. 251 hashes := dl.StorageList(account) 252 index := sort.Search(len(hashes), func(i int) bool { 253 return bytes.Compare(seek[:], hashes[i][:]) <= 0 254 }) 255 // Assemble and returned the already seeked iterator 256 return &diffStorageIterator{ 257 layer: dl, 258 account: account, 259 keys: hashes[index:], 260 } 261 } 262 263 // Next steps the iterator forward one element, returning false if exhausted. 264 func (it *diffStorageIterator) Next() bool { 265 // If the iterator was already stale, consider it a programmer error. Although 266 // we could just return false here, triggering this path would probably mean 267 // somebody forgot to check for Error, so lets blow up instead of undefined 268 // behavior that's hard to debug. 269 if it.fail != nil { 270 panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) 271 } 272 // Stop iterating if all keys were exhausted 273 if len(it.keys) == 0 { 274 return false 275 } 276 if it.layer.Stale() { 277 it.fail, it.keys = ErrSnapshotStale, nil 278 return false 279 } 280 // Iterator seems to be still alive, retrieve and cache the live hash 281 it.curHash = it.keys[0] 282 // key cached, shift the iterator and notify the user of success 283 it.keys = it.keys[1:] 284 return true 285 } 286 287 // Error returns any failure that occurred during iteration, which might have 288 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 289 func (it *diffStorageIterator) Error() error { 290 return it.fail 291 } 292 293 // Hash returns the hash of the storage slot the iterator is currently at. 294 func (it *diffStorageIterator) Hash() common.Hash { 295 return it.curHash 296 } 297 298 // Slot returns the raw storage slot value the iterator is currently at. 299 // This method may _fail_, if the underlying layer has been flattened between 300 // the call to Next and Value. That type of error will set it.Err. 301 // This method assumes that flattening does not delete elements from 302 // the storage mapping (writing nil into it is fine though), and will panic 303 // if elements have been deleted. 304 // 305 // Note the returned slot is not a copy, please don't modify it. 306 func (it *diffStorageIterator) Slot() []byte { 307 it.layer.lock.RLock() 308 storage, ok := it.layer.storageData[it.account] 309 if !ok { 310 panic(fmt.Sprintf("iterator referenced non-existent account storage: %x", it.account)) 311 } 312 // Storage slot might be nil(deleted), but it must exist 313 blob, ok := storage[it.curHash] 314 if !ok { 315 panic(fmt.Sprintf("iterator referenced non-existent storage slot: %x", it.curHash)) 316 } 317 it.layer.lock.RUnlock() 318 if it.layer.Stale() { 319 it.fail, it.keys = ErrSnapshotStale, nil 320 } 321 return blob 322 } 323 324 // Release is a noop for diff account iterators as there are no held resources. 325 func (it *diffStorageIterator) Release() {} 326 327 // diskStorageIterator is a storage iterator that steps over the live storage 328 // contained within a disk layer. 329 type diskStorageIterator struct { 330 layer *diskLayer 331 account common.Hash 332 it ethdb.Iterator 333 } 334 335 // StorageIterator creates a storage iterator over a disk layer. 336 // If the whole storage is destructed, then all entries in the disk 337 // layer are deleted already. So the "destructed" flag returned here 338 // is always false. 339 func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) StorageIterator { 340 pos := common.TrimRightZeroes(seek[:]) 341 return &diskStorageIterator{ 342 layer: dl, 343 account: account, 344 it: dl.diskdb.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos), 345 } 346 } 347 348 // Next steps the iterator forward one element, returning false if exhausted. 349 func (it *diskStorageIterator) Next() bool { 350 // If the iterator was already exhausted, don't bother 351 if it.it == nil { 352 return false 353 } 354 // Try to advance the iterator and release it if we reached the end 355 for { 356 if !it.it.Next() { 357 it.it.Release() 358 it.it = nil 359 return false 360 } 361 if len(it.it.Key()) == len(rawdb.SnapshotStoragePrefix)+common.HashLength+common.HashLength { 362 break 363 } 364 } 365 return true 366 } 367 368 // Error returns any failure that occurred during iteration, which might have 369 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 370 // 371 // A diff layer is immutable after creation content wise and can always be fully 372 // iterated without error, so this method always returns nil. 373 func (it *diskStorageIterator) Error() error { 374 if it.it == nil { 375 return nil // Iterator is exhausted and released 376 } 377 return it.it.Error() 378 } 379 380 // Hash returns the hash of the storage slot the iterator is currently at. 381 func (it *diskStorageIterator) Hash() common.Hash { 382 return common.BytesToHash(it.it.Key()) // The prefix will be truncated 383 } 384 385 // Slot returns the raw storage slot content the iterator is currently at. 386 func (it *diskStorageIterator) Slot() []byte { 387 return it.it.Value() 388 } 389 390 // Release releases the database snapshot held during iteration. 391 func (it *diskStorageIterator) Release() { 392 // The iterator is auto-released on exhaustion, so make sure it's still alive 393 if it.it != nil { 394 it.it.Release() 395 it.it = nil 396 } 397 }