github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/iterator.go (about) 1 // Copyright 2024 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 pathdb 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. layer 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 type ( 71 // loadAccount is the function to retrieve the account from the associated 72 // layer. An error will be returned if the associated layer is stale. 73 loadAccount func(hash common.Hash) ([]byte, error) 74 75 // loadStorage is the function to retrieve the storage slot from the associated 76 // layer. An error will be returned if the associated layer is stale. 77 loadStorage func(addrHash common.Hash, slotHash common.Hash) ([]byte, error) 78 ) 79 80 // diffAccountIterator is an account iterator that steps over the accounts (both 81 // live and deleted) contained within a state set. Higher order iterators will 82 // use the deleted accounts to skip deeper iterators. 83 // 84 // This iterator could be created from the diff layer or the disk layer (the 85 // aggregated state buffer). 86 type diffAccountIterator struct { 87 curHash common.Hash // The current hash the iterator is positioned on 88 keys []common.Hash // Keys left in the layer to iterate 89 fail error // Any failures encountered (stale) 90 loadFn loadAccount // Function to retrieve the account from with supplied hash 91 } 92 93 // newDiffAccountIterator creates an account iterator over the given state set. 94 func newDiffAccountIterator(seek common.Hash, accountList []common.Hash, fn loadAccount) AccountIterator { 95 // Seek out the requested starting account 96 index := sort.Search(len(accountList), func(i int) bool { 97 return bytes.Compare(seek[:], accountList[i][:]) <= 0 98 }) 99 // Assemble and returned the already seeked iterator 100 return &diffAccountIterator{ 101 keys: accountList[index:], 102 loadFn: fn, 103 } 104 } 105 106 // Next steps the iterator forward one element, returning false if exhausted. 107 func (it *diffAccountIterator) Next() bool { 108 // If the iterator was already stale, consider it a programmer error. Although 109 // we could just return false here, triggering this path would probably mean 110 // somebody forgot to check for Error, so lets blow up instead of undefined 111 // behavior that's hard to debug. 112 if it.fail != nil { 113 panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) 114 } 115 // Stop iterating if all keys were exhausted 116 if len(it.keys) == 0 { 117 return false 118 } 119 // Iterator seems to be still alive, retrieve and cache the live hash 120 it.curHash = it.keys[0] 121 122 // key cached, shift the iterator and notify the user of success 123 it.keys = it.keys[1:] 124 return true 125 } 126 127 // Error returns any failure that occurred during iteration, which might have 128 // caused a premature iteration exit (e.g. the linked state set becoming stale). 129 func (it *diffAccountIterator) Error() error { 130 return it.fail 131 } 132 133 // Hash returns the hash of the account the iterator is currently at. 134 func (it *diffAccountIterator) Hash() common.Hash { 135 return it.curHash 136 } 137 138 // Account returns the RLP encoded slim account the iterator is currently at. 139 // This method may fail if the associated state goes stale. An error will 140 // be set to it.fail just in case. 141 // 142 // Note the returned account is not a copy, please don't modify it. 143 func (it *diffAccountIterator) Account() []byte { 144 blob, err := it.loadFn(it.curHash) 145 if err != nil { 146 it.fail = err 147 return nil 148 } 149 return blob 150 } 151 152 // Release is a noop for diff account iterators as there are no held resources. 153 func (it *diffAccountIterator) Release() {} 154 155 // diskAccountIterator is an account iterator that steps over the persistent 156 // accounts within the database. 157 // 158 // To simplify, the staleness of the persistent state is not tracked. The disk 159 // iterator is not intended to be used alone. It should always be wrapped with 160 // a diff iterator, as the bottom-most disk layer uses both the in-memory 161 // aggregated buffer and the persistent disk layer as the data sources. The 162 // staleness of the diff iterator is sufficient to invalidate the iterator pair. 163 type diskAccountIterator struct { 164 it ethdb.Iterator 165 } 166 167 // newDiskAccountIterator creates an account iterator over the persistent state. 168 func newDiskAccountIterator(db ethdb.KeyValueStore, seek common.Hash) AccountIterator { 169 pos := common.TrimRightZeroes(seek[:]) 170 return &diskAccountIterator{ 171 it: db.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, any error occurred in the database) 197 func (it *diskAccountIterator) Error() error { 198 if it.it == nil { 199 return nil // Iterator is exhausted and released 200 } 201 return it.it.Error() 202 } 203 204 // Hash returns the hash of the account the iterator is currently at. 205 func (it *diskAccountIterator) Hash() common.Hash { 206 return common.BytesToHash(it.it.Key()) // The prefix will be truncated 207 } 208 209 // Account returns the RLP encoded slim account the iterator is currently at. 210 func (it *diskAccountIterator) Account() []byte { 211 return it.it.Value() 212 } 213 214 // Release releases the database snapshot held during iteration. 215 func (it *diskAccountIterator) Release() { 216 // The iterator is auto-released on exhaustion, so make sure it's still alive 217 if it.it != nil { 218 it.it.Release() 219 it.it = nil 220 } 221 } 222 223 // diffStorageIterator is a storage iterator that steps over the specific storage 224 // (both live and deleted) contained within a state set. Higher order iterators 225 // will use the deleted slot to skip deeper iterators. 226 // 227 // This iterator could be created from the diff layer or the disk layer (the 228 // aggregated state buffer). 229 type diffStorageIterator struct { 230 curHash common.Hash // The current slot hash the iterator is positioned on 231 account common.Hash // The account hash the storage slots belonging to 232 keys []common.Hash // Keys left in the layer to iterate 233 fail error // Any failures encountered (stale) 234 loadFn loadStorage // Function to retrieve the storage slot from with supplied hash 235 } 236 237 // newDiffStorageIterator creates a storage iterator over a single diff layer. 238 func newDiffStorageIterator(account common.Hash, seek common.Hash, storageList []common.Hash, fn loadStorage) StorageIterator { 239 index := sort.Search(len(storageList), func(i int) bool { 240 return bytes.Compare(seek[:], storageList[i][:]) <= 0 241 }) 242 // Assemble and returned the already seeked iterator 243 return &diffStorageIterator{ 244 account: account, 245 keys: storageList[index:], 246 loadFn: fn, 247 } 248 } 249 250 // Next steps the iterator forward one element, returning false if exhausted. 251 func (it *diffStorageIterator) Next() bool { 252 // If the iterator was already stale, consider it a programmer error. Although 253 // we could just return false here, triggering this path would probably mean 254 // somebody forgot to check for Error, so lets blow up instead of undefined 255 // behavior that's hard to debug. 256 if it.fail != nil { 257 panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) 258 } 259 // Stop iterating if all keys were exhausted 260 if len(it.keys) == 0 { 261 return false 262 } 263 // Iterator seems to be still alive, retrieve and cache the live hash 264 it.curHash = it.keys[0] 265 266 // key cached, shift the iterator and notify the user of success 267 it.keys = it.keys[1:] 268 return true 269 } 270 271 // Error returns any failure that occurred during iteration, which might have 272 // caused a premature iteration exit (e.g. the state set becoming stale). 273 func (it *diffStorageIterator) Error() error { 274 return it.fail 275 } 276 277 // Hash returns the hash of the storage slot the iterator is currently at. 278 func (it *diffStorageIterator) Hash() common.Hash { 279 return it.curHash 280 } 281 282 // Slot returns the raw storage slot value the iterator is currently at. 283 // This method may fail if the associated state goes stale. An error will 284 // be set to it.fail just in case. 285 // 286 // Note the returned slot is not a copy, please don't modify it. 287 func (it *diffStorageIterator) Slot() []byte { 288 storage, err := it.loadFn(it.account, it.curHash) 289 if err != nil { 290 it.fail = err 291 return nil 292 } 293 return storage 294 } 295 296 // Release is a noop for diff account iterators as there are no held resources. 297 func (it *diffStorageIterator) Release() {} 298 299 // diskStorageIterator is a storage iterator that steps over the persistent 300 // storage slots contained within the database. 301 // 302 // To simplify, the staleness of the persistent state is not tracked. The disk 303 // iterator is not intended to be used alone. It should always be wrapped with 304 // a diff iterator, as the bottom-most disk layer uses both the in-memory 305 // aggregated buffer and the persistent disk layer as the data sources. The 306 // staleness of the diff iterator is sufficient to invalidate the iterator pair. 307 type diskStorageIterator struct { 308 account common.Hash 309 it ethdb.Iterator 310 } 311 312 // StorageIterator creates a storage iterator over the persistent state. 313 func newDiskStorageIterator(db ethdb.KeyValueStore, account common.Hash, seek common.Hash) StorageIterator { 314 pos := common.TrimRightZeroes(seek[:]) 315 return &diskStorageIterator{ 316 account: account, 317 it: db.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos), 318 } 319 } 320 321 // Next steps the iterator forward one element, returning false if exhausted. 322 func (it *diskStorageIterator) Next() bool { 323 // If the iterator was already exhausted, don't bother 324 if it.it == nil { 325 return false 326 } 327 // Try to advance the iterator and release it if we reached the end 328 for { 329 if !it.it.Next() { 330 it.it.Release() 331 it.it = nil 332 return false 333 } 334 if len(it.it.Key()) == len(rawdb.SnapshotStoragePrefix)+common.HashLength+common.HashLength { 335 break 336 } 337 } 338 return true 339 } 340 341 // Error returns any failure that occurred during iteration, which might have 342 // caused a premature iteration exit (e.g. the error occurred in the database). 343 func (it *diskStorageIterator) Error() error { 344 if it.it == nil { 345 return nil // Iterator is exhausted and released 346 } 347 return it.it.Error() 348 } 349 350 // Hash returns the hash of the storage slot the iterator is currently at. 351 func (it *diskStorageIterator) Hash() common.Hash { 352 return common.BytesToHash(it.it.Key()) // The prefix will be truncated 353 } 354 355 // Slot returns the raw storage slot content the iterator is currently at. 356 func (it *diskStorageIterator) Slot() []byte { 357 return it.it.Value() 358 } 359 360 // Release releases the database snapshot held during iteration. 361 func (it *diskStorageIterator) Release() { 362 // The iterator is auto-released on exhaustion, so make sure it's still alive 363 if it.it != nil { 364 it.it.Release() 365 it.it = nil 366 } 367 }