core/state/snapshot: full featured account iteration
This commit is contained in:
		
							parent
							
								
									e570835356
								
							
						
					
					
						commit
						6ddb92a089
					
				| @ -229,6 +229,11 @@ func (dl *diffLayer) Root() common.Hash { | |||||||
| 	return dl.root | 	return dl.root | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Parent returns the subsequent layer of a diff layer.
 | ||||||
|  | func (dl *diffLayer) Parent() snapshot { | ||||||
|  | 	return dl.parent | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Stale return whether this layer has become stale (was flattened across) or if
 | // Stale return whether this layer has become stale (was flattened across) or if
 | ||||||
| // it's still live.
 | // it's still live.
 | ||||||
| func (dl *diffLayer) Stale() bool { | func (dl *diffLayer) Stale() bool { | ||||||
| @ -405,7 +410,7 @@ func (dl *diffLayer) flatten() snapshot { | |||||||
| 	for hash, data := range dl.accountData { | 	for hash, data := range dl.accountData { | ||||||
| 		parent.accountData[hash] = data | 		parent.accountData[hash] = data | ||||||
| 	} | 	} | ||||||
| 	// Overwrite all the updates storage slots (individually)
 | 	// Overwrite all the updated storage slots (individually)
 | ||||||
| 	for accountHash, storage := range dl.storageData { | 	for accountHash, storage := range dl.storageData { | ||||||
| 		// If storage didn't exist (or was deleted) in the parent; or if the storage
 | 		// If storage didn't exist (or was deleted) in the parent; or if the storage
 | ||||||
| 		// was freshly deleted in the child, overwrite blindly
 | 		// was freshly deleted in the child, overwrite blindly
 | ||||||
| @ -425,53 +430,62 @@ func (dl *diffLayer) flatten() snapshot { | |||||||
| 		parent:      parent.parent, | 		parent:      parent.parent, | ||||||
| 		origin:      parent.origin, | 		origin:      parent.origin, | ||||||
| 		root:        dl.root, | 		root:        dl.root, | ||||||
| 		storageList: parent.storageList, |  | ||||||
| 		storageData: parent.storageData, |  | ||||||
| 		accountList: parent.accountList, |  | ||||||
| 		accountData: parent.accountData, | 		accountData: parent.accountData, | ||||||
|  | 		storageData: parent.storageData, | ||||||
|  | 		storageList: make(map[common.Hash][]common.Hash), | ||||||
| 		diffed:      dl.diffed, | 		diffed:      dl.diffed, | ||||||
| 		memory:      parent.memory + dl.memory, | 		memory:      parent.memory + dl.memory, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AccountList returns a sorted list of all accounts in this difflayer.
 | // AccountList returns a sorted list of all accounts in this difflayer, including
 | ||||||
|  | // the deleted ones.
 | ||||||
|  | //
 | ||||||
|  | // Note, the returned slice is not a copy, so do not modify it.
 | ||||||
| func (dl *diffLayer) AccountList() []common.Hash { | func (dl *diffLayer) AccountList() []common.Hash { | ||||||
|  | 	// If an old list already exists, return it
 | ||||||
|  | 	dl.lock.RLock() | ||||||
|  | 	list := dl.accountList | ||||||
|  | 	dl.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	if list != nil { | ||||||
|  | 		return list | ||||||
|  | 	} | ||||||
|  | 	// No old sorted account list exists, generate a new one
 | ||||||
| 	dl.lock.Lock() | 	dl.lock.Lock() | ||||||
| 	defer dl.lock.Unlock() | 	defer dl.lock.Unlock() | ||||||
| 	if dl.accountList != nil { | 
 | ||||||
| 		return dl.accountList | 	dl.accountList = make([]common.Hash, 0, len(dl.accountData)) | ||||||
|  | 	for hash := range dl.accountData { | ||||||
|  | 		dl.accountList = append(dl.accountList, hash) | ||||||
| 	} | 	} | ||||||
| 	accountList := make([]common.Hash, len(dl.accountData)) | 	sort.Sort(hashes(dl.accountList)) | ||||||
| 	i := 0 |  | ||||||
| 	for k, _ := range dl.accountData { |  | ||||||
| 		accountList[i] = k |  | ||||||
| 		i++ |  | ||||||
| 		// This would be a pretty good opportunity to also
 |  | ||||||
| 		// calculate the size, if we want to
 |  | ||||||
| 	} |  | ||||||
| 	sort.Sort(hashes(accountList)) |  | ||||||
| 	dl.accountList = accountList |  | ||||||
| 	return dl.accountList | 	return dl.accountList | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // StorageList returns a sorted list of all storage slot hashes
 | // StorageList returns a sorted list of all storage slot hashes in this difflayer
 | ||||||
| // in this difflayer for the given account.
 | // for the given account.
 | ||||||
|  | //
 | ||||||
|  | // Note, the returned slice is not a copy, so do not modify it.
 | ||||||
| func (dl *diffLayer) StorageList(accountHash common.Hash) []common.Hash { | func (dl *diffLayer) StorageList(accountHash common.Hash) []common.Hash { | ||||||
|  | 	// If an old list already exists, return it
 | ||||||
|  | 	dl.lock.RLock() | ||||||
|  | 	list := dl.storageList[accountHash] | ||||||
|  | 	dl.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	if list != nil { | ||||||
|  | 		return list | ||||||
|  | 	} | ||||||
|  | 	// No old sorted account list exists, generate a new one
 | ||||||
| 	dl.lock.Lock() | 	dl.lock.Lock() | ||||||
| 	defer dl.lock.Unlock() | 	defer dl.lock.Unlock() | ||||||
| 	if dl.storageList[accountHash] != nil { | 
 | ||||||
| 		return dl.storageList[accountHash] | 	storageMap := dl.storageData[accountHash] | ||||||
|  | 	storageList := make([]common.Hash, 0, len(storageMap)) | ||||||
|  | 	for k, _ := range storageMap { | ||||||
|  | 		storageList = append(storageList, k) | ||||||
| 	} | 	} | ||||||
| 	accountStorageMap := dl.storageData[accountHash] | 	sort.Sort(hashes(storageList)) | ||||||
| 	accountStorageList := make([]common.Hash, len(accountStorageMap)) | 	dl.storageList[accountHash] = storageList | ||||||
| 	i := 0 | 	return storageList | ||||||
| 	for k, _ := range accountStorageMap { |  | ||||||
| 		accountStorageList[i] = k |  | ||||||
| 		i++ |  | ||||||
| 		// This would be a pretty good opportunity to also
 |  | ||||||
| 		// calculate the size, if we want to
 |  | ||||||
| 	} |  | ||||||
| 	sort.Sort(hashes(accountStorageList)) |  | ||||||
| 	dl.storageList[accountHash] = accountStorageList |  | ||||||
| 	return accountStorageList |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,7 +18,6 @@ package snapshot | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"math/big" |  | ||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| @ -26,21 +25,8 @@ import ( | |||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
| 	"github.com/ethereum/go-ethereum/crypto" | 	"github.com/ethereum/go-ethereum/crypto" | ||||||
| 	"github.com/ethereum/go-ethereum/ethdb/memorydb" | 	"github.com/ethereum/go-ethereum/ethdb/memorydb" | ||||||
| 	"github.com/ethereum/go-ethereum/rlp" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func randomAccount() []byte { |  | ||||||
| 	root := randomHash() |  | ||||||
| 	a := Account{ |  | ||||||
| 		Balance:  big.NewInt(rand.Int63()), |  | ||||||
| 		Nonce:    rand.Uint64(), |  | ||||||
| 		Root:     root[:], |  | ||||||
| 		CodeHash: emptyCode[:], |  | ||||||
| 	} |  | ||||||
| 	data, _ := rlp.EncodeToBytes(a) |  | ||||||
| 	return data |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // TestMergeBasics tests some simple merges
 | // TestMergeBasics tests some simple merges
 | ||||||
| func TestMergeBasics(t *testing.T) { | func TestMergeBasics(t *testing.T) { | ||||||
| 	var ( | 	var ( | ||||||
|  | |||||||
| @ -48,6 +48,11 @@ func (dl *diskLayer) Root() common.Hash { | |||||||
| 	return dl.root | 	return dl.root | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Parent always returns nil as there's no layer below the disk.
 | ||||||
|  | func (dl *diskLayer) Parent() snapshot { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Stale return whether this layer has become stale (was flattened across) or if
 | // Stale return whether this layer has become stale (was flattened across) or if
 | ||||||
| // it's still live.
 | // it's still live.
 | ||||||
| func (dl *diskLayer) Stale() bool { | func (dl *diskLayer) Stale() bool { | ||||||
|  | |||||||
| @ -18,18 +18,17 @@ package snapshot | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
| 	"sort" | 	"sort" | ||||||
| 
 | 
 | ||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
|  | 	"github.com/ethereum/go-ethereum/core/rawdb" | ||||||
|  | 	"github.com/ethereum/go-ethereum/ethdb" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // AccountIterator is an iterator to step over all the accounts in a snapshot,
 | // AccountIterator is an iterator to step over all the accounts in a snapshot,
 | ||||||
| // which may or may npt be composed of multiple layers.
 | // which may or may npt be composed of multiple layers.
 | ||||||
| type AccountIterator interface { | type AccountIterator interface { | ||||||
| 	// Seek steps the iterator forward as many elements as needed, so that after
 |  | ||||||
| 	// calling Next(), the iterator will be at a key higher than the given hash.
 |  | ||||||
| 	Seek(hash common.Hash) |  | ||||||
| 
 |  | ||||||
| 	// Next steps the iterator forward one element, returning false if exhausted,
 | 	// Next steps the iterator forward one element, returning false if exhausted,
 | ||||||
| 	// or an error if iteration failed for some reason (e.g. root being iterated
 | 	// or an error if iteration failed for some reason (e.g. root being iterated
 | ||||||
| 	// becomes stale and garbage collected).
 | 	// becomes stale and garbage collected).
 | ||||||
| @ -39,43 +38,133 @@ type AccountIterator interface { | |||||||
| 	// caused a premature iteration exit (e.g. snapshot stack becoming stale).
 | 	// caused a premature iteration exit (e.g. snapshot stack becoming stale).
 | ||||||
| 	Error() error | 	Error() error | ||||||
| 
 | 
 | ||||||
| 	// Key returns the hash of the account the iterator is currently at.
 | 	// Hash returns the hash of the account the iterator is currently at.
 | ||||||
| 	Key() common.Hash | 	Hash() common.Hash | ||||||
| 
 | 
 | ||||||
| 	// Value returns the RLP encoded slim account the iterator is currently at.
 | 	// Account returns the RLP encoded slim account the iterator is currently at.
 | ||||||
| 	// An error will be returned if the iterator becomes invalid (e.g. snaph
 | 	// An error will be returned if the iterator becomes invalid (e.g. snaph
 | ||||||
| 	Value() []byte | 	Account() []byte | ||||||
|  | 
 | ||||||
|  | 	// Release releases associated resources. Release should always succeed and
 | ||||||
|  | 	// can be called multiple times without causing error.
 | ||||||
|  | 	Release() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // diffAccountIterator is an account iterator that steps over the accounts (both
 | // diffAccountIterator is an account iterator that steps over the accounts (both
 | ||||||
| // live and deleted) contained within a single
 | // live and deleted) contained within a single diff layer. Higher order iterators
 | ||||||
|  | // will use the deleted accounts to skip deeper iterators.
 | ||||||
| type diffAccountIterator struct { | type diffAccountIterator struct { | ||||||
| 	layer *diffLayer | 	// curHash is the current hash the iterator is positioned on. The field is
 | ||||||
| 	index int | 	// explicitly tracked since the referenced diff layer might go stale after
 | ||||||
|  | 	// the iterator was positioned and we don't want to fail accessing the old
 | ||||||
|  | 	// hash as long as the iterator is not touched any more.
 | ||||||
|  | 	curHash common.Hash | ||||||
|  | 
 | ||||||
|  | 	// curAccount is the current value the iterator is positioned on. The field
 | ||||||
|  | 	// is explicitly tracked since the referenced diff layer might go stale after
 | ||||||
|  | 	// the iterator was positioned and we don't want to fail accessing the old
 | ||||||
|  | 	// value as long as the iterator is not touched any more.
 | ||||||
|  | 	curAccount []byte | ||||||
|  | 
 | ||||||
|  | 	layer *diffLayer    // Live layer to retrieve values from
 | ||||||
|  | 	keys  []common.Hash // Keys left in the layer to iterate
 | ||||||
|  | 	fail  error         // Any failures encountered (stale)
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (dl *diffLayer) newAccountIterator() *diffAccountIterator { | // AccountIterator creates an account iterator over a single diff layer.
 | ||||||
| 	dl.AccountList() | func (dl *diffLayer) AccountIterator(seek common.Hash) AccountIterator { | ||||||
| 	return &diffAccountIterator{layer: dl, index: -1} | 	// Seek out the requested starting account
 | ||||||
| } | 	hashes := dl.AccountList() | ||||||
| 
 | 	index := sort.Search(len(hashes), func(i int) bool { | ||||||
| // Seek steps the iterator forward as many elements as needed, so that after
 | 		return bytes.Compare(seek[:], hashes[i][:]) < 0 | ||||||
| // calling Next(), the iterator will be at a key higher than the given hash.
 |  | ||||||
| func (it *diffAccountIterator) Seek(key common.Hash) { |  | ||||||
| 	// Search uses binary search to find and return the smallest index i
 |  | ||||||
| 	// in [0, n) at which f(i) is true
 |  | ||||||
| 	index := sort.Search(len(it.layer.accountList), func(i int) bool { |  | ||||||
| 		return bytes.Compare(key[:], it.layer.accountList[i][:]) < 0 |  | ||||||
| 	}) | 	}) | ||||||
| 	it.index = index - 1 | 	// Assemble and returned the already seeked iterator
 | ||||||
|  | 	return &diffAccountIterator{ | ||||||
|  | 		layer: dl, | ||||||
|  | 		keys:  hashes[index:], | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Next steps the iterator forward one element, returning false if exhausted.
 | // Next steps the iterator forward one element, returning false if exhausted.
 | ||||||
| func (it *diffAccountIterator) Next() bool { | func (it *diffAccountIterator) Next() bool { | ||||||
| 	if it.index < len(it.layer.accountList) { | 	// If the iterator was already stale, consider it a programmer error. Although
 | ||||||
| 		it.index++ | 	// we could just return false here, triggering this path would probably mean
 | ||||||
|  | 	// somebody forgot to check for Error, so lets blow up instead of undefined
 | ||||||
|  | 	// behavior that's hard to debug.
 | ||||||
|  | 	if it.fail != nil { | ||||||
|  | 		panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) | ||||||
| 	} | 	} | ||||||
| 	return it.index < len(it.layer.accountList) | 	// Stop iterating if all keys were exhausted
 | ||||||
|  | 	if len(it.keys) == 0 { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	// Iterator seems to be still alive, retrieve and cache the live hash and
 | ||||||
|  | 	// account value, or fail now if layer became stale
 | ||||||
|  | 	it.layer.lock.RLock() | ||||||
|  | 	defer it.layer.lock.RUnlock() | ||||||
|  | 
 | ||||||
|  | 	if it.layer.stale { | ||||||
|  | 		it.fail, it.keys = ErrSnapshotStale, nil | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	it.curHash = it.keys[0] | ||||||
|  | 	if blob, ok := it.layer.accountData[it.curHash]; !ok { | ||||||
|  | 		panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash)) | ||||||
|  | 	} else { | ||||||
|  | 		it.curAccount = blob | ||||||
|  | 	} | ||||||
|  | 	// Values cached, shift the iterator and notify the user of success
 | ||||||
|  | 	it.keys = it.keys[1:] | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Error returns any failure that occurred during iteration, which might have
 | ||||||
|  | // caused a premature iteration exit (e.g. snapshot stack becoming stale).
 | ||||||
|  | func (it *diffAccountIterator) Error() error { | ||||||
|  | 	return it.fail | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Hash returns the hash of the account the iterator is currently at.
 | ||||||
|  | func (it *diffAccountIterator) Hash() common.Hash { | ||||||
|  | 	return it.curHash | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Account returns the RLP encoded slim account the iterator is currently at.
 | ||||||
|  | func (it *diffAccountIterator) Account() []byte { | ||||||
|  | 	return it.curAccount | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release is a noop for diff account iterators as there are no held resources.
 | ||||||
|  | func (it *diffAccountIterator) Release() {} | ||||||
|  | 
 | ||||||
|  | // diskAccountIterator is an account iterator that steps over the live accounts
 | ||||||
|  | // contained within a disk layer.
 | ||||||
|  | type diskAccountIterator struct { | ||||||
|  | 	layer *diskLayer | ||||||
|  | 	it    ethdb.Iterator | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // AccountIterator creates an account iterator over a disk layer.
 | ||||||
|  | func (dl *diskLayer) AccountIterator(seek common.Hash) AccountIterator { | ||||||
|  | 	return &diskAccountIterator{ | ||||||
|  | 		layer: dl, | ||||||
|  | 		it:    dl.diskdb.NewIteratorWithPrefix(append(rawdb.SnapshotAccountPrefix, seek[:]...)), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Next steps the iterator forward one element, returning false if exhausted.
 | ||||||
|  | func (it *diskAccountIterator) Next() bool { | ||||||
|  | 	// If the iterator was already exhausted, don't bother
 | ||||||
|  | 	if it.it == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	// Try to advance the iterator and release it if we reahed the end
 | ||||||
|  | 	if !it.it.Next() || !bytes.HasPrefix(it.it.Key(), rawdb.SnapshotAccountPrefix) { | ||||||
|  | 		it.it.Release() | ||||||
|  | 		it.it = nil | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Error returns any failure that occurred during iteration, which might have
 | // Error returns any failure that occurred during iteration, which might have
 | ||||||
| @ -83,34 +172,25 @@ func (it *diffAccountIterator) Next() bool { | |||||||
| //
 | //
 | ||||||
| // A diff layer is immutable after creation content wise and can always be fully
 | // A diff layer is immutable after creation content wise and can always be fully
 | ||||||
| // iterated without error, so this method always returns nil.
 | // iterated without error, so this method always returns nil.
 | ||||||
| func (it *diffAccountIterator) Error() error { | func (it *diskAccountIterator) Error() error { | ||||||
| 	return nil | 	return it.it.Error() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Key returns the hash of the account the iterator is currently at.
 | // Hash returns the hash of the account the iterator is currently at.
 | ||||||
| func (it *diffAccountIterator) Key() common.Hash { | func (it *diskAccountIterator) Hash() common.Hash { | ||||||
| 	if it.index < len(it.layer.accountList) { | 	return common.BytesToHash(it.it.Key()) | ||||||
| 		return it.layer.accountList[it.index] | } | ||||||
|  | 
 | ||||||
|  | // Account returns the RLP encoded slim account the iterator is currently at.
 | ||||||
|  | func (it *diskAccountIterator) Account() []byte { | ||||||
|  | 	return it.it.Value() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release releases the database snapshot held during iteration.
 | ||||||
|  | func (it *diskAccountIterator) Release() { | ||||||
|  | 	// The iterator is auto-released on exhaustion, so make sure it's still alive
 | ||||||
|  | 	if it.it != nil { | ||||||
|  | 		it.it.Release() | ||||||
|  | 		it.it = nil | ||||||
| 	} | 	} | ||||||
| 	return common.Hash{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Value returns the RLP encoded slim account the iterator is currently at.
 |  | ||||||
| func (it *diffAccountIterator) Value() []byte { |  | ||||||
| 	it.layer.lock.RLock() |  | ||||||
| 	defer it.layer.lock.RUnlock() |  | ||||||
| 
 |  | ||||||
| 	hash := it.layer.accountList[it.index] |  | ||||||
| 	if data, ok := it.layer.accountData[hash]; ok { |  | ||||||
| 		return data |  | ||||||
| 	} |  | ||||||
| 	panic("iterator references non-existent layer account") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (dl *diffLayer) iterators() []AccountIterator { |  | ||||||
| 	if parent, ok := dl.parent.(*diffLayer); ok { |  | ||||||
| 		iterators := parent.iterators() |  | ||||||
| 		return append(iterators, dl.newAccountIterator()) |  | ||||||
| 	} |  | ||||||
| 	return []AccountIterator{dl.newAccountIterator()} |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -40,10 +40,10 @@ func (dl *diffLayer) newBinaryAccountIterator() AccountIterator { | |||||||
| 	parent, ok := dl.parent.(*diffLayer) | 	parent, ok := dl.parent.(*diffLayer) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		// parent is the disk layer
 | 		// parent is the disk layer
 | ||||||
| 		return dl.newAccountIterator() | 		return dl.AccountIterator(common.Hash{}) | ||||||
| 	} | 	} | ||||||
| 	l := &binaryAccountIterator{ | 	l := &binaryAccountIterator{ | ||||||
| 		a: dl.newAccountIterator(), | 		a: dl.AccountIterator(common.Hash{}).(*diffAccountIterator), | ||||||
| 		b: parent.newBinaryAccountIterator(), | 		b: parent.newBinaryAccountIterator(), | ||||||
| 	} | 	} | ||||||
| 	l.aDone = !l.a.Next() | 	l.aDone = !l.a.Next() | ||||||
| @ -51,12 +51,6 @@ func (dl *diffLayer) newBinaryAccountIterator() AccountIterator { | |||||||
| 	return l | 	return l | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Seek steps the iterator forward as many elements as needed, so that after
 |  | ||||||
| // calling Next(), the iterator will be at a key higher than the given hash.
 |  | ||||||
| func (it *binaryAccountIterator) Seek(key common.Hash) { |  | ||||||
| 	panic("todo: implement") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Next steps the iterator forward one element, returning false if exhausted,
 | // Next steps the iterator forward one element, returning false if exhausted,
 | ||||||
| // or an error if iteration failed for some reason (e.g. root being iterated
 | // or an error if iteration failed for some reason (e.g. root being iterated
 | ||||||
| // becomes stale and garbage collected).
 | // becomes stale and garbage collected).
 | ||||||
| @ -64,9 +58,9 @@ func (it *binaryAccountIterator) Next() bool { | |||||||
| 	if it.aDone && it.bDone { | 	if it.aDone && it.bDone { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	nextB := it.b.Key() | 	nextB := it.b.Hash() | ||||||
| first: | first: | ||||||
| 	nextA := it.a.Key() | 	nextA := it.a.Hash() | ||||||
| 	if it.aDone { | 	if it.aDone { | ||||||
| 		it.bDone = !it.b.Next() | 		it.bDone = !it.b.Next() | ||||||
| 		it.k = nextB | 		it.k = nextB | ||||||
| @ -97,15 +91,15 @@ func (it *binaryAccountIterator) Error() error { | |||||||
| 	return it.fail | 	return it.fail | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Key returns the hash of the account the iterator is currently at.
 | // Hash returns the hash of the account the iterator is currently at.
 | ||||||
| func (it *binaryAccountIterator) Key() common.Hash { | func (it *binaryAccountIterator) Hash() common.Hash { | ||||||
| 	return it.k | 	return it.k | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Value returns the RLP encoded slim account the iterator is currently at, or
 | // Account returns the RLP encoded slim account the iterator is currently at, or
 | ||||||
| // nil if the iterated snapshot stack became stale (you can check Error after
 | // nil if the iterated snapshot stack became stale (you can check Error after
 | ||||||
| // to see if it failed or not).
 | // to see if it failed or not).
 | ||||||
| func (it *binaryAccountIterator) Value() []byte { | func (it *binaryAccountIterator) Account() []byte { | ||||||
| 	blob, err := it.a.layer.AccountRLP(it.k) | 	blob, err := it.a.layer.AccountRLP(it.k) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		it.fail = err | 		it.fail = err | ||||||
| @ -113,3 +107,9 @@ func (it *binaryAccountIterator) Value() []byte { | |||||||
| 	} | 	} | ||||||
| 	return blob | 	return blob | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // Release recursively releases all the iterators in the stack.
 | ||||||
|  | func (it *binaryAccountIterator) Release() { | ||||||
|  | 	it.a.Release() | ||||||
|  | 	it.b.Release() | ||||||
|  | } | ||||||
|  | |||||||
| @ -24,90 +24,121 @@ import ( | |||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type weightedIterator struct { | // weightedAccountIterator is an account iterator with an assigned weight. It is
 | ||||||
|  | // used to prioritise which account is the correct one if multiple iterators find
 | ||||||
|  | // the same one (modified in multiple consecutive blocks).
 | ||||||
|  | type weightedAccountIterator struct { | ||||||
| 	it       AccountIterator | 	it       AccountIterator | ||||||
| 	priority int | 	priority int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // weightedAccountIterators is a set of iterators implementing the sort.Interface.
 | ||||||
|  | type weightedAccountIterators []*weightedAccountIterator | ||||||
|  | 
 | ||||||
|  | // Len implements sort.Interface, returning the number of active iterators.
 | ||||||
|  | func (its weightedAccountIterators) Len() int { return len(its) } | ||||||
|  | 
 | ||||||
|  | // Less implements sort.Interface, returning which of two iterators in the stack
 | ||||||
|  | // is before the other.
 | ||||||
|  | func (its weightedAccountIterators) Less(i, j int) bool { | ||||||
|  | 	// Order the iterators primarilly by the account hashes
 | ||||||
|  | 	hashI := its[i].it.Hash() | ||||||
|  | 	hashJ := its[j].it.Hash() | ||||||
|  | 
 | ||||||
|  | 	switch bytes.Compare(hashI[:], hashJ[:]) { | ||||||
|  | 	case -1: | ||||||
|  | 		return true | ||||||
|  | 	case 1: | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	// Same account in multiple layers, split by priority
 | ||||||
|  | 	return its[i].priority < its[j].priority | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Swap implements sort.Interface, swapping two entries in the iterator stack.
 | ||||||
|  | func (its weightedAccountIterators) Swap(i, j int) { | ||||||
|  | 	its[i], its[j] = its[j], its[i] | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // fastAccountIterator is a more optimized multi-layer iterator which maintains a
 | // fastAccountIterator is a more optimized multi-layer iterator which maintains a
 | ||||||
| // direct mapping of all iterators leading down to the bottom layer
 | // direct mapping of all iterators leading down to the bottom layer.
 | ||||||
| type fastAccountIterator struct { | type fastAccountIterator struct { | ||||||
| 	iterators []*weightedIterator | 	tree *Tree       // Snapshot tree to reinitialize stale sub-iterators with
 | ||||||
|  | 	root common.Hash // Root hash to reinitialize stale sub-iterators through
 | ||||||
|  | 
 | ||||||
|  | 	iterators weightedAccountIterators | ||||||
| 	initiated bool | 	initiated bool | ||||||
| 	fail      error | 	fail      error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // newFastAccountIterator creates a new fastAccountIterator
 | // newFastAccountIterator creates a new hierarhical account iterator with one
 | ||||||
| func (dl *diffLayer) newFastAccountIterator() AccountIterator { | // element per diff layer. The returned combo iterator can be used to walk over
 | ||||||
| 	f := &fastAccountIterator{ | // the entire snapshot diff stack simultaneously.
 | ||||||
| 		initiated: false, | func newFastAccountIterator(tree *Tree, root common.Hash, seek common.Hash) (AccountIterator, error) { | ||||||
|  | 	snap := tree.Snapshot(root) | ||||||
|  | 	if snap == nil { | ||||||
|  | 		return nil, fmt.Errorf("unknown snapshot: %x", root) | ||||||
| 	} | 	} | ||||||
| 	for i, it := range dl.iterators() { | 	fi := &fastAccountIterator{ | ||||||
| 		f.iterators = append(f.iterators, &weightedIterator{it, -i}) | 		tree: tree, | ||||||
|  | 		root: root, | ||||||
| 	} | 	} | ||||||
| 	f.Seek(common.Hash{}) | 	current := snap.(snapshot) | ||||||
| 	return f | 	for depth := 0; current != nil; depth++ { | ||||||
|  | 		fi.iterators = append(fi.iterators, &weightedAccountIterator{ | ||||||
|  | 			it:       current.AccountIterator(seek), | ||||||
|  | 			priority: depth, | ||||||
|  | 		}) | ||||||
|  | 		current = current.Parent() | ||||||
|  | 	} | ||||||
|  | 	fi.init() | ||||||
|  | 	return fi, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Len returns the number of active iterators
 | // init walks over all the iterators and resolves any clashes between them, after
 | ||||||
| func (fi *fastAccountIterator) Len() int { | // which it prepares the stack for step-by-step iteration.
 | ||||||
| 	return len(fi.iterators) | func (fi *fastAccountIterator) init() { | ||||||
| } | 	// Track which account hashes are iterators positioned on
 | ||||||
|  | 	var positioned = make(map[common.Hash]int) | ||||||
| 
 | 
 | ||||||
| // Less implements sort.Interface
 | 	// Position all iterators and track how many remain live
 | ||||||
| func (fi *fastAccountIterator) Less(i, j int) bool { |  | ||||||
| 	a := fi.iterators[i].it.Key() |  | ||||||
| 	b := fi.iterators[j].it.Key() |  | ||||||
| 	bDiff := bytes.Compare(a[:], b[:]) |  | ||||||
| 	if bDiff < 0 { |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 	if bDiff > 0 { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	// keys are equal, sort by iterator priority
 |  | ||||||
| 	return fi.iterators[i].priority < fi.iterators[j].priority |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Swap implements sort.Interface
 |  | ||||||
| func (fi *fastAccountIterator) Swap(i, j int) { |  | ||||||
| 	fi.iterators[i], fi.iterators[j] = fi.iterators[j], fi.iterators[i] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (fi *fastAccountIterator) Seek(key common.Hash) { |  | ||||||
| 	// We need to apply this across all iterators
 |  | ||||||
| 	var seen = make(map[common.Hash]int) |  | ||||||
| 
 |  | ||||||
| 	length := len(fi.iterators) |  | ||||||
| 	for i := 0; i < len(fi.iterators); i++ { | 	for i := 0; i < len(fi.iterators); i++ { | ||||||
| 		//for i, it := range fi.iterators {
 | 		// Retrieve the first element and if it clashes with a previous iterator,
 | ||||||
|  | 		// advance either the current one or the old one. Repeat until nothing is
 | ||||||
|  | 		// clashing any more.
 | ||||||
| 		it := fi.iterators[i] | 		it := fi.iterators[i] | ||||||
| 		it.it.Seek(key) |  | ||||||
| 		for { | 		for { | ||||||
|  | 			// If the iterator is exhausted, drop it off the end
 | ||||||
| 			if !it.it.Next() { | 			if !it.it.Next() { | ||||||
| 				// To be removed
 | 				it.it.Release() | ||||||
| 				// swap it to the last position for now
 | 				last := len(fi.iterators) - 1 | ||||||
| 				fi.iterators[i], fi.iterators[length-1] = fi.iterators[length-1], fi.iterators[i] | 
 | ||||||
| 				length-- | 				fi.iterators[i] = fi.iterators[last] | ||||||
|  | 				fi.iterators[last] = nil | ||||||
|  | 				fi.iterators = fi.iterators[:last] | ||||||
|  | 
 | ||||||
|  | 				i-- | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 			v := it.it.Key() | 			// The iterator is still alive, check for collisions with previous ones
 | ||||||
| 			if other, exist := seen[v]; !exist { | 			hash := it.it.Hash() | ||||||
| 				seen[v] = i | 			if other, exist := positioned[hash]; !exist { | ||||||
|  | 				positioned[hash] = i | ||||||
| 				break | 				break | ||||||
| 			} else { | 			} else { | ||||||
|  | 				// Iterators collide, one needs to be progressed, use priority to
 | ||||||
|  | 				// determine which.
 | ||||||
|  | 				//
 | ||||||
| 				// This whole else-block can be avoided, if we instead
 | 				// This whole else-block can be avoided, if we instead
 | ||||||
| 				// do an inital priority-sort of the iterators. If we do that,
 | 				// do an inital priority-sort of the iterators. If we do that,
 | ||||||
| 				// then we'll only wind up here if a lower-priority (preferred) iterator
 | 				// then we'll only wind up here if a lower-priority (preferred) iterator
 | ||||||
| 				// has the same value, and then we will always just continue.
 | 				// has the same value, and then we will always just continue.
 | ||||||
| 				// However, it costs an extra sort, so it's probably not better
 | 				// However, it costs an extra sort, so it's probably not better
 | ||||||
| 
 |  | ||||||
| 				// One needs to be progressed, use priority to determine which
 |  | ||||||
| 				if fi.iterators[other].priority < it.priority { | 				if fi.iterators[other].priority < it.priority { | ||||||
| 					// the 'it' should be progressed
 | 					// The 'it' should be progressed
 | ||||||
| 					continue | 					continue | ||||||
| 				} else { | 				} else { | ||||||
| 					// the 'other' should be progressed - swap them
 | 					// The 'other' should be progressed, swap them
 | ||||||
| 					it = fi.iterators[other] | 					it = fi.iterators[other] | ||||||
| 					fi.iterators[other], fi.iterators[i] = fi.iterators[i], fi.iterators[other] | 					fi.iterators[other], fi.iterators[i] = fi.iterators[i], fi.iterators[other] | ||||||
| 					continue | 					continue | ||||||
| @ -115,15 +146,12 @@ func (fi *fastAccountIterator) Seek(key common.Hash) { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	// Now remove those that were placed in the end
 | 	// Re-sort the entire list
 | ||||||
| 	fi.iterators = fi.iterators[:length] | 	sort.Sort(fi.iterators) | ||||||
| 	// The list is now totally unsorted, need to re-sort the entire list
 |  | ||||||
| 	sort.Sort(fi) |  | ||||||
| 	fi.initiated = false | 	fi.initiated = false | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Next implements the Iterator interface. It returns false if no more elemnts
 | // Next steps the iterator forward one element, returning false if exhausted.
 | ||||||
| // can be retrieved (false == exhausted)
 |  | ||||||
| func (fi *fastAccountIterator) Next() bool { | func (fi *fastAccountIterator) Next() bool { | ||||||
| 	if len(fi.iterators) == 0 { | 	if len(fi.iterators) == 0 { | ||||||
| 		return false | 		return false | ||||||
| @ -134,101 +162,88 @@ func (fi *fastAccountIterator) Next() bool { | |||||||
| 		fi.initiated = true | 		fi.initiated = true | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| 	return fi.innerNext(0) | 	return fi.next(0) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // innerNext handles the next operation internally,
 | // next handles the next operation internally and should be invoked when we know
 | ||||||
| // and should be invoked when we know that two elements in the list may have
 | // that two elements in the list may have the same value.
 | ||||||
| // the same value.
 | //
 | ||||||
| // For example, if the list becomes [2,3,5,5,8,9,10], then we should invoke
 | // For example, if the iterated hashes become [2,3,5,5,8,9,10], then we should
 | ||||||
| // innerNext(3), which will call Next on elem 3 (the second '5'). It will continue
 | // invoke next(3), which will call Next on elem 3 (the second '5') and will
 | ||||||
| // along the list and apply the same operation if needed
 | // cascade along the list, applying the same operation if needed.
 | ||||||
| func (fi *fastAccountIterator) innerNext(pos int) bool { | func (fi *fastAccountIterator) next(idx int) bool { | ||||||
| 	if !fi.iterators[pos].it.Next() { | 	// If this particular iterator got exhausted, remove it and return true (the
 | ||||||
| 		//Exhausted, remove this iterator
 | 	// next one is surely not exhausted yet, otherwise it would have been removed
 | ||||||
| 		fi.remove(pos) | 	// already).
 | ||||||
| 		if len(fi.iterators) == 0 { | 	if it := fi.iterators[idx].it; !it.Next() { | ||||||
| 			return false | 		it.Release() | ||||||
| 		} | 
 | ||||||
|  | 		fi.iterators = append(fi.iterators[:idx], fi.iterators[idx+1:]...) | ||||||
|  | 		return len(fi.iterators) > 0 | ||||||
|  | 	} | ||||||
|  | 	// If there's noone left to cascade into, return
 | ||||||
|  | 	if idx == len(fi.iterators)-1 { | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| 	if pos == len(fi.iterators)-1 { | 	// We next-ed the iterator at 'idx', now we may have to re-sort that element
 | ||||||
| 		// Only one iterator left
 |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 	// We next:ed the elem at 'pos'. Now we may have to re-sort that elem
 |  | ||||||
| 	var ( | 	var ( | ||||||
| 		current, neighbour = fi.iterators[pos], fi.iterators[pos+1] | 		cur, next         = fi.iterators[idx], fi.iterators[idx+1] | ||||||
| 		val, neighbourVal  = current.it.Key(), neighbour.it.Key() | 		curHash, nextHash = cur.it.Hash(), next.it.Hash() | ||||||
| 	) | 	) | ||||||
| 	if diff := bytes.Compare(val[:], neighbourVal[:]); diff < 0 { | 	if diff := bytes.Compare(curHash[:], nextHash[:]); diff < 0 { | ||||||
| 		// It is still in correct place
 | 		// It is still in correct place
 | ||||||
| 		return true | 		return true | ||||||
| 	} else if diff == 0 && current.priority < neighbour.priority { | 	} else if diff == 0 && cur.priority < next.priority { | ||||||
| 		// So still in correct place, but we need to iterate on the neighbour
 | 		// So still in correct place, but we need to iterate on the next
 | ||||||
| 		fi.innerNext(pos + 1) | 		fi.next(idx + 1) | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| 	// At this point, the elem is in the wrong location, but the
 | 	// At this point, the iterator is in the wrong location, but the remaining
 | ||||||
| 	// remaining list is sorted. Find out where to move the elem
 | 	// list is sorted. Find out where to move the item.
 | ||||||
| 	iteratee := -1 | 	clash := -1 | ||||||
| 	index := sort.Search(len(fi.iterators), func(n int) bool { | 	index := sort.Search(len(fi.iterators), func(n int) bool { | ||||||
| 		if n < pos { | 		// The iterator always advances forward, so anything before the old slot
 | ||||||
| 			// No need to search 'behind' us
 | 		// is known to be behind us, so just skip them altogether. This actually
 | ||||||
|  | 		// is an important clause since the sort order got invalidated.
 | ||||||
|  | 		if n < idx { | ||||||
| 			return false | 			return false | ||||||
| 		} | 		} | ||||||
| 		if n == len(fi.iterators)-1 { | 		if n == len(fi.iterators)-1 { | ||||||
| 			// Can always place an elem last
 | 			// Can always place an elem last
 | ||||||
| 			return true | 			return true | ||||||
| 		} | 		} | ||||||
| 		neighbour := fi.iterators[n+1].it.Key() | 		nextHash := fi.iterators[n+1].it.Hash() | ||||||
| 		if diff := bytes.Compare(val[:], neighbour[:]); diff < 0 { | 		if diff := bytes.Compare(curHash[:], nextHash[:]); diff < 0 { | ||||||
| 			return true | 			return true | ||||||
| 		} else if diff > 0 { | 		} else if diff > 0 { | ||||||
| 			return false | 			return false | ||||||
| 		} | 		} | ||||||
| 		// The elem we're placing it next to has the same value,
 | 		// The elem we're placing it next to has the same value,
 | ||||||
| 		// so whichever winds up on n+1 will need further iteraton
 | 		// so whichever winds up on n+1 will need further iteraton
 | ||||||
| 		iteratee = n + 1 | 		clash = n + 1 | ||||||
| 		if current.priority < fi.iterators[n+1].priority { | 		if cur.priority < fi.iterators[n+1].priority { | ||||||
| 			// We can drop the iterator here
 | 			// We can drop the iterator here
 | ||||||
| 			return true | 			return true | ||||||
| 		} | 		} | ||||||
| 		// We need to move it one step further
 | 		// We need to move it one step further
 | ||||||
| 		return false | 		return false | ||||||
| 		// TODO benchmark which is best, this works too:
 | 		// TODO benchmark which is best, this works too:
 | ||||||
| 		//iteratee = n
 | 		//clash = n
 | ||||||
| 		//return true
 | 		//return true
 | ||||||
| 		// Doing so should finish the current search earlier
 | 		// Doing so should finish the current search earlier
 | ||||||
| 	}) | 	}) | ||||||
| 	fi.move(pos, index) | 	fi.move(idx, index) | ||||||
| 	if iteratee != -1 { | 	if clash != -1 { | ||||||
| 		fi.innerNext(iteratee) | 		fi.next(clash) | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // move moves an iterator to another position in the list
 | // move advances an iterator to another position in the list.
 | ||||||
| func (fi *fastAccountIterator) move(index, newpos int) { | func (fi *fastAccountIterator) move(index, newpos int) { | ||||||
| 	if newpos > len(fi.iterators)-1 { | 	elem := fi.iterators[index] | ||||||
| 		newpos = len(fi.iterators) - 1 | 	copy(fi.iterators[index:], fi.iterators[index+1:newpos+1]) | ||||||
| 	} | 	fi.iterators[newpos] = elem | ||||||
| 	var ( |  | ||||||
| 		elem   = fi.iterators[index] |  | ||||||
| 		middle = fi.iterators[index+1 : newpos+1] |  | ||||||
| 		suffix []*weightedIterator |  | ||||||
| 	) |  | ||||||
| 	if newpos < len(fi.iterators)-1 { |  | ||||||
| 		suffix = fi.iterators[newpos+1:] |  | ||||||
| 	} |  | ||||||
| 	fi.iterators = append(fi.iterators[:index], middle...) |  | ||||||
| 	fi.iterators = append(fi.iterators, elem) |  | ||||||
| 	fi.iterators = append(fi.iterators, suffix...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // remove drops an iterator from the list
 |  | ||||||
| func (fi *fastAccountIterator) remove(index int) { |  | ||||||
| 	fi.iterators = append(fi.iterators[:index], fi.iterators[index+1:]...) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Error returns any failure that occurred during iteration, which might have
 | // Error returns any failure that occurred during iteration, which might have
 | ||||||
| @ -237,20 +252,29 @@ func (fi *fastAccountIterator) Error() error { | |||||||
| 	return fi.fail | 	return fi.fail | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Key returns the current key
 | // Hash returns the current key
 | ||||||
| func (fi *fastAccountIterator) Key() common.Hash { | func (fi *fastAccountIterator) Hash() common.Hash { | ||||||
| 	return fi.iterators[0].it.Key() | 	return fi.iterators[0].it.Hash() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Value returns the current key
 | // Account returns the current key
 | ||||||
| func (fi *fastAccountIterator) Value() []byte { | func (fi *fastAccountIterator) Account() []byte { | ||||||
| 	return fi.iterators[0].it.Value() | 	return fi.iterators[0].it.Account() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Release iterates over all the remaining live layer iterators and releases each
 | ||||||
|  | // of thme individually.
 | ||||||
|  | func (fi *fastAccountIterator) Release() { | ||||||
|  | 	for _, it := range fi.iterators { | ||||||
|  | 		it.it.Release() | ||||||
|  | 	} | ||||||
|  | 	fi.iterators = nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Debug is a convencience helper during testing
 | // Debug is a convencience helper during testing
 | ||||||
| func (fi *fastAccountIterator) Debug() { | func (fi *fastAccountIterator) Debug() { | ||||||
| 	for _, it := range fi.iterators { | 	for _, it := range fi.iterators { | ||||||
| 		fmt.Printf("[p=%v v=%v] ", it.priority, it.it.Key()[0]) | 		fmt.Printf("[p=%v v=%v] ", it.priority, it.it.Hash()[0]) | ||||||
| 	} | 	} | ||||||
| 	fmt.Println() | 	fmt.Println() | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,7 +23,9 @@ import ( | |||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/VictoriaMetrics/fastcache" | ||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
|  | 	"github.com/ethereum/go-ethereum/core/rawdb" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // TestIteratorBasics tests some simple single-layer iteration
 | // TestIteratorBasics tests some simple single-layer iteration
 | ||||||
| @ -47,7 +49,7 @@ func TestIteratorBasics(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	// Add some (identical) layers on top
 | 	// Add some (identical) layers on top
 | ||||||
| 	parent := newDiffLayer(emptyLayer(), common.Hash{}, accounts, storage) | 	parent := newDiffLayer(emptyLayer(), common.Hash{}, accounts, storage) | ||||||
| 	it := parent.newAccountIterator() | 	it := parent.AccountIterator(common.Hash{}) | ||||||
| 	verifyIterator(t, 100, it) | 	verifyIterator(t, 100, it) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -75,14 +77,16 @@ func (ti *testIterator) Error() error { | |||||||
| 	panic("implement me") | 	panic("implement me") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ti *testIterator) Key() common.Hash { | func (ti *testIterator) Hash() common.Hash { | ||||||
| 	return common.BytesToHash([]byte{ti.values[0]}) | 	return common.BytesToHash([]byte{ti.values[0]}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ti *testIterator) Value() []byte { | func (ti *testIterator) Account() []byte { | ||||||
| 	panic("implement me") | 	panic("implement me") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (ti *testIterator) Release() {} | ||||||
|  | 
 | ||||||
| func TestFastIteratorBasics(t *testing.T) { | func TestFastIteratorBasics(t *testing.T) { | ||||||
| 	type testCase struct { | 	type testCase struct { | ||||||
| 		lists   [][]byte | 		lists   [][]byte | ||||||
| @ -96,10 +100,10 @@ func TestFastIteratorBasics(t *testing.T) { | |||||||
| 			{9, 10}, {10, 13, 15, 16}}, | 			{9, 10}, {10, 13, 15, 16}}, | ||||||
| 			expKeys: []byte{0, 1, 2, 7, 8, 9, 10, 13, 14, 15, 16}}, | 			expKeys: []byte{0, 1, 2, 7, 8, 9, 10, 13, 14, 15, 16}}, | ||||||
| 	} { | 	} { | ||||||
| 		var iterators []*weightedIterator | 		var iterators []*weightedAccountIterator | ||||||
| 		for i, data := range tc.lists { | 		for i, data := range tc.lists { | ||||||
| 			it := newTestIterator(data...) | 			it := newTestIterator(data...) | ||||||
| 			iterators = append(iterators, &weightedIterator{it, i}) | 			iterators = append(iterators, &weightedAccountIterator{it, i}) | ||||||
| 
 | 
 | ||||||
| 		} | 		} | ||||||
| 		fi := &fastAccountIterator{ | 		fi := &fastAccountIterator{ | ||||||
| @ -108,7 +112,7 @@ func TestFastIteratorBasics(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 		count := 0 | 		count := 0 | ||||||
| 		for fi.Next() { | 		for fi.Next() { | ||||||
| 			if got, exp := fi.Key()[31], tc.expKeys[count]; exp != got { | 			if got, exp := fi.Hash()[31], tc.expKeys[count]; exp != got { | ||||||
| 				t.Errorf("tc %d, [%d]: got %d exp %d", i, count, got, exp) | 				t.Errorf("tc %d, [%d]: got %d exp %d", i, count, got, exp) | ||||||
| 			} | 			} | ||||||
| 			count++ | 			count++ | ||||||
| @ -117,68 +121,86 @@ func TestFastIteratorBasics(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func verifyIterator(t *testing.T, expCount int, it AccountIterator) { | func verifyIterator(t *testing.T, expCount int, it AccountIterator) { | ||||||
|  | 	t.Helper() | ||||||
|  | 
 | ||||||
| 	var ( | 	var ( | ||||||
| 		i    = 0 | 		count = 0 | ||||||
| 		last = common.Hash{} | 		last  = common.Hash{} | ||||||
| 	) | 	) | ||||||
| 	for it.Next() { | 	for it.Next() { | ||||||
| 		v := it.Key() | 		if hash := it.Hash(); bytes.Compare(last[:], hash[:]) >= 0 { | ||||||
| 		if bytes.Compare(last[:], v[:]) >= 0 { | 			t.Errorf("wrong order: %x >= %x", last, hash) | ||||||
| 			t.Errorf("Wrong order:\n%x \n>=\n%x", last, v) |  | ||||||
| 		} | 		} | ||||||
| 		i++ | 		count++ | ||||||
| 	} | 	} | ||||||
| 	if i != expCount { | 	if count != expCount { | ||||||
| 		t.Errorf("iterator len wrong, expected %d, got %d", expCount, i) | 		t.Errorf("iterator count mismatch: have %d, want %d", count, expCount) | ||||||
|  | 	} | ||||||
|  | 	if err := it.Error(); err != nil { | ||||||
|  | 		t.Errorf("iterator failed: %v", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TestIteratorTraversal tests some simple multi-layer iteration
 | // TestIteratorTraversal tests some simple multi-layer iteration.
 | ||||||
| func TestIteratorTraversal(t *testing.T) { | func TestIteratorTraversal(t *testing.T) { | ||||||
| 	var ( | 	// Create an empty base layer and a snapshot tree out of it
 | ||||||
| 		storage = make(map[common.Hash]map[common.Hash][]byte) | 	base := &diskLayer{ | ||||||
| 	) | 		diskdb: rawdb.NewMemoryDatabase(), | ||||||
| 
 | 		root:   common.HexToHash("0x01"), | ||||||
| 	mkAccounts := func(args ...string) map[common.Hash][]byte { | 		cache:  fastcache.New(1024 * 500), | ||||||
| 		accounts := make(map[common.Hash][]byte) |  | ||||||
| 		for _, h := range args { |  | ||||||
| 			accounts[common.HexToHash(h)] = randomAccount() |  | ||||||
| 		} |  | ||||||
| 		return accounts |  | ||||||
| 	} | 	} | ||||||
| 	// entries in multiple layers should only become output once
 | 	snaps := &Tree{ | ||||||
| 	parent := newDiffLayer(emptyLayer(), common.Hash{}, | 		layers: map[common.Hash]snapshot{ | ||||||
| 		mkAccounts("0xaa", "0xee", "0xff", "0xf0"), storage) | 			base.root: base, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	// Stack three diff layers on top with various overlaps
 | ||||||
|  | 	snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), | ||||||
|  | 		randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil) | ||||||
| 
 | 
 | ||||||
| 	child := parent.Update(common.Hash{}, | 	snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), | ||||||
| 		mkAccounts("0xbb", "0xdd", "0xf0"), storage) | 		randomAccountSet("0xbb", "0xdd", "0xf0"), nil) | ||||||
| 
 | 
 | ||||||
| 	child = child.Update(common.Hash{}, | 	snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), | ||||||
| 		mkAccounts("0xcc", "0xf0", "0xff"), storage) | 		randomAccountSet("0xcc", "0xf0", "0xff"), nil) | ||||||
| 
 | 
 | ||||||
| 	// single layer iterator
 | 	// Verify the single and multi-layer iterators
 | ||||||
| 	verifyIterator(t, 3, child.newAccountIterator()) | 	head := snaps.Snapshot(common.HexToHash("0x04")) | ||||||
| 	// multi-layered binary iterator
 | 
 | ||||||
| 	verifyIterator(t, 7, child.newBinaryAccountIterator()) | 	verifyIterator(t, 3, head.(snapshot).AccountIterator(common.Hash{})) | ||||||
| 	// multi-layered fast iterator
 | 	verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator()) | ||||||
| 	verifyIterator(t, 7, child.newFastAccountIterator()) | 
 | ||||||
|  | 	it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) | ||||||
|  | 	defer it.Release() | ||||||
|  | 
 | ||||||
|  | 	verifyIterator(t, 7, it) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TestIteratorTraversalValues tests some multi-layer iteration, where we
 | // TestIteratorTraversalValues tests some multi-layer iteration, where we
 | ||||||
| // also expect the correct values to show up
 | // also expect the correct values to show up.
 | ||||||
| func TestIteratorTraversalValues(t *testing.T) { | func TestIteratorTraversalValues(t *testing.T) { | ||||||
|  | 	// Create an empty base layer and a snapshot tree out of it
 | ||||||
|  | 	base := &diskLayer{ | ||||||
|  | 		diskdb: rawdb.NewMemoryDatabase(), | ||||||
|  | 		root:   common.HexToHash("0x01"), | ||||||
|  | 		cache:  fastcache.New(1024 * 500), | ||||||
|  | 	} | ||||||
|  | 	snaps := &Tree{ | ||||||
|  | 		layers: map[common.Hash]snapshot{ | ||||||
|  | 			base.root: base, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	// Create a batch of account sets to seed subsequent layers with
 | ||||||
| 	var ( | 	var ( | ||||||
| 		storage = make(map[common.Hash]map[common.Hash][]byte) | 		a = make(map[common.Hash][]byte) | ||||||
| 		a       = make(map[common.Hash][]byte) | 		b = make(map[common.Hash][]byte) | ||||||
| 		b       = make(map[common.Hash][]byte) | 		c = make(map[common.Hash][]byte) | ||||||
| 		c       = make(map[common.Hash][]byte) | 		d = make(map[common.Hash][]byte) | ||||||
| 		d       = make(map[common.Hash][]byte) | 		e = make(map[common.Hash][]byte) | ||||||
| 		e       = make(map[common.Hash][]byte) | 		f = make(map[common.Hash][]byte) | ||||||
| 		f       = make(map[common.Hash][]byte) | 		g = make(map[common.Hash][]byte) | ||||||
| 		g       = make(map[common.Hash][]byte) | 		h = make(map[common.Hash][]byte) | ||||||
| 		h       = make(map[common.Hash][]byte) |  | ||||||
| 	) | 	) | ||||||
| 	// entries in multiple layers should only become output once
 |  | ||||||
| 	for i := byte(2); i < 0xff; i++ { | 	for i := byte(2); i < 0xff; i++ { | ||||||
| 		a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i)) | 		a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i)) | ||||||
| 		if i > 20 && i%2 == 0 { | 		if i > 20 && i%2 == 0 { | ||||||
| @ -203,35 +225,36 @@ func TestIteratorTraversalValues(t *testing.T) { | |||||||
| 			h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i)) | 			h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	child := newDiffLayer(emptyLayer(), common.Hash{}, a, storage). | 	// Assemble a stack of snapshots from the account layers
 | ||||||
| 		Update(common.Hash{}, b, storage). | 	snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), a, nil) | ||||||
| 		Update(common.Hash{}, c, storage). | 	snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), b, nil) | ||||||
| 		Update(common.Hash{}, d, storage). | 	snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), c, nil) | ||||||
| 		Update(common.Hash{}, e, storage). | 	snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), d, nil) | ||||||
| 		Update(common.Hash{}, f, storage). | 	snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), e, nil) | ||||||
| 		Update(common.Hash{}, g, storage). | 	snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), f, nil) | ||||||
| 		Update(common.Hash{}, h, storage) | 	snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), g, nil) | ||||||
|  | 	snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), h, nil) | ||||||
| 
 | 
 | ||||||
| 	it := child.newFastAccountIterator() | 	it, _ := snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{}) | ||||||
|  | 	defer it.Release() | ||||||
|  | 
 | ||||||
|  | 	head := snaps.Snapshot(common.HexToHash("0x09")) | ||||||
| 	for it.Next() { | 	for it.Next() { | ||||||
| 		key := it.Key() | 		hash := it.Hash() | ||||||
| 		exp, err := child.accountRLP(key, 0) | 		want, err := head.AccountRLP(hash) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Fatal(err) | 			t.Fatalf("failed to retrieve expected account: %v", err) | ||||||
| 		} | 		} | ||||||
| 		got := it.Value() | 		if have := it.Account(); !bytes.Equal(want, have) { | ||||||
| 		if !bytes.Equal(exp, got) { | 			t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) | ||||||
| 			t.Fatalf("Error on key %x, got %v exp %v", key, string(got), string(exp)) |  | ||||||
| 		} | 		} | ||||||
| 		//fmt.Printf("val: %v\n", string(it.Value()))
 |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // This testcase is notorious, all layers contain the exact same 200 accounts.
 | ||||||
| func TestIteratorLargeTraversal(t *testing.T) { | func TestIteratorLargeTraversal(t *testing.T) { | ||||||
| 	// This testcase is a bit notorious -- all layers contain the exact
 | 	// Create a custom account factory to recreate the same addresses
 | ||||||
| 	// same 200 accounts.
 | 	makeAccounts := func(num int) map[common.Hash][]byte { | ||||||
| 	var storage = make(map[common.Hash]map[common.Hash][]byte) |  | ||||||
| 	mkAccounts := func(num int) map[common.Hash][]byte { |  | ||||||
| 		accounts := make(map[common.Hash][]byte) | 		accounts := make(map[common.Hash][]byte) | ||||||
| 		for i := 0; i < num; i++ { | 		for i := 0; i < num; i++ { | ||||||
| 			h := common.Hash{} | 			h := common.Hash{} | ||||||
| @ -240,25 +263,121 @@ func TestIteratorLargeTraversal(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 		return accounts | 		return accounts | ||||||
| 	} | 	} | ||||||
| 	parent := newDiffLayer(emptyLayer(), common.Hash{}, | 	// Build up a large stack of snapshots
 | ||||||
| 		mkAccounts(200), storage) | 	base := &diskLayer{ | ||||||
| 	child := parent.Update(common.Hash{}, | 		diskdb: rawdb.NewMemoryDatabase(), | ||||||
| 		mkAccounts(200), storage) | 		root:   common.HexToHash("0x01"), | ||||||
| 	for i := 2; i < 100; i++ { | 		cache:  fastcache.New(1024 * 500), | ||||||
| 		child = child.Update(common.Hash{}, |  | ||||||
| 			mkAccounts(200), storage) |  | ||||||
| 	} | 	} | ||||||
| 	// single layer iterator
 | 	snaps := &Tree{ | ||||||
| 	verifyIterator(t, 200, child.newAccountIterator()) | 		layers: map[common.Hash]snapshot{ | ||||||
| 	// multi-layered binary iterator
 | 			base.root: base, | ||||||
| 	verifyIterator(t, 200, child.newBinaryAccountIterator()) | 		}, | ||||||
| 	// multi-layered fast iterator
 | 	} | ||||||
| 	verifyIterator(t, 200, child.newFastAccountIterator()) | 	for i := 1; i < 128; i++ { | ||||||
|  | 		snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), makeAccounts(200), nil) | ||||||
|  | 	} | ||||||
|  | 	// Iterate the entire stack and ensure everything is hit only once
 | ||||||
|  | 	head := snaps.Snapshot(common.HexToHash("0x80")) | ||||||
|  | 	verifyIterator(t, 200, head.(snapshot).AccountIterator(common.Hash{})) | ||||||
|  | 	verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator()) | ||||||
|  | 
 | ||||||
|  | 	it, _ := snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{}) | ||||||
|  | 	defer it.Release() | ||||||
|  | 
 | ||||||
|  | 	verifyIterator(t, 200, it) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BenchmarkIteratorTraversal is a bit a bit notorious -- all layers contain the exact
 | // TestIteratorFlattening tests what happens when we
 | ||||||
| // same 200 accounts. That means that we need to process 2000 items, but only
 | // - have a live iterator on child C (parent C1 -> C2 .. CN)
 | ||||||
| // spit out 200 values eventually.
 | // - flattens C2 all the way into CN
 | ||||||
|  | // - continues iterating
 | ||||||
|  | func TestIteratorFlattening(t *testing.T) { | ||||||
|  | 	// Create an empty base layer and a snapshot tree out of it
 | ||||||
|  | 	base := &diskLayer{ | ||||||
|  | 		diskdb: rawdb.NewMemoryDatabase(), | ||||||
|  | 		root:   common.HexToHash("0x01"), | ||||||
|  | 		cache:  fastcache.New(1024 * 500), | ||||||
|  | 	} | ||||||
|  | 	snaps := &Tree{ | ||||||
|  | 		layers: map[common.Hash]snapshot{ | ||||||
|  | 			base.root: base, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	// Create a stack of diffs on top
 | ||||||
|  | 	snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), | ||||||
|  | 		randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil) | ||||||
|  | 
 | ||||||
|  | 	snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), | ||||||
|  | 		randomAccountSet("0xbb", "0xdd", "0xf0"), nil) | ||||||
|  | 
 | ||||||
|  | 	snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), | ||||||
|  | 		randomAccountSet("0xcc", "0xf0", "0xff"), nil) | ||||||
|  | 
 | ||||||
|  | 	// Create an iterator and flatten the data from underneath it
 | ||||||
|  | 	it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) | ||||||
|  | 	defer it.Release() | ||||||
|  | 
 | ||||||
|  | 	if err := snaps.Cap(common.HexToHash("0x04"), 1); err != nil { | ||||||
|  | 		t.Fatalf("failed to flatten snapshot stack: %v", err) | ||||||
|  | 	} | ||||||
|  | 	//verifyIterator(t, 7, it)
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestIteratorSeek(t *testing.T) { | ||||||
|  | 	// Create a snapshot stack with some initial data
 | ||||||
|  | 	base := &diskLayer{ | ||||||
|  | 		diskdb: rawdb.NewMemoryDatabase(), | ||||||
|  | 		root:   common.HexToHash("0x01"), | ||||||
|  | 		cache:  fastcache.New(1024 * 500), | ||||||
|  | 	} | ||||||
|  | 	snaps := &Tree{ | ||||||
|  | 		layers: map[common.Hash]snapshot{ | ||||||
|  | 			base.root: base, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), | ||||||
|  | 		randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil) | ||||||
|  | 
 | ||||||
|  | 	snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), | ||||||
|  | 		randomAccountSet("0xbb", "0xdd", "0xf0"), nil) | ||||||
|  | 
 | ||||||
|  | 	snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), | ||||||
|  | 		randomAccountSet("0xcc", "0xf0", "0xff"), nil) | ||||||
|  | 
 | ||||||
|  | 	// Construct various iterators and ensure their tranversal is correct
 | ||||||
|  | 	it, _ := snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xdd")) | ||||||
|  | 	defer it.Release() | ||||||
|  | 	verifyIterator(t, 3, it) // expected: ee, f0, ff
 | ||||||
|  | 
 | ||||||
|  | 	it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xaa")) | ||||||
|  | 	defer it.Release() | ||||||
|  | 	verifyIterator(t, 3, it) // expected: ee, f0, ff
 | ||||||
|  | 
 | ||||||
|  | 	it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff")) | ||||||
|  | 	defer it.Release() | ||||||
|  | 	verifyIterator(t, 0, it) // expected: nothing
 | ||||||
|  | 
 | ||||||
|  | 	it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xbb")) | ||||||
|  | 	defer it.Release() | ||||||
|  | 	verifyIterator(t, 5, it) // expected: cc, dd, ee, f0, ff
 | ||||||
|  | 
 | ||||||
|  | 	it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xef")) | ||||||
|  | 	defer it.Release() | ||||||
|  | 	verifyIterator(t, 2, it) // expected: f0, ff
 | ||||||
|  | 
 | ||||||
|  | 	it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xf0")) | ||||||
|  | 	defer it.Release() | ||||||
|  | 	verifyIterator(t, 1, it) // expected: ff
 | ||||||
|  | 
 | ||||||
|  | 	it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff")) | ||||||
|  | 	defer it.Release() | ||||||
|  | 	verifyIterator(t, 0, it) // expected: nothing
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // BenchmarkIteratorTraversal is a bit a bit notorious -- all layers contain the
 | ||||||
|  | // exact same 200 accounts. That means that we need to process 2000 items, but
 | ||||||
|  | // only spit out 200 values eventually.
 | ||||||
| //
 | //
 | ||||||
| // The value-fetching benchmark is easy on the binary iterator, since it never has to reach
 | // The value-fetching benchmark is easy on the binary iterator, since it never has to reach
 | ||||||
| // down at any depth for retrieving the values -- all are on the toppmost layer
 | // down at any depth for retrieving the values -- all are on the toppmost layer
 | ||||||
| @ -267,12 +386,9 @@ func TestIteratorLargeTraversal(t *testing.T) { | |||||||
| // BenchmarkIteratorTraversal/binary_iterator_values-6       	    2403	    501810 ns/op
 | // BenchmarkIteratorTraversal/binary_iterator_values-6       	    2403	    501810 ns/op
 | ||||||
| // BenchmarkIteratorTraversal/fast_iterator_keys-6           	    1923	    677966 ns/op
 | // BenchmarkIteratorTraversal/fast_iterator_keys-6           	    1923	    677966 ns/op
 | ||||||
| // BenchmarkIteratorTraversal/fast_iterator_values-6         	    1741	    649967 ns/op
 | // BenchmarkIteratorTraversal/fast_iterator_values-6         	    1741	    649967 ns/op
 | ||||||
| //
 |  | ||||||
| func BenchmarkIteratorTraversal(b *testing.B) { | func BenchmarkIteratorTraversal(b *testing.B) { | ||||||
| 
 | 	// Create a custom account factory to recreate the same addresses
 | ||||||
| 	var storage = make(map[common.Hash]map[common.Hash][]byte) | 	makeAccounts := func(num int) map[common.Hash][]byte { | ||||||
| 
 |  | ||||||
| 	mkAccounts := func(num int) map[common.Hash][]byte { |  | ||||||
| 		accounts := make(map[common.Hash][]byte) | 		accounts := make(map[common.Hash][]byte) | ||||||
| 		for i := 0; i < num; i++ { | 		for i := 0; i < num; i++ { | ||||||
| 			h := common.Hash{} | 			h := common.Hash{} | ||||||
| @ -281,24 +397,29 @@ func BenchmarkIteratorTraversal(b *testing.B) { | |||||||
| 		} | 		} | ||||||
| 		return accounts | 		return accounts | ||||||
| 	} | 	} | ||||||
| 	parent := newDiffLayer(emptyLayer(), common.Hash{}, | 	// Build up a large stack of snapshots
 | ||||||
| 		mkAccounts(200), storage) | 	base := &diskLayer{ | ||||||
| 
 | 		diskdb: rawdb.NewMemoryDatabase(), | ||||||
| 	child := parent.Update(common.Hash{}, | 		root:   common.HexToHash("0x01"), | ||||||
| 		mkAccounts(200), storage) | 		cache:  fastcache.New(1024 * 500), | ||||||
| 
 | 	} | ||||||
| 	for i := 2; i < 100; i++ { | 	snaps := &Tree{ | ||||||
| 		child = child.Update(common.Hash{}, | 		layers: map[common.Hash]snapshot{ | ||||||
| 			mkAccounts(200), storage) | 			base.root: base, | ||||||
| 
 | 		}, | ||||||
|  | 	} | ||||||
|  | 	for i := 1; i <= 100; i++ { | ||||||
|  | 		snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), makeAccounts(200), nil) | ||||||
| 	} | 	} | ||||||
| 	// We call this once before the benchmark, so the creation of
 | 	// We call this once before the benchmark, so the creation of
 | ||||||
| 	// sorted accountlists are not included in the results.
 | 	// sorted accountlists are not included in the results.
 | ||||||
| 	child.newBinaryAccountIterator() | 	head := snaps.Snapshot(common.HexToHash("0x65")) | ||||||
|  | 	head.(*diffLayer).newBinaryAccountIterator() | ||||||
|  | 
 | ||||||
| 	b.Run("binary iterator keys", func(b *testing.B) { | 	b.Run("binary iterator keys", func(b *testing.B) { | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
| 			got := 0 | 			got := 0 | ||||||
| 			it := child.newBinaryAccountIterator() | 			it := head.(*diffLayer).newBinaryAccountIterator() | ||||||
| 			for it.Next() { | 			for it.Next() { | ||||||
| 				got++ | 				got++ | ||||||
| 			} | 			} | ||||||
| @ -310,10 +431,10 @@ func BenchmarkIteratorTraversal(b *testing.B) { | |||||||
| 	b.Run("binary iterator values", func(b *testing.B) { | 	b.Run("binary iterator values", func(b *testing.B) { | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
| 			got := 0 | 			got := 0 | ||||||
| 			it := child.newBinaryAccountIterator() | 			it := head.(*diffLayer).newBinaryAccountIterator() | ||||||
| 			for it.Next() { | 			for it.Next() { | ||||||
| 				got++ | 				got++ | ||||||
| 				child.accountRLP(it.Key(), 0) | 				head.(*diffLayer).accountRLP(it.Hash(), 0) | ||||||
| 			} | 			} | ||||||
| 			if exp := 200; got != exp { | 			if exp := 200; got != exp { | ||||||
| 				b.Errorf("iterator len wrong, expected %d, got %d", exp, got) | 				b.Errorf("iterator len wrong, expected %d, got %d", exp, got) | ||||||
| @ -322,8 +443,10 @@ func BenchmarkIteratorTraversal(b *testing.B) { | |||||||
| 	}) | 	}) | ||||||
| 	b.Run("fast iterator keys", func(b *testing.B) { | 	b.Run("fast iterator keys", func(b *testing.B) { | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
|  | 			it, _ := snaps.AccountIterator(common.HexToHash("0x65"), common.Hash{}) | ||||||
|  | 			defer it.Release() | ||||||
|  | 
 | ||||||
| 			got := 0 | 			got := 0 | ||||||
| 			it := child.newFastAccountIterator() |  | ||||||
| 			for it.Next() { | 			for it.Next() { | ||||||
| 				got++ | 				got++ | ||||||
| 			} | 			} | ||||||
| @ -334,11 +457,13 @@ func BenchmarkIteratorTraversal(b *testing.B) { | |||||||
| 	}) | 	}) | ||||||
| 	b.Run("fast iterator values", func(b *testing.B) { | 	b.Run("fast iterator values", func(b *testing.B) { | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
|  | 			it, _ := snaps.AccountIterator(common.HexToHash("0x65"), common.Hash{}) | ||||||
|  | 			defer it.Release() | ||||||
|  | 
 | ||||||
| 			got := 0 | 			got := 0 | ||||||
| 			it := child.newFastAccountIterator() |  | ||||||
| 			for it.Next() { | 			for it.Next() { | ||||||
| 				got++ | 				got++ | ||||||
| 				it.Value() | 				it.Account() | ||||||
| 			} | 			} | ||||||
| 			if exp := 200; got != exp { | 			if exp := 200; got != exp { | ||||||
| 				b.Errorf("iterator len wrong, expected %d, got %d", exp, got) | 				b.Errorf("iterator len wrong, expected %d, got %d", exp, got) | ||||||
| @ -354,13 +479,12 @@ func BenchmarkIteratorTraversal(b *testing.B) { | |||||||
| // call recursively 100 times for the majority of the values
 | // call recursively 100 times for the majority of the values
 | ||||||
| //
 | //
 | ||||||
| // BenchmarkIteratorLargeBaselayer/binary_iterator_(keys)-6         	     514	   1971999 ns/op
 | // BenchmarkIteratorLargeBaselayer/binary_iterator_(keys)-6         	     514	   1971999 ns/op
 | ||||||
| // BenchmarkIteratorLargeBaselayer/fast_iterator_(keys)-6           	   10000	    114385 ns/op
 |  | ||||||
| // BenchmarkIteratorLargeBaselayer/binary_iterator_(values)-6       	      61	  18997492 ns/op
 | // BenchmarkIteratorLargeBaselayer/binary_iterator_(values)-6       	      61	  18997492 ns/op
 | ||||||
|  | // BenchmarkIteratorLargeBaselayer/fast_iterator_(keys)-6           	   10000	    114385 ns/op
 | ||||||
| // BenchmarkIteratorLargeBaselayer/fast_iterator_(values)-6         	    4047	    296823 ns/op
 | // BenchmarkIteratorLargeBaselayer/fast_iterator_(values)-6         	    4047	    296823 ns/op
 | ||||||
| func BenchmarkIteratorLargeBaselayer(b *testing.B) { | func BenchmarkIteratorLargeBaselayer(b *testing.B) { | ||||||
| 	var storage = make(map[common.Hash]map[common.Hash][]byte) | 	// Create a custom account factory to recreate the same addresses
 | ||||||
| 
 | 	makeAccounts := func(num int) map[common.Hash][]byte { | ||||||
| 	mkAccounts := func(num int) map[common.Hash][]byte { |  | ||||||
| 		accounts := make(map[common.Hash][]byte) | 		accounts := make(map[common.Hash][]byte) | ||||||
| 		for i := 0; i < num; i++ { | 		for i := 0; i < num; i++ { | ||||||
| 			h := common.Hash{} | 			h := common.Hash{} | ||||||
| @ -369,37 +493,30 @@ func BenchmarkIteratorLargeBaselayer(b *testing.B) { | |||||||
| 		} | 		} | ||||||
| 		return accounts | 		return accounts | ||||||
| 	} | 	} | ||||||
| 
 | 	// Build up a large stack of snapshots
 | ||||||
| 	parent := newDiffLayer(emptyLayer(), common.Hash{}, | 	base := &diskLayer{ | ||||||
| 		mkAccounts(2000), storage) | 		diskdb: rawdb.NewMemoryDatabase(), | ||||||
| 
 | 		root:   common.HexToHash("0x01"), | ||||||
| 	child := parent.Update(common.Hash{}, | 		cache:  fastcache.New(1024 * 500), | ||||||
| 		mkAccounts(20), storage) | 	} | ||||||
| 
 | 	snaps := &Tree{ | ||||||
| 	for i := 2; i < 100; i++ { | 		layers: map[common.Hash]snapshot{ | ||||||
| 		child = child.Update(common.Hash{}, | 			base.root: base, | ||||||
| 			mkAccounts(20), storage) | 		}, | ||||||
| 
 | 	} | ||||||
|  | 	snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), makeAccounts(2000), nil) | ||||||
|  | 	for i := 2; i <= 100; i++ { | ||||||
|  | 		snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), makeAccounts(20), nil) | ||||||
| 	} | 	} | ||||||
| 	// We call this once before the benchmark, so the creation of
 | 	// We call this once before the benchmark, so the creation of
 | ||||||
| 	// sorted accountlists are not included in the results.
 | 	// sorted accountlists are not included in the results.
 | ||||||
| 	child.newBinaryAccountIterator() | 	head := snaps.Snapshot(common.HexToHash("0x65")) | ||||||
|  | 	head.(*diffLayer).newBinaryAccountIterator() | ||||||
|  | 
 | ||||||
| 	b.Run("binary iterator (keys)", func(b *testing.B) { | 	b.Run("binary iterator (keys)", func(b *testing.B) { | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
| 			got := 0 | 			got := 0 | ||||||
| 			it := child.newBinaryAccountIterator() | 			it := head.(*diffLayer).newBinaryAccountIterator() | ||||||
| 			for it.Next() { |  | ||||||
| 				got++ |  | ||||||
| 			} |  | ||||||
| 			if exp := 2000; got != exp { |  | ||||||
| 				b.Errorf("iterator len wrong, expected %d, got %d", exp, got) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
| 	b.Run("fast iterator (keys)", func(b *testing.B) { |  | ||||||
| 		for i := 0; i < b.N; i++ { |  | ||||||
| 			got := 0 |  | ||||||
| 			it := child.newFastAccountIterator() |  | ||||||
| 			for it.Next() { | 			for it.Next() { | ||||||
| 				got++ | 				got++ | ||||||
| 			} | 			} | ||||||
| @ -411,24 +528,39 @@ func BenchmarkIteratorLargeBaselayer(b *testing.B) { | |||||||
| 	b.Run("binary iterator (values)", func(b *testing.B) { | 	b.Run("binary iterator (values)", func(b *testing.B) { | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
| 			got := 0 | 			got := 0 | ||||||
| 			it := child.newBinaryAccountIterator() | 			it := head.(*diffLayer).newBinaryAccountIterator() | ||||||
| 			for it.Next() { | 			for it.Next() { | ||||||
| 				got++ | 				got++ | ||||||
| 				v := it.Key() | 				v := it.Hash() | ||||||
| 				child.accountRLP(v, -0) | 				head.(*diffLayer).accountRLP(v, 0) | ||||||
| 			} | 			} | ||||||
| 			if exp := 2000; got != exp { | 			if exp := 2000; got != exp { | ||||||
| 				b.Errorf("iterator len wrong, expected %d, got %d", exp, got) | 				b.Errorf("iterator len wrong, expected %d, got %d", exp, got) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
|  | 	b.Run("fast iterator (keys)", func(b *testing.B) { | ||||||
|  | 		for i := 0; i < b.N; i++ { | ||||||
|  | 			it, _ := snaps.AccountIterator(common.HexToHash("0x65"), common.Hash{}) | ||||||
|  | 			defer it.Release() | ||||||
| 
 | 
 | ||||||
|  | 			got := 0 | ||||||
|  | 			for it.Next() { | ||||||
|  | 				got++ | ||||||
|  | 			} | ||||||
|  | 			if exp := 2000; got != exp { | ||||||
|  | 				b.Errorf("iterator len wrong, expected %d, got %d", exp, got) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
| 	b.Run("fast iterator (values)", func(b *testing.B) { | 	b.Run("fast iterator (values)", func(b *testing.B) { | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
|  | 			it, _ := snaps.AccountIterator(common.HexToHash("0x65"), common.Hash{}) | ||||||
|  | 			defer it.Release() | ||||||
|  | 
 | ||||||
| 			got := 0 | 			got := 0 | ||||||
| 			it := child.newFastAccountIterator() |  | ||||||
| 			for it.Next() { | 			for it.Next() { | ||||||
| 				it.Value() | 				it.Account() | ||||||
| 				got++ | 				got++ | ||||||
| 			} | 			} | ||||||
| 			if exp := 2000; got != exp { | 			if exp := 2000; got != exp { | ||||||
| @ -438,117 +570,38 @@ func BenchmarkIteratorLargeBaselayer(b *testing.B) { | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TestIteratorFlatting tests what happens when we
 | /* | ||||||
| // - have a live iterator on child C (parent C1 -> C2 .. CN)
 | func BenchmarkBinaryAccountIteration(b *testing.B) { | ||||||
| // - flattens C2 all the way into CN
 | 	benchmarkAccountIteration(b, func(snap snapshot) AccountIterator { | ||||||
| // - continues iterating
 | 		return snap.(*diffLayer).newBinaryAccountIterator() | ||||||
| // Right now, this "works" simply because the keys do not change -- the
 |  | ||||||
| // iterator is not aware that a layer has become stale. This naive
 |  | ||||||
| // solution probably won't work in the long run, however
 |  | ||||||
| func TestIteratorFlattning(t *testing.T) { |  | ||||||
| 	var ( |  | ||||||
| 		storage = make(map[common.Hash]map[common.Hash][]byte) |  | ||||||
| 	) |  | ||||||
| 	mkAccounts := func(args ...string) map[common.Hash][]byte { |  | ||||||
| 		accounts := make(map[common.Hash][]byte) |  | ||||||
| 		for _, h := range args { |  | ||||||
| 			accounts[common.HexToHash(h)] = randomAccount() |  | ||||||
| 		} |  | ||||||
| 		return accounts |  | ||||||
| 	} |  | ||||||
| 	// entries in multiple layers should only become output once
 |  | ||||||
| 	parent := newDiffLayer(emptyLayer(), common.Hash{}, |  | ||||||
| 		mkAccounts("0xaa", "0xee", "0xff", "0xf0"), storage) |  | ||||||
| 
 |  | ||||||
| 	child := parent.Update(common.Hash{}, |  | ||||||
| 		mkAccounts("0xbb", "0xdd", "0xf0"), storage) |  | ||||||
| 
 |  | ||||||
| 	child = child.Update(common.Hash{}, |  | ||||||
| 		mkAccounts("0xcc", "0xf0", "0xff"), storage) |  | ||||||
| 
 |  | ||||||
| 	it := child.newFastAccountIterator() |  | ||||||
| 	child.parent.(*diffLayer).flatten() |  | ||||||
| 	// The parent should now be stale
 |  | ||||||
| 	verifyIterator(t, 7, it) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestIteratorSeek(t *testing.T) { |  | ||||||
| 	storage := make(map[common.Hash]map[common.Hash][]byte) |  | ||||||
| 	mkAccounts := func(args ...string) map[common.Hash][]byte { |  | ||||||
| 		accounts := make(map[common.Hash][]byte) |  | ||||||
| 		for _, h := range args { |  | ||||||
| 			accounts[common.HexToHash(h)] = randomAccount() |  | ||||||
| 		} |  | ||||||
| 		return accounts |  | ||||||
| 	} |  | ||||||
| 	parent := newDiffLayer(emptyLayer(), common.Hash{}, |  | ||||||
| 		mkAccounts("0xaa", "0xee", "0xff", "0xf0"), storage) |  | ||||||
| 	it := AccountIterator(parent.newAccountIterator()) |  | ||||||
| 	// expected: ee, f0, ff
 |  | ||||||
| 	it.Seek(common.HexToHash("0xdd")) |  | ||||||
| 	verifyIterator(t, 3, it) |  | ||||||
| 
 |  | ||||||
| 	it = parent.newAccountIterator() |  | ||||||
| 	// expected: ee, f0, ff
 |  | ||||||
| 	it.Seek(common.HexToHash("0xaa")) |  | ||||||
| 	verifyIterator(t, 3, it) |  | ||||||
| 
 |  | ||||||
| 	it = parent.newAccountIterator() |  | ||||||
| 	// expected: nothing
 |  | ||||||
| 	it.Seek(common.HexToHash("0xff")) |  | ||||||
| 	verifyIterator(t, 0, it) |  | ||||||
| 
 |  | ||||||
| 	child := parent.Update(common.Hash{}, |  | ||||||
| 		mkAccounts("0xbb", "0xdd", "0xf0"), storage) |  | ||||||
| 
 |  | ||||||
| 	child = child.Update(common.Hash{}, |  | ||||||
| 		mkAccounts("0xcc", "0xf0", "0xff"), storage) |  | ||||||
| 
 |  | ||||||
| 	it = child.newFastAccountIterator() |  | ||||||
| 	// expected: cc, dd, ee, f0, ff
 |  | ||||||
| 	it.Seek(common.HexToHash("0xbb")) |  | ||||||
| 	verifyIterator(t, 5, it) |  | ||||||
| 
 |  | ||||||
| 	it = child.newFastAccountIterator() |  | ||||||
| 	it.Seek(common.HexToHash("0xef")) |  | ||||||
| 	// exp: f0, ff
 |  | ||||||
| 	verifyIterator(t, 2, it) |  | ||||||
| 
 |  | ||||||
| 	it = child.newFastAccountIterator() |  | ||||||
| 	it.Seek(common.HexToHash("0xf0")) |  | ||||||
| 	verifyIterator(t, 1, it) |  | ||||||
| 
 |  | ||||||
| 	it.Seek(common.HexToHash("0xff")) |  | ||||||
| 	verifyIterator(t, 0, it) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| //BenchmarkIteratorSeek/init+seek-6         	    4328	    245477 ns/op
 |  | ||||||
| func BenchmarkIteratorSeek(b *testing.B) { |  | ||||||
| 
 |  | ||||||
| 	var storage = make(map[common.Hash]map[common.Hash][]byte) |  | ||||||
| 	mkAccounts := func(num int) map[common.Hash][]byte { |  | ||||||
| 		accounts := make(map[common.Hash][]byte) |  | ||||||
| 		for i := 0; i < num; i++ { |  | ||||||
| 			h := common.Hash{} |  | ||||||
| 			binary.BigEndian.PutUint64(h[:], uint64(i+1)) |  | ||||||
| 			accounts[h] = randomAccount() |  | ||||||
| 		} |  | ||||||
| 		return accounts |  | ||||||
| 	} |  | ||||||
| 	layer := newDiffLayer(emptyLayer(), common.Hash{}, mkAccounts(200), storage) |  | ||||||
| 	for i := 1; i < 100; i++ { |  | ||||||
| 		layer = layer.Update(common.Hash{}, |  | ||||||
| 			mkAccounts(200), storage) |  | ||||||
| 	} |  | ||||||
| 	b.Run("init+seek", func(b *testing.B) { |  | ||||||
| 		b.ResetTimer() |  | ||||||
| 		seekpos := make([]byte, 20) |  | ||||||
| 		for i := 0; i < b.N; i++ { |  | ||||||
| 			b.StopTimer() |  | ||||||
| 			rand.Read(seekpos) |  | ||||||
| 			it := layer.newFastAccountIterator() |  | ||||||
| 			b.StartTimer() |  | ||||||
| 			it.Seek(common.BytesToHash(seekpos)) |  | ||||||
| 		} |  | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func BenchmarkFastAccountIteration(b *testing.B) { | ||||||
|  | 	benchmarkAccountIteration(b, newFastAccountIterator) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func benchmarkAccountIteration(b *testing.B, iterator func(snap snapshot) AccountIterator) { | ||||||
|  | 	// Create a diff stack and randomize the accounts across them
 | ||||||
|  | 	layers := make([]map[common.Hash][]byte, 128) | ||||||
|  | 	for i := 0; i < len(layers); i++ { | ||||||
|  | 		layers[i] = make(map[common.Hash][]byte) | ||||||
|  | 	} | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		depth := rand.Intn(len(layers)) | ||||||
|  | 		layers[depth][randomHash()] = randomAccount() | ||||||
|  | 	} | ||||||
|  | 	stack := snapshot(emptyLayer()) | ||||||
|  | 	for _, layer := range layers { | ||||||
|  | 		stack = stack.Update(common.Hash{}, layer, nil) | ||||||
|  | 	} | ||||||
|  | 	// Reset the timers and report all the stats
 | ||||||
|  | 	it := iterator(stack) | ||||||
|  | 
 | ||||||
|  | 	b.ResetTimer() | ||||||
|  | 	b.ReportAllocs() | ||||||
|  | 
 | ||||||
|  | 	for it.Next() { | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | |||||||
| @ -113,9 +113,17 @@ type Snapshot interface { | |||||||
| type snapshot interface { | type snapshot interface { | ||||||
| 	Snapshot | 	Snapshot | ||||||
| 
 | 
 | ||||||
|  | 	// Parent returns the subsequent layer of a snapshot, or nil if the base was
 | ||||||
|  | 	// reached.
 | ||||||
|  | 	//
 | ||||||
|  | 	// Note, the method is an internal helper to avoid type switching between the
 | ||||||
|  | 	// disk and diff layers. There is no locking involved.
 | ||||||
|  | 	Parent() snapshot | ||||||
|  | 
 | ||||||
| 	// Update creates a new layer on top of the existing snapshot diff tree with
 | 	// Update creates a new layer on top of the existing snapshot diff tree with
 | ||||||
| 	// the specified data items. Note, the maps are retained by the method to avoid
 | 	// the specified data items.
 | ||||||
| 	// copying everything.
 | 	//
 | ||||||
|  | 	// Note, the maps are retained by the method to avoid copying everything.
 | ||||||
| 	Update(blockRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer | 	Update(blockRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer | ||||||
| 
 | 
 | ||||||
| 	// Journal commits an entire diff hierarchy to disk into a single journal entry.
 | 	// Journal commits an entire diff hierarchy to disk into a single journal entry.
 | ||||||
| @ -126,6 +134,9 @@ type snapshot interface { | |||||||
| 	// Stale return whether this layer has become stale (was flattened across) or
 | 	// Stale return whether this layer has become stale (was flattened across) or
 | ||||||
| 	// if it's still live.
 | 	// if it's still live.
 | ||||||
| 	Stale() bool | 	Stale() bool | ||||||
|  | 
 | ||||||
|  | 	// AccountIterator creates an account iterator over an arbitrary layer.
 | ||||||
|  | 	AccountIterator(seek common.Hash) AccountIterator | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SnapshotTree is an Ethereum state snapshot tree. It consists of one persistent
 | // SnapshotTree is an Ethereum state snapshot tree. It consists of one persistent
 | ||||||
| @ -170,15 +181,7 @@ func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root comm | |||||||
| 	// Existing snapshot loaded, seed all the layers
 | 	// Existing snapshot loaded, seed all the layers
 | ||||||
| 	for head != nil { | 	for head != nil { | ||||||
| 		snap.layers[head.Root()] = head | 		snap.layers[head.Root()] = head | ||||||
| 
 | 		head = head.Parent() | ||||||
| 		switch self := head.(type) { |  | ||||||
| 		case *diffLayer: |  | ||||||
| 			head = self.parent |  | ||||||
| 		case *diskLayer: |  | ||||||
| 			head = nil |  | ||||||
| 		default: |  | ||||||
| 			panic(fmt.Sprintf("unknown data layer: %T", self)) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return snap | 	return snap | ||||||
| } | } | ||||||
| @ -563,3 +566,9 @@ func (t *Tree) Rebuild(root common.Hash) { | |||||||
| 		root: generateSnapshot(t.diskdb, t.triedb, t.cache, root, wiper), | 		root: generateSnapshot(t.diskdb, t.triedb, t.cache, root, wiper), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // AccountIterator creates a new account iterator for the specified root hash and
 | ||||||
|  | // seeks to a starting account hash.
 | ||||||
|  | func (t *Tree) AccountIterator(root common.Hash, seek common.Hash) (AccountIterator, error) { | ||||||
|  | 	return newFastAccountIterator(t, root, seek) | ||||||
|  | } | ||||||
|  | |||||||
| @ -18,13 +18,48 @@ package snapshot | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"math/big" | ||||||
|  | 	"math/rand" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/VictoriaMetrics/fastcache" | 	"github.com/VictoriaMetrics/fastcache" | ||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
| 	"github.com/ethereum/go-ethereum/core/rawdb" | 	"github.com/ethereum/go-ethereum/core/rawdb" | ||||||
|  | 	"github.com/ethereum/go-ethereum/rlp" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // randomHash generates a random blob of data and returns it as a hash.
 | ||||||
|  | func randomHash() common.Hash { | ||||||
|  | 	var hash common.Hash | ||||||
|  | 	if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	return hash | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // randomAccount generates a random account and returns it RLP encoded.
 | ||||||
|  | func randomAccount() []byte { | ||||||
|  | 	root := randomHash() | ||||||
|  | 	a := Account{ | ||||||
|  | 		Balance:  big.NewInt(rand.Int63()), | ||||||
|  | 		Nonce:    rand.Uint64(), | ||||||
|  | 		Root:     root[:], | ||||||
|  | 		CodeHash: emptyCode[:], | ||||||
|  | 	} | ||||||
|  | 	data, _ := rlp.EncodeToBytes(a) | ||||||
|  | 	return data | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // randomAccountSet generates a set of random accounts with the given strings as
 | ||||||
|  | // the account address hashes.
 | ||||||
|  | func randomAccountSet(hashes ...string) map[common.Hash][]byte { | ||||||
|  | 	accounts := make(map[common.Hash][]byte) | ||||||
|  | 	for _, hash := range hashes { | ||||||
|  | 		accounts[common.HexToHash(hash)] = randomAccount() | ||||||
|  | 	} | ||||||
|  | 	return accounts | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Tests that if a disk layer becomes stale, no active external references will
 | // Tests that if a disk layer becomes stale, no active external references will
 | ||||||
| // be returned with junk data. This version of the test flattens every diff layer
 | // be returned with junk data. This version of the test flattens every diff layer
 | ||||||
| // to check internal corner case around the bottom-most memory accumulator.
 | // to check internal corner case around the bottom-most memory accumulator.
 | ||||||
| @ -46,8 +81,7 @@ func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) { | |||||||
| 	accounts := map[common.Hash][]byte{ | 	accounts := map[common.Hash][]byte{ | ||||||
| 		common.HexToHash("0xa1"): randomAccount(), | 		common.HexToHash("0xa1"): randomAccount(), | ||||||
| 	} | 	} | ||||||
| 	storage := make(map[common.Hash]map[common.Hash][]byte) | 	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil { | ||||||
| 	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, storage); err != nil { |  | ||||||
| 		t.Fatalf("failed to create a diff layer: %v", err) | 		t.Fatalf("failed to create a diff layer: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if n := len(snaps.layers); n != 2 { | 	if n := len(snaps.layers); n != 2 { | ||||||
| @ -91,11 +125,10 @@ func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) { | |||||||
| 	accounts := map[common.Hash][]byte{ | 	accounts := map[common.Hash][]byte{ | ||||||
| 		common.HexToHash("0xa1"): randomAccount(), | 		common.HexToHash("0xa1"): randomAccount(), | ||||||
| 	} | 	} | ||||||
| 	storage := make(map[common.Hash]map[common.Hash][]byte) | 	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil { | ||||||
| 	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, storage); err != nil { |  | ||||||
| 		t.Fatalf("failed to create a diff layer: %v", err) | 		t.Fatalf("failed to create a diff layer: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, storage); err != nil { | 	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, nil); err != nil { | ||||||
| 		t.Fatalf("failed to create a diff layer: %v", err) | 		t.Fatalf("failed to create a diff layer: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if n := len(snaps.layers); n != 3 { | 	if n := len(snaps.layers); n != 3 { | ||||||
| @ -140,11 +173,10 @@ func TestDiffLayerExternalInvalidationFullFlatten(t *testing.T) { | |||||||
| 	accounts := map[common.Hash][]byte{ | 	accounts := map[common.Hash][]byte{ | ||||||
| 		common.HexToHash("0xa1"): randomAccount(), | 		common.HexToHash("0xa1"): randomAccount(), | ||||||
| 	} | 	} | ||||||
| 	storage := make(map[common.Hash]map[common.Hash][]byte) | 	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil { | ||||||
| 	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, storage); err != nil { |  | ||||||
| 		t.Fatalf("failed to create a diff layer: %v", err) | 		t.Fatalf("failed to create a diff layer: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, storage); err != nil { | 	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, nil); err != nil { | ||||||
| 		t.Fatalf("failed to create a diff layer: %v", err) | 		t.Fatalf("failed to create a diff layer: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if n := len(snaps.layers); n != 3 { | 	if n := len(snaps.layers); n != 3 { | ||||||
| @ -188,14 +220,13 @@ func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) { | |||||||
| 	accounts := map[common.Hash][]byte{ | 	accounts := map[common.Hash][]byte{ | ||||||
| 		common.HexToHash("0xa1"): randomAccount(), | 		common.HexToHash("0xa1"): randomAccount(), | ||||||
| 	} | 	} | ||||||
| 	storage := make(map[common.Hash]map[common.Hash][]byte) | 	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil { | ||||||
| 	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, storage); err != nil { |  | ||||||
| 		t.Fatalf("failed to create a diff layer: %v", err) | 		t.Fatalf("failed to create a diff layer: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, storage); err != nil { | 	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, nil); err != nil { | ||||||
| 		t.Fatalf("failed to create a diff layer: %v", err) | 		t.Fatalf("failed to create a diff layer: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), accounts, storage); err != nil { | 	if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), accounts, nil); err != nil { | ||||||
| 		t.Fatalf("failed to create a diff layer: %v", err) | 		t.Fatalf("failed to create a diff layer: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if n := len(snaps.layers); n != 4 { | 	if n := len(snaps.layers); n != 4 { | ||||||
|  | |||||||
| @ -25,15 +25,6 @@ import ( | |||||||
| 	"github.com/ethereum/go-ethereum/ethdb/memorydb" | 	"github.com/ethereum/go-ethereum/ethdb/memorydb" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // randomHash generates a random blob of data and returns it as a hash.
 |  | ||||||
| func randomHash() common.Hash { |  | ||||||
| 	var hash common.Hash |  | ||||||
| 	if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	return hash |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Tests that given a database with random data content, all parts of a snapshot
 | // Tests that given a database with random data content, all parts of a snapshot
 | ||||||
| // can be crrectly wiped without touching anything else.
 | // can be crrectly wiped without touching anything else.
 | ||||||
| func TestWipe(t *testing.T) { | func TestWipe(t *testing.T) { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user