core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to simplify the code. Previously, this flag indicated that an account was removed during a state transition, making all associated storage slots inaccessible. Because storage deletion can involve a large number of slots, the actual deletion is deferred until the end of the process, where it is handled in batches. With the deprecation of self-destruct in the Cancun fork, storage deletions are no longer expected. Historically, the largest storage deletion event in Ethereum was around 15 megabytes—manageable in memory. In this pull request, the single destruct flag is replaced by a set of deletion markers for individual storage slots. Each deleted storage slot will now appear in the Storage set with a nil value. This change will simplify a lot logics, such as storage accessing, storage flushing, storage iteration and so on.
This commit is contained in:
parent
6eeff3ee7d
commit
6485d5e3ff
|
@ -50,16 +50,6 @@ type (
|
||||||
leafCallbackFn func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error)
|
leafCallbackFn func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateAccountTrieRoot takes an account iterator and reproduces the root hash.
|
|
||||||
func GenerateAccountTrieRoot(it AccountIterator) (common.Hash, error) {
|
|
||||||
return generateTrieRoot(nil, "", it, common.Hash{}, stackTrieGenerate, nil, newGenerateStats(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateStorageTrieRoot takes a storage iterator and reproduces the root hash.
|
|
||||||
func GenerateStorageTrieRoot(account common.Hash, it StorageIterator) (common.Hash, error) {
|
|
||||||
return generateTrieRoot(nil, "", it, account, stackTrieGenerate, nil, newGenerateStats(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateTrie takes the whole snapshot tree as the input, traverses all the
|
// GenerateTrie takes the whole snapshot tree as the input, traverses all the
|
||||||
// accounts as well as the corresponding storages and regenerate the whole state
|
// accounts as well as the corresponding storages and regenerate the whole state
|
||||||
// (account trie + all storage tries).
|
// (account trie + all storage tries).
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
bloomfilter "github.com/holiman/bloomfilter/v2"
|
bloomfilter "github.com/holiman/bloomfilter/v2"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -73,23 +74,14 @@ var (
|
||||||
// bloom key for an account/slot. This is randomized at init(), so that the
|
// bloom key for an account/slot. This is randomized at init(), so that the
|
||||||
// global population of nodes do not all display the exact same behaviour with
|
// global population of nodes do not all display the exact same behaviour with
|
||||||
// regards to bloom content
|
// regards to bloom content
|
||||||
bloomDestructHasherOffset = 0
|
bloomAccountHasherOffset = 0
|
||||||
bloomAccountHasherOffset = 0
|
bloomStorageHasherOffset = 0
|
||||||
bloomStorageHasherOffset = 0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Init the bloom offsets in the range [0:24] (requires 8 bytes)
|
// Init the bloom offsets in the range [0:24] (requires 8 bytes)
|
||||||
bloomDestructHasherOffset = rand.Intn(25)
|
|
||||||
bloomAccountHasherOffset = rand.Intn(25)
|
bloomAccountHasherOffset = rand.Intn(25)
|
||||||
bloomStorageHasherOffset = rand.Intn(25)
|
bloomStorageHasherOffset = rand.Intn(25)
|
||||||
|
|
||||||
// The destruct and account blooms must be different, as the storage slots
|
|
||||||
// will check for destruction too for every bloom miss. It should not collide
|
|
||||||
// with modified accounts.
|
|
||||||
for bloomAccountHasherOffset == bloomDestructHasherOffset {
|
|
||||||
bloomAccountHasherOffset = rand.Intn(25)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// diffLayer represents a collection of modifications made to a state snapshot
|
// diffLayer represents a collection of modifications made to a state snapshot
|
||||||
|
@ -106,29 +98,16 @@ type diffLayer struct {
|
||||||
root common.Hash // Root hash to which this snapshot diff belongs to
|
root common.Hash // Root hash to which this snapshot diff belongs to
|
||||||
stale atomic.Bool // Signals that the layer became stale (state progressed)
|
stale atomic.Bool // Signals that the layer became stale (state progressed)
|
||||||
|
|
||||||
// destructSet is a very special helper marker. If an account is marked as
|
|
||||||
// deleted, then it's recorded in this set. However it's allowed that an account
|
|
||||||
// is included here but still available in other sets(e.g. storageData). The
|
|
||||||
// reason is the diff layer includes all the changes in a *block*. It can
|
|
||||||
// happen that in the tx_1, account A is self-destructed while in the tx_2
|
|
||||||
// it's recreated. But we still need this marker to indicate the "old" A is
|
|
||||||
// deleted, all data in other set belongs to the "new" A.
|
|
||||||
destructSet map[common.Hash]struct{} // Keyed markers for deleted (and potentially) recreated accounts
|
|
||||||
accountList []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil
|
|
||||||
accountData map[common.Hash][]byte // Keyed accounts for direct retrieval (nil means deleted)
|
accountData map[common.Hash][]byte // Keyed accounts for direct retrieval (nil means deleted)
|
||||||
storageList map[common.Hash][]common.Hash // List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil
|
|
||||||
storageData map[common.Hash]map[common.Hash][]byte // Keyed storage slots for direct retrieval. one per account (nil means deleted)
|
storageData map[common.Hash]map[common.Hash][]byte // Keyed storage slots for direct retrieval. one per account (nil means deleted)
|
||||||
|
accountList []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil
|
||||||
|
storageList map[common.Hash][]common.Hash // List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil
|
||||||
|
|
||||||
diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer
|
diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer
|
||||||
|
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// destructBloomHash is used to convert a destruct event into a 64 bit mini hash.
|
|
||||||
func destructBloomHash(h common.Hash) uint64 {
|
|
||||||
return binary.BigEndian.Uint64(h[bloomDestructHasherOffset : bloomDestructHasherOffset+8])
|
|
||||||
}
|
|
||||||
|
|
||||||
// accountBloomHash is used to convert an account hash into a 64 bit mini hash.
|
// accountBloomHash is used to convert an account hash into a 64 bit mini hash.
|
||||||
func accountBloomHash(h common.Hash) uint64 {
|
func accountBloomHash(h common.Hash) uint64 {
|
||||||
return binary.BigEndian.Uint64(h[bloomAccountHasherOffset : bloomAccountHasherOffset+8])
|
return binary.BigEndian.Uint64(h[bloomAccountHasherOffset : bloomAccountHasherOffset+8])
|
||||||
|
@ -142,12 +121,11 @@ func storageBloomHash(h0, h1 common.Hash) uint64 {
|
||||||
|
|
||||||
// newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low
|
// newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low
|
||||||
// level persistent database or a hierarchical diff already.
|
// level persistent database or a hierarchical diff already.
|
||||||
func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
func newDiffLayer(parent snapshot, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
||||||
// Create the new layer with some pre-allocated data segments
|
// Create the new layer with some pre-allocated data segments
|
||||||
dl := &diffLayer{
|
dl := &diffLayer{
|
||||||
parent: parent,
|
parent: parent,
|
||||||
root: root,
|
root: root,
|
||||||
destructSet: destructs,
|
|
||||||
accountData: accounts,
|
accountData: accounts,
|
||||||
storageData: storage,
|
storageData: storage,
|
||||||
storageList: make(map[common.Hash][]common.Hash),
|
storageList: make(map[common.Hash][]common.Hash),
|
||||||
|
@ -161,10 +139,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
|
||||||
panic("unknown parent type")
|
panic("unknown parent type")
|
||||||
}
|
}
|
||||||
// Sanity check that accounts or storage slots are never nil
|
// Sanity check that accounts or storage slots are never nil
|
||||||
for accountHash, blob := range accounts {
|
for _, blob := range accounts {
|
||||||
if blob == nil {
|
|
||||||
panic(fmt.Sprintf("account %#x nil", accountHash))
|
|
||||||
}
|
|
||||||
// Determine memory size and track the dirty writes
|
// Determine memory size and track the dirty writes
|
||||||
dl.memory += uint64(common.HashLength + len(blob))
|
dl.memory += uint64(common.HashLength + len(blob))
|
||||||
snapshotDirtyAccountWriteMeter.Mark(int64(len(blob)))
|
snapshotDirtyAccountWriteMeter.Mark(int64(len(blob)))
|
||||||
|
@ -179,7 +154,6 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
|
||||||
snapshotDirtyStorageWriteMeter.Mark(int64(len(data)))
|
snapshotDirtyStorageWriteMeter.Mark(int64(len(data)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dl.memory += uint64(len(destructs) * common.HashLength)
|
|
||||||
return dl
|
return dl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,10 +178,6 @@ func (dl *diffLayer) rebloom(origin *diskLayer) {
|
||||||
} else {
|
} else {
|
||||||
dl.diffed, _ = bloomfilter.New(uint64(bloomSize), uint64(bloomFuncs))
|
dl.diffed, _ = bloomfilter.New(uint64(bloomSize), uint64(bloomFuncs))
|
||||||
}
|
}
|
||||||
// Iterate over all the accounts and storage slots and index them
|
|
||||||
for hash := range dl.destructSet {
|
|
||||||
dl.diffed.AddHash(destructBloomHash(hash))
|
|
||||||
}
|
|
||||||
for hash := range dl.accountData {
|
for hash := range dl.accountData {
|
||||||
dl.diffed.AddHash(accountBloomHash(hash))
|
dl.diffed.AddHash(accountBloomHash(hash))
|
||||||
}
|
}
|
||||||
|
@ -274,11 +244,8 @@ func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) {
|
||||||
}
|
}
|
||||||
// Check the bloom filter first whether there's even a point in reaching into
|
// Check the bloom filter first whether there's even a point in reaching into
|
||||||
// all the maps in all the layers below
|
// all the maps in all the layers below
|
||||||
hit := dl.diffed.ContainsHash(accountBloomHash(hash))
|
|
||||||
if !hit {
|
|
||||||
hit = dl.diffed.ContainsHash(destructBloomHash(hash))
|
|
||||||
}
|
|
||||||
var origin *diskLayer
|
var origin *diskLayer
|
||||||
|
hit := dl.diffed.ContainsHash(accountBloomHash(hash))
|
||||||
if !hit {
|
if !hit {
|
||||||
origin = dl.origin // extract origin while holding the lock
|
origin = dl.origin // extract origin while holding the lock
|
||||||
}
|
}
|
||||||
|
@ -310,18 +277,14 @@ func (dl *diffLayer) accountRLP(hash common.Hash, depth int) ([]byte, error) {
|
||||||
if data, ok := dl.accountData[hash]; ok {
|
if data, ok := dl.accountData[hash]; ok {
|
||||||
snapshotDirtyAccountHitMeter.Mark(1)
|
snapshotDirtyAccountHitMeter.Mark(1)
|
||||||
snapshotDirtyAccountHitDepthHist.Update(int64(depth))
|
snapshotDirtyAccountHitDepthHist.Update(int64(depth))
|
||||||
snapshotDirtyAccountReadMeter.Mark(int64(len(data)))
|
if n := len(data); n > 0 {
|
||||||
|
snapshotDirtyAccountReadMeter.Mark(int64(n))
|
||||||
|
} else {
|
||||||
|
snapshotDirtyAccountInexMeter.Mark(1)
|
||||||
|
}
|
||||||
snapshotBloomAccountTrueHitMeter.Mark(1)
|
snapshotBloomAccountTrueHitMeter.Mark(1)
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
// If the account is known locally, but deleted, return it
|
|
||||||
if _, ok := dl.destructSet[hash]; ok {
|
|
||||||
snapshotDirtyAccountHitMeter.Mark(1)
|
|
||||||
snapshotDirtyAccountHitDepthHist.Update(int64(depth))
|
|
||||||
snapshotDirtyAccountInexMeter.Mark(1)
|
|
||||||
snapshotBloomAccountTrueHitMeter.Mark(1)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
// Account unknown to this diff, resolve from parent
|
// Account unknown to this diff, resolve from parent
|
||||||
if diff, ok := dl.parent.(*diffLayer); ok {
|
if diff, ok := dl.parent.(*diffLayer); ok {
|
||||||
return diff.accountRLP(hash, depth+1)
|
return diff.accountRLP(hash, depth+1)
|
||||||
|
@ -345,11 +308,8 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro
|
||||||
dl.lock.RUnlock()
|
dl.lock.RUnlock()
|
||||||
return nil, ErrSnapshotStale
|
return nil, ErrSnapshotStale
|
||||||
}
|
}
|
||||||
hit := dl.diffed.ContainsHash(storageBloomHash(accountHash, storageHash))
|
|
||||||
if !hit {
|
|
||||||
hit = dl.diffed.ContainsHash(destructBloomHash(accountHash))
|
|
||||||
}
|
|
||||||
var origin *diskLayer
|
var origin *diskLayer
|
||||||
|
hit := dl.diffed.ContainsHash(storageBloomHash(accountHash, storageHash))
|
||||||
if !hit {
|
if !hit {
|
||||||
origin = dl.origin // extract origin while holding the lock
|
origin = dl.origin // extract origin while holding the lock
|
||||||
}
|
}
|
||||||
|
@ -391,14 +351,6 @@ func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the account is known locally, but deleted, return an empty slot
|
|
||||||
if _, ok := dl.destructSet[accountHash]; ok {
|
|
||||||
snapshotDirtyStorageHitMeter.Mark(1)
|
|
||||||
snapshotDirtyStorageHitDepthHist.Update(int64(depth))
|
|
||||||
snapshotDirtyStorageInexMeter.Mark(1)
|
|
||||||
snapshotBloomStorageTrueHitMeter.Mark(1)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
// Storage slot unknown to this diff, resolve from parent
|
// Storage slot unknown to this diff, resolve from parent
|
||||||
if diff, ok := dl.parent.(*diffLayer); ok {
|
if diff, ok := dl.parent.(*diffLayer); ok {
|
||||||
return diff.storage(accountHash, storageHash, depth+1)
|
return diff.storage(accountHash, storageHash, depth+1)
|
||||||
|
@ -410,8 +362,8 @@ func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([
|
||||||
|
|
||||||
// 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.
|
// the specified data items.
|
||||||
func (dl *diffLayer) Update(blockRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
func (dl *diffLayer) Update(blockRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
||||||
return newDiffLayer(dl, blockRoot, destructs, accounts, storage)
|
return newDiffLayer(dl, blockRoot, accounts, storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flatten pushes all data from this point downwards, flattening everything into
|
// flatten pushes all data from this point downwards, flattening everything into
|
||||||
|
@ -436,12 +388,6 @@ func (dl *diffLayer) flatten() snapshot {
|
||||||
if parent.stale.Swap(true) {
|
if parent.stale.Swap(true) {
|
||||||
panic("parent diff layer is stale") // we've flattened into the same parent from two children, boo
|
panic("parent diff layer is stale") // we've flattened into the same parent from two children, boo
|
||||||
}
|
}
|
||||||
// Overwrite all the updated accounts blindly, merge the sorted list
|
|
||||||
for hash := range dl.destructSet {
|
|
||||||
parent.destructSet[hash] = struct{}{}
|
|
||||||
delete(parent.accountData, hash)
|
|
||||||
delete(parent.storageData, hash)
|
|
||||||
}
|
|
||||||
for hash, data := range dl.accountData {
|
for hash, data := range dl.accountData {
|
||||||
parent.accountData[hash] = data
|
parent.accountData[hash] = data
|
||||||
}
|
}
|
||||||
|
@ -453,17 +399,13 @@ func (dl *diffLayer) flatten() snapshot {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Storage exists in both parent and child, merge the slots
|
// Storage exists in both parent and child, merge the slots
|
||||||
comboData := parent.storageData[accountHash]
|
maps.Copy(parent.storageData[accountHash], storage)
|
||||||
for storageHash, data := range storage {
|
|
||||||
comboData[storageHash] = data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Return the combo parent
|
// Return the combo parent
|
||||||
return &diffLayer{
|
return &diffLayer{
|
||||||
parent: parent.parent,
|
parent: parent.parent,
|
||||||
origin: parent.origin,
|
origin: parent.origin,
|
||||||
root: dl.root,
|
root: dl.root,
|
||||||
destructSet: parent.destructSet,
|
|
||||||
accountData: parent.accountData,
|
accountData: parent.accountData,
|
||||||
storageData: parent.storageData,
|
storageData: parent.storageData,
|
||||||
storageList: make(map[common.Hash][]common.Hash),
|
storageList: make(map[common.Hash][]common.Hash),
|
||||||
|
@ -489,15 +431,7 @@ func (dl *diffLayer) AccountList() []common.Hash {
|
||||||
dl.lock.Lock()
|
dl.lock.Lock()
|
||||||
defer dl.lock.Unlock()
|
defer dl.lock.Unlock()
|
||||||
|
|
||||||
dl.accountList = make([]common.Hash, 0, len(dl.destructSet)+len(dl.accountData))
|
dl.accountList = maps.Keys(dl.accountData)
|
||||||
for hash := range dl.accountData {
|
|
||||||
dl.accountList = append(dl.accountList, hash)
|
|
||||||
}
|
|
||||||
for hash := range dl.destructSet {
|
|
||||||
if _, ok := dl.accountData[hash]; !ok {
|
|
||||||
dl.accountList = append(dl.accountList, hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slices.SortFunc(dl.accountList, common.Hash.Cmp)
|
slices.SortFunc(dl.accountList, common.Hash.Cmp)
|
||||||
dl.memory += uint64(len(dl.accountList) * common.HashLength)
|
dl.memory += uint64(len(dl.accountList) * common.HashLength)
|
||||||
return dl.accountList
|
return dl.accountList
|
||||||
|
@ -512,18 +446,17 @@ func (dl *diffLayer) AccountList() []common.Hash {
|
||||||
// not empty but the flag is true.
|
// not empty but the flag is true.
|
||||||
//
|
//
|
||||||
// Note, the returned slice is not a copy, so do not modify it.
|
// Note, the returned slice is not a copy, so do not modify it.
|
||||||
func (dl *diffLayer) StorageList(accountHash common.Hash) ([]common.Hash, bool) {
|
func (dl *diffLayer) StorageList(accountHash common.Hash) []common.Hash {
|
||||||
dl.lock.RLock()
|
dl.lock.RLock()
|
||||||
_, destructed := dl.destructSet[accountHash]
|
|
||||||
if _, ok := dl.storageData[accountHash]; !ok {
|
if _, ok := dl.storageData[accountHash]; !ok {
|
||||||
// Account not tracked by this layer
|
// Account not tracked by this layer
|
||||||
dl.lock.RUnlock()
|
dl.lock.RUnlock()
|
||||||
return nil, destructed
|
return nil
|
||||||
}
|
}
|
||||||
// If an old list already exists, return it
|
// If an old list already exists, return it
|
||||||
if list, exist := dl.storageList[accountHash]; exist {
|
if list, exist := dl.storageList[accountHash]; exist {
|
||||||
dl.lock.RUnlock()
|
dl.lock.RUnlock()
|
||||||
return list, destructed // the cached list can't be nil
|
return list // the cached list can't be nil
|
||||||
}
|
}
|
||||||
dl.lock.RUnlock()
|
dl.lock.RUnlock()
|
||||||
|
|
||||||
|
@ -531,13 +464,9 @@ func (dl *diffLayer) StorageList(accountHash common.Hash) ([]common.Hash, bool)
|
||||||
dl.lock.Lock()
|
dl.lock.Lock()
|
||||||
defer dl.lock.Unlock()
|
defer dl.lock.Unlock()
|
||||||
|
|
||||||
storageMap := dl.storageData[accountHash]
|
storageList := maps.Keys(dl.storageData[accountHash])
|
||||||
storageList := make([]common.Hash, 0, len(storageMap))
|
|
||||||
for k := range storageMap {
|
|
||||||
storageList = append(storageList, k)
|
|
||||||
}
|
|
||||||
slices.SortFunc(storageList, common.Hash.Cmp)
|
slices.SortFunc(storageList, common.Hash.Cmp)
|
||||||
dl.storageList[accountHash] = storageList
|
dl.storageList[accountHash] = storageList
|
||||||
dl.memory += uint64(len(dl.storageList)*common.HashLength + common.HashLength)
|
dl.memory += uint64(len(dl.storageList)*common.HashLength + common.HashLength)
|
||||||
return storageList, destructed
|
return storageList
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,14 +28,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func copyDestructs(destructs map[common.Hash]struct{}) map[common.Hash]struct{} {
|
|
||||||
copy := make(map[common.Hash]struct{})
|
|
||||||
for hash := range destructs {
|
|
||||||
copy[hash] = struct{}{}
|
|
||||||
}
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyAccounts(accounts map[common.Hash][]byte) map[common.Hash][]byte {
|
func copyAccounts(accounts map[common.Hash][]byte) map[common.Hash][]byte {
|
||||||
copy := make(map[common.Hash][]byte)
|
copy := make(map[common.Hash][]byte)
|
||||||
for hash, blob := range accounts {
|
for hash, blob := range accounts {
|
||||||
|
@ -58,9 +50,8 @@ func copyStorage(storage map[common.Hash]map[common.Hash][]byte) map[common.Hash
|
||||||
// TestMergeBasics tests some simple merges
|
// TestMergeBasics tests some simple merges
|
||||||
func TestMergeBasics(t *testing.T) {
|
func TestMergeBasics(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
accounts = make(map[common.Hash][]byte)
|
||||||
accounts = make(map[common.Hash][]byte)
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
)
|
||||||
// Fill up a parent
|
// Fill up a parent
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
|
@ -69,7 +60,7 @@ func TestMergeBasics(t *testing.T) {
|
||||||
|
|
||||||
accounts[h] = data
|
accounts[h] = data
|
||||||
if rand.Intn(4) == 0 {
|
if rand.Intn(4) == 0 {
|
||||||
destructs[h] = struct{}{}
|
accounts[h] = nil
|
||||||
}
|
}
|
||||||
if rand.Intn(2) == 0 {
|
if rand.Intn(2) == 0 {
|
||||||
accStorage := make(map[common.Hash][]byte)
|
accStorage := make(map[common.Hash][]byte)
|
||||||
|
@ -80,11 +71,12 @@ func TestMergeBasics(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add some (identical) layers on top
|
// Add some (identical) layers on top
|
||||||
parent := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage))
|
parent := newDiffLayer(emptyLayer(), common.Hash{}, copyAccounts(accounts), copyStorage(storage))
|
||||||
child := newDiffLayer(parent, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage))
|
child := newDiffLayer(parent, common.Hash{}, copyAccounts(accounts), copyStorage(storage))
|
||||||
child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage))
|
child = newDiffLayer(child, common.Hash{}, copyAccounts(accounts), copyStorage(storage))
|
||||||
child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage))
|
child = newDiffLayer(child, common.Hash{}, copyAccounts(accounts), copyStorage(storage))
|
||||||
child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage))
|
child = newDiffLayer(child, common.Hash{}, copyAccounts(accounts), copyStorage(storage))
|
||||||
|
|
||||||
// And flatten
|
// And flatten
|
||||||
merged := (child.flatten()).(*diffLayer)
|
merged := (child.flatten()).(*diffLayer)
|
||||||
|
|
||||||
|
@ -99,18 +91,13 @@ func TestMergeBasics(t *testing.T) {
|
||||||
t.Errorf("accountList [2] wrong: have %v, want %v", have, want)
|
t.Errorf("accountList [2] wrong: have %v, want %v", have, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{ // Check account drops
|
|
||||||
if have, want := len(merged.destructSet), len(destructs); have != want {
|
|
||||||
t.Errorf("accountDrop wrong: have %v, want %v", have, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{ // Check storage lists
|
{ // Check storage lists
|
||||||
i := 0
|
i := 0
|
||||||
for aHash, sMap := range storage {
|
for aHash, sMap := range storage {
|
||||||
if have, want := len(merged.storageList), i; have != want {
|
if have, want := len(merged.storageList), i; have != want {
|
||||||
t.Errorf("[1] storageList wrong: have %v, want %v", have, want)
|
t.Errorf("[1] storageList wrong: have %v, want %v", have, want)
|
||||||
}
|
}
|
||||||
list, _ := merged.StorageList(aHash)
|
list := merged.StorageList(aHash)
|
||||||
if have, want := len(list), len(sMap); have != want {
|
if have, want := len(list), len(sMap); have != want {
|
||||||
t.Errorf("[2] StorageList() wrong: have %v, want %v", have, want)
|
t.Errorf("[2] StorageList() wrong: have %v, want %v", have, want)
|
||||||
}
|
}
|
||||||
|
@ -124,41 +111,32 @@ func TestMergeBasics(t *testing.T) {
|
||||||
|
|
||||||
// TestMergeDelete tests some deletion
|
// TestMergeDelete tests some deletion
|
||||||
func TestMergeDelete(t *testing.T) {
|
func TestMergeDelete(t *testing.T) {
|
||||||
var (
|
storage := make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
|
||||||
// Fill up a parent
|
// Fill up a parent
|
||||||
h1 := common.HexToHash("0x01")
|
h1 := common.HexToHash("0x01")
|
||||||
h2 := common.HexToHash("0x02")
|
h2 := common.HexToHash("0x02")
|
||||||
|
|
||||||
flipDrops := func() map[common.Hash]struct{} {
|
flip := func() map[common.Hash][]byte {
|
||||||
return map[common.Hash]struct{}{
|
|
||||||
h2: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flipAccs := func() map[common.Hash][]byte {
|
|
||||||
return map[common.Hash][]byte{
|
return map[common.Hash][]byte{
|
||||||
h1: randomAccount(),
|
h1: randomAccount(),
|
||||||
|
h2: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flopDrops := func() map[common.Hash]struct{} {
|
flop := func() map[common.Hash][]byte {
|
||||||
return map[common.Hash]struct{}{
|
|
||||||
h1: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flopAccs := func() map[common.Hash][]byte {
|
|
||||||
return map[common.Hash][]byte{
|
return map[common.Hash][]byte{
|
||||||
|
h1: nil,
|
||||||
h2: randomAccount(),
|
h2: randomAccount(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add some flipAccs-flopping layers on top
|
// Add some flipAccs-flopping layers on top
|
||||||
parent := newDiffLayer(emptyLayer(), common.Hash{}, flipDrops(), flipAccs(), storage)
|
parent := newDiffLayer(emptyLayer(), common.Hash{}, flip(), storage)
|
||||||
child := parent.Update(common.Hash{}, flopDrops(), flopAccs(), storage)
|
child := parent.Update(common.Hash{}, flop(), storage)
|
||||||
child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage)
|
child = child.Update(common.Hash{}, flip(), storage)
|
||||||
child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage)
|
child = child.Update(common.Hash{}, flop(), storage)
|
||||||
child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage)
|
child = child.Update(common.Hash{}, flip(), storage)
|
||||||
child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage)
|
child = child.Update(common.Hash{}, flop(), storage)
|
||||||
child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage)
|
child = child.Update(common.Hash{}, flip(), storage)
|
||||||
|
|
||||||
if data, _ := child.Account(h1); data == nil {
|
if data, _ := child.Account(h1); data == nil {
|
||||||
t.Errorf("last diff layer: expected %x account to be non-nil", h1)
|
t.Errorf("last diff layer: expected %x account to be non-nil", h1)
|
||||||
|
@ -166,12 +144,7 @@ func TestMergeDelete(t *testing.T) {
|
||||||
if data, _ := child.Account(h2); data != nil {
|
if data, _ := child.Account(h2); data != nil {
|
||||||
t.Errorf("last diff layer: expected %x account to be nil", h2)
|
t.Errorf("last diff layer: expected %x account to be nil", h2)
|
||||||
}
|
}
|
||||||
if _, ok := child.destructSet[h1]; ok {
|
|
||||||
t.Errorf("last diff layer: expected %x drop to be missing", h1)
|
|
||||||
}
|
|
||||||
if _, ok := child.destructSet[h2]; !ok {
|
|
||||||
t.Errorf("last diff layer: expected %x drop to be present", h1)
|
|
||||||
}
|
|
||||||
// And flatten
|
// And flatten
|
||||||
merged := (child.flatten()).(*diffLayer)
|
merged := (child.flatten()).(*diffLayer)
|
||||||
|
|
||||||
|
@ -181,12 +154,6 @@ func TestMergeDelete(t *testing.T) {
|
||||||
if data, _ := merged.Account(h2); data != nil {
|
if data, _ := merged.Account(h2); data != nil {
|
||||||
t.Errorf("merged layer: expected %x account to be nil", h2)
|
t.Errorf("merged layer: expected %x account to be nil", h2)
|
||||||
}
|
}
|
||||||
if _, ok := merged.destructSet[h1]; !ok { // Note, drops stay alive until persisted to disk!
|
|
||||||
t.Errorf("merged diff layer: expected %x drop to be present", h1)
|
|
||||||
}
|
|
||||||
if _, ok := merged.destructSet[h2]; !ok { // Note, drops stay alive until persisted to disk!
|
|
||||||
t.Errorf("merged diff layer: expected %x drop to be present", h1)
|
|
||||||
}
|
|
||||||
// If we add more granular metering of memory, we can enable this again,
|
// If we add more granular metering of memory, we can enable this again,
|
||||||
// but it's not implemented for now
|
// but it's not implemented for now
|
||||||
//if have, want := merged.memory, child.memory; have != want {
|
//if have, want := merged.memory, child.memory; have != want {
|
||||||
|
@ -206,22 +173,20 @@ func TestInsertAndMerge(t *testing.T) {
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
accounts = make(map[common.Hash][]byte)
|
||||||
accounts = make(map[common.Hash][]byte)
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
)
|
||||||
parent = newDiffLayer(emptyLayer(), common.Hash{}, destructs, accounts, storage)
|
parent = newDiffLayer(emptyLayer(), common.Hash{}, accounts, storage)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
accounts = make(map[common.Hash][]byte)
|
||||||
accounts = make(map[common.Hash][]byte)
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
)
|
||||||
accounts[acc] = randomAccount()
|
accounts[acc] = randomAccount()
|
||||||
storage[acc] = make(map[common.Hash][]byte)
|
storage[acc] = make(map[common.Hash][]byte)
|
||||||
storage[acc][slot] = []byte{0x01}
|
storage[acc][slot] = []byte{0x01}
|
||||||
child = newDiffLayer(parent, common.Hash{}, destructs, accounts, storage)
|
child = newDiffLayer(parent, common.Hash{}, accounts, storage)
|
||||||
}
|
}
|
||||||
// And flatten
|
// And flatten
|
||||||
merged := (child.flatten()).(*diffLayer)
|
merged := (child.flatten()).(*diffLayer)
|
||||||
|
@ -250,14 +215,13 @@ func BenchmarkSearch(b *testing.B) {
|
||||||
// First, we set up 128 diff layers, with 1K items each
|
// First, we set up 128 diff layers, with 1K items each
|
||||||
fill := func(parent snapshot) *diffLayer {
|
fill := func(parent snapshot) *diffLayer {
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
accounts = make(map[common.Hash][]byte)
|
||||||
accounts = make(map[common.Hash][]byte)
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
)
|
||||||
for i := 0; i < 10000; i++ {
|
for i := 0; i < 10000; i++ {
|
||||||
accounts[randomHash()] = randomAccount()
|
accounts[randomHash()] = randomAccount()
|
||||||
}
|
}
|
||||||
return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage)
|
return newDiffLayer(parent, common.Hash{}, accounts, storage)
|
||||||
}
|
}
|
||||||
var layer snapshot
|
var layer snapshot
|
||||||
layer = emptyLayer()
|
layer = emptyLayer()
|
||||||
|
@ -286,9 +250,8 @@ func BenchmarkSearchSlot(b *testing.B) {
|
||||||
accountRLP := randomAccount()
|
accountRLP := randomAccount()
|
||||||
fill := func(parent snapshot) *diffLayer {
|
fill := func(parent snapshot) *diffLayer {
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
accounts = make(map[common.Hash][]byte)
|
||||||
accounts = make(map[common.Hash][]byte)
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
)
|
||||||
accounts[accountKey] = accountRLP
|
accounts[accountKey] = accountRLP
|
||||||
|
|
||||||
|
@ -299,7 +262,7 @@ func BenchmarkSearchSlot(b *testing.B) {
|
||||||
accStorage[randomHash()] = value
|
accStorage[randomHash()] = value
|
||||||
storage[accountKey] = accStorage
|
storage[accountKey] = accStorage
|
||||||
}
|
}
|
||||||
return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage)
|
return newDiffLayer(parent, common.Hash{}, accounts, storage)
|
||||||
}
|
}
|
||||||
var layer snapshot
|
var layer snapshot
|
||||||
layer = emptyLayer()
|
layer = emptyLayer()
|
||||||
|
@ -320,9 +283,8 @@ func BenchmarkSearchSlot(b *testing.B) {
|
||||||
func BenchmarkFlatten(b *testing.B) {
|
func BenchmarkFlatten(b *testing.B) {
|
||||||
fill := func(parent snapshot) *diffLayer {
|
fill := func(parent snapshot) *diffLayer {
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
accounts = make(map[common.Hash][]byte)
|
||||||
accounts = make(map[common.Hash][]byte)
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
)
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
accountKey := randomHash()
|
accountKey := randomHash()
|
||||||
|
@ -336,7 +298,7 @@ func BenchmarkFlatten(b *testing.B) {
|
||||||
}
|
}
|
||||||
storage[accountKey] = accStorage
|
storage[accountKey] = accStorage
|
||||||
}
|
}
|
||||||
return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage)
|
return newDiffLayer(parent, common.Hash{}, accounts, storage)
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
@ -369,9 +331,8 @@ func BenchmarkFlatten(b *testing.B) {
|
||||||
func BenchmarkJournal(b *testing.B) {
|
func BenchmarkJournal(b *testing.B) {
|
||||||
fill := func(parent snapshot) *diffLayer {
|
fill := func(parent snapshot) *diffLayer {
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
accounts = make(map[common.Hash][]byte)
|
||||||
accounts = make(map[common.Hash][]byte)
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
)
|
||||||
for i := 0; i < 200; i++ {
|
for i := 0; i < 200; i++ {
|
||||||
accountKey := randomHash()
|
accountKey := randomHash()
|
||||||
|
@ -385,7 +346,7 @@ func BenchmarkJournal(b *testing.B) {
|
||||||
}
|
}
|
||||||
storage[accountKey] = accStorage
|
storage[accountKey] = accStorage
|
||||||
}
|
}
|
||||||
return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage)
|
return newDiffLayer(parent, common.Hash{}, accounts, storage)
|
||||||
}
|
}
|
||||||
layer := snapshot(emptyLayer())
|
layer := snapshot(emptyLayer())
|
||||||
for i := 1; i < 128; i++ {
|
for i := 1; i < 128; i++ {
|
||||||
|
|
|
@ -180,8 +180,8 @@ func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro
|
||||||
// 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. Note, the maps are retained by the method to avoid
|
||||||
// copying everything.
|
// copying everything.
|
||||||
func (dl *diskLayer) Update(blockHash common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
func (dl *diskLayer) Update(blockHash common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
||||||
return newDiffLayer(dl, blockHash, destructs, accounts, storage)
|
return newDiffLayer(dl, blockHash, accounts, storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopGeneration aborts the state snapshot generation if it is currently running.
|
// stopGeneration aborts the state snapshot generation if it is currently running.
|
||||||
|
|
|
@ -117,20 +117,22 @@ func TestDiskMerge(t *testing.T) {
|
||||||
base.Storage(conNukeCache, conNukeCacheSlot)
|
base.Storage(conNukeCache, conNukeCacheSlot)
|
||||||
|
|
||||||
// Modify or delete some accounts, flatten everything onto disk
|
// Modify or delete some accounts, flatten everything onto disk
|
||||||
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
if err := snaps.Update(diffRoot, baseRoot,
|
||||||
accDelNoCache: {},
|
map[common.Hash][]byte{
|
||||||
accDelCache: {},
|
accDelNoCache: nil,
|
||||||
conNukeNoCache: {},
|
accDelCache: nil,
|
||||||
conNukeCache: {},
|
conNukeNoCache: nil,
|
||||||
}, map[common.Hash][]byte{
|
conNukeCache: nil,
|
||||||
accModNoCache: reverse(accModNoCache[:]),
|
accModNoCache: reverse(accModNoCache[:]),
|
||||||
accModCache: reverse(accModCache[:]),
|
accModCache: reverse(accModCache[:]),
|
||||||
}, map[common.Hash]map[common.Hash][]byte{
|
}, map[common.Hash]map[common.Hash][]byte{
|
||||||
conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
|
conNukeNoCache: {conNukeNoCacheSlot: nil},
|
||||||
conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])},
|
conNukeCache: {conNukeCacheSlot: nil},
|
||||||
conDelNoCache: {conDelNoCacheSlot: nil},
|
conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
|
||||||
conDelCache: {conDelCacheSlot: nil},
|
conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])},
|
||||||
}); err != nil {
|
conDelNoCache: {conDelNoCacheSlot: nil},
|
||||||
|
conDelCache: {conDelCacheSlot: nil},
|
||||||
|
}); err != nil {
|
||||||
t.Fatalf("failed to update snapshot tree: %v", err)
|
t.Fatalf("failed to update snapshot tree: %v", err)
|
||||||
}
|
}
|
||||||
if err := snaps.Cap(diffRoot, 0); err != nil {
|
if err := snaps.Cap(diffRoot, 0); err != nil {
|
||||||
|
@ -340,20 +342,27 @@ func TestDiskPartialMerge(t *testing.T) {
|
||||||
assertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
|
assertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
|
||||||
|
|
||||||
// Modify or delete some accounts, flatten everything onto disk
|
// Modify or delete some accounts, flatten everything onto disk
|
||||||
if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
|
if err := snaps.Update(diffRoot, baseRoot,
|
||||||
accDelNoCache: {},
|
map[common.Hash][]byte{
|
||||||
accDelCache: {},
|
accDelNoCache: nil,
|
||||||
conNukeNoCache: {},
|
accDelCache: nil,
|
||||||
conNukeCache: {},
|
conNukeNoCache: nil,
|
||||||
}, map[common.Hash][]byte{
|
conNukeCache: nil,
|
||||||
accModNoCache: reverse(accModNoCache[:]),
|
accModNoCache: reverse(accModNoCache[:]),
|
||||||
accModCache: reverse(accModCache[:]),
|
accModCache: reverse(accModCache[:]),
|
||||||
}, map[common.Hash]map[common.Hash][]byte{
|
},
|
||||||
conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
|
map[common.Hash]map[common.Hash][]byte{
|
||||||
conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])},
|
conNukeNoCache: {
|
||||||
conDelNoCache: {conDelNoCacheSlot: nil},
|
conNukeNoCacheSlot: nil,
|
||||||
conDelCache: {conDelCacheSlot: nil},
|
},
|
||||||
}); err != nil {
|
conNukeCache: {
|
||||||
|
conNukeCacheSlot: nil,
|
||||||
|
},
|
||||||
|
conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
|
||||||
|
conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])},
|
||||||
|
conDelNoCache: {conDelNoCacheSlot: nil},
|
||||||
|
conDelCache: {conDelCacheSlot: nil},
|
||||||
|
}); err != nil {
|
||||||
t.Fatalf("test %d: failed to update snapshot tree: %v", i, err)
|
t.Fatalf("test %d: failed to update snapshot tree: %v", i, err)
|
||||||
}
|
}
|
||||||
if err := snaps.Cap(diffRoot, 0); err != nil {
|
if err := snaps.Cap(diffRoot, 0); err != nil {
|
||||||
|
@ -462,9 +471,11 @@ func TestDiskGeneratorPersistence(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Modify or delete some accounts, flatten everything onto disk
|
// Modify or delete some accounts, flatten everything onto disk
|
||||||
if err := snaps.Update(diffRoot, baseRoot, nil, map[common.Hash][]byte{
|
if err := snaps.Update(diffRoot, baseRoot,
|
||||||
accTwo: accTwo[:],
|
map[common.Hash][]byte{
|
||||||
}, nil); err != nil {
|
accTwo: accTwo[:],
|
||||||
|
}, nil,
|
||||||
|
); err != nil {
|
||||||
t.Fatalf("failed to update snapshot tree: %v", err)
|
t.Fatalf("failed to update snapshot tree: %v", err)
|
||||||
}
|
}
|
||||||
if err := snaps.Cap(diffRoot, 0); err != nil {
|
if err := snaps.Cap(diffRoot, 0); err != nil {
|
||||||
|
@ -480,11 +491,14 @@ func TestDiskGeneratorPersistence(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Test scenario 2, the disk layer is fully generated
|
// Test scenario 2, the disk layer is fully generated
|
||||||
// Modify or delete some accounts, flatten everything onto disk
|
// Modify or delete some accounts, flatten everything onto disk
|
||||||
if err := snaps.Update(diffTwoRoot, diffRoot, nil, map[common.Hash][]byte{
|
if err := snaps.Update(diffTwoRoot, diffRoot,
|
||||||
accThree: accThree.Bytes(),
|
map[common.Hash][]byte{
|
||||||
}, map[common.Hash]map[common.Hash][]byte{
|
accThree: accThree.Bytes(),
|
||||||
accThree: {accThreeSlot: accThreeSlot.Bytes()},
|
},
|
||||||
}); err != nil {
|
map[common.Hash]map[common.Hash][]byte{
|
||||||
|
accThree: {accThreeSlot: accThreeSlot.Bytes()},
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
t.Fatalf("failed to update snapshot tree: %v", err)
|
t.Fatalf("failed to update snapshot tree: %v", err)
|
||||||
}
|
}
|
||||||
diskLayer := snaps.layers[snaps.diskRoot()].(*diskLayer)
|
diskLayer := snaps.layers[snaps.diskRoot()].(*diskLayer)
|
||||||
|
|
|
@ -134,7 +134,7 @@ func checkSnapRoot(t *testing.T, snap *diskLayer, trieRoot common.Hash) {
|
||||||
|
|
||||||
snapRoot, err := generateTrieRoot(nil, "", accIt, common.Hash{}, stackTrieGenerate,
|
snapRoot, err := generateTrieRoot(nil, "", accIt, common.Hash{}, stackTrieGenerate,
|
||||||
func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) {
|
func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) {
|
||||||
storageIt, _ := snap.StorageIterator(accountHash, common.Hash{})
|
storageIt := snap.StorageIterator(accountHash, common.Hash{})
|
||||||
defer storageIt.Release()
|
defer storageIt.Release()
|
||||||
|
|
||||||
hash, err := generateTrieRoot(nil, "", storageIt, accountHash, stackTrieGenerate, nil, stat, false)
|
hash, err := generateTrieRoot(nil, "", storageIt, accountHash, stackTrieGenerate, nil, stat, false)
|
||||||
|
|
|
@ -115,6 +115,7 @@ func (it *diffAccountIterator) Next() bool {
|
||||||
}
|
}
|
||||||
// Iterator seems to be still alive, retrieve and cache the live hash
|
// Iterator seems to be still alive, retrieve and cache the live hash
|
||||||
it.curHash = it.keys[0]
|
it.curHash = it.keys[0]
|
||||||
|
|
||||||
// key cached, shift the iterator and notify the user of success
|
// key cached, shift the iterator and notify the user of success
|
||||||
it.keys = it.keys[1:]
|
it.keys = it.keys[1:]
|
||||||
return true
|
return true
|
||||||
|
@ -135,7 +136,7 @@ func (it *diffAccountIterator) Hash() common.Hash {
|
||||||
// This method may _fail_, if the underlying layer has been flattened between
|
// This method may _fail_, if the underlying layer has been flattened between
|
||||||
// the call to Next and Account. That type of error will set it.Err.
|
// the call to Next and Account. That type of error will set it.Err.
|
||||||
// This method assumes that flattening does not delete elements from
|
// This method assumes that flattening does not delete elements from
|
||||||
// the accountdata mapping (writing nil into it is fine though), and will panic
|
// the accountData mapping (writing nil into it is fine though), and will panic
|
||||||
// if elements have been deleted.
|
// if elements have been deleted.
|
||||||
//
|
//
|
||||||
// Note the returned account is not a copy, please don't modify it.
|
// Note the returned account is not a copy, please don't modify it.
|
||||||
|
@ -143,10 +144,6 @@ func (it *diffAccountIterator) Account() []byte {
|
||||||
it.layer.lock.RLock()
|
it.layer.lock.RLock()
|
||||||
blob, ok := it.layer.accountData[it.curHash]
|
blob, ok := it.layer.accountData[it.curHash]
|
||||||
if !ok {
|
if !ok {
|
||||||
if _, ok := it.layer.destructSet[it.curHash]; ok {
|
|
||||||
it.layer.lock.RUnlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash))
|
panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash))
|
||||||
}
|
}
|
||||||
it.layer.lock.RUnlock()
|
it.layer.lock.RUnlock()
|
||||||
|
@ -247,11 +244,11 @@ type diffStorageIterator struct {
|
||||||
// "destructed" returned. If it's true then it means the whole storage is
|
// "destructed" returned. If it's true then it means the whole storage is
|
||||||
// destructed in this layer(maybe recreated too), don't bother deeper layer
|
// destructed in this layer(maybe recreated too), don't bother deeper layer
|
||||||
// for storage retrieval.
|
// for storage retrieval.
|
||||||
func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) {
|
func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) StorageIterator {
|
||||||
// Create the storage for this account even it's marked
|
// Create the storage for this account even it's marked
|
||||||
// as destructed. The iterator is for the new one which
|
// as destructed. The iterator is for the new one which
|
||||||
// just has the same address as the deleted one.
|
// just has the same address as the deleted one.
|
||||||
hashes, destructed := dl.StorageList(account)
|
hashes := dl.StorageList(account)
|
||||||
index := sort.Search(len(hashes), func(i int) bool {
|
index := sort.Search(len(hashes), func(i int) bool {
|
||||||
return bytes.Compare(seek[:], hashes[i][:]) <= 0
|
return bytes.Compare(seek[:], hashes[i][:]) <= 0
|
||||||
})
|
})
|
||||||
|
@ -260,7 +257,7 @@ func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) (Sto
|
||||||
layer: dl,
|
layer: dl,
|
||||||
account: account,
|
account: account,
|
||||||
keys: hashes[index:],
|
keys: hashes[index:],
|
||||||
}, destructed
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next steps the iterator forward one element, returning false if exhausted.
|
// Next steps the iterator forward one element, returning false if exhausted.
|
||||||
|
@ -339,13 +336,13 @@ type diskStorageIterator struct {
|
||||||
// If the whole storage is destructed, then all entries in the disk
|
// If the whole storage is destructed, then all entries in the disk
|
||||||
// layer are deleted already. So the "destructed" flag returned here
|
// layer are deleted already. So the "destructed" flag returned here
|
||||||
// is always false.
|
// is always false.
|
||||||
func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) {
|
func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) StorageIterator {
|
||||||
pos := common.TrimRightZeroes(seek[:])
|
pos := common.TrimRightZeroes(seek[:])
|
||||||
return &diskStorageIterator{
|
return &diskStorageIterator{
|
||||||
layer: dl,
|
layer: dl,
|
||||||
account: account,
|
account: account,
|
||||||
it: dl.diskdb.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos),
|
it: dl.diskdb.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos),
|
||||||
}, false
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next steps the iterator forward one element, returning false if exhausted.
|
// Next steps the iterator forward one element, returning false if exhausted.
|
||||||
|
|
|
@ -67,44 +67,17 @@ func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) Iterator {
|
||||||
func (dl *diffLayer) initBinaryStorageIterator(account, seek common.Hash) Iterator {
|
func (dl *diffLayer) initBinaryStorageIterator(account, seek common.Hash) Iterator {
|
||||||
parent, ok := dl.parent.(*diffLayer)
|
parent, ok := dl.parent.(*diffLayer)
|
||||||
if !ok {
|
if !ok {
|
||||||
// If the storage in this layer is already destructed, discard all
|
|
||||||
// deeper layers but still return a valid single-branch iterator.
|
|
||||||
a, destructed := dl.StorageIterator(account, seek)
|
|
||||||
if destructed {
|
|
||||||
l := &binaryIterator{
|
|
||||||
a: a,
|
|
||||||
account: account,
|
|
||||||
}
|
|
||||||
l.aDone = !l.a.Next()
|
|
||||||
l.bDone = true
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
// The parent is disk layer, don't need to take care "destructed"
|
|
||||||
// anymore.
|
|
||||||
b, _ := dl.Parent().StorageIterator(account, seek)
|
|
||||||
l := &binaryIterator{
|
l := &binaryIterator{
|
||||||
a: a,
|
a: dl.StorageIterator(account, seek),
|
||||||
b: b,
|
b: dl.Parent().StorageIterator(account, seek),
|
||||||
account: account,
|
account: account,
|
||||||
}
|
}
|
||||||
l.aDone = !l.a.Next()
|
l.aDone = !l.a.Next()
|
||||||
l.bDone = !l.b.Next()
|
l.bDone = !l.b.Next()
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
// If the storage in this layer is already destructed, discard all
|
|
||||||
// deeper layers but still return a valid single-branch iterator.
|
|
||||||
a, destructed := dl.StorageIterator(account, seek)
|
|
||||||
if destructed {
|
|
||||||
l := &binaryIterator{
|
|
||||||
a: a,
|
|
||||||
account: account,
|
|
||||||
}
|
|
||||||
l.aDone = !l.a.Next()
|
|
||||||
l.bDone = true
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
l := &binaryIterator{
|
l := &binaryIterator{
|
||||||
a: a,
|
a: dl.StorageIterator(account, seek),
|
||||||
b: parent.initBinaryStorageIterator(account, seek),
|
b: parent.initBinaryStorageIterator(account, seek),
|
||||||
account: account,
|
account: account,
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,18 +90,10 @@ func newFastIterator(tree *Tree, root common.Hash, account common.Hash, seek com
|
||||||
priority: depth,
|
priority: depth,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// If the whole storage is destructed in this layer, don't
|
|
||||||
// bother deeper layer anymore. But we should still keep
|
|
||||||
// the iterator for this layer, since the iterator can contain
|
|
||||||
// some valid slots which belongs to the re-created account.
|
|
||||||
it, destructed := current.StorageIterator(account, seek)
|
|
||||||
fi.iterators = append(fi.iterators, &weightedIterator{
|
fi.iterators = append(fi.iterators, &weightedIterator{
|
||||||
it: it,
|
it: current.StorageIterator(account, seek),
|
||||||
priority: depth,
|
priority: depth,
|
||||||
})
|
})
|
||||||
if destructed {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
current = current.Parent()
|
current = current.Parent()
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,8 @@ import (
|
||||||
// TestAccountIteratorBasics tests some simple single-layer(diff and disk) iteration
|
// TestAccountIteratorBasics tests some simple single-layer(diff and disk) iteration
|
||||||
func TestAccountIteratorBasics(t *testing.T) {
|
func TestAccountIteratorBasics(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
accounts = make(map[common.Hash][]byte)
|
||||||
accounts = make(map[common.Hash][]byte)
|
storage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
storage = make(map[common.Hash]map[common.Hash][]byte)
|
|
||||||
)
|
)
|
||||||
// Fill up a parent
|
// Fill up a parent
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
|
@ -42,9 +41,6 @@ func TestAccountIteratorBasics(t *testing.T) {
|
||||||
data := randomAccount()
|
data := randomAccount()
|
||||||
|
|
||||||
accounts[h] = data
|
accounts[h] = data
|
||||||
if rand.Intn(4) == 0 {
|
|
||||||
destructs[h] = struct{}{}
|
|
||||||
}
|
|
||||||
if rand.Intn(2) == 0 {
|
if rand.Intn(2) == 0 {
|
||||||
accStorage := make(map[common.Hash][]byte)
|
accStorage := make(map[common.Hash][]byte)
|
||||||
value := make([]byte, 32)
|
value := make([]byte, 32)
|
||||||
|
@ -54,7 +50,7 @@ func TestAccountIteratorBasics(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add some (identical) layers on top
|
// Add some (identical) layers on top
|
||||||
diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage))
|
diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyAccounts(accounts), copyStorage(storage))
|
||||||
it := diffLayer.AccountIterator(common.Hash{})
|
it := diffLayer.AccountIterator(common.Hash{})
|
||||||
verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator
|
verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator
|
||||||
|
|
||||||
|
@ -95,15 +91,15 @@ func TestStorageIteratorBasics(t *testing.T) {
|
||||||
nilStorage[h] = nilstorage
|
nilStorage[h] = nilstorage
|
||||||
}
|
}
|
||||||
// Add some (identical) layers on top
|
// Add some (identical) layers on top
|
||||||
diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, nil, copyAccounts(accounts), copyStorage(storage))
|
diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyAccounts(accounts), copyStorage(storage))
|
||||||
for account := range accounts {
|
for account := range accounts {
|
||||||
it, _ := diffLayer.StorageIterator(account, common.Hash{})
|
it := diffLayer.StorageIterator(account, common.Hash{})
|
||||||
verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator
|
verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator
|
||||||
}
|
}
|
||||||
|
|
||||||
diskLayer := diffToDisk(diffLayer)
|
diskLayer := diffToDisk(diffLayer)
|
||||||
for account := range accounts {
|
for account := range accounts {
|
||||||
it, _ := diskLayer.StorageIterator(account, common.Hash{})
|
it := diskLayer.StorageIterator(account, common.Hash{})
|
||||||
verifyIterator(t, 100-nilStorage[account], it, verifyNothing) // Nil is allowed for single layer iterator
|
verifyIterator(t, 100-nilStorage[account], it, verifyNothing) // Nil is allowed for single layer iterator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,13 +221,13 @@ func TestAccountIteratorTraversal(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Stack three diff layers on top with various overlaps
|
// Stack three diff layers on top with various overlaps
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"),
|
||||||
randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil)
|
randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil)
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
||||||
randomAccountSet("0xbb", "0xdd", "0xf0"), nil)
|
randomAccountSet("0xbb", "0xdd", "0xf0"), nil)
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"),
|
||||||
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
||||||
|
|
||||||
// Verify the single and multi-layer iterators
|
// Verify the single and multi-layer iterators
|
||||||
|
@ -272,19 +268,19 @@ func TestStorageIteratorTraversal(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Stack three diff layers on top with various overlaps
|
// Stack three diff layers on top with various overlaps
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil))
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x04", "0x05", "0x06"}}, nil))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x04", "0x05", "0x06"}}, nil))
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil))
|
||||||
|
|
||||||
// Verify the single and multi-layer iterators
|
// Verify the single and multi-layer iterators
|
||||||
head := snaps.Snapshot(common.HexToHash("0x04"))
|
head := snaps.Snapshot(common.HexToHash("0x04"))
|
||||||
|
|
||||||
diffIter, _ := head.(snapshot).StorageIterator(common.HexToHash("0xaa"), common.Hash{})
|
diffIter := head.(snapshot).StorageIterator(common.HexToHash("0xaa"), common.Hash{})
|
||||||
verifyIterator(t, 3, diffIter, verifyNothing)
|
verifyIterator(t, 3, diffIter, verifyNothing)
|
||||||
verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage)
|
verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage)
|
||||||
|
|
||||||
|
@ -357,14 +353,14 @@ func TestAccountIteratorTraversalValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble a stack of snapshots from the account layers
|
// Assemble a stack of snapshots from the account layers
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, a, nil)
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), a, nil)
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, b, nil)
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), b, nil)
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, c, nil)
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), c, nil)
|
||||||
snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), nil, d, nil)
|
snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), d, nil)
|
||||||
snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), nil, e, nil)
|
snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), e, nil)
|
||||||
snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), nil, f, nil)
|
snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), f, nil)
|
||||||
snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), nil, g, nil)
|
snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), g, nil)
|
||||||
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, h, nil)
|
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), h, nil)
|
||||||
|
|
||||||
it, _ := snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{})
|
it, _ := snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{})
|
||||||
head := snaps.Snapshot(common.HexToHash("0x09"))
|
head := snaps.Snapshot(common.HexToHash("0x09"))
|
||||||
|
@ -456,14 +452,14 @@ func TestStorageIteratorTraversalValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble a stack of snapshots from the account layers
|
// Assemble a stack of snapshots from the account layers
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, randomAccountSet("0xaa"), wrapStorage(a))
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0xaa"), wrapStorage(a))
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, randomAccountSet("0xaa"), wrapStorage(b))
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), randomAccountSet("0xaa"), wrapStorage(b))
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, randomAccountSet("0xaa"), wrapStorage(c))
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), randomAccountSet("0xaa"), wrapStorage(c))
|
||||||
snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), nil, randomAccountSet("0xaa"), wrapStorage(d))
|
snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), randomAccountSet("0xaa"), wrapStorage(d))
|
||||||
snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), nil, randomAccountSet("0xaa"), wrapStorage(e))
|
snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), randomAccountSet("0xaa"), wrapStorage(e))
|
||||||
snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), nil, randomAccountSet("0xaa"), wrapStorage(e))
|
snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), randomAccountSet("0xaa"), wrapStorage(e))
|
||||||
snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), nil, randomAccountSet("0xaa"), wrapStorage(g))
|
snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), randomAccountSet("0xaa"), wrapStorage(g))
|
||||||
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, randomAccountSet("0xaa"), wrapStorage(h))
|
snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), randomAccountSet("0xaa"), wrapStorage(h))
|
||||||
|
|
||||||
it, _ := snaps.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{})
|
it, _ := snaps.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
head := snaps.Snapshot(common.HexToHash("0x09"))
|
head := snaps.Snapshot(common.HexToHash("0x09"))
|
||||||
|
@ -526,7 +522,7 @@ func TestAccountIteratorLargeTraversal(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i := 1; i < 128; i++ {
|
for i := 1; i < 128; i++ {
|
||||||
snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), nil, makeAccounts(200), nil)
|
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
|
// Iterate the entire stack and ensure everything is hit only once
|
||||||
head := snaps.Snapshot(common.HexToHash("0x80"))
|
head := snaps.Snapshot(common.HexToHash("0x80"))
|
||||||
|
@ -584,13 +580,13 @@ func testAccountIteratorFlattening(t *testing.T, newIterator func(snaps *Tree, r
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Create a stack of diffs on top
|
// Create a stack of diffs on top
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"),
|
||||||
randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil)
|
randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil)
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
||||||
randomAccountSet("0xbb", "0xdd", "0xf0"), nil)
|
randomAccountSet("0xbb", "0xdd", "0xf0"), nil)
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"),
|
||||||
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
||||||
|
|
||||||
// Create an iterator and flatten the data from underneath it
|
// Create an iterator and flatten the data from underneath it
|
||||||
|
@ -630,13 +626,13 @@ func testAccountIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, s
|
||||||
base.root: base,
|
base.root: base,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"),
|
||||||
randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil)
|
randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil)
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
||||||
randomAccountSet("0xbb", "0xdd", "0xf0"), nil)
|
randomAccountSet("0xbb", "0xdd", "0xf0"), nil)
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"),
|
||||||
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
randomAccountSet("0xcc", "0xf0", "0xff"), nil)
|
||||||
|
|
||||||
// Account set is now
|
// Account set is now
|
||||||
|
@ -708,13 +704,13 @@ func testStorageIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, a
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Stack three diff layers on top with various overlaps
|
// Stack three diff layers on top with various overlaps
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil))
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x05", "0x06"}}, nil))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x05", "0x06"}}, nil))
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil,
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x05", "0x08"}}, nil))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x05", "0x08"}}, nil))
|
||||||
|
|
||||||
// Account set is now
|
// Account set is now
|
||||||
|
@ -785,21 +781,16 @@ func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Stack three diff layers on top with various overlaps
|
// Stack three diff layers on top with various overlaps
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"),
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0x11", "0x22", "0x33"), nil)
|
||||||
nil, randomAccountSet("0x11", "0x22", "0x33"), nil)
|
|
||||||
|
|
||||||
deleted := common.HexToHash("0x22")
|
|
||||||
destructed := map[common.Hash]struct{}{
|
|
||||||
deleted: {},
|
|
||||||
}
|
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
|
||||||
destructed, randomAccountSet("0x11", "0x33"), nil)
|
|
||||||
|
|
||||||
|
set := randomAccountSet("0x11", "0x33")
|
||||||
|
set[common.HexToHash("0x22")] = nil
|
||||||
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), set, nil)
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"),
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"),
|
||||||
nil, randomAccountSet("0x33", "0x44", "0x55"), nil)
|
randomAccountSet("0x33", "0x44", "0x55"), nil)
|
||||||
|
|
||||||
// The output should be 11,33,44,55
|
// The output should be 11,33,44,55
|
||||||
it := newIterator(snaps, common.HexToHash("0x04"), (common.Hash{}))
|
it := newIterator(snaps, common.HexToHash("0x04"), common.Hash{})
|
||||||
|
|
||||||
// Do a quick check
|
// Do a quick check
|
||||||
verifyIterator(t, 4, it, verifyAccount)
|
verifyIterator(t, 4, it, verifyAccount)
|
||||||
|
@ -813,8 +804,8 @@ func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro
|
||||||
if it.Account() == nil {
|
if it.Account() == nil {
|
||||||
t.Errorf("iterator returned nil-value for hash %x", hash)
|
t.Errorf("iterator returned nil-value for hash %x", hash)
|
||||||
}
|
}
|
||||||
if hash == deleted {
|
if hash == common.HexToHash("0x22") {
|
||||||
t.Errorf("expected deleted elem %x to not be returned by iterator", deleted)
|
t.Errorf("expected deleted elem %x to not be returned by iterator", common.HexToHash("0x22"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -846,10 +837,10 @@ func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Stack three diff layers on top with various overlaps
|
// Stack three diff layers on top with various overlaps
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil,
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil))
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil,
|
snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x04", "0x06"}}, [][]string{{"0x01", "0x03"}}))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x04", "0x06"}}, [][]string{{"0x01", "0x03"}}))
|
||||||
|
|
||||||
// The output should be 02,04,05,06
|
// The output should be 02,04,05,06
|
||||||
|
@ -863,17 +854,16 @@ func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro
|
||||||
it.Release()
|
it.Release()
|
||||||
|
|
||||||
// Destruct the whole storage
|
// Destruct the whole storage
|
||||||
destructed := map[common.Hash]struct{}{
|
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"),
|
||||||
common.HexToHash("0xaa"): {},
|
map[common.Hash][]byte{common.HexToHash("0xaa"): nil},
|
||||||
}
|
randomStorageSet([]string{"0xaa"}, nil, [][]string{{"0x02", "0x04", "0x05", "0x06"}}))
|
||||||
snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), destructed, nil, nil)
|
|
||||||
|
|
||||||
it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{})
|
it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
verifyIterator(t, 0, it, verifyStorage)
|
verifyIterator(t, 0, it, verifyStorage)
|
||||||
it.Release()
|
it.Release()
|
||||||
|
|
||||||
// Re-insert the slots of the same account
|
// Re-insert the slots of the same account
|
||||||
snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), nil,
|
snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"),
|
||||||
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x07", "0x08", "0x09"}}, nil))
|
randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x07", "0x08", "0x09"}}, nil))
|
||||||
|
|
||||||
// The output should be 07,08,09
|
// The output should be 07,08,09
|
||||||
|
@ -883,7 +873,9 @@ func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro
|
||||||
it.Release()
|
it.Release()
|
||||||
|
|
||||||
// Destruct the whole storage but re-create the account in the same layer
|
// Destruct the whole storage but re-create the account in the same layer
|
||||||
snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), destructed, randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x11", "0x12"}}, nil))
|
snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"),
|
||||||
|
randomAccountSet("0xaa"),
|
||||||
|
randomStorageSet([]string{"0xaa"}, [][]string{{"0x11", "0x12"}}, [][]string{{"0x07", "0x08", "0x09"}}))
|
||||||
it = newIterator(snaps, common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{})
|
it = newIterator(snaps, common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{})
|
||||||
verifyIterator(t, 2, it, verifyStorage) // The output should be 11,12
|
verifyIterator(t, 2, it, verifyStorage) // The output should be 11,12
|
||||||
it.Release()
|
it.Release()
|
||||||
|
@ -928,7 +920,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i := 1; i <= 100; i++ {
|
for i := 1; i <= 100; i++ {
|
||||||
snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), nil, makeAccounts(200), nil)
|
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.
|
||||||
|
@ -1023,9 +1015,9 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) {
|
||||||
base.root: base,
|
base.root: base,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, makeAccounts(2000), nil)
|
snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), makeAccounts(2000), nil)
|
||||||
for i := 2; i <= 100; i++ {
|
for i := 2; i <= 100; i++ {
|
||||||
snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), nil, makeAccounts(20), nil)
|
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.
|
||||||
|
|
|
@ -33,7 +33,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const journalVersion uint64 = 0
|
// 0: initial version
|
||||||
|
// 1: destruct flag in diff layer is removed
|
||||||
|
const journalVersion uint64 = 1
|
||||||
|
|
||||||
// journalGenerator is a disk layer entry containing the generator progress marker.
|
// journalGenerator is a disk layer entry containing the generator progress marker.
|
||||||
type journalGenerator struct {
|
type journalGenerator struct {
|
||||||
|
@ -48,11 +50,6 @@ type journalGenerator struct {
|
||||||
Storage uint64
|
Storage uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// journalDestruct is an account deletion entry in a diffLayer's disk journal.
|
|
||||||
type journalDestruct struct {
|
|
||||||
Hash common.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// journalAccount is an account entry in a diffLayer's disk journal.
|
// journalAccount is an account entry in a diffLayer's disk journal.
|
||||||
type journalAccount struct {
|
type journalAccount struct {
|
||||||
Hash common.Hash
|
Hash common.Hash
|
||||||
|
@ -109,8 +106,8 @@ func loadAndParseJournal(db ethdb.KeyValueStore, base *diskLayer) (snapshot, jou
|
||||||
// is not matched with disk layer; or the it's the legacy-format journal,
|
// is not matched with disk layer; or the it's the legacy-format journal,
|
||||||
// etc.), we just discard all diffs and try to recover them later.
|
// etc.), we just discard all diffs and try to recover them later.
|
||||||
var current snapshot = base
|
var current snapshot = base
|
||||||
err := iterateJournal(db, func(parent common.Hash, root common.Hash, destructSet map[common.Hash]struct{}, accountData map[common.Hash][]byte, storageData map[common.Hash]map[common.Hash][]byte) error {
|
err := iterateJournal(db, func(parent common.Hash, root common.Hash, accountData map[common.Hash][]byte, storageData map[common.Hash]map[common.Hash][]byte) error {
|
||||||
current = newDiffLayer(current, root, destructSet, accountData, storageData)
|
current = newDiffLayer(current, root, accountData, storageData)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -238,16 +235,12 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) {
|
||||||
if err := rlp.Encode(buffer, dl.root); err != nil {
|
if err := rlp.Encode(buffer, dl.root); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
destructs := make([]journalDestruct, 0, len(dl.destructSet))
|
|
||||||
for hash := range dl.destructSet {
|
|
||||||
destructs = append(destructs, journalDestruct{Hash: hash})
|
|
||||||
}
|
|
||||||
if err := rlp.Encode(buffer, destructs); err != nil {
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
accounts := make([]journalAccount, 0, len(dl.accountData))
|
accounts := make([]journalAccount, 0, len(dl.accountData))
|
||||||
for hash, blob := range dl.accountData {
|
for hash, blob := range dl.accountData {
|
||||||
accounts = append(accounts, journalAccount{Hash: hash, Blob: blob})
|
accounts = append(accounts, journalAccount{
|
||||||
|
Hash: hash,
|
||||||
|
Blob: blob,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if err := rlp.Encode(buffer, accounts); err != nil {
|
if err := rlp.Encode(buffer, accounts); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
|
@ -271,7 +264,7 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) {
|
||||||
|
|
||||||
// journalCallback is a function which is invoked by iterateJournal, every
|
// journalCallback is a function which is invoked by iterateJournal, every
|
||||||
// time a difflayer is loaded from disk.
|
// time a difflayer is loaded from disk.
|
||||||
type journalCallback = func(parent common.Hash, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error
|
type journalCallback = func(parent common.Hash, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error
|
||||||
|
|
||||||
// iterateJournal iterates through the journalled difflayers, loading them from
|
// iterateJournal iterates through the journalled difflayers, loading them from
|
||||||
// the database, and invoking the callback for each loaded layer.
|
// the database, and invoking the callback for each loaded layer.
|
||||||
|
@ -310,10 +303,8 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error {
|
||||||
for {
|
for {
|
||||||
var (
|
var (
|
||||||
root common.Hash
|
root common.Hash
|
||||||
destructs []journalDestruct
|
|
||||||
accounts []journalAccount
|
accounts []journalAccount
|
||||||
storage []journalStorage
|
storage []journalStorage
|
||||||
destructSet = make(map[common.Hash]struct{})
|
|
||||||
accountData = make(map[common.Hash][]byte)
|
accountData = make(map[common.Hash][]byte)
|
||||||
storageData = make(map[common.Hash]map[common.Hash][]byte)
|
storageData = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
)
|
)
|
||||||
|
@ -325,18 +316,12 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error {
|
||||||
}
|
}
|
||||||
return fmt.Errorf("load diff root: %v", err)
|
return fmt.Errorf("load diff root: %v", err)
|
||||||
}
|
}
|
||||||
if err := r.Decode(&destructs); err != nil {
|
|
||||||
return fmt.Errorf("load diff destructs: %v", err)
|
|
||||||
}
|
|
||||||
if err := r.Decode(&accounts); err != nil {
|
if err := r.Decode(&accounts); err != nil {
|
||||||
return fmt.Errorf("load diff accounts: %v", err)
|
return fmt.Errorf("load diff accounts: %v", err)
|
||||||
}
|
}
|
||||||
if err := r.Decode(&storage); err != nil {
|
if err := r.Decode(&storage); err != nil {
|
||||||
return fmt.Errorf("load diff storage: %v", err)
|
return fmt.Errorf("load diff storage: %v", err)
|
||||||
}
|
}
|
||||||
for _, entry := range destructs {
|
|
||||||
destructSet[entry.Hash] = struct{}{}
|
|
||||||
}
|
|
||||||
for _, entry := range accounts {
|
for _, entry := range accounts {
|
||||||
if len(entry.Blob) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that
|
if len(entry.Blob) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that
|
||||||
accountData[entry.Hash] = entry.Blob
|
accountData[entry.Hash] = entry.Blob
|
||||||
|
@ -355,7 +340,7 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error {
|
||||||
}
|
}
|
||||||
storageData[entry.Hash] = slots
|
storageData[entry.Hash] = slots
|
||||||
}
|
}
|
||||||
if err := callback(parent, root, destructSet, accountData, storageData); err != nil {
|
if err := callback(parent, root, accountData, storageData); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
parent = root
|
parent = root
|
||||||
|
|
|
@ -130,7 +130,7 @@ type snapshot interface {
|
||||||
// the specified data items.
|
// the specified data items.
|
||||||
//
|
//
|
||||||
// Note, the maps are retained by the method to avoid copying everything.
|
// Note, the maps are retained by the method to avoid copying everything.
|
||||||
Update(blockRoot common.Hash, destructs map[common.Hash]struct{}, 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.
|
||||||
// This is meant to be used during shutdown to persist the snapshot without
|
// This is meant to be used during shutdown to persist the snapshot without
|
||||||
|
@ -145,7 +145,7 @@ type snapshot interface {
|
||||||
AccountIterator(seek common.Hash) AccountIterator
|
AccountIterator(seek common.Hash) AccountIterator
|
||||||
|
|
||||||
// StorageIterator creates a storage iterator over an arbitrary layer.
|
// StorageIterator creates a storage iterator over an arbitrary layer.
|
||||||
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
|
StorageIterator(account common.Hash, seek common.Hash) StorageIterator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config includes the configurations for snapshots.
|
// Config includes the configurations for snapshots.
|
||||||
|
@ -335,7 +335,7 @@ func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot {
|
||||||
|
|
||||||
// Update adds a new snapshot into the tree, if that can be linked to an existing
|
// Update adds a new snapshot into the tree, if that can be linked to an existing
|
||||||
// old parent. It is disallowed to insert a disk layer (the origin of all).
|
// old parent. It is disallowed to insert a disk layer (the origin of all).
|
||||||
func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
|
func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
|
||||||
// Reject noop updates to avoid self-loops in the snapshot tree. This is a
|
// Reject noop updates to avoid self-loops in the snapshot tree. This is a
|
||||||
// special case that can only happen for Clique networks where empty blocks
|
// special case that can only happen for Clique networks where empty blocks
|
||||||
// don't modify the state (0 block subsidy).
|
// don't modify the state (0 block subsidy).
|
||||||
|
@ -350,7 +350,7 @@ func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs m
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return fmt.Errorf("parent [%#x] snapshot missing", parentRoot)
|
return fmt.Errorf("parent [%#x] snapshot missing", parentRoot)
|
||||||
}
|
}
|
||||||
snap := parent.(snapshot).Update(blockRoot, destructs, accounts, storage)
|
snap := parent.(snapshot).Update(blockRoot, accounts, storage)
|
||||||
|
|
||||||
// Save the new snapshot for later
|
// Save the new snapshot for later
|
||||||
t.lock.Lock()
|
t.lock.Lock()
|
||||||
|
@ -539,35 +539,6 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
|
||||||
base.stale = true
|
base.stale = true
|
||||||
base.lock.Unlock()
|
base.lock.Unlock()
|
||||||
|
|
||||||
// Destroy all the destructed accounts from the database
|
|
||||||
for hash := range bottom.destructSet {
|
|
||||||
// Skip any account not covered yet by the snapshot
|
|
||||||
if base.genMarker != nil && bytes.Compare(hash[:], base.genMarker) > 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Remove all storage slots
|
|
||||||
rawdb.DeleteAccountSnapshot(batch, hash)
|
|
||||||
base.cache.Set(hash[:], nil)
|
|
||||||
|
|
||||||
it := rawdb.IterateStorageSnapshots(base.diskdb, hash)
|
|
||||||
for it.Next() {
|
|
||||||
key := it.Key()
|
|
||||||
batch.Delete(key)
|
|
||||||
base.cache.Del(key[1:])
|
|
||||||
snapshotFlushStorageItemMeter.Mark(1)
|
|
||||||
|
|
||||||
// Ensure we don't delete too much data blindly (contract can be
|
|
||||||
// huge). It's ok to flush, the root will go missing in case of a
|
|
||||||
// crash and we'll detect and regenerate the snapshot.
|
|
||||||
if batch.ValueSize() > 64*1024*1024 {
|
|
||||||
if err := batch.Write(); err != nil {
|
|
||||||
log.Crit("Failed to write storage deletions", "err", err)
|
|
||||||
}
|
|
||||||
batch.Reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.Release()
|
|
||||||
}
|
|
||||||
// Push all updated accounts into the database
|
// Push all updated accounts into the database
|
||||||
for hash, data := range bottom.accountData {
|
for hash, data := range bottom.accountData {
|
||||||
// Skip any account not covered yet by the snapshot
|
// Skip any account not covered yet by the snapshot
|
||||||
|
@ -575,10 +546,14 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Push the account to disk
|
// Push the account to disk
|
||||||
rawdb.WriteAccountSnapshot(batch, hash, data)
|
if len(data) != 0 {
|
||||||
base.cache.Set(hash[:], data)
|
rawdb.WriteAccountSnapshot(batch, hash, data)
|
||||||
snapshotCleanAccountWriteMeter.Mark(int64(len(data)))
|
base.cache.Set(hash[:], data)
|
||||||
|
snapshotCleanAccountWriteMeter.Mark(int64(len(data)))
|
||||||
|
} else {
|
||||||
|
rawdb.DeleteAccountSnapshot(batch, hash)
|
||||||
|
base.cache.Set(hash[:], nil)
|
||||||
|
}
|
||||||
snapshotFlushAccountItemMeter.Mark(1)
|
snapshotFlushAccountItemMeter.Mark(1)
|
||||||
snapshotFlushAccountSizeMeter.Mark(int64(len(data)))
|
snapshotFlushAccountSizeMeter.Mark(int64(len(data)))
|
||||||
|
|
||||||
|
@ -587,7 +562,7 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
|
||||||
// the snapshot.
|
// the snapshot.
|
||||||
if batch.ValueSize() > 64*1024*1024 {
|
if batch.ValueSize() > 64*1024*1024 {
|
||||||
if err := batch.Write(); err != nil {
|
if err := batch.Write(); err != nil {
|
||||||
log.Crit("Failed to write storage deletions", "err", err)
|
log.Crit("Failed to write state changes", "err", err)
|
||||||
}
|
}
|
||||||
batch.Reset()
|
batch.Reset()
|
||||||
}
|
}
|
||||||
|
@ -616,6 +591,16 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
|
||||||
}
|
}
|
||||||
snapshotFlushStorageItemMeter.Mark(1)
|
snapshotFlushStorageItemMeter.Mark(1)
|
||||||
snapshotFlushStorageSizeMeter.Mark(int64(len(data)))
|
snapshotFlushStorageSizeMeter.Mark(int64(len(data)))
|
||||||
|
|
||||||
|
// Ensure we don't write too much data blindly. It's ok to flush, the
|
||||||
|
// root will go missing in case of a crash and we'll detect and regen
|
||||||
|
// the snapshot.
|
||||||
|
if batch.ValueSize() > 64*1024*1024 {
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
log.Crit("Failed to write state changes", "err", err)
|
||||||
|
}
|
||||||
|
batch.Reset()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update the snapshot block marker and write any remainder data
|
// Update the snapshot block marker and write any remainder data
|
||||||
|
|
|
@ -107,7 +107,7 @@ func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) {
|
||||||
accounts := map[common.Hash][]byte{
|
accounts := map[common.Hash][]byte{
|
||||||
common.HexToHash("0xa1"): randomAccount(),
|
common.HexToHash("0xa1"): randomAccount(),
|
||||||
}
|
}
|
||||||
if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
|
if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), 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 != 2 {
|
if n := len(snaps.layers); n != 2 {
|
||||||
|
@ -151,10 +151,10 @@ func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) {
|
||||||
accounts := map[common.Hash][]byte{
|
accounts := map[common.Hash][]byte{
|
||||||
common.HexToHash("0xa1"): randomAccount(),
|
common.HexToHash("0xa1"): randomAccount(),
|
||||||
}
|
}
|
||||||
if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
|
if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), 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("0x03"), common.HexToHash("0x02"), nil, accounts, nil); 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 {
|
||||||
|
@ -203,13 +203,13 @@ func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) {
|
||||||
accounts := map[common.Hash][]byte{
|
accounts := map[common.Hash][]byte{
|
||||||
common.HexToHash("0xa1"): randomAccount(),
|
common.HexToHash("0xa1"): randomAccount(),
|
||||||
}
|
}
|
||||||
if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
|
if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), 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("0x03"), common.HexToHash("0x02"), nil, accounts, nil); 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"), nil, accounts, nil); 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 {
|
||||||
|
@ -263,12 +263,12 @@ func TestPostCapBasicDataAccess(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// The lowest difflayer
|
// The lowest difflayer
|
||||||
snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil)
|
snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), setAccount("0xa1"), nil)
|
||||||
snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil)
|
snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), setAccount("0xa2"), nil)
|
||||||
snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), nil, setAccount("0xb2"), nil)
|
snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), setAccount("0xb2"), nil)
|
||||||
|
|
||||||
snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil)
|
snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), setAccount("0xa3"), nil)
|
||||||
snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil)
|
snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), setAccount("0xb3"), nil)
|
||||||
|
|
||||||
// checkExist verifies if an account exists in a snapshot
|
// checkExist verifies if an account exists in a snapshot
|
||||||
checkExist := func(layer *diffLayer, key string) error {
|
checkExist := func(layer *diffLayer, key string) error {
|
||||||
|
@ -363,7 +363,7 @@ func TestSnaphots(t *testing.T) {
|
||||||
)
|
)
|
||||||
for i := 0; i < 129; i++ {
|
for i := 0; i < 129; i++ {
|
||||||
head = makeRoot(uint64(i + 2))
|
head = makeRoot(uint64(i + 2))
|
||||||
snaps.Update(head, last, nil, setAccount(fmt.Sprintf("%d", i+2)), nil)
|
snaps.Update(head, last, setAccount(fmt.Sprintf("%d", i+2)), nil)
|
||||||
last = head
|
last = head
|
||||||
snaps.Cap(head, 128) // 130 layers (128 diffs + 1 accumulator + 1 disk)
|
snaps.Cap(head, 128) // 130 layers (128 diffs + 1 accumulator + 1 disk)
|
||||||
}
|
}
|
||||||
|
@ -456,9 +456,9 @@ func TestReadStateDuringFlattening(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// 4 layers in total, 3 diff layers and 1 disk layers
|
// 4 layers in total, 3 diff layers and 1 disk layers
|
||||||
snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil)
|
snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), setAccount("0xa1"), nil)
|
||||||
snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil)
|
snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), setAccount("0xa2"), nil)
|
||||||
snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil)
|
snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), setAccount("0xa3"), nil)
|
||||||
|
|
||||||
// Obtain the topmost snapshot handler for state accessing
|
// Obtain the topmost snapshot handler for state accessing
|
||||||
snap := snaps.Snapshot(common.HexToHash("0xa3"))
|
snap := snaps.Snapshot(common.HexToHash("0xa3"))
|
||||||
|
|
|
@ -75,7 +75,7 @@ func checkDanglingDiskStorage(chaindb ethdb.KeyValueStore) error {
|
||||||
func checkDanglingMemStorage(db ethdb.KeyValueStore) error {
|
func checkDanglingMemStorage(db ethdb.KeyValueStore) error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Info("Checking dangling journalled storage")
|
log.Info("Checking dangling journalled storage")
|
||||||
err := iterateJournal(db, func(pRoot, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
|
err := iterateJournal(db, func(pRoot, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
|
||||||
for accHash := range storage {
|
for accHash := range storage {
|
||||||
if _, ok := accounts[accHash]; !ok {
|
if _, ok := accounts[accHash]; !ok {
|
||||||
log.Error("Dangling storage - missing account", "account", fmt.Sprintf("%#x", accHash), "root", root)
|
log.Error("Dangling storage - missing account", "account", fmt.Sprintf("%#x", accHash), "root", root)
|
||||||
|
@ -119,12 +119,11 @@ func CheckJournalAccount(db ethdb.KeyValueStore, hash common.Hash) error {
|
||||||
}
|
}
|
||||||
var depth = 0
|
var depth = 0
|
||||||
|
|
||||||
return iterateJournal(db, func(pRoot, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
|
return iterateJournal(db, func(pRoot, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
|
||||||
_, a := accounts[hash]
|
_, a := accounts[hash]
|
||||||
_, b := destructs[hash]
|
_, b := storage[hash]
|
||||||
_, c := storage[hash]
|
|
||||||
depth++
|
depth++
|
||||||
if !a && !b && !c {
|
if !a && !b {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Printf("Disklayer+%d: Root: %x, parent %x\n", depth, root, pRoot)
|
fmt.Printf("Disklayer+%d: Root: %x, parent %x\n", depth, root, pRoot)
|
||||||
|
@ -138,9 +137,6 @@ func CheckJournalAccount(db ethdb.KeyValueStore, hash common.Hash) error {
|
||||||
fmt.Printf("\taccount.root: %x\n", account.Root)
|
fmt.Printf("\taccount.root: %x\n", account.Root)
|
||||||
fmt.Printf("\taccount.codehash: %x\n", account.CodeHash)
|
fmt.Printf("\taccount.codehash: %x\n", account.CodeHash)
|
||||||
}
|
}
|
||||||
if _, ok := destructs[hash]; ok {
|
|
||||||
fmt.Printf("\t Destructed!")
|
|
||||||
}
|
|
||||||
if data, ok := storage[hash]; ok {
|
if data, ok := storage[hash]; ok {
|
||||||
fmt.Printf("\tStorage\n")
|
fmt.Printf("\tStorage\n")
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
|
|
|
@ -932,16 +932,17 @@ func (s *StateDB) clearJournalAndRefund() {
|
||||||
// of a specific account. It leverages the associated state snapshot for fast
|
// of a specific account. It leverages the associated state snapshot for fast
|
||||||
// storage iteration and constructs trie node deletion markers by creating
|
// storage iteration and constructs trie node deletion markers by creating
|
||||||
// stack trie with iterated slots.
|
// stack trie with iterated slots.
|
||||||
func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||||
iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
|
iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
defer iter.Release()
|
defer iter.Release()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nodes = trienode.NewNodeSet(addrHash)
|
nodes = trienode.NewNodeSet(addrHash) // the set for trie node mutations (value is nil)
|
||||||
slots = make(map[common.Hash][]byte)
|
storages = make(map[common.Hash][]byte) // the set for storage mutations (value is nil)
|
||||||
|
storageOrigins = make(map[common.Hash][]byte) // the set for tracking the original value of slot
|
||||||
)
|
)
|
||||||
stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
|
||||||
nodes.AddNode(path, trienode.NewDeleted())
|
nodes.AddNode(path, trienode.NewDeleted())
|
||||||
|
@ -949,42 +950,47 @@ func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash,
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
slot := common.CopyBytes(iter.Slot())
|
slot := common.CopyBytes(iter.Slot())
|
||||||
if err := iter.Error(); err != nil { // error might occur after Slot function
|
if err := iter.Error(); err != nil { // error might occur after Slot function
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
slots[iter.Hash()] = slot
|
key := iter.Hash()
|
||||||
|
storages[key] = nil
|
||||||
|
storageOrigins[key] = slot
|
||||||
|
|
||||||
if err := stack.Update(iter.Hash().Bytes(), slot); err != nil {
|
if err := stack.Update(key.Bytes(), slot); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := iter.Error(); err != nil { // error might occur during iteration
|
if err := iter.Error(); err != nil { // error might occur during iteration
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if stack.Hash() != root {
|
if stack.Hash() != root {
|
||||||
return nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash())
|
return nil, nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash())
|
||||||
}
|
}
|
||||||
return slots, nodes, nil
|
return storages, storageOrigins, nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage,"
|
// slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage,"
|
||||||
// employed when the associated state snapshot is not available. It iterates the
|
// employed when the associated state snapshot is not available. It iterates the
|
||||||
// storage slots along with all internal trie nodes via trie directly.
|
// storage slots along with all internal trie nodes via trie directly.
|
||||||
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||||
tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie)
|
tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
return nil, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
||||||
}
|
}
|
||||||
it, err := tr.NodeIterator(nil)
|
it, err := tr.NodeIterator(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
|
return nil, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
nodes = trienode.NewNodeSet(addrHash)
|
nodes = trienode.NewNodeSet(addrHash) // the set for trie node mutations (value is nil)
|
||||||
slots = make(map[common.Hash][]byte)
|
storages = make(map[common.Hash][]byte) // the set for storage mutations (value is nil)
|
||||||
|
storageOrigins = make(map[common.Hash][]byte) // the set for tracking the original value of slot
|
||||||
)
|
)
|
||||||
for it.Next(true) {
|
for it.Next(true) {
|
||||||
if it.Leaf() {
|
if it.Leaf() {
|
||||||
slots[common.BytesToHash(it.LeafKey())] = common.CopyBytes(it.LeafBlob())
|
key := common.BytesToHash(it.LeafKey())
|
||||||
|
storages[key] = nil
|
||||||
|
storageOrigins[key] = common.CopyBytes(it.LeafBlob())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if it.Hash() == (common.Hash{}) {
|
if it.Hash() == (common.Hash{}) {
|
||||||
|
@ -993,35 +999,36 @@ func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, r
|
||||||
nodes.AddNode(it.Path(), trienode.NewDeleted())
|
nodes.AddNode(it.Path(), trienode.NewDeleted())
|
||||||
}
|
}
|
||||||
if err := it.Error(); err != nil {
|
if err := it.Error(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
return slots, nodes, nil
|
return storages, storageOrigins, nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteStorage is designed to delete the storage trie of a designated account.
|
// deleteStorage is designed to delete the storage trie of a designated account.
|
||||||
// The function will make an attempt to utilize an efficient strategy if the
|
// The function will make an attempt to utilize an efficient strategy if the
|
||||||
// associated state snapshot is reachable; otherwise, it will resort to a less
|
// associated state snapshot is reachable; otherwise, it will resort to a less
|
||||||
// efficient approach.
|
// efficient approach.
|
||||||
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
slots map[common.Hash][]byte
|
nodes *trienode.NodeSet // the set for trie node mutations (value is nil)
|
||||||
nodes *trienode.NodeSet
|
storages map[common.Hash][]byte // the set for storage mutations (value is nil)
|
||||||
|
storageOrigins map[common.Hash][]byte // the set for tracking the original value of slot
|
||||||
)
|
)
|
||||||
// The fast approach can be failed if the snapshot is not fully
|
// The fast approach can be failed if the snapshot is not fully
|
||||||
// generated, or it's internally corrupted. Fallback to the slow
|
// generated, or it's internally corrupted. Fallback to the slow
|
||||||
// one just in case.
|
// one just in case.
|
||||||
snaps := s.db.Snapshot()
|
snaps := s.db.Snapshot()
|
||||||
if snaps != nil {
|
if snaps != nil {
|
||||||
slots, nodes, err = s.fastDeleteStorage(snaps, addrHash, root)
|
storages, storageOrigins, nodes, err = s.fastDeleteStorage(snaps, addrHash, root)
|
||||||
}
|
}
|
||||||
if snaps == nil || err != nil {
|
if snaps == nil || err != nil {
|
||||||
slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
|
storages, storageOrigins, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
return slots, nodes, nil
|
return storages, storageOrigins, nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleDestruction processes all destruction markers and deletes the account
|
// handleDestruction processes all destruction markers and deletes the account
|
||||||
|
@ -1068,16 +1075,16 @@ func (s *StateDB) handleDestruction() (map[common.Hash]*accountDelete, []*trieno
|
||||||
deletes[addrHash] = op
|
deletes[addrHash] = op
|
||||||
|
|
||||||
// Short circuit if the origin storage was empty.
|
// Short circuit if the origin storage was empty.
|
||||||
|
|
||||||
if prev.Root == types.EmptyRootHash || s.db.TrieDB().IsVerkle() {
|
if prev.Root == types.EmptyRootHash || s.db.TrieDB().IsVerkle() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Remove storage slots belonging to the account.
|
// Remove storage slots belonging to the account.
|
||||||
slots, set, err := s.deleteStorage(addr, addrHash, prev.Root)
|
storages, storagesOrigin, set, err := s.deleteStorage(addr, addrHash, prev.Root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to delete storage, err: %w", err)
|
return nil, nil, fmt.Errorf("failed to delete storage, err: %w", err)
|
||||||
}
|
}
|
||||||
op.storagesOrigin = slots
|
op.storages = storages
|
||||||
|
op.storagesOrigin = storagesOrigin
|
||||||
|
|
||||||
// Aggregate the associated trie node changes.
|
// Aggregate the associated trie node changes.
|
||||||
nodes = append(nodes, set)
|
nodes = append(nodes, set)
|
||||||
|
@ -1267,7 +1274,7 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool) (*stateU
|
||||||
// If snapshotting is enabled, update the snapshot tree with this new version
|
// If snapshotting is enabled, update the snapshot tree with this new version
|
||||||
if snap := s.db.Snapshot(); snap != nil && snap.Snapshot(ret.originRoot) != nil {
|
if snap := s.db.Snapshot(); snap != nil && snap.Snapshot(ret.originRoot) != nil {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
if err := snap.Update(ret.root, ret.originRoot, ret.destructs, ret.accounts, ret.storages); err != nil {
|
if err := snap.Update(ret.root, ret.originRoot, ret.accounts, ret.storages); err != nil {
|
||||||
log.Warn("Failed to update snapshot tree", "from", ret.originRoot, "to", ret.root, "err", err)
|
log.Warn("Failed to update snapshot tree", "from", ret.originRoot, "to", ret.root, "err", err)
|
||||||
}
|
}
|
||||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -176,24 +177,16 @@ func (test *stateTest) String() string {
|
||||||
|
|
||||||
func (test *stateTest) run() bool {
|
func (test *stateTest) run() bool {
|
||||||
var (
|
var (
|
||||||
roots []common.Hash
|
roots []common.Hash
|
||||||
accountList []map[common.Address][]byte
|
accounts []map[common.Hash][]byte
|
||||||
storageList []map[common.Address]map[common.Hash][]byte
|
accountOrigin []map[common.Address][]byte
|
||||||
copyUpdate = func(update *stateUpdate) {
|
storages []map[common.Hash]map[common.Hash][]byte
|
||||||
accounts := make(map[common.Address][]byte, len(update.accountsOrigin))
|
storageOrigin []map[common.Address]map[common.Hash][]byte
|
||||||
for key, val := range update.accountsOrigin {
|
copyUpdate = func(update *stateUpdate) {
|
||||||
accounts[key] = common.CopyBytes(val)
|
accounts = append(accounts, maps.Clone(update.accounts))
|
||||||
}
|
accountOrigin = append(accountOrigin, maps.Clone(update.accountsOrigin))
|
||||||
accountList = append(accountList, accounts)
|
storages = append(storages, maps.Clone(update.storages))
|
||||||
|
storageOrigin = append(storageOrigin, maps.Clone(update.storagesOrigin))
|
||||||
storages := make(map[common.Address]map[common.Hash][]byte, len(update.storagesOrigin))
|
|
||||||
for addr, subset := range update.storagesOrigin {
|
|
||||||
storages[addr] = make(map[common.Hash][]byte, len(subset))
|
|
||||||
for key, val := range subset {
|
|
||||||
storages[addr][key] = common.CopyBytes(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
storageList = append(storageList, storages)
|
|
||||||
}
|
}
|
||||||
disk = rawdb.NewMemoryDatabase()
|
disk = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults})
|
tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults})
|
||||||
|
@ -250,7 +243,7 @@ func (test *stateTest) run() bool {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
root = roots[i-1]
|
root = roots[i-1]
|
||||||
}
|
}
|
||||||
test.err = test.verify(root, roots[i], tdb, accountList[i], storageList[i])
|
test.err = test.verify(root, roots[i], tdb, accounts[i], accountOrigin[i], storages[i], storageOrigin[i])
|
||||||
if test.err != nil {
|
if test.err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -265,7 +258,7 @@ func (test *stateTest) run() bool {
|
||||||
// - the account was indeed not present in trie
|
// - the account was indeed not present in trie
|
||||||
// - the account is present in new trie, nil->nil is regarded as invalid
|
// - the account is present in new trie, nil->nil is regarded as invalid
|
||||||
// - the slots transition is correct
|
// - the slots transition is correct
|
||||||
func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, slots map[common.Hash][]byte) error {
|
func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, account []byte, storages map[common.Hash][]byte, storagesOrigin map[common.Hash][]byte) error {
|
||||||
// Verify account change
|
// Verify account change
|
||||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||||
oBlob, err := otr.Get(addrHash.Bytes())
|
oBlob, err := otr.Get(addrHash.Bytes())
|
||||||
|
@ -282,6 +275,13 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Databa
|
||||||
if len(nBlob) == 0 {
|
if len(nBlob) == 0 {
|
||||||
return fmt.Errorf("missing account in new trie, %x", addrHash)
|
return fmt.Errorf("missing account in new trie, %x", addrHash)
|
||||||
}
|
}
|
||||||
|
full, err := types.FullAccountRLP(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(nBlob, full) {
|
||||||
|
return fmt.Errorf("unexpected account data, want: %v, got: %v", full, nBlob)
|
||||||
|
}
|
||||||
|
|
||||||
// Verify storage changes
|
// Verify storage changes
|
||||||
var nAcct types.StateAccount
|
var nAcct types.StateAccount
|
||||||
|
@ -290,7 +290,10 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Databa
|
||||||
}
|
}
|
||||||
// Account has no slot, empty slot set is expected
|
// Account has no slot, empty slot set is expected
|
||||||
if nAcct.Root == types.EmptyRootHash {
|
if nAcct.Root == types.EmptyRootHash {
|
||||||
if len(slots) != 0 {
|
if len(storagesOrigin) != 0 {
|
||||||
|
return fmt.Errorf("unexpected slot changes %x", addrHash)
|
||||||
|
}
|
||||||
|
if len(storages) != 0 {
|
||||||
return fmt.Errorf("unexpected slot changes %x", addrHash)
|
return fmt.Errorf("unexpected slot changes %x", addrHash)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -300,9 +303,22 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Databa
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for key, val := range slots {
|
for key, val := range storagesOrigin {
|
||||||
|
if _, exist := storages[key]; !exist {
|
||||||
|
return errors.New("storage data is not found")
|
||||||
|
}
|
||||||
|
got, err := st.Get(key.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(got, storages[key]) {
|
||||||
|
return fmt.Errorf("unexpected storage data, want: %v, got: %v", storages[key], got)
|
||||||
|
}
|
||||||
st.Update(key.Bytes(), val)
|
st.Update(key.Bytes(), val)
|
||||||
}
|
}
|
||||||
|
if len(storagesOrigin) != len(storages) {
|
||||||
|
return fmt.Errorf("extra storage found, want: %d, got: %d", len(storagesOrigin), len(storages))
|
||||||
|
}
|
||||||
if st.Hash() != types.EmptyRootHash {
|
if st.Hash() != types.EmptyRootHash {
|
||||||
return errors.New("invalid slot changes")
|
return errors.New("invalid slot changes")
|
||||||
}
|
}
|
||||||
|
@ -316,7 +332,7 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Databa
|
||||||
// - the account was indeed present in trie
|
// - the account was indeed present in trie
|
||||||
// - the account in old trie matches the provided value
|
// - the account in old trie matches the provided value
|
||||||
// - the slots transition is correct
|
// - the slots transition is correct
|
||||||
func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, origin []byte, slots map[common.Hash][]byte) error {
|
func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, account []byte, accountOrigin []byte, storages map[common.Hash][]byte, storageOrigin map[common.Hash][]byte) error {
|
||||||
// Verify account change
|
// Verify account change
|
||||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||||
oBlob, err := otr.Get(addrHash.Bytes())
|
oBlob, err := otr.Get(addrHash.Bytes())
|
||||||
|
@ -330,14 +346,23 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database
|
||||||
if len(oBlob) == 0 {
|
if len(oBlob) == 0 {
|
||||||
return fmt.Errorf("missing account in old trie, %x", addrHash)
|
return fmt.Errorf("missing account in old trie, %x", addrHash)
|
||||||
}
|
}
|
||||||
full, err := types.FullAccountRLP(origin)
|
full, err := types.FullAccountRLP(accountOrigin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !bytes.Equal(full, oBlob) {
|
if !bytes.Equal(full, oBlob) {
|
||||||
return fmt.Errorf("account value is not matched, %x", addrHash)
|
return fmt.Errorf("account value is not matched, %x", addrHash)
|
||||||
}
|
}
|
||||||
|
if len(nBlob) == 0 {
|
||||||
|
if len(account) != 0 {
|
||||||
|
return errors.New("unexpected account data")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
full, _ = types.FullAccountRLP(account)
|
||||||
|
if !bytes.Equal(full, nBlob) {
|
||||||
|
return fmt.Errorf("unexpected account data, %x, want %v, got: %v", addrHash, full, nBlob)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Decode accounts
|
// Decode accounts
|
||||||
var (
|
var (
|
||||||
oAcct types.StateAccount
|
oAcct types.StateAccount
|
||||||
|
@ -361,16 +386,29 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for key, val := range slots {
|
for key, val := range storageOrigin {
|
||||||
|
if _, exist := storages[key]; !exist {
|
||||||
|
return errors.New("storage data is not found")
|
||||||
|
}
|
||||||
|
got, err := st.Get(key.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(got, storages[key]) {
|
||||||
|
return fmt.Errorf("unexpected storage data, want: %v, got: %v", storages[key], got)
|
||||||
|
}
|
||||||
st.Update(key.Bytes(), val)
|
st.Update(key.Bytes(), val)
|
||||||
}
|
}
|
||||||
|
if len(storageOrigin) != len(storages) {
|
||||||
|
return fmt.Errorf("extra storage found, want: %d, got: %d", len(storageOrigin), len(storages))
|
||||||
|
}
|
||||||
if st.Hash() != oAcct.Root {
|
if st.Hash() != oAcct.Root {
|
||||||
return errors.New("invalid slot changes")
|
return errors.New("invalid slot changes")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Database, accountsOrigin map[common.Address][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error {
|
func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Database, accounts map[common.Hash][]byte, accountsOrigin map[common.Address][]byte, storages map[common.Hash]map[common.Hash][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error {
|
||||||
otr, err := trie.New(trie.StateTrieID(root), db)
|
otr, err := trie.New(trie.StateTrieID(root), db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -379,12 +417,15 @@ func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Dat
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for addr, account := range accountsOrigin {
|
for addr, accountOrigin := range accountsOrigin {
|
||||||
var err error
|
var (
|
||||||
if len(account) == 0 {
|
err error
|
||||||
err = test.verifyAccountCreation(next, db, otr, ntr, addr, storagesOrigin[addr])
|
addrHash = crypto.Keccak256Hash(addr.Bytes())
|
||||||
|
)
|
||||||
|
if len(accountOrigin) == 0 {
|
||||||
|
err = test.verifyAccountCreation(next, db, otr, ntr, addr, accounts[addrHash], storages[addrHash], storagesOrigin[addr])
|
||||||
} else {
|
} else {
|
||||||
err = test.verifyAccountUpdate(next, db, otr, ntr, addr, accountsOrigin[addr], storagesOrigin[addr])
|
err = test.verifyAccountUpdate(next, db, otr, ntr, addr, accounts[addrHash], accountsOrigin[addr], storages[addrHash], storagesOrigin[addr])
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1305,12 +1305,12 @@ func TestDeleteStorage(t *testing.T) {
|
||||||
obj := fastState.getOrNewStateObject(addr)
|
obj := fastState.getOrNewStateObject(addr)
|
||||||
storageRoot := obj.data.Root
|
storageRoot := obj.data.Root
|
||||||
|
|
||||||
_, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
|
_, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
|
_, _, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"maps"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||||
|
@ -33,6 +35,7 @@ type contractCode struct {
|
||||||
type accountDelete struct {
|
type accountDelete struct {
|
||||||
address common.Address // address is the unique account identifier
|
address common.Address // address is the unique account identifier
|
||||||
origin []byte // origin is the original value of account data in slim-RLP encoding.
|
origin []byte // origin is the original value of account data in slim-RLP encoding.
|
||||||
|
storages map[common.Hash][]byte // storages stores mutated slots, the value should be nil.
|
||||||
storagesOrigin map[common.Hash][]byte // storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format.
|
storagesOrigin map[common.Hash][]byte // storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +55,6 @@ type accountUpdate struct {
|
||||||
type stateUpdate struct {
|
type stateUpdate struct {
|
||||||
originRoot common.Hash // hash of the state before applying mutation
|
originRoot common.Hash // hash of the state before applying mutation
|
||||||
root common.Hash // hash of the state after applying mutation
|
root common.Hash // hash of the state after applying mutation
|
||||||
destructs map[common.Hash]struct{} // destructs contains the list of destructed accounts
|
|
||||||
accounts map[common.Hash][]byte // accounts stores mutated accounts in 'slim RLP' encoding
|
accounts map[common.Hash][]byte // accounts stores mutated accounts in 'slim RLP' encoding
|
||||||
accountsOrigin map[common.Address][]byte // accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding
|
accountsOrigin map[common.Address][]byte // accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding
|
||||||
storages map[common.Hash]map[common.Hash][]byte // storages stores mutated slots in 'prefix-zero-trimmed' RLP format
|
storages map[common.Hash]map[common.Hash][]byte // storages stores mutated slots in 'prefix-zero-trimmed' RLP format
|
||||||
|
@ -71,7 +73,6 @@ func (sc *stateUpdate) empty() bool {
|
||||||
// account deletions and account updates to form a comprehensive state update.
|
// account deletions and account updates to form a comprehensive state update.
|
||||||
func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate {
|
func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate {
|
||||||
var (
|
var (
|
||||||
destructs = make(map[common.Hash]struct{})
|
|
||||||
accounts = make(map[common.Hash][]byte)
|
accounts = make(map[common.Hash][]byte)
|
||||||
accountsOrigin = make(map[common.Address][]byte)
|
accountsOrigin = make(map[common.Address][]byte)
|
||||||
storages = make(map[common.Hash]map[common.Hash][]byte)
|
storages = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
|
@ -82,8 +83,12 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common
|
||||||
// within the same block, the deletions must be aggregated first.
|
// within the same block, the deletions must be aggregated first.
|
||||||
for addrHash, op := range deletes {
|
for addrHash, op := range deletes {
|
||||||
addr := op.address
|
addr := op.address
|
||||||
destructs[addrHash] = struct{}{}
|
accounts[addrHash] = nil
|
||||||
accountsOrigin[addr] = op.origin
|
accountsOrigin[addr] = op.origin
|
||||||
|
|
||||||
|
if len(op.storages) > 0 {
|
||||||
|
storages[addrHash] = op.storages
|
||||||
|
}
|
||||||
if len(op.storagesOrigin) > 0 {
|
if len(op.storagesOrigin) > 0 {
|
||||||
storagesOrigin[addr] = op.storagesOrigin
|
storagesOrigin[addr] = op.storagesOrigin
|
||||||
}
|
}
|
||||||
|
@ -95,35 +100,41 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common
|
||||||
if op.code != nil {
|
if op.code != nil {
|
||||||
codes[addr] = *op.code
|
codes[addr] = *op.code
|
||||||
}
|
}
|
||||||
// Aggregate the account changes. The original account value will only
|
|
||||||
// be tracked if it's not present yet.
|
|
||||||
accounts[addrHash] = op.data
|
accounts[addrHash] = op.data
|
||||||
|
|
||||||
|
// Aggregate the account original value. If the account is already
|
||||||
|
// present in the aggregated accountsOrigin set, skip it.
|
||||||
if _, found := accountsOrigin[addr]; !found {
|
if _, found := accountsOrigin[addr]; !found {
|
||||||
accountsOrigin[addr] = op.origin
|
accountsOrigin[addr] = op.origin
|
||||||
}
|
}
|
||||||
// Aggregate the storage changes. The original storage slot value will
|
// Aggregate the storage mutation list. If a slot in op.storages is
|
||||||
// only be tracked if it's not present yet.
|
// already present in aggregated storages set, the value will be
|
||||||
|
// overwritten.
|
||||||
if len(op.storages) > 0 {
|
if len(op.storages) > 0 {
|
||||||
storages[addrHash] = op.storages
|
if _, exist := storages[addrHash]; !exist {
|
||||||
}
|
storages[addrHash] = op.storages
|
||||||
if len(op.storagesOrigin) > 0 {
|
} else {
|
||||||
origin := storagesOrigin[addr]
|
maps.Copy(storages[addrHash], op.storages)
|
||||||
if origin == nil {
|
|
||||||
storagesOrigin[addr] = op.storagesOrigin
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
for key, slot := range op.storagesOrigin {
|
}
|
||||||
if _, found := origin[key]; !found {
|
// Aggregate the storage original values. If the slot is already present
|
||||||
origin[key] = slot
|
// in aggregated storagesOrigin set, skip it.
|
||||||
|
if len(op.storagesOrigin) > 0 {
|
||||||
|
origin, exist := storagesOrigin[addr]
|
||||||
|
if !exist {
|
||||||
|
storagesOrigin[addr] = op.storagesOrigin
|
||||||
|
} else {
|
||||||
|
for key, slot := range op.storagesOrigin {
|
||||||
|
if _, found := origin[key]; !found {
|
||||||
|
origin[key] = slot
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storagesOrigin[addr] = origin
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &stateUpdate{
|
return &stateUpdate{
|
||||||
originRoot: types.TrieRootHash(originRoot),
|
originRoot: types.TrieRootHash(originRoot),
|
||||||
root: types.TrieRootHash(root),
|
root: types.TrieRootHash(root),
|
||||||
destructs: destructs,
|
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
accountsOrigin: accountsOrigin,
|
accountsOrigin: accountsOrigin,
|
||||||
storages: storages,
|
storages: storages,
|
||||||
|
@ -139,7 +150,6 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common
|
||||||
// package.
|
// package.
|
||||||
func (sc *stateUpdate) stateSet() *triedb.StateSet {
|
func (sc *stateUpdate) stateSet() *triedb.StateSet {
|
||||||
return &triedb.StateSet{
|
return &triedb.StateSet{
|
||||||
Destructs: sc.destructs,
|
|
||||||
Accounts: sc.accounts,
|
Accounts: sc.accounts,
|
||||||
AccountsOrigin: sc.accountsOrigin,
|
AccountsOrigin: sc.accountsOrigin,
|
||||||
Storages: sc.storages,
|
Storages: sc.storages,
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
|
|
||||||
// StateSet represents a collection of mutated states during a state transition.
|
// StateSet represents a collection of mutated states during a state transition.
|
||||||
type StateSet struct {
|
type StateSet struct {
|
||||||
Destructs map[common.Hash]struct{} // Destructed accounts
|
|
||||||
Accounts map[common.Hash][]byte // Mutated accounts in 'slim RLP' encoding
|
Accounts map[common.Hash][]byte // Mutated accounts in 'slim RLP' encoding
|
||||||
AccountsOrigin map[common.Address][]byte // Original values of mutated accounts in 'slim RLP' encoding
|
AccountsOrigin map[common.Address][]byte // Original values of mutated accounts in 'slim RLP' encoding
|
||||||
Storages map[common.Hash]map[common.Hash][]byte // Mutated storage slots in 'prefix-zero-trimmed' RLP format
|
Storages map[common.Hash]map[common.Hash][]byte // Mutated storage slots in 'prefix-zero-trimmed' RLP format
|
||||||
|
@ -33,7 +32,6 @@ type StateSet struct {
|
||||||
// NewStateSet initializes an empty state set.
|
// NewStateSet initializes an empty state set.
|
||||||
func NewStateSet() *StateSet {
|
func NewStateSet() *StateSet {
|
||||||
return &StateSet{
|
return &StateSet{
|
||||||
Destructs: make(map[common.Hash]struct{}),
|
|
||||||
Accounts: make(map[common.Hash][]byte),
|
Accounts: make(map[common.Hash][]byte),
|
||||||
AccountsOrigin: make(map[common.Address][]byte),
|
AccountsOrigin: make(map[common.Address][]byte),
|
||||||
Storages: make(map[common.Hash]map[common.Hash][]byte),
|
Storages: make(map[common.Hash]map[common.Hash][]byte),
|
||||||
|
|
Loading…
Reference in New Issue