gitlab.com/yannislg/go-pulse@v0.0.0-20210722055913-a3e24e95638d/core/state/snapshot/iterator.go (about)

     1  // Copyright 2019 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 snapshot
    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  // AccountIterator is an iterator to step over all the accounts in a snapshot,
    30  // which may or may npt be composed of multiple layers.
    31  type AccountIterator 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. snapshot stack becoming stale).
    39  	Error() error
    40  
    41  	// Hash returns the hash of the account the iterator is currently at.
    42  	Hash() common.Hash
    43  
    44  	// Account returns the RLP encoded slim account the iterator is currently at.
    45  	// An error will be returned if the iterator becomes invalid (e.g. snaph
    46  	Account() []byte
    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  // diffAccountIterator is an account iterator that steps over the accounts (both
    54  // live and deleted) contained within a single diff layer. Higher order iterators
    55  // will use the deleted accounts to skip deeper iterators.
    56  type diffAccountIterator struct {
    57  	// curHash is the current hash the iterator is positioned on. The field is
    58  	// explicitly tracked since the referenced diff layer might go stale after
    59  	// the iterator was positioned and we don't want to fail accessing the old
    60  	// hash as long as the iterator is not touched any more.
    61  	curHash common.Hash
    62  
    63  	layer *diffLayer    // Live layer to retrieve values from
    64  	keys  []common.Hash // Keys left in the layer to iterate
    65  	fail  error         // Any failures encountered (stale)
    66  }
    67  
    68  // AccountIterator creates an account iterator over a single diff layer.
    69  func (dl *diffLayer) AccountIterator(seek common.Hash) AccountIterator {
    70  	// Seek out the requested starting account
    71  	hashes := dl.AccountList()
    72  	index := sort.Search(len(hashes), func(i int) bool {
    73  		return bytes.Compare(seek[:], hashes[i][:]) < 0
    74  	})
    75  	// Assemble and returned the already seeked iterator
    76  	return &diffAccountIterator{
    77  		layer: dl,
    78  		keys:  hashes[index:],
    79  	}
    80  }
    81  
    82  // Next steps the iterator forward one element, returning false if exhausted.
    83  func (it *diffAccountIterator) Next() bool {
    84  	// If the iterator was already stale, consider it a programmer error. Although
    85  	// we could just return false here, triggering this path would probably mean
    86  	// somebody forgot to check for Error, so lets blow up instead of undefined
    87  	// behavior that's hard to debug.
    88  	if it.fail != nil {
    89  		panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail))
    90  	}
    91  	// Stop iterating if all keys were exhausted
    92  	if len(it.keys) == 0 {
    93  		return false
    94  	}
    95  	if it.layer.Stale() {
    96  		it.fail, it.keys = ErrSnapshotStale, nil
    97  		return false
    98  	}
    99  	// Iterator seems to be still alive, retrieve and cache the live hash
   100  	it.curHash = it.keys[0]
   101  	// key cached, shift the iterator and notify the user of success
   102  	it.keys = it.keys[1:]
   103  	return true
   104  }
   105  
   106  // Error returns any failure that occurred during iteration, which might have
   107  // caused a premature iteration exit (e.g. snapshot stack becoming stale).
   108  func (it *diffAccountIterator) Error() error {
   109  	return it.fail
   110  }
   111  
   112  // Hash returns the hash of the account the iterator is currently at.
   113  func (it *diffAccountIterator) Hash() common.Hash {
   114  	return it.curHash
   115  }
   116  
   117  // Account returns the RLP encoded slim account the iterator is currently at.
   118  // This method may _fail_, if the underlying layer has been flattened between
   119  // the call to Next and Acccount. That type of error will set it.Err.
   120  // This method assumes that flattening does not delete elements from
   121  // the accountdata mapping (writing nil into it is fine though), and will panic
   122  // if elements have been deleted.
   123  func (it *diffAccountIterator) Account() []byte {
   124  	it.layer.lock.RLock()
   125  	blob, ok := it.layer.accountData[it.curHash]
   126  	if !ok {
   127  		if _, ok := it.layer.destructSet[it.curHash]; ok {
   128  			return nil
   129  		}
   130  		panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash))
   131  	}
   132  	it.layer.lock.RUnlock()
   133  	if it.layer.Stale() {
   134  		it.fail, it.keys = ErrSnapshotStale, nil
   135  	}
   136  	return blob
   137  }
   138  
   139  // Release is a noop for diff account iterators as there are no held resources.
   140  func (it *diffAccountIterator) Release() {}
   141  
   142  // diskAccountIterator is an account iterator that steps over the live accounts
   143  // contained within a disk layer.
   144  type diskAccountIterator struct {
   145  	layer *diskLayer
   146  	it    ethdb.Iterator
   147  }
   148  
   149  // AccountIterator creates an account iterator over a disk layer.
   150  func (dl *diskLayer) AccountIterator(seek common.Hash) AccountIterator {
   151  	pos := common.TrimRightZeroes(seek[:])
   152  	return &diskAccountIterator{
   153  		layer: dl,
   154  		it:    dl.diskdb.NewIterator(rawdb.SnapshotAccountPrefix, pos),
   155  	}
   156  }
   157  
   158  // Next steps the iterator forward one element, returning false if exhausted.
   159  func (it *diskAccountIterator) Next() bool {
   160  	// If the iterator was already exhausted, don't bother
   161  	if it.it == nil {
   162  		return false
   163  	}
   164  	// Try to advance the iterator and release it if we reached the end
   165  	for {
   166  		if !it.it.Next() || !bytes.HasPrefix(it.it.Key(), rawdb.SnapshotAccountPrefix) {
   167  			it.it.Release()
   168  			it.it = nil
   169  			return false
   170  		}
   171  		if len(it.it.Key()) == len(rawdb.SnapshotAccountPrefix)+common.HashLength {
   172  			break
   173  		}
   174  	}
   175  	return true
   176  }
   177  
   178  // Error returns any failure that occurred during iteration, which might have
   179  // caused a premature iteration exit (e.g. snapshot stack becoming stale).
   180  //
   181  // A diff layer is immutable after creation content wise and can always be fully
   182  // iterated without error, so this method always returns nil.
   183  func (it *diskAccountIterator) Error() error {
   184  	return it.it.Error()
   185  }
   186  
   187  // Hash returns the hash of the account the iterator is currently at.
   188  func (it *diskAccountIterator) Hash() common.Hash {
   189  	return common.BytesToHash(it.it.Key())
   190  }
   191  
   192  // Account returns the RLP encoded slim account the iterator is currently at.
   193  func (it *diskAccountIterator) Account() []byte {
   194  	return it.it.Value()
   195  }
   196  
   197  // Release releases the database snapshot held during iteration.
   198  func (it *diskAccountIterator) Release() {
   199  	// The iterator is auto-released on exhaustion, so make sure it's still alive
   200  	if it.it != nil {
   201  		it.it.Release()
   202  		it.it = nil
   203  	}
   204  }