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