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  }