From 71705d0de1faf1b03017fa4b543393188d5a1d18 Mon Sep 17 00:00:00 2001 From: unclereal Date: Thu, 16 Dec 2021 20:09:32 +0800 Subject: [PATCH] pipeline commit trie --- core/block_validator.go | 8 +- core/blockchain.go | 2 +- core/blockchain_test.go | 2 +- core/state/snapshot/difflayer.go | 19 ++++- core/state/snapshot/difflayer_test.go | 26 +++--- core/state/snapshot/disklayer.go | 8 +- core/state/snapshot/iterator_test.go | 4 +- core/state/snapshot/journal.go | 2 +- core/state/snapshot/snapshot.go | 21 +++-- core/state/statedb.go | 111 ++++++++++++++++++-------- core/state_processor.go | 2 +- core/types.go | 2 +- 12 files changed, 139 insertions(+), 68 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index 12fa908cd0..bbcca56926 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -106,7 +106,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // transition, such as amount of used gas, the receipt roots and the state root // itself. ValidateState returns a database batch if the validation was a success // otherwise nil and an error is returned. -func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error { +func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, skipHeavyVerify bool) error { header := block.Header() if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) @@ -129,13 +129,15 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD return nil } }, - func() error { + } + if !skipHeavyVerify { + validateFuns = append(validateFuns, func() error { if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } else { return nil } - }, + }) } validateRes := make(chan error, len(validateFuns)) for _, f := range validateFuns { diff --git a/core/blockchain.go b/core/blockchain.go index 4a7cdf9a54..104025b62c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2073,7 +2073,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // Validate the state using the default validator substart = time.Now() if !statedb.IsLightProcessed() { - if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, true); err != nil { log.Error("validate state failed", "error", err) bc.reportBlock(block, receipts, err) return it.index, err diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 6874534817..86e69e624c 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -156,7 +156,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas) + err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, false) if err != nil { blockchain.reportBlock(block, receipts, err) return err diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index c0f0dab568..e8294be9f1 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -118,7 +118,8 @@ type diffLayer struct { 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) - diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer + verifiedCh chan struct{} + diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer lock sync.RWMutex } @@ -168,7 +169,7 @@ func (h storageBloomHasher) Sum64() uint64 { // newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low // 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, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte, verified chan struct{}) *diffLayer { // Create the new layer with some pre-allocated data segments dl := &diffLayer{ parent: parent, @@ -177,6 +178,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s accountData: accounts, storageData: storage, storageList: make(map[common.Hash][]common.Hash), + verifiedCh: verified, } switch parent := parent.(type) { case *diskLayer: @@ -256,6 +258,15 @@ func (dl *diffLayer) Root() common.Hash { return dl.root } +// WaitVerified will wait until the diff layer been verified +func (dl *diffLayer) WaitVerified() { + if dl.verifiedCh == nil { + return + } + <-dl.verifiedCh + return +} + // Parent returns the subsequent layer of a diff layer. func (dl *diffLayer) Parent() snapshot { return dl.parent @@ -423,8 +434,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 // 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 { - return newDiffLayer(dl, blockRoot, destructs, accounts, storage) +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, verified chan struct{}) *diffLayer { + return newDiffLayer(dl, blockRoot, destructs, accounts, storage, verified) } // flatten pushes all data from this point downwards, flattening everything into diff --git a/core/state/snapshot/difflayer_test.go b/core/state/snapshot/difflayer_test.go index 919af5fa86..328ed4749a 100644 --- a/core/state/snapshot/difflayer_test.go +++ b/core/state/snapshot/difflayer_test.go @@ -151,13 +151,13 @@ func TestMergeDelete(t *testing.T) { } } // Add some flipAccs-flopping layers on top - parent := newDiffLayer(emptyLayer(), common.Hash{}, flipDrops(), flipAccs(), storage) - child := parent.Update(common.Hash{}, flopDrops(), flopAccs(), storage) - child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) - child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage) - child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) - child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage) - child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) + parent := newDiffLayer(emptyLayer(), common.Hash{}, flipDrops(), flipAccs(), storage, nil) + child := parent.Update(common.Hash{}, flopDrops(), flopAccs(), storage, nil) + child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage, nil) + child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage, nil) + child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage, nil) + child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage, nil) + child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage, nil) if data, _ := child.Account(h1); data == nil { t.Errorf("last diff layer: expected %x account to be non-nil", h1) @@ -209,7 +209,7 @@ func TestInsertAndMerge(t *testing.T) { accounts = make(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{}, destructs, accounts, storage, nil) } { var ( @@ -220,7 +220,7 @@ func TestInsertAndMerge(t *testing.T) { accounts[acc] = randomAccount() storage[acc] = make(map[common.Hash][]byte) storage[acc][slot] = []byte{0x01} - child = newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + child = newDiffLayer(parent, common.Hash{}, destructs, accounts, storage, nil) } // And flatten merged := (child.flatten()).(*diffLayer) @@ -256,7 +256,7 @@ func BenchmarkSearch(b *testing.B) { for i := 0; i < 10000; i++ { accounts[randomHash()] = randomAccount() } - return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage, nil) } var layer snapshot layer = emptyLayer() @@ -298,7 +298,7 @@ func BenchmarkSearchSlot(b *testing.B) { accStorage[randomHash()] = value storage[accountKey] = accStorage } - return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage, nil) } var layer snapshot layer = emptyLayer() @@ -336,7 +336,7 @@ func BenchmarkFlatten(b *testing.B) { } storage[accountKey] = accStorage } - return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage, nil) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -386,7 +386,7 @@ func BenchmarkJournal(b *testing.B) { } storage[accountKey] = accStorage } - return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage, nil) } layer := snapshot(new(diskLayer)) for i := 1; i < 128; i++ { diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index 7cbf6e293d..9d1778cc76 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -49,6 +49,10 @@ func (dl *diskLayer) Root() common.Hash { return dl.root } +func (dl *diskLayer) WaitVerified() { + return +} + // Parent always returns nil as there's no layer below the disk. func (dl *diskLayer) Parent() snapshot { return nil @@ -161,6 +165,6 @@ func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro // 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 // 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 { - return newDiffLayer(dl, blockHash, destructs, accounts, storage) +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, verified chan struct{}) *diffLayer { + return newDiffLayer(dl, blockHash, destructs, accounts, storage, verified) } diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go index 2a27b01577..52116e2c85 100644 --- a/core/state/snapshot/iterator_test.go +++ b/core/state/snapshot/iterator_test.go @@ -53,7 +53,7 @@ func TestAccountIteratorBasics(t *testing.T) { } } // Add some (identical) layers on top - diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) + diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage), nil) it := diffLayer.AccountIterator(common.Hash{}) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator @@ -91,7 +91,7 @@ func TestStorageIteratorBasics(t *testing.T) { nilStorage[h] = nilstorage } // Add some (identical) layers on top - diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, nil, copyAccounts(accounts), copyStorage(storage)) + diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, nil, copyAccounts(accounts), copyStorage(storage), nil) for account := range accounts { it, _ := diffLayer.StorageIterator(account, common.Hash{}) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index 5cfb9a9f2a..35c69cfd6b 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -243,7 +243,7 @@ func loadDiffLayer(parent snapshot, r *rlp.Stream) (snapshot, error) { } storageData[entry.Hash] = slots } - return loadDiffLayer(newDiffLayer(parent, root, destructSet, accountData, storageData), r) + return loadDiffLayer(newDiffLayer(parent, root, destructSet, accountData, storageData, nil), r) } // Journal terminates any in-progress snapshot generation, also implicitly pushing diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 46d1b06def..af6f8c314d 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -101,6 +101,9 @@ type Snapshot interface { // Root returns the root hash for which this snapshot was made. Root() common.Hash + // WaitVerified will wait until the Snapshot been verified + WaitVerified() + // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. Account(hash common.Hash) (*Account, error) @@ -130,7 +133,7 @@ type snapshot interface { // the specified data items. // // 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, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte, verified chan struct{}) *diffLayer // 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 @@ -289,6 +292,14 @@ func (t *Tree) Snapshot(blockRoot common.Hash) Snapshot { return t.layers[blockRoot] } +// Snapshot retrieves a snapshot belonging to the given block root, or nil if no +func (t *Tree) Remove(blockRoot common.Hash) { + t.lock.RLock() + defer t.lock.RUnlock() + + delete(t.layers, blockRoot) +} + // Snapshots returns all visited layers from the topmost layer with specific // root and traverses downward. The layer amount is limited by the given number. // If nodisk is set, then disk layer is excluded. @@ -322,14 +333,14 @@ func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot { return ret } -func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs map[common.Address]struct{}, accounts map[common.Address][]byte, storage map[common.Address]map[string][]byte) error { +func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs map[common.Address]struct{}, accounts map[common.Address][]byte, storage map[common.Address]map[string][]byte, verified chan struct{}) error { hashDestructs, hashAccounts, hashStorage := transformSnapData(destructs, accounts, storage) - return t.update(blockRoot, parentRoot, hashDestructs, hashAccounts, hashStorage) + return t.update(blockRoot, parentRoot, hashDestructs, hashAccounts, hashStorage, verified) } // 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). -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, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte, verified chan struct{}) error { // 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 // don't modify the state (0 block subsidy). @@ -344,7 +355,7 @@ func (t *Tree) update(blockRoot common.Hash, parentRoot common.Hash, destructs m if parent == nil { return fmt.Errorf("parent [%#x] snapshot missing", parentRoot) } - snap := parent.(snapshot).Update(blockRoot, destructs, accounts, storage) + snap := parent.(snapshot).Update(blockRoot, destructs, accounts, storage, verified) // Save the new snapshot for later t.lock.Lock() diff --git a/core/state/statedb.go b/core/state/statedb.go index 6f150915ca..6e93396260 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -76,6 +76,7 @@ type StateDB struct { db Database prefetcher *triePrefetcher originalRoot common.Hash // The pre-state root, before any changes were made + expectedRoot common.Hash trie Trie hasher crypto.KeccakState diffLayer *types.DiffLayer @@ -1029,6 +1030,9 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Now we're about to start to write changes to the trie. The trie is so far // _untouched_. We can check with the prefetcher, if it can give us a trie // which has the same root, but also has some content loaded into it. + if s.snap != nil { + s.snap.WaitVerified() + } if prefetcher != nil { if trie := prefetcher.trie(s.originalRoot); trie != nil { s.trie = trie @@ -1081,9 +1085,12 @@ func (s *StateDB) clearJournalAndRefund() { s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entires } -func (s *StateDB) LightCommit(root common.Hash) (common.Hash, *types.DiffLayer, error) { +func (s *StateDB) LightCommit() (common.Hash, *types.DiffLayer, error) { codeWriter := s.db.TrieDB().DiskDB().NewBatch() + // light process already verified it, expectedRoot is trustworthy. + root := s.expectedRoot + commitFuncs := []func() error{ func() error { for codeHash, code := range s.diffCode { @@ -1171,7 +1178,8 @@ func (s *StateDB) LightCommit(root common.Hash) (common.Hash, *types.DiffLayer, } // Only update if there's a state transition (skip empty Clique blocks) if parent := s.snap.Root(); parent != root { - if err := s.snaps.Update(root, parent, s.snapDestructs, s.snapAccounts, s.snapStorage); err != nil { + // for light sync, always do sync commit + if err := s.snaps.Update(root, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, nil); err != nil { log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", err) } // Keep n diff layers in the memory @@ -1210,18 +1218,28 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, *types.DiffLayer return common.Hash{}, nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) } // Finalize any pending changes and merge everything into the tries - root := s.IntermediateRoot(deleteEmptyObjects) if s.lightProcessed { - return s.LightCommit(root) + return s.LightCommit() } var diffLayer *types.DiffLayer if s.snap != nil { diffLayer = &types.DiffLayer{} } - commitFuncs := []func() error{ - func() error { - // Commit objects to the trie, measuring the elapsed time - tasks := make(chan func(batch ethdb.KeyValueWriter)) + var verified chan struct{} + var snapUpdated chan struct{} + if s.snap != nil { + verified = make(chan struct{}) + snapUpdated = make(chan struct{}) + } + + commmitTrie := func() error { + commitErr := func() error { + if root := s.IntermediateRoot(deleteEmptyObjects); s.expectedRoot != root { + return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, root) + } else { + return nil + } + tasks := make(chan func()) taskResults := make(chan error, len(s.stateObjectsDirty)) tasksNum := 0 finishCh := make(chan struct{}) @@ -1232,17 +1250,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, *types.DiffLayer wg.Add(1) go func() { defer wg.Done() - codeWriter := s.db.TrieDB().DiskDB().NewBatch() for { select { case task := <-tasks: - task(codeWriter) + task() case <-finishCh: - if codeWriter.ValueSize() > 0 { - if err := codeWriter.Write(); err != nil { - log.Crit("Failed to commit dirty codes", "error", err) - } - } return } } @@ -1265,11 +1277,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, *types.DiffLayer for addr := range s.stateObjectsDirty { if obj := s.stateObjects[addr]; !obj.deleted { // Write any contract code associated with the state object - tasks <- func(codeWriter ethdb.KeyValueWriter) { - if obj.code != nil && obj.dirtyCode { - rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code) - obj.dirtyCode = false - } + tasks <- func() { // Write any storage changes in the state object to its storage trie if err := obj.CommitTrie(s.db); err != nil { taskResults <- err @@ -1292,11 +1300,6 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, *types.DiffLayer if len(s.stateObjectsDirty) > 0 { s.stateObjectsDirty = make(map[common.Address]struct{}, len(s.stateObjectsDirty)/2) } - // Write the account trie changes, measuing the amount of wasted time - var start time.Time - if metrics.EnabledExpensive { - start = time.Now() - } // The onleaf func is called _serially_, so we can reuse the same account // for unmarshalling every time. var account Account @@ -1312,14 +1315,48 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, *types.DiffLayer if err != nil { return err } - if metrics.EnabledExpensive { - s.AccountCommits += time.Since(start) - } if root != emptyRoot { s.db.CacheAccount(root, s.trie) } wg.Wait() return nil + }() + if commitErr != nil && snapUpdated != nil { + // wait for snap commit done + <-snapUpdated + s.snaps.Remove(s.expectedRoot) + //TODO Rewind the block further + } + if verified != nil { + close(verified) + } + return commitErr + } + + commitFuncs := []func() error{ + func() error { + codeWriter := s.db.TrieDB().DiskDB().NewBatch() + for addr := range s.stateObjectsDirty { + if obj := s.stateObjects[addr]; !obj.deleted { + if obj.code != nil && obj.dirtyCode { + rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code) + obj.dirtyCode = false + if codeWriter.ValueSize() > ethdb.IdealBatchSize { + if err := codeWriter.Write(); err != nil { + return err + } + codeWriter.Reset() + } + } + } + } + if codeWriter.ValueSize() > 0 { + if err := codeWriter.Write(); err != nil { + log.Crit("Failed to commit dirty codes", "error", err) + return err + } + } + return nil }, func() error { // If snapshotting is enabled, update the snapshot tree with this new version @@ -1327,17 +1364,18 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, *types.DiffLayer if metrics.EnabledExpensive { defer func(start time.Time) { s.SnapshotCommits += time.Since(start) }(time.Now()) } + defer close(snapUpdated) // Only update if there's a state transition (skip empty Clique blocks) - if parent := s.snap.Root(); parent != root { - if err := s.snaps.Update(root, parent, s.snapDestructs, s.snapAccounts, s.snapStorage); err != nil { - log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", err) + if parent := s.snap.Root(); parent != s.expectedRoot { + if err := s.snaps.Update(s.expectedRoot, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, verified); err != nil { + log.Warn("Failed to update snapshot tree", "from", parent, "to", s.expectedRoot, "err", err) } // Keep n diff layers in the memory // - head layer is paired with HEAD state // - head-1 layer is paired with HEAD-1 state // - head-(n-1) layer(bottom-most diff layer) is paired with HEAD-(n-1)state - if err := s.snaps.Cap(root, s.snaps.CapLimit()); err != nil { - log.Warn("Failed to cap snapshot tree", "root", root, "layers", s.snaps.CapLimit(), "err", err) + if err := s.snaps.Cap(s.expectedRoot, s.snaps.CapLimit()); err != nil { + log.Warn("Failed to cap snapshot tree", "root", s.expectedRoot, "layers", s.snaps.CapLimit(), "err", err) } } } @@ -1350,6 +1388,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, *types.DiffLayer return nil }, } + if s.snap != nil { + go commmitTrie() + } else { + commitFuncs = append(commitFuncs, commmitTrie) + } commitRes := make(chan error, len(commitFuncs)) for _, f := range commitFuncs { tmpFunc := f @@ -1364,7 +1407,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, *types.DiffLayer } } s.snap, s.snapDestructs, s.snapAccounts, s.snapStorage = nil, nil, nil, nil - return root, diffLayer, nil + return s.expectedRoot, diffLayer, nil } func (s *StateDB) DiffLayerToSnap(diffLayer *types.DiffLayer) (map[common.Address]struct{}, map[common.Address][]byte, map[common.Address]map[string][]byte, error) { diff --git a/core/state_processor.go b/core/state_processor.go index 6ca99f2eec..a3d95a050e 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -330,7 +330,7 @@ func (p *LightStateProcessor) LightProcess(diffLayer *types.DiffLayer, block *ty } // Do validate in advance so that we can fall back to full process - if err := p.bc.validator.ValidateState(block, statedb, diffLayer.Receipts, gasUsed); err != nil { + if err := p.bc.validator.ValidateState(block, statedb, diffLayer.Receipts, gasUsed, false); err != nil { log.Error("validate state failed during diff sync", "error", err) return nil, nil, 0, err } diff --git a/core/types.go b/core/types.go index 49bd58e086..5ed4817e68 100644 --- a/core/types.go +++ b/core/types.go @@ -31,7 +31,7 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. - ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error + ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, skipHeavyVerify bool) error } // Prefetcher is an interface for pre-caching transaction signatures and state.