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