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  }