From 98fedc9efbd2ea507278bbecd3f3119dd7ca9037 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Thu, 3 Mar 2022 09:45:02 +0800 Subject: [PATCH 01/28] pipeline state verification --- core/block_validator.go | 3 +- core/state/snapshot/difflayer.go | 50 +++++++++++++++++++---- core/state/snapshot/disklayer.go | 8 ++++ core/state/snapshot/journal.go | 4 ++ core/state/snapshot/snapshot.go | 9 +++++ core/state/statedb.go | 68 +++++++++++++++++++++++++++++++- 6 files changed, 133 insertions(+), 9 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index b109c1e54b..dd35d2ca1d 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -141,7 +141,8 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD return err } statedb.Finalise(v.config.IsEIP158(header.Number)) - statedb.AccountsIntermediateRoot() + //state verification pipeline - accounts root are not calculated here + statedb.AccountsIntermediateWithoutRoot() return nil }) } else { diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 65b2729d9c..2260a29bf6 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -121,6 +121,9 @@ type diffLayer struct { verifiedCh chan struct{} // the difflayer is verified when verifiedCh is nil or closed valid bool // mark the difflayer is valid or not. + accountCorrectedCh chan struct{} // To communicate whether the accountData has been corrected + accountCorrected bool // Mark whether the accountData of the difflayer is correct or not + diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer lock sync.RWMutex @@ -174,14 +177,20 @@ func (h storageBloomHasher) Sum64() uint64 { 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, - root: root, - destructSet: destructs, - accountData: accounts, - storageData: storage, - storageList: make(map[common.Hash][]common.Hash), - verifiedCh: verified, + parent: parent, + root: root, + destructSet: destructs, + accountData: accounts, + storageData: storage, + storageList: make(map[common.Hash][]common.Hash), + verifiedCh: verified, } + + // this snapshot is used in pipeline commit + if verified != nil { + dl.accountCorrectedCh = make(chan struct{}) + } + switch parent := parent.(type) { case *diskLayer: dl.rebloom(parent) @@ -286,6 +295,33 @@ func (dl *diffLayer) Verified() bool { } } +func (dl *diffLayer) CorrectAccounts(accounts map[common.Hash][]byte) { + dl.lock.Lock() + defer dl.lock.Unlock() + + for k, v := range accounts { + dl.accountData[k] = v + } + dl.accountCorrected = true + if dl.accountCorrectedCh != nil { + dl.accountCorrectedCh <- struct{}{} + } +} + +func (dl *diffLayer) AccountCorrected() bool { + dl.lock.RLock() + defer dl.lock.RUnlock() + + return dl.accountCorrected +} + +func (dl *diffLayer) WaitAccountCorrected() { + if dl.accountCorrectedCh == nil { + return + } + <-dl.accountCorrectedCh +} + // Parent returns the subsequent layer of a diff layer. func (dl *diffLayer) Parent() snapshot { return dl.parent diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index c1de41782c..ed13f38a93 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -59,6 +59,14 @@ func (dl *diskLayer) Verified() bool { return true } +func (dl *diskLayer) CorrectAccounts(map[common.Hash][]byte) {} + +func (dl *diskLayer) AccountCorrected() bool { + return true +} + +func (dl *diskLayer) WaitAccountCorrected() {} + // Parent always returns nil as there's no layer below the disk. func (dl *diskLayer) Parent() snapshot { return nil diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index 35c69cfd6b..af76dcdf42 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -288,6 +288,10 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) { if dl.Stale() { return common.Hash{}, ErrSnapshotStale } + + // Wait the accountData in the layer is corrected + dl.WaitAccountCorrected() + // Everything below was journalled, persist this layer too if err := rlp.Encode(buffer, dl.root); err != nil { return common.Hash{}, err diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 8ac93f28e4..7495825e8e 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -110,6 +110,15 @@ type Snapshot interface { // Store the verification result MarkValid() + // Update account data for storing the correct data + CorrectAccounts(map[common.Hash][]byte) + + // Check whether the account data is holding the correct data + AccountCorrected() bool + + // Wait until the account data is corrected + WaitAccountCorrected() + // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. Account(hash common.Hash) (*Account, error) diff --git a/core/state/statedb.go b/core/state/statedb.go index 5ea84f4032..ba21904906 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1023,7 +1023,15 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.StateIntermediateRoot() } +func (s *StateDB) AccountsIntermediateWithoutRoot() { + s.accountsIntermediateRoot(false) +} + func (s *StateDB) AccountsIntermediateRoot() { + s.accountsIntermediateRoot(true) +} + +func (s *StateDB) accountsIntermediateRoot(updateRoot bool) { tasks := make(chan func()) finishCh := make(chan struct{}) defer close(finishCh) @@ -1050,7 +1058,9 @@ func (s *StateDB) AccountsIntermediateRoot() { if obj := s.stateObjects[addr]; !obj.deleted { wg.Add(1) tasks <- func() { - obj.updateRoot(s.db) + if updateRoot { + obj.updateRoot(s.db) + } // If state snapshotting is active, cache the data til commit. Note, this // update mechanism is not symmetric to the deletion, because whereas it is @@ -1074,6 +1084,54 @@ func (s *StateDB) AccountsIntermediateRoot() { wg.Wait() } +func (s *StateDB) accountDataForDiffLayer() map[common.Hash][]byte { + tasks := make(chan func()) + finishCh := make(chan struct{}) + defer close(finishCh) + wg := sync.WaitGroup{} + for i := 0; i < runtime.NumCPU(); i++ { + go func() { + for { + select { + case task := <-tasks: + task() + case <-finishCh: + return + } + } + }() + } + lock := sync.Mutex{} + accountData := make(map[common.Hash][]byte) + for addr := range s.stateObjectsPending { + if obj := s.stateObjects[addr]; !obj.deleted { + wg.Add(1) + tasks <- func() { + obj.updateRoot(s.db) + if s.snap != nil && !obj.deleted { + s.snapMux.Lock() + // It is possible to add unnecessary change, but it is fine. + s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) + s.snapMux.Unlock() + + lock.Lock() + accountData[obj.address.Hash()] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) + lock.Unlock() + } + data, err := rlp.EncodeToBytes(obj) + if err != nil { + panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) + } + obj.encodeData = data + + wg.Done() + } + } + } + wg.Wait() + return accountData +} + func (s *StateDB) StateIntermediateRoot() common.Hash { // If there was a trie prefetcher operating, it gets aborted and irrevocably // modified after we start retrieving tries. Remove it from the statedb after @@ -1308,6 +1366,14 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er commmitTrie := func() error { commitErr := func() error { + + if s.pipeCommit && s.snap != nil { + // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct + // Fix the wrong data here + accountData := s.accountDataForDiffLayer() + s.snap.CorrectAccounts(accountData) + } + if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } From a0f073f80f30e2a3143b2023492ad9ae9279a2f4 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 4 Mar 2022 10:52:44 +0800 Subject: [PATCH 02/28] update codes and add logs for debug --- core/state/snapshot/difflayer.go | 4 +-- core/state/snapshot/snapshot.go | 4 +++ core/state/statedb.go | 42 ++++++++++++++++++++------------ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 2260a29bf6..be47449098 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -299,9 +299,7 @@ func (dl *diffLayer) CorrectAccounts(accounts map[common.Hash][]byte) { dl.lock.Lock() defer dl.lock.Unlock() - for k, v := range accounts { - dl.accountData[k] = v - } + dl.accountData = accounts dl.accountCorrected = true if dl.accountCorrectedCh != nil { dl.accountCorrectedCh <- struct{}{} diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 7495825e8e..83c0782b69 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -342,6 +342,10 @@ func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot { 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) + fmt.Println("hashAccounts: ", len(hashAccounts)) + for k, _ := range hashAccounts { + fmt.Println("key=", k) + } return t.update(blockRoot, parentRoot, hashDestructs, hashAccounts, hashStorage, verified) } diff --git a/core/state/statedb.go b/core/state/statedb.go index ba21904906..0b6290e23c 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1024,14 +1024,23 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { } func (s *StateDB) AccountsIntermediateWithoutRoot() { - s.accountsIntermediateRoot(false) + for addr := range s.stateObjectsPending { + if obj := s.stateObjects[addr]; !obj.deleted { + if s.snap != nil && !obj.deleted { + s.snapMux.Lock() + s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) + s.snapMux.Unlock() + } + data, err := rlp.EncodeToBytes(obj) + if err != nil { + panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) + } + obj.encodeData = data + } + } } func (s *StateDB) AccountsIntermediateRoot() { - s.accountsIntermediateRoot(true) -} - -func (s *StateDB) accountsIntermediateRoot(updateRoot bool) { tasks := make(chan func()) finishCh := make(chan struct{}) defer close(finishCh) @@ -1058,9 +1067,7 @@ func (s *StateDB) accountsIntermediateRoot(updateRoot bool) { if obj := s.stateObjects[addr]; !obj.deleted { wg.Add(1) tasks <- func() { - if updateRoot { - obj.updateRoot(s.db) - } + obj.updateRoot(s.db) // If state snapshotting is active, cache the data til commit. Note, this // update mechanism is not symmetric to the deletion, because whereas it is @@ -1109,11 +1116,6 @@ func (s *StateDB) accountDataForDiffLayer() map[common.Hash][]byte { tasks <- func() { obj.updateRoot(s.db) if s.snap != nil && !obj.deleted { - s.snapMux.Lock() - // It is possible to add unnecessary change, but it is fine. - s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) - s.snapMux.Unlock() - lock.Lock() accountData[obj.address.Hash()] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) lock.Unlock() @@ -1166,6 +1168,7 @@ func (s *StateDB) StateIntermediateRoot() common.Hash { } s.trie = tr } + //TODO: stateObjectsPending here usedAddrs := make([][]byte, 0, len(s.stateObjectsPending)) for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; obj.deleted { @@ -1178,6 +1181,7 @@ func (s *StateDB) StateIntermediateRoot() common.Hash { if prefetcher != nil { prefetcher.used(s.originalRoot, usedAddrs) } + //TODO: stateObjectsPending here if len(s.stateObjectsPending) > 0 { s.stateObjectsPending = make(map[common.Address]struct{}) } @@ -1367,16 +1371,24 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er commmitTrie := func() error { commitErr := func() error { + accountData := make(map[common.Hash][]byte ) if s.pipeCommit && s.snap != nil { // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct // Fix the wrong data here - accountData := s.accountDataForDiffLayer() - s.snap.CorrectAccounts(accountData) + accountData = s.accountDataForDiffLayer() + fmt.Println("accountData:", len(accountData)) + for k, _ := range accountData { + fmt.Println("key=", k) + } } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } + + if s.pipeCommit && s.snap != nil { + s.snap.CorrectAccounts(accountData) + } tasks := make(chan func()) taskResults := make(chan error, len(s.stateObjectsDirty)) tasksNum := 0 From 99d8a0b10a987fdb9d618b7f268cb931aad75a60 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 4 Mar 2022 11:40:05 +0800 Subject: [PATCH 03/28] refactor --- core/state/snapshot/difflayer.go | 40 ++++++-------------------------- core/state/snapshot/disklayer.go | 6 ----- core/state/snapshot/journal.go | 4 ++-- core/state/snapshot/snapshot.go | 6 ----- 4 files changed, 9 insertions(+), 47 deletions(-) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index be47449098..77473fef65 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -121,9 +121,6 @@ type diffLayer struct { verifiedCh chan struct{} // the difflayer is verified when verifiedCh is nil or closed valid bool // mark the difflayer is valid or not. - accountCorrectedCh chan struct{} // To communicate whether the accountData has been corrected - accountCorrected bool // Mark whether the accountData of the difflayer is correct or not - diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer lock sync.RWMutex @@ -177,18 +174,13 @@ func (h storageBloomHasher) Sum64() uint64 { 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, - root: root, - destructSet: destructs, - accountData: accounts, - storageData: storage, - storageList: make(map[common.Hash][]common.Hash), - verifiedCh: verified, - } - - // this snapshot is used in pipeline commit - if verified != nil { - dl.accountCorrectedCh = make(chan struct{}) + parent: parent, + root: root, + destructSet: destructs, + accountData: accounts, + storageData: storage, + storageList: make(map[common.Hash][]common.Hash), + verifiedCh: verified, } switch parent := parent.(type) { @@ -300,24 +292,6 @@ func (dl *diffLayer) CorrectAccounts(accounts map[common.Hash][]byte) { defer dl.lock.Unlock() dl.accountData = accounts - dl.accountCorrected = true - if dl.accountCorrectedCh != nil { - dl.accountCorrectedCh <- struct{}{} - } -} - -func (dl *diffLayer) AccountCorrected() bool { - dl.lock.RLock() - defer dl.lock.RUnlock() - - return dl.accountCorrected -} - -func (dl *diffLayer) WaitAccountCorrected() { - if dl.accountCorrectedCh == nil { - return - } - <-dl.accountCorrectedCh } // Parent returns the subsequent layer of a diff layer. diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index ed13f38a93..a4ba0fe692 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -61,12 +61,6 @@ func (dl *diskLayer) Verified() bool { func (dl *diskLayer) CorrectAccounts(map[common.Hash][]byte) {} -func (dl *diskLayer) AccountCorrected() bool { - return true -} - -func (dl *diskLayer) WaitAccountCorrected() {} - // Parent always returns nil as there's no layer below the disk. func (dl *diskLayer) Parent() snapshot { return nil diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index af76dcdf42..ee3d943680 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -289,8 +289,8 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) { return common.Hash{}, ErrSnapshotStale } - // Wait the accountData in the layer is corrected - dl.WaitAccountCorrected() + // TODO: Wait the accountData in the layer is corrected + dl.WaitAndGetVerifyRes() // Everything below was journalled, persist this layer too if err := rlp.Encode(buffer, dl.root); err != nil { diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 83c0782b69..ff0c3e5df9 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -113,12 +113,6 @@ type Snapshot interface { // Update account data for storing the correct data CorrectAccounts(map[common.Hash][]byte) - // Check whether the account data is holding the correct data - AccountCorrected() bool - - // Wait until the account data is corrected - WaitAccountCorrected() - // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. Account(hash common.Hash) (*Account, error) From b284a37ca4059205361be592e7e4af358493ec25 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Tue, 8 Mar 2022 20:43:48 +0800 Subject: [PATCH 04/28] update and add logs --- core/state/snapshot/difflayer.go | 16 ++++++++++++++++ core/state/snapshot/disklayer.go | 15 ++++++++++++++- core/state/statedb.go | 30 +++++++++++++++++++++++++----- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 77473fef65..4103ddaab0 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -21,6 +21,7 @@ import ( "fmt" "math" "math/rand" + "reflect" "sort" "sync" "sync/atomic" @@ -191,6 +192,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s default: panic("unknown parent type") } + fmt.Println("parent type:", reflect.TypeOf(parent)) // Sanity check that accounts or storage slots are never nil for accountHash, blob := range accounts { if blob == nil { @@ -291,7 +293,20 @@ func (dl *diffLayer) CorrectAccounts(accounts map[common.Hash][]byte) { dl.lock.Lock() defer dl.lock.Unlock() + fmt.Println("diffLayer CorrectAccounts") + fmt.Println("diffLayer hash:", dl.root.Hex()) + fmt.Println("------------") + fmt.Println("difflayer before:", len(dl.accountData)) + for k, v := range dl.accountData { + fmt.Printf("key:= %s, v:= %x \n", k.Hex(), v) + } + dl.accountData = accounts + fmt.Println("difflayer after:", len(dl.accountData)) + for k, v := range dl.accountData { + fmt.Printf("key:= %s, v:= %x \n", k.Hex(), v) + } + fmt.Println("------------") } // Parent returns the subsequent layer of a diff layer. @@ -462,6 +477,7 @@ 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, verified chan struct{}) *diffLayer { + fmt.Println("diffLayer Update") return newDiffLayer(dl, blockRoot, destructs, accounts, storage, verified) } diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index a4ba0fe692..d494883a10 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -18,6 +18,7 @@ package snapshot import ( "bytes" + "fmt" "sync" "github.com/VictoriaMetrics/fastcache" @@ -59,7 +60,18 @@ func (dl *diskLayer) Verified() bool { return true } -func (dl *diskLayer) CorrectAccounts(map[common.Hash][]byte) {} +func (dl *diskLayer) CorrectAccounts(accounts map[common.Hash][]byte) { + dl.lock.Lock() + defer dl.lock.Unlock() + + fmt.Println("diskLayer CorrectAccounts") + fmt.Println("-----") + fmt.Println("diskLayer to use :", len(accounts)) + for k, v := range accounts { + fmt.Printf("key:= %s, v:= %x \n", k.Hex(), v) + } + fmt.Println("-----") +} // Parent always returns nil as there's no layer below the disk. func (dl *diskLayer) Parent() snapshot { @@ -174,5 +186,6 @@ func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro // 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, verified chan struct{}) *diffLayer { + fmt.Println("disklayer update") return newDiffLayer(dl, blockHash, destructs, accounts, storage, verified) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 0b6290e23c..d31e910468 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1117,7 +1117,8 @@ func (s *StateDB) accountDataForDiffLayer() map[common.Hash][]byte { obj.updateRoot(s.db) if s.snap != nil && !obj.deleted { lock.Lock() - accountData[obj.address.Hash()] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) + s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) + accountData[crypto.Keccak256Hash(obj.address[:])] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) lock.Unlock() } data, err := rlp.EncodeToBytes(obj) @@ -1359,6 +1360,8 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er var diffLayer *types.DiffLayer var verified chan struct{} var snapUpdated chan struct{} + var snapCreated chan common.Hash + var accountRefreshed chan struct{} if s.snap != nil { diffLayer = &types.DiffLayer{} } @@ -1366,16 +1369,18 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er // async commit the MPT verified = make(chan struct{}) snapUpdated = make(chan struct{}) + snapCreated = make(chan common.Hash) + accountRefreshed = make(chan struct{}) } commmitTrie := func() error { commitErr := func() error { - - accountData := make(map[common.Hash][]byte ) + accountData := make(map[common.Hash][]byte) if s.pipeCommit && s.snap != nil { // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct // Fix the wrong data here accountData = s.accountDataForDiffLayer() + close(accountRefreshed) fmt.Println("accountData:", len(accountData)) for k, _ := range accountData { fmt.Println("key=", k) @@ -1386,9 +1391,16 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } - if s.pipeCommit && s.snap != nil { - s.snap.CorrectAccounts(accountData) + if s.pipeCommit { + fmt.Println("s.stateRoot:", s.stateRoot.Hex()) + root := <-snapCreated + + empty := common.Hash{} + if root != empty { + s.snaps.Snapshot(root).CorrectAccounts(accountData) + } } + tasks := make(chan func()) taskResults := make(chan error, len(s.stateObjectsDirty)) tasksNum := 0 @@ -1530,6 +1542,11 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er 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) + snapCreated <- common.Hash{} + } + if s.pipeCommit { + fmt.Println("s.expectedRoot", s.expectedRoot.Hex()) + snapCreated <- s.expectedRoot } // Keep n diff layers in the memory // - head layer is paired with HEAD state @@ -1546,6 +1563,9 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er }, func() error { if s.snap != nil { + if s.pipeCommit { + <-accountRefreshed + } diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer() } return nil From 4d8c1481fd3de59a40f8b0bb4699569b4b3509b3 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Thu, 10 Mar 2022 15:27:30 +0800 Subject: [PATCH 05/28] refactor --- core/block_validator.go | 2 +- core/state/snapshot/difflayer.go | 4 +- core/state/snapshot/disklayer.go | 14 +------ core/state/snapshot/journal.go | 6 ++- core/state/snapshot/snapshot.go | 4 -- core/state/statedb.go | 64 +++++++++++++++++++++----------- 6 files changed, 49 insertions(+), 45 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index dd35d2ca1d..7b681252b9 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -142,7 +142,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } statedb.Finalise(v.config.IsEIP158(header.Number)) //state verification pipeline - accounts root are not calculated here - statedb.AccountsIntermediateWithoutRoot() + statedb.PopulateSnapAccountAndStorage() return nil }) } else { diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 4103ddaab0..83be74a83a 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -21,7 +21,6 @@ import ( "fmt" "math" "math/rand" - "reflect" "sort" "sync" "sync/atomic" @@ -192,7 +191,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s default: panic("unknown parent type") } - fmt.Println("parent type:", reflect.TypeOf(parent)) + // Sanity check that accounts or storage slots are never nil for accountHash, blob := range accounts { if blob == nil { @@ -477,7 +476,6 @@ 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, verified chan struct{}) *diffLayer { - fmt.Println("diffLayer Update") return newDiffLayer(dl, blockRoot, destructs, accounts, storage, verified) } diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index d494883a10..d4f4dc80f7 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -18,7 +18,6 @@ package snapshot import ( "bytes" - "fmt" "sync" "github.com/VictoriaMetrics/fastcache" @@ -60,17 +59,7 @@ func (dl *diskLayer) Verified() bool { return true } -func (dl *diskLayer) CorrectAccounts(accounts map[common.Hash][]byte) { - dl.lock.Lock() - defer dl.lock.Unlock() - - fmt.Println("diskLayer CorrectAccounts") - fmt.Println("-----") - fmt.Println("diskLayer to use :", len(accounts)) - for k, v := range accounts { - fmt.Printf("key:= %s, v:= %x \n", k.Hex(), v) - } - fmt.Println("-----") +func (dl *diskLayer) CorrectAccounts(map[common.Hash][]byte) { } // Parent always returns nil as there's no layer below the disk. @@ -186,6 +175,5 @@ func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro // 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, verified chan struct{}) *diffLayer { - fmt.Println("disklayer update") return newDiffLayer(dl, blockHash, destructs, accounts, storage, verified) } diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index ee3d943680..d83c5db51d 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -289,8 +289,10 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) { return common.Hash{}, ErrSnapshotStale } - // TODO: Wait the accountData in the layer is corrected - dl.WaitAndGetVerifyRes() + // Wait the snapshot(difflayer) is verified, it means the account data also been refreshed with the correct data + if !dl.WaitAndGetVerifyRes() { + return common.Hash{}, ErrSnapshotStale + } // Everything below was journalled, persist this layer too if err := rlp.Encode(buffer, dl.root); err != nil { diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index ff0c3e5df9..ad10ac3eb6 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -336,10 +336,6 @@ func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot { 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) - fmt.Println("hashAccounts: ", len(hashAccounts)) - for k, _ := range hashAccounts { - fmt.Println("key=", k) - } return t.update(blockRoot, parentRoot, hashDestructs, hashAccounts, hashStorage, verified) } diff --git a/core/state/statedb.go b/core/state/statedb.go index d31e910468..b624fc0db9 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1023,13 +1023,12 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.StateIntermediateRoot() } -func (s *StateDB) AccountsIntermediateWithoutRoot() { +func (s *StateDB) PopulateSnapAccountAndStorage() { for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; !obj.deleted { if s.snap != nil && !obj.deleted { - s.snapMux.Lock() + s.populateSnapStorage(obj) s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) - s.snapMux.Unlock() } data, err := rlp.EncodeToBytes(obj) if err != nil { @@ -1117,7 +1116,6 @@ func (s *StateDB) accountDataForDiffLayer() map[common.Hash][]byte { obj.updateRoot(s.db) if s.snap != nil && !obj.deleted { lock.Lock() - s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) accountData[crypto.Keccak256Hash(obj.address[:])] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) lock.Unlock() } @@ -1135,6 +1133,38 @@ func (s *StateDB) accountDataForDiffLayer() map[common.Hash][]byte { return accountData } +func (s *StateDB) populateSnapStorage(obj *StateObject) { + tr := obj.getTrie(s.db) + var storage map[string][]byte + for key, value := range obj.pendingStorage { + // Skip noop changes, persist actual changes + if value == obj.originStorage[key] { + continue + } + obj.originStorage[key] = value + + var v []byte + if (value == common.Hash{}) { + obj.setError(tr.TryDelete(key[:])) + } else { + // Encoding []byte cannot fail, ok to ignore the error. + v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) + obj.setError(tr.TryUpdate(key[:], v)) + } + // If state snapshotting is active, cache the data til commit + if obj.db.snap != nil { + if storage == nil { + // Retrieve the old storage map, if available, create a new one otherwise + if storage = obj.db.snapStorage[obj.address]; storage == nil { + storage = make(map[string][]byte) + obj.db.snapStorage[obj.address] = storage + } + } + storage[string(key[:])] = v // v will be nil if value is 0x00 + } + } +} + func (s *StateDB) StateIntermediateRoot() common.Hash { // If there was a trie prefetcher operating, it gets aborted and irrevocably // modified after we start retrieving tries. Remove it from the statedb after @@ -1169,7 +1199,7 @@ func (s *StateDB) StateIntermediateRoot() common.Hash { } s.trie = tr } - //TODO: stateObjectsPending here + usedAddrs := make([][]byte, 0, len(s.stateObjectsPending)) for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; obj.deleted { @@ -1182,7 +1212,7 @@ func (s *StateDB) StateIntermediateRoot() common.Hash { if prefetcher != nil { prefetcher.used(s.originalRoot, usedAddrs) } - //TODO: stateObjectsPending here + if len(s.stateObjectsPending) > 0 { s.stateObjectsPending = make(map[common.Address]struct{}) } @@ -1361,7 +1391,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er var verified chan struct{} var snapUpdated chan struct{} var snapCreated chan common.Hash - var accountRefreshed chan struct{} if s.snap != nil { diffLayer = &types.DiffLayer{} } @@ -1370,21 +1399,15 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er verified = make(chan struct{}) snapUpdated = make(chan struct{}) snapCreated = make(chan common.Hash) - accountRefreshed = make(chan struct{}) } commmitTrie := func() error { commitErr := func() error { accountData := make(map[common.Hash][]byte) - if s.pipeCommit && s.snap != nil { + if s.pipeCommit { // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct // Fix the wrong data here accountData = s.accountDataForDiffLayer() - close(accountRefreshed) - fmt.Println("accountData:", len(accountData)) - for k, _ := range accountData { - fmt.Println("key=", k) - } } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { @@ -1392,11 +1415,8 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } if s.pipeCommit { - fmt.Println("s.stateRoot:", s.stateRoot.Hex()) root := <-snapCreated - - empty := common.Hash{} - if root != empty { + if root != (common.Hash{}) { s.snaps.Snapshot(root).CorrectAccounts(accountData) } } @@ -1545,7 +1565,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er snapCreated <- common.Hash{} } if s.pipeCommit { - fmt.Println("s.expectedRoot", s.expectedRoot.Hex()) snapCreated <- s.expectedRoot } // Keep n diff layers in the memory @@ -1557,15 +1576,16 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er log.Warn("Failed to cap snapshot tree", "root", s.expectedRoot, "layers", s.snaps.CapLimit(), "err", err) } }() + } else { + if s.pipeCommit { // If no snap created, still need to put data into the channel + snapCreated <- common.Hash{} + } } } return nil }, func() error { if s.snap != nil { - if s.pipeCommit { - <-accountRefreshed - } diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer() } return nil From a0180a2ba8cb901c2bc091b4763c0c0157a4a881 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 11 Mar 2022 11:59:50 +0800 Subject: [PATCH 06/28] refactor --- core/state/statedb.go | 80 +++++++++++++------------------------------ 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index b624fc0db9..5470da2d29 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1028,7 +1028,7 @@ func (s *StateDB) PopulateSnapAccountAndStorage() { if obj := s.stateObjects[addr]; !obj.deleted { if s.snap != nil && !obj.deleted { s.populateSnapStorage(obj) - s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) + s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, emptyRoot, obj.data.CodeHash) } data, err := rlp.EncodeToBytes(obj) if err != nil { @@ -1090,66 +1090,20 @@ func (s *StateDB) AccountsIntermediateRoot() { wg.Wait() } -func (s *StateDB) accountDataForDiffLayer() map[common.Hash][]byte { - tasks := make(chan func()) - finishCh := make(chan struct{}) - defer close(finishCh) - wg := sync.WaitGroup{} - for i := 0; i < runtime.NumCPU(); i++ { - go func() { - for { - select { - case task := <-tasks: - task() - case <-finishCh: - return - } - } - }() +func (s *StateDB) populateSnapStorage(obj *StateObject) { + for key, value := range obj.dirtyStorage { + obj.pendingStorage[key] = value } - lock := sync.Mutex{} - accountData := make(map[common.Hash][]byte) - for addr := range s.stateObjectsPending { - if obj := s.stateObjects[addr]; !obj.deleted { - wg.Add(1) - tasks <- func() { - obj.updateRoot(s.db) - if s.snap != nil && !obj.deleted { - lock.Lock() - accountData[crypto.Keccak256Hash(obj.address[:])] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) - lock.Unlock() - } - data, err := rlp.EncodeToBytes(obj) - if err != nil { - panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) - } - obj.encodeData = data - - wg.Done() - } - } + if len(obj.pendingStorage) == 0 { + return } - wg.Wait() - return accountData -} - -func (s *StateDB) populateSnapStorage(obj *StateObject) { - tr := obj.getTrie(s.db) var storage map[string][]byte for key, value := range obj.pendingStorage { - // Skip noop changes, persist actual changes - if value == obj.originStorage[key] { - continue - } - obj.originStorage[key] = value - var v []byte if (value == common.Hash{}) { - obj.setError(tr.TryDelete(key[:])) } else { // Encoding []byte cannot fail, ok to ignore the error. v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) - obj.setError(tr.TryUpdate(key[:], v)) } // If state snapshotting is active, cache the data til commit if obj.db.snap != nil { @@ -1391,6 +1345,9 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er var verified chan struct{} var snapUpdated chan struct{} var snapCreated chan common.Hash + + var snapAccountLock sync.Mutex // To protect snapAccount + if s.snap != nil { diffLayer = &types.DiffLayer{} } @@ -1405,12 +1362,17 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er commitErr := func() error { accountData := make(map[common.Hash][]byte) if s.pipeCommit { - // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct - // Fix the wrong data here - accountData = s.accountDataForDiffLayer() + // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, fix the wrong data here + snapAccountLock.Lock() + s.AccountsIntermediateRoot() + snapAccountLock.Unlock() + for k, v := range s.snapAccounts { + accountData[crypto.Keccak256Hash(k[:])] = v + } } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { + fmt.Printf("StateIntermediateRoot - invalid merkle root (remote: %x local: %x) \n", s.expectedRoot, s.stateRoot) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } @@ -1560,7 +1522,11 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } // Only update if there's a state transition (skip empty Clique blocks) 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 { + snapAccountLock.Lock() + err := s.snaps.Update(s.expectedRoot, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, verified) + snapAccountLock.Unlock() + + if err != nil { log.Warn("Failed to update snapshot tree", "from", parent, "to", s.expectedRoot, "err", err) snapCreated <- common.Hash{} } @@ -1586,7 +1552,9 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er }, func() error { if s.snap != nil { + snapAccountLock.Lock() diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer() + snapAccountLock.Unlock() } return nil }, From 51034909d33ba55a5769deb8aedcd68547ea9fe9 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Mon, 14 Mar 2022 10:20:17 +0800 Subject: [PATCH 07/28] remove unneeded logs --- core/state/snapshot/difflayer.go | 13 ------------- core/state/statedb.go | 1 - 2 files changed, 14 deletions(-) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 83be74a83a..6d5384f053 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -292,20 +292,7 @@ func (dl *diffLayer) CorrectAccounts(accounts map[common.Hash][]byte) { dl.lock.Lock() defer dl.lock.Unlock() - fmt.Println("diffLayer CorrectAccounts") - fmt.Println("diffLayer hash:", dl.root.Hex()) - fmt.Println("------------") - fmt.Println("difflayer before:", len(dl.accountData)) - for k, v := range dl.accountData { - fmt.Printf("key:= %s, v:= %x \n", k.Hex(), v) - } - dl.accountData = accounts - fmt.Println("difflayer after:", len(dl.accountData)) - for k, v := range dl.accountData { - fmt.Printf("key:= %s, v:= %x \n", k.Hex(), v) - } - fmt.Println("------------") } // Parent returns the subsequent layer of a diff layer. diff --git a/core/state/statedb.go b/core/state/statedb.go index 5470da2d29..cd19df1998 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1372,7 +1372,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { - fmt.Printf("StateIntermediateRoot - invalid merkle root (remote: %x local: %x) \n", s.expectedRoot, s.stateRoot) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } From f6eb04e920d457f8a68fbda55ef05f62218c6ba8 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Mon, 14 Mar 2022 17:15:04 +0800 Subject: [PATCH 08/28] fix a blocking issue --- core/state/statedb.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index cd19df1998..366103e955 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1362,7 +1362,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er commitErr := func() error { accountData := make(map[common.Hash][]byte) if s.pipeCommit { - // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, fix the wrong data here + // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, capture the correct data here snapAccountLock.Lock() s.AccountsIntermediateRoot() snapAccountLock.Unlock() @@ -1372,10 +1372,14 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { + if s.pipeCommit { + <-snapCreated + } return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } if s.pipeCommit { + //Fix the account data in difflayer here root := <-snapCreated if root != (common.Hash{}) { s.snaps.Snapshot(root).CorrectAccounts(accountData) From e0717490305316f9551e67ba22a227820fdd14d1 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Tue, 15 Mar 2022 21:23:54 +0800 Subject: [PATCH 09/28] fix sync issue when force kill --- core/blockchain.go | 2 +- core/state/statedb.go | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 6c87ffc708..319cb1eeea 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2143,7 +2143,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, bc.pipeCommit); err != nil { + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, statedb.IsPipeCommit()); err != nil { log.Error("validate state failed", "error", err) bc.reportBlock(block, receipts, err) return it.index, err diff --git a/core/state/statedb.go b/core/state/statedb.go index cd19df1998..9125a0460d 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -221,6 +221,13 @@ func (s *StateDB) EnablePipeCommit() { } } +func (s *StateDB) IsPipeCommit() bool { + if s.snap != nil { + return s.pipeCommit + } + return false +} + // Mark that the block is full processed func (s *StateDB) MarkFullProcessed() { s.fullProcessed = true @@ -1362,7 +1369,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er commitErr := func() error { accountData := make(map[common.Hash][]byte) if s.pipeCommit { - // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, fix the wrong data here + // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, capture the correct data here snapAccountLock.Lock() s.AccountsIntermediateRoot() snapAccountLock.Unlock() @@ -1372,10 +1379,15 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { + if s.pipeCommit { + <-snapCreated + } + fmt.Printf("invalid merkle root (remote: %x local: %x) \n", s.expectedRoot, s.stateRoot) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } if s.pipeCommit { + //Fix the account data in difflayer here root := <-snapCreated if root != (common.Hash{}) { s.snaps.Snapshot(root).CorrectAccounts(accountData) @@ -1525,12 +1537,12 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er err := s.snaps.Update(s.expectedRoot, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, verified) snapAccountLock.Unlock() + hashToSend := s.expectedRoot if err != nil { log.Warn("Failed to update snapshot tree", "from", parent, "to", s.expectedRoot, "err", err) - snapCreated <- common.Hash{} } if s.pipeCommit { - snapCreated <- s.expectedRoot + snapCreated <- hashToSend } // Keep n diff layers in the memory // - head layer is paired with HEAD state From 74ced5682c23522fc17f6cd4d3ef17118d5c96db Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Tue, 15 Mar 2022 21:24:48 +0800 Subject: [PATCH 10/28] remove logs --- core/state/statedb.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 9125a0460d..40bfcf55e9 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1382,7 +1382,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er if s.pipeCommit { <-snapCreated } - fmt.Printf("invalid merkle root (remote: %x local: %x) \n", s.expectedRoot, s.stateRoot) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } From bbad02ed3407f084d066766e904536958d9d846a Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Wed, 16 Mar 2022 14:46:42 +0800 Subject: [PATCH 11/28] refactor based on comments --- core/block_validator.go | 4 +- core/blockchain.go | 2 +- core/blockchain_test.go | 2 +- core/state/snapshot/snapshot.go | 4 +- core/state/statedb.go | 68 +++++++++++++++------------------ core/state_processor.go | 2 +- core/types.go | 2 +- 7 files changed, 39 insertions(+), 45 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index 7b681252b9..5e2a687430 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -112,7 +112,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, skipHeavyVerify bool) error { +func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error { header := block.Header() if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) @@ -135,7 +135,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD return nil }, } - if skipHeavyVerify { + if statedb.IsPipeCommit() { validateFuns = append(validateFuns, func() error { if err := statedb.WaitPipeVerification(); err != nil { return err diff --git a/core/blockchain.go b/core/blockchain.go index 319cb1eeea..3ed56b6e7f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2143,7 +2143,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, statedb.IsPipeCommit()); err != nil { + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); 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 50d02e0acc..07cb51933a 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -209,7 +209,7 @@ func testBlockChainImport(chain types.Blocks, pipelineCommit bool, blockchain *B blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, pipelineCommit) + err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas) if err != nil { blockchain.reportBlock(block, receipts, err) return err diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index ad10ac3eb6..a6de809e30 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -107,10 +107,10 @@ type Snapshot interface { // Verified returns whether the snapshot is verified Verified() bool - // Store the verification result + // MarkValid stores the verification result MarkValid() - // Update account data for storing the correct data + // CorrectAccounts updates account data for storing the correct data during pipecommit CorrectAccounts(map[common.Hash][]byte) // Account directly retrieves the account associated with a particular hash in diff --git a/core/state/statedb.go b/core/state/statedb.go index 40bfcf55e9..6ed0927e14 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -221,11 +221,9 @@ func (s *StateDB) EnablePipeCommit() { } } +// IsPipeCommit checks whether pipecommit is enabled on the statedb or not func (s *StateDB) IsPipeCommit() bool { - if s.snap != nil { - return s.pipeCommit - } - return false + return s.pipeCommit } // Mark that the block is full processed @@ -1030,6 +1028,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.StateIntermediateRoot() } +//PopulateSnapAccountAndStorage tries to populate required accounts and storages for pipecommit func (s *StateDB) PopulateSnapAccountAndStorage() { for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; !obj.deleted { @@ -1037,11 +1036,35 @@ func (s *StateDB) PopulateSnapAccountAndStorage() { s.populateSnapStorage(obj) s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, emptyRoot, obj.data.CodeHash) } - data, err := rlp.EncodeToBytes(obj) - if err != nil { - panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) + } + } +} + +//populateSnapStorage tries to populate required storages for pipecommit +func (s *StateDB) populateSnapStorage(obj *StateObject) { + for key, value := range obj.dirtyStorage { + obj.pendingStorage[key] = value + } + if len(obj.pendingStorage) == 0 { + return + } + var storage map[string][]byte + for key, value := range obj.pendingStorage { + var v []byte + if (value != common.Hash{}) { + // Encoding []byte cannot fail, ok to ignore the error. + v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) + } + // If state snapshotting is active, cache the data til commit + if obj.db.snap != nil { + if storage == nil { + // Retrieve the old storage map, if available, create a new one otherwise + if storage = obj.db.snapStorage[obj.address]; storage == nil { + storage = make(map[string][]byte) + obj.db.snapStorage[obj.address] = storage + } } - obj.encodeData = data + storage[string(key[:])] = v // v will be nil if value is 0x00 } } } @@ -1097,35 +1120,6 @@ func (s *StateDB) AccountsIntermediateRoot() { wg.Wait() } -func (s *StateDB) populateSnapStorage(obj *StateObject) { - for key, value := range obj.dirtyStorage { - obj.pendingStorage[key] = value - } - if len(obj.pendingStorage) == 0 { - return - } - var storage map[string][]byte - for key, value := range obj.pendingStorage { - var v []byte - if (value == common.Hash{}) { - } else { - // Encoding []byte cannot fail, ok to ignore the error. - v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) - } - // If state snapshotting is active, cache the data til commit - if obj.db.snap != nil { - if storage == nil { - // Retrieve the old storage map, if available, create a new one otherwise - if storage = obj.db.snapStorage[obj.address]; storage == nil { - storage = make(map[string][]byte) - obj.db.snapStorage[obj.address] = storage - } - } - storage[string(key[:])] = v // v will be nil if value is 0x00 - } - } -} - func (s *StateDB) StateIntermediateRoot() common.Hash { // If there was a trie prefetcher operating, it gets aborted and irrevocably // modified after we start retrieving tries. Remove it from the statedb after diff --git a/core/state_processor.go b/core/state_processor.go index 14fe9b4b92..682643803e 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -337,7 +337,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, false); err != nil { + if err := p.bc.validator.ValidateState(block, statedb, diffLayer.Receipts, gasUsed); 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 5ed4817e68..49bd58e086 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, skipHeavyVerify bool) error + ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error } // Prefetcher is an interface for pre-caching transaction signatures and state. From dfbb0409e7b0af78f4d93c226fee27707da39257 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Thu, 17 Mar 2022 16:00:15 +0800 Subject: [PATCH 12/28] refactor based on comments --- core/state/snapshot/snapshot.go | 4 ++++ core/state/statedb.go | 30 +++++------------------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index a6de809e30..47b53d60b4 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -243,6 +243,10 @@ func (t *Tree) waitBuild() { } } +func (t *Tree) Layers() int { + return len(t.layers) +} + // Disable interrupts any pending snapshot generator, deletes all the snapshot // layers in memory and marks snapshots disabled globally. In order to resume // the snapshot functionality, the caller must invoke Rebuild. diff --git a/core/state/statedb.go b/core/state/statedb.go index 6ed0927e14..d588c6cfba 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -216,7 +216,7 @@ func (s *StateDB) MarkLightProcessed() { // Enable the pipeline commit function of statedb func (s *StateDB) EnablePipeCommit() { - if s.snap != nil { + if s.snap != nil && s.snaps.Layers() > 1 { s.pipeCommit = true } } @@ -1345,9 +1345,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er var diffLayer *types.DiffLayer var verified chan struct{} var snapUpdated chan struct{} - var snapCreated chan common.Hash - - var snapAccountLock sync.Mutex // To protect snapAccount if s.snap != nil { diffLayer = &types.DiffLayer{} @@ -1356,7 +1353,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er // async commit the MPT verified = make(chan struct{}) snapUpdated = make(chan struct{}) - snapCreated = make(chan common.Hash) } commmitTrie := func() error { @@ -1364,26 +1360,21 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er accountData := make(map[common.Hash][]byte) if s.pipeCommit { // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, capture the correct data here - snapAccountLock.Lock() + <-snapUpdated s.AccountsIntermediateRoot() - snapAccountLock.Unlock() for k, v := range s.snapAccounts { accountData[crypto.Keccak256Hash(k[:])] = v } } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { - if s.pipeCommit { - <-snapCreated - } return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } if s.pipeCommit { //Fix the account data in difflayer here - root := <-snapCreated - if root != (common.Hash{}) { - s.snaps.Snapshot(root).CorrectAccounts(accountData) + if parent := s.snap.Root(); parent != s.expectedRoot { + s.snaps.Snapshot(s.expectedRoot).CorrectAccounts(accountData) } } @@ -1526,17 +1517,12 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } // Only update if there's a state transition (skip empty Clique blocks) if parent := s.snap.Root(); parent != s.expectedRoot { - snapAccountLock.Lock() err := s.snaps.Update(s.expectedRoot, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, verified) - snapAccountLock.Unlock() - hashToSend := s.expectedRoot if err != nil { log.Warn("Failed to update snapshot tree", "from", parent, "to", s.expectedRoot, "err", err) } - if s.pipeCommit { - snapCreated <- hashToSend - } + // Keep n diff layers in the memory // - head layer is paired with HEAD state // - head-1 layer is paired with HEAD-1 state @@ -1546,19 +1532,13 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er log.Warn("Failed to cap snapshot tree", "root", s.expectedRoot, "layers", s.snaps.CapLimit(), "err", err) } }() - } else { - if s.pipeCommit { // If no snap created, still need to put data into the channel - snapCreated <- common.Hash{} - } } } return nil }, func() error { if s.snap != nil { - snapAccountLock.Lock() diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer() - snapAccountLock.Unlock() } return nil }, From b79f2e6e917e6e6dd3cdcadd25b33451c55dca66 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 18 Mar 2022 09:24:36 +0800 Subject: [PATCH 13/28] refactor based on comments --- core/state/statedb.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index d588c6cfba..e4e574c8bb 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1345,6 +1345,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er var diffLayer *types.DiffLayer var verified chan struct{} var snapUpdated chan struct{} + var accountCorrected chan struct{} if s.snap != nil { diffLayer = &types.DiffLayer{} @@ -1353,6 +1354,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er // async commit the MPT verified = make(chan struct{}) snapUpdated = make(chan struct{}) + accountCorrected = make(chan struct{}) } commmitTrie := func() error { @@ -1365,19 +1367,17 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er for k, v := range s.snapAccounts { accountData[crypto.Keccak256Hash(k[:])] = v } + if parent := s.snap.Root(); parent != s.expectedRoot { + s.snaps.Snapshot(s.expectedRoot).CorrectAccounts(accountData) + } + close(accountCorrected) } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { + fmt.Printf("Invalid merkle root (remote: %x local: %x) \n", s.expectedRoot, s.stateRoot) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } - if s.pipeCommit { - //Fix the account data in difflayer here - if parent := s.snap.Root(); parent != s.expectedRoot { - s.snaps.Snapshot(s.expectedRoot).CorrectAccounts(accountData) - } - } - tasks := make(chan func()) taskResults := make(chan error, len(s.stateObjectsDirty)) tasksNum := 0 @@ -1476,6 +1476,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } log.Error("state verification failed", "err", commitErr) } + <-accountCorrected close(verified) } return commitErr From 898fe3ea773a0477739dfd9a6b096e296e89855c Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 18 Mar 2022 13:59:15 +0800 Subject: [PATCH 14/28] refactor based on comments --- core/state/statedb.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index e4e574c8bb..d29d7e0316 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1466,12 +1466,10 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er if s.pipeCommit { if commitErr == nil { - <-snapUpdated s.snaps.Snapshot(s.stateRoot).MarkValid() } else { // The blockchain will do the further rewind if write block not finish yet if failPostCommitFunc != nil { - <-snapUpdated failPostCommitFunc() } log.Error("state verification failed", "err", commitErr) From c96e48c7fa5146d7cb813f8c10dd5998c7eb1d63 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 18 Mar 2022 16:04:52 +0800 Subject: [PATCH 15/28] refactor based on comments --- core/state/statedb.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index d29d7e0316..545789d8c1 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1345,7 +1345,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er var diffLayer *types.DiffLayer var verified chan struct{} var snapUpdated chan struct{} - var accountCorrected chan struct{} if s.snap != nil { diffLayer = &types.DiffLayer{} @@ -1354,7 +1353,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er // async commit the MPT verified = make(chan struct{}) snapUpdated = make(chan struct{}) - accountCorrected = make(chan struct{}) } commmitTrie := func() error { @@ -1370,7 +1368,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er if parent := s.snap.Root(); parent != s.expectedRoot { s.snaps.Snapshot(s.expectedRoot).CorrectAccounts(accountData) } - close(accountCorrected) } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { @@ -1474,7 +1471,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } log.Error("state verification failed", "err", commitErr) } - <-accountCorrected close(verified) } return commitErr From 118000c49e42a0ab72aea1464b6403d12503abb4 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Mon, 21 Mar 2022 21:58:05 +0800 Subject: [PATCH 16/28] fix a deadlock issue --- core/state/snapshot/journal.go | 5 ----- core/state/snapshot/snapshot.go | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index d83c5db51d..587f78a474 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -289,11 +289,6 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) { return common.Hash{}, ErrSnapshotStale } - // Wait the snapshot(difflayer) is verified, it means the account data also been refreshed with the correct data - if !dl.WaitAndGetVerifyRes() { - return common.Hash{}, ErrSnapshotStale - } - // Everything below was journalled, persist this layer too if err := rlp.Encode(buffer, dl.root); err != nil { return common.Hash{}, err diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 47b53d60b4..baadd5c1a6 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -673,6 +673,11 @@ func (t *Tree) Journal(root common.Hash) (common.Hash, error) { if snap == nil { return common.Hash{}, fmt.Errorf("snapshot [%#x] missing", root) } + // Wait the snapshot(difflayer) is verified, it means the account data also been refreshed with the correct data + if !snap.WaitAndGetVerifyRes() { + return common.Hash{}, ErrSnapshotStale + } + // Run the journaling t.lock.Lock() defer t.lock.Unlock() From 15baf950d6b3a2ad6b49bd5c6bcf2fb0827a28fd Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Tue, 22 Mar 2022 21:20:19 +0800 Subject: [PATCH 17/28] fix merkle root mismatch issue during sync --- core/block_validator.go | 6 ++++++ core/blockchain.go | 18 +++++++++++++++++- core/state/statedb.go | 36 ++++++++++++++++++++++++------------ 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index 5e2a687430..ccbf37d415 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -140,6 +140,12 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD if err := statedb.WaitPipeVerification(); err != nil { return err } + statedb.CorrectAccountsRoot() + + diffLayer := &types.DiffLayer{} + diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = statedb.SnapToDiffLayer() + v.bc.finalisePendingDiffLayer(diffLayer) + statedb.Finalise(v.config.IsEIP158(header.Number)) //state verification pipeline - accounts root are not calculated here statedb.PopulateSnapAccountAndStorage() diff --git a/core/blockchain.go b/core/blockchain.go index 3ed56b6e7f..1088bb99f9 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -226,6 +226,7 @@ type BlockChain struct { diffQueue *prque.Prque // A Priority queue to store recent diff layer diffQueueBuffer chan *types.DiffLayer diffLayerFreezerBlockLimit uint64 + diffLayerPending *types.DiffLayer //Diff layer pending to be corrected // untrusted diff layers diffMux sync.RWMutex @@ -1790,7 +1791,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. diffLayer.Receipts = receipts diffLayer.BlockHash = block.Hash() diffLayer.Number = block.NumberU64() - bc.cacheDiffLayer(diffLayer) + if state.IsPipeCommit() { + bc.diffLayerPending = diffLayer + } else { + bc.cacheDiffLayer(diffLayer) + } } wg.Wait() @@ -2766,6 +2771,17 @@ func (bc *BlockChain) pruneDiffLayer() { } } +// finalisePendingDiffLayer finalises the pending diff layer and cache it in pipecommit mode +func (bc *BlockChain) finalisePendingDiffLayer(diffLayer *types.DiffLayer) { + if bc.diffLayerPending != nil { + diffLayer.Receipts = bc.diffLayerPending.Receipts + diffLayer.BlockHash = bc.diffLayerPending.BlockHash + diffLayer.Number = bc.diffLayerPending.Number + bc.cacheDiffLayer(diffLayer) + bc.diffLayerPending = nil + } +} + // Process received diff layers func (bc *BlockChain) HandleDiffLayer(diffLayer *types.DiffLayer, pid string, fulfilled bool) error { // Basic check diff --git a/core/state/statedb.go b/core/state/statedb.go index 545789d8c1..c2bb6e72ff 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1028,6 +1028,24 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.StateIntermediateRoot() } +//CorrectAccountsRoot will fix account roots in pipecommit mode +func (s *StateDB) CorrectAccountsRoot() { + addressesToPrefetch := make([][]byte, 0, len(s.stateObjectsPending)) + for addr := range s.stateObjectsPending { + if obj := s.stateObjects[addr]; !obj.deleted { + if acc, err := s.snap.Account(crypto.HashData(s.hasher, obj.address.Bytes())); err == nil { + if acc != nil && len(acc.Root) != 0 { + obj.data.Root = common.BytesToHash(acc.Root) + } + } + } + addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) + } + if s.prefetcher != nil && len(addressesToPrefetch) > 0 { + s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch, emptyAddr) + } +} + //PopulateSnapAccountAndStorage tries to populate required accounts and storages for pipecommit func (s *StateDB) PopulateSnapAccountAndStorage() { for addr := range s.stateObjectsPending { @@ -1357,21 +1375,20 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er commmitTrie := func() error { commitErr := func() error { - accountData := make(map[common.Hash][]byte) if s.pipeCommit { - // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, capture the correct data here <-snapUpdated + // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, capture the correct data here s.AccountsIntermediateRoot() - for k, v := range s.snapAccounts { - accountData[crypto.Keccak256Hash(k[:])] = v - } if parent := s.snap.Root(); parent != s.expectedRoot { + accountData := make(map[common.Hash][]byte) + for k, v := range s.snapAccounts { + accountData[crypto.Keccak256Hash(k[:])] = v + } s.snaps.Snapshot(s.expectedRoot).CorrectAccounts(accountData) } } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { - fmt.Printf("Invalid merkle root (remote: %x local: %x) \n", s.expectedRoot, s.stateRoot) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } @@ -1510,6 +1527,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er if s.pipeCommit { defer close(snapUpdated) } + diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer() // Only update if there's a state transition (skip empty Clique blocks) if parent := s.snap.Root(); parent != s.expectedRoot { err := s.snaps.Update(s.expectedRoot, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, verified) @@ -1531,12 +1549,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } return nil }, - func() error { - if s.snap != nil { - diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer() - } - return nil - }, } if s.pipeCommit { go commmitTrie() From 11d7595a2fc1ac4c53d4f5b6f6089f62baed630c Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Wed, 23 Mar 2022 14:11:51 +0800 Subject: [PATCH 18/28] refactor based on review comments --- core/block_validator.go | 6 ------ core/blockchain.go | 18 +----------------- core/state/snapshot/difflayer.go | 13 +++++++++++-- core/state/snapshot/disklayer.go | 4 ++++ core/state/snapshot/snapshot.go | 3 +++ core/state/state_object.go | 24 ++++++++++++++++++++---- core/state/statedb.go | 6 +----- 7 files changed, 40 insertions(+), 34 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index ccbf37d415..5e2a687430 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -140,12 +140,6 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD if err := statedb.WaitPipeVerification(); err != nil { return err } - statedb.CorrectAccountsRoot() - - diffLayer := &types.DiffLayer{} - diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = statedb.SnapToDiffLayer() - v.bc.finalisePendingDiffLayer(diffLayer) - statedb.Finalise(v.config.IsEIP158(header.Number)) //state verification pipeline - accounts root are not calculated here statedb.PopulateSnapAccountAndStorage() diff --git a/core/blockchain.go b/core/blockchain.go index 1088bb99f9..3ed56b6e7f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -226,7 +226,6 @@ type BlockChain struct { diffQueue *prque.Prque // A Priority queue to store recent diff layer diffQueueBuffer chan *types.DiffLayer diffLayerFreezerBlockLimit uint64 - diffLayerPending *types.DiffLayer //Diff layer pending to be corrected // untrusted diff layers diffMux sync.RWMutex @@ -1791,11 +1790,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. diffLayer.Receipts = receipts diffLayer.BlockHash = block.Hash() diffLayer.Number = block.NumberU64() - if state.IsPipeCommit() { - bc.diffLayerPending = diffLayer - } else { - bc.cacheDiffLayer(diffLayer) - } + bc.cacheDiffLayer(diffLayer) } wg.Wait() @@ -2771,17 +2766,6 @@ func (bc *BlockChain) pruneDiffLayer() { } } -// finalisePendingDiffLayer finalises the pending diff layer and cache it in pipecommit mode -func (bc *BlockChain) finalisePendingDiffLayer(diffLayer *types.DiffLayer) { - if bc.diffLayerPending != nil { - diffLayer.Receipts = bc.diffLayerPending.Receipts - diffLayer.BlockHash = bc.diffLayerPending.BlockHash - diffLayer.Number = bc.diffLayerPending.Number - bc.cacheDiffLayer(diffLayer) - bc.diffLayerPending = nil - } -} - // Process received diff layers func (bc *BlockChain) HandleDiffLayer(diffLayer *types.DiffLayer, pid string, fulfilled bool) error { // Basic check diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 6d5384f053..9f9f6e7c54 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -118,8 +118,9 @@ 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) - verifiedCh chan struct{} // the difflayer is verified when verifiedCh is nil or closed - valid bool // mark the difflayer is valid or not. + verifiedCh chan struct{} // the difflayer is verified when verifiedCh is nil or closed + valid bool // mark the difflayer is valid or not. + accountCorrected bool // mark the accountData has been corrected ort not diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer @@ -293,6 +294,14 @@ func (dl *diffLayer) CorrectAccounts(accounts map[common.Hash][]byte) { defer dl.lock.Unlock() dl.accountData = accounts + dl.accountCorrected = true +} + +func (dl *diffLayer) AccountsCorrected() bool { + dl.lock.RLock() + defer dl.lock.RUnlock() + + return dl.accountCorrected } // Parent returns the subsequent layer of a diff layer. diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index d4f4dc80f7..2aea224425 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -62,6 +62,10 @@ func (dl *diskLayer) Verified() bool { func (dl *diskLayer) CorrectAccounts(map[common.Hash][]byte) { } +func (dl *diskLayer) AccountsCorrected() bool { + return true +} + // Parent always returns nil as there's no layer below the disk. func (dl *diskLayer) Parent() snapshot { return nil diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index baadd5c1a6..daefca8143 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -113,6 +113,9 @@ type Snapshot interface { // CorrectAccounts updates account data for storing the correct data during pipecommit CorrectAccounts(map[common.Hash][]byte) + // AccountsCorrected checks whether the account data has been corrected during pipecommit + AccountsCorrected() bool + // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. Account(hash common.Hash) (*Account, error) diff --git a/core/state/state_object.go b/core/state/state_object.go index 298f4305ba..3a16ef60cd 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -63,10 +63,11 @@ func (s Storage) Copy() Storage { // Account values can be accessed and modified through the object. // Finally, call CommitTrie to write the modified storage trie into a database. type StateObject struct { - address common.Address - addrHash common.Hash // hash of ethereum address of the account - data Account - db *StateDB + address common.Address + addrHash common.Hash // hash of ethereum address of the account + data Account + db *StateDB + rootCorrected bool // To indicate whether the root has been corrected in pipecommit mode // DB error. // State objects are used by the consensus core and VM which are @@ -320,6 +321,21 @@ func (s *StateObject) finalise(prefetch bool) { slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure } } + + // The account root need to be updated before prefetch, otherwise the account root is empty + if s.db.pipeCommit { + if s.data.Root == emptyRoot && !s.rootCorrected { + if s.db.snap.AccountsCorrected() { + if acc, err := s.db.snap.Account(crypto.HashData(s.db.hasher, s.address.Bytes())); err == nil { + if acc != nil && len(acc.Root) != 0 { + s.data.Root = common.BytesToHash(acc.Root) + s.rootCorrected = true + } + } + } + } + } + if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot { s.db.prefetcher.prefetch(s.data.Root, slotsToPrefetch, s.addrHash) } diff --git a/core/state/statedb.go b/core/state/statedb.go index c2bb6e72ff..16afd9f5d6 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1030,7 +1030,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { //CorrectAccountsRoot will fix account roots in pipecommit mode func (s *StateDB) CorrectAccountsRoot() { - addressesToPrefetch := make([][]byte, 0, len(s.stateObjectsPending)) for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; !obj.deleted { if acc, err := s.snap.Account(crypto.HashData(s.hasher, obj.address.Bytes())); err == nil { @@ -1039,10 +1038,6 @@ func (s *StateDB) CorrectAccountsRoot() { } } } - addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) - } - if s.prefetcher != nil && len(addressesToPrefetch) > 0 { - s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch, emptyAddr) } } @@ -1389,6 +1384,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { + panic(fmt.Sprintf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot)) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } From 8ac2cc03d745fc772d83d5b5831148b607d249d6 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Wed, 23 Mar 2022 14:54:24 +0800 Subject: [PATCH 19/28] remove unnecessary code --- core/state/statedb.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 16afd9f5d6..8390294885 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1384,7 +1384,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { - panic(fmt.Sprintf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot)) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } From 31e4243128951f503f89b248caef051832389e36 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Wed, 23 Mar 2022 16:13:17 +0800 Subject: [PATCH 20/28] remove unnecessary code --- core/state/statedb.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 8390294885..13c9f59b0d 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1028,19 +1028,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.StateIntermediateRoot() } -//CorrectAccountsRoot will fix account roots in pipecommit mode -func (s *StateDB) CorrectAccountsRoot() { - for addr := range s.stateObjectsPending { - if obj := s.stateObjects[addr]; !obj.deleted { - if acc, err := s.snap.Account(crypto.HashData(s.hasher, obj.address.Bytes())); err == nil { - if acc != nil && len(acc.Root) != 0 { - obj.data.Root = common.BytesToHash(acc.Root) - } - } - } - } -} - //PopulateSnapAccountAndStorage tries to populate required accounts and storages for pipecommit func (s *StateDB) PopulateSnapAccountAndStorage() { for addr := range s.stateObjectsPending { From 5f149666678df40b4313f693d9578023b915ea4d Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Wed, 23 Mar 2022 18:28:10 +0800 Subject: [PATCH 21/28] refactor based on review comments --- core/state/statedb.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 13c9f59b0d..552657ecf8 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1033,20 +1033,24 @@ func (s *StateDB) PopulateSnapAccountAndStorage() { for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; !obj.deleted { if s.snap != nil && !obj.deleted { - s.populateSnapStorage(obj) - s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, emptyRoot, obj.data.CodeHash) + root := obj.data.Root + storageChanged := s.populateSnapStorage(obj) + if storageChanged { + root = emptyRoot + } + s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, root, obj.data.CodeHash) } } } } -//populateSnapStorage tries to populate required storages for pipecommit -func (s *StateDB) populateSnapStorage(obj *StateObject) { +//populateSnapStorage tries to populate required storages for pipecommit, and returns a flag to indicate whether the storage root changed or not +func (s *StateDB) populateSnapStorage(obj *StateObject) bool { for key, value := range obj.dirtyStorage { obj.pendingStorage[key] = value } if len(obj.pendingStorage) == 0 { - return + return false } var storage map[string][]byte for key, value := range obj.pendingStorage { @@ -1067,6 +1071,7 @@ func (s *StateDB) populateSnapStorage(obj *StateObject) { storage[string(key[:])] = v // v will be nil if value is 0x00 } } + return true } func (s *StateDB) AccountsIntermediateRoot() { From 81e7b60fe9bf58802d54e8433861b7801cd8b1d5 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Thu, 24 Mar 2022 22:40:09 +0800 Subject: [PATCH 22/28] change based on comments --- core/block_validator.go | 1 + core/state/state_object.go | 14 +++++--------- core/state/statedb.go | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index 5e2a687430..78bff8db6b 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -140,6 +140,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD if err := statedb.WaitPipeVerification(); err != nil { return err } + statedb.CorrectAccountsRoot() statedb.Finalise(v.config.IsEIP158(header.Number)) //state verification pipeline - accounts root are not calculated here statedb.PopulateSnapAccountAndStorage() diff --git a/core/state/state_object.go b/core/state/state_object.go index 3a16ef60cd..19513a29cd 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -323,15 +323,11 @@ func (s *StateObject) finalise(prefetch bool) { } // The account root need to be updated before prefetch, otherwise the account root is empty - if s.db.pipeCommit { - if s.data.Root == emptyRoot && !s.rootCorrected { - if s.db.snap.AccountsCorrected() { - if acc, err := s.db.snap.Account(crypto.HashData(s.db.hasher, s.address.Bytes())); err == nil { - if acc != nil && len(acc.Root) != 0 { - s.data.Root = common.BytesToHash(acc.Root) - s.rootCorrected = true - } - } + if s.db.pipeCommit && s.data.Root == emptyRoot && !s.rootCorrected && s.db.snap.AccountsCorrected() { + if acc, err := s.db.snap.Account(crypto.HashData(s.db.hasher, s.address.Bytes())); err == nil { + if acc != nil && len(acc.Root) != 0 { + s.data.Root = common.BytesToHash(acc.Root) + s.rootCorrected = true } } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 552657ecf8..75304a926f 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1028,6 +1028,19 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.StateIntermediateRoot() } +//CorrectAccountsRoot will fix account roots in pipecommit mode +func (s *StateDB) CorrectAccountsRoot() { + for addr := range s.stateObjectsPending { + if obj := s.stateObjects[addr]; !obj.deleted { + if acc, err := s.snap.Account(crypto.HashData(s.hasher, obj.address.Bytes())); err == nil { + if acc != nil && len(acc.Root) != 0 { + obj.data.Root = common.BytesToHash(acc.Root) + } + } + } + } +} + //PopulateSnapAccountAndStorage tries to populate required accounts and storages for pipecommit func (s *StateDB) PopulateSnapAccountAndStorage() { for addr := range s.stateObjectsPending { @@ -1376,6 +1389,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er } if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { + log.Error("Invalid merkle root", "remote", s.expectedRoot, "local", s.stateRoot) return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) } From 427d1bf9eb1e62287a49c88d2da2b9e258d6f9b5 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 25 Mar 2022 14:37:36 +0800 Subject: [PATCH 23/28] refactor --- core/state/snapshot/difflayer.go | 18 ++++++++++++++++++ core/state/snapshot/disklayer.go | 6 ++++++ core/state/snapshot/snapshot.go | 4 ++++ core/state/statedb.go | 10 +++++----- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 9f9f6e7c54..d2b1b2778b 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -332,6 +332,24 @@ func (dl *diffLayer) Account(hash common.Hash) (*Account, error) { return account, nil } +// Accounts directly retrieves all accounts in current snapshot in +// the snapshot slim data format. +func (dl *diffLayer) Accounts() (map[common.Hash]*Account, error) { + dl.lock.RLock() + defer dl.lock.RUnlock() + + accounts := make(map[common.Hash]*Account, len(dl.accountData)) + for hash, data := range dl.accountData { + account := new(Account) + if err := rlp.DecodeBytes(data, account); err != nil { + return nil, err + } + accounts[hash] = account + } + + return accounts, nil +} + // AccountRLP directly retrieves the account RLP associated with a particular // hash in the snapshot slim data format. // diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index 2aea224425..6d46496a71 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -80,6 +80,12 @@ func (dl *diskLayer) Stale() bool { return dl.stale } +// Accounts directly retrieves all accounts in current snapshot in +// the snapshot slim data format. +func (dl *diskLayer) Accounts() (map[common.Hash]*Account, error) { + return nil, nil +} + // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. func (dl *diskLayer) Account(hash common.Hash) (*Account, error) { diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index daefca8143..b022c5cc80 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -120,6 +120,10 @@ type Snapshot interface { // the snapshot slim data format. Account(hash common.Hash) (*Account, error) + // Accounts directly retrieves all accounts in current snapshot in + // the snapshot slim data format. + Accounts() (map[common.Hash]*Account, error) + // AccountRLP directly retrieves the account RLP associated with a particular // hash in the snapshot slim data format. AccountRLP(hash common.Hash) ([]byte, error) diff --git a/core/state/statedb.go b/core/state/statedb.go index 75304a926f..8ad0770456 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1030,11 +1030,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { //CorrectAccountsRoot will fix account roots in pipecommit mode func (s *StateDB) CorrectAccountsRoot() { - for addr := range s.stateObjectsPending { - if obj := s.stateObjects[addr]; !obj.deleted { - if acc, err := s.snap.Account(crypto.HashData(s.hasher, obj.address.Bytes())); err == nil { - if acc != nil && len(acc.Root) != 0 { - obj.data.Root = common.BytesToHash(acc.Root) + if accounts, err := s.snap.Accounts(); err == nil { + for _, obj := range s.stateObjects { + if !obj.deleted && !obj.rootCorrected { + if account, exist := accounts[crypto.Keccak256Hash(obj.address[:])]; exist && len(account.Root) != 0 { + obj.data.Root = common.BytesToHash(account.Root) } } } From f4514783e76d85d93f8924e845fcc7b02ea92ab4 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 25 Mar 2022 16:13:55 +0800 Subject: [PATCH 24/28] uew dummyRoot to replace emptyRoot --- core/state/state_object.go | 4 ++-- core/state/statedb.go | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/state/state_object.go b/core/state/state_object.go index 19513a29cd..88c0b8d614 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -323,7 +323,7 @@ func (s *StateObject) finalise(prefetch bool) { } // The account root need to be updated before prefetch, otherwise the account root is empty - if s.db.pipeCommit && s.data.Root == emptyRoot && !s.rootCorrected && s.db.snap.AccountsCorrected() { + if s.db.pipeCommit && s.data.Root == dummyRoot && !s.rootCorrected && s.db.snap.AccountsCorrected() { if acc, err := s.db.snap.Account(crypto.HashData(s.db.hasher, s.address.Bytes())); err == nil { if acc != nil && len(acc.Root) != 0 { s.data.Root = common.BytesToHash(acc.Root) @@ -332,7 +332,7 @@ func (s *StateObject) finalise(prefetch bool) { } } - if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot { + if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot && s.data.Root != dummyRoot { s.db.prefetcher.prefetch(s.data.Root, slotsToPrefetch, s.addrHash) } if len(s.dirtyStorage) > 0 { diff --git a/core/state/statedb.go b/core/state/statedb.go index 8ad0770456..fdb536c752 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -18,6 +18,7 @@ package state import ( + "encoding/hex" "errors" "fmt" "math/big" @@ -53,6 +54,9 @@ var ( // emptyRoot is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + // dummyRoot is the dummy account root before corrected in pipecommit sync mode. + dummyRoot = common.HexToHash(hex.EncodeToString([]byte("dummy_account_root"))) + emptyAddr = crypto.Keccak256Hash(common.Address{}.Bytes()) ) @@ -1032,7 +1036,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { func (s *StateDB) CorrectAccountsRoot() { if accounts, err := s.snap.Accounts(); err == nil { for _, obj := range s.stateObjects { - if !obj.deleted && !obj.rootCorrected { + if !obj.deleted && !obj.rootCorrected && obj.data.Root == dummyRoot { if account, exist := accounts[crypto.Keccak256Hash(obj.address[:])]; exist && len(account.Root) != 0 { obj.data.Root = common.BytesToHash(account.Root) } @@ -1049,7 +1053,7 @@ func (s *StateDB) PopulateSnapAccountAndStorage() { root := obj.data.Root storageChanged := s.populateSnapStorage(obj) if storageChanged { - root = emptyRoot + root = dummyRoot } s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, root, obj.data.CodeHash) } From c7b23756b7be99be64341507d5da0e1fd8ff1c55 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 25 Mar 2022 16:20:27 +0800 Subject: [PATCH 25/28] add nil check --- core/state/statedb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index fdb536c752..33a930939f 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1034,7 +1034,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { //CorrectAccountsRoot will fix account roots in pipecommit mode func (s *StateDB) CorrectAccountsRoot() { - if accounts, err := s.snap.Accounts(); err == nil { + if accounts, err := s.snap.Accounts(); err == nil && accounts != nil { for _, obj := range s.stateObjects { if !obj.deleted && !obj.rootCorrected && obj.data.Root == dummyRoot { if account, exist := accounts[crypto.Keccak256Hash(obj.address[:])]; exist && len(account.Root) != 0 { From d5fcb4dbd10eb9b085258eaf6f15649743d6cb79 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 25 Mar 2022 16:31:43 +0800 Subject: [PATCH 26/28] add comments --- core/state/snapshot/snapshot.go | 1 + core/state/statedb.go | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index b022c5cc80..7ad4bcc91b 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -250,6 +250,7 @@ func (t *Tree) waitBuild() { } } +// Layers returns the number of layers func (t *Tree) Layers() int { return len(t.layers) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 33a930939f..32ceb59da5 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -18,7 +18,6 @@ package state import ( - "encoding/hex" "errors" "fmt" "math/big" @@ -54,8 +53,9 @@ var ( // emptyRoot is the known root hash of an empty trie. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - // dummyRoot is the dummy account root before corrected in pipecommit sync mode. - dummyRoot = common.HexToHash(hex.EncodeToString([]byte("dummy_account_root"))) + // dummyRoot is the dummy account root before corrected in pipecommit sync mode, + // the value is 542e5fc2709de84248e9bce43a9c0c8943a608029001360f8ab55bf113b23d28 + dummyRoot = crypto.Keccak256Hash([]byte("dummy_account_root")) emptyAddr = crypto.Keccak256Hash(common.Address{}.Bytes()) ) @@ -148,6 +148,8 @@ type StateDB struct { // New creates a new state from a given trie. func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) { + fmt.Printf("empty root = %x\n", emptyRoot) + fmt.Printf("dummy root = %x\n", dummyRoot) return newStateDB(root, db, snaps) } From 93b685671aeeb5416e75f68fca0027368ad30452 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Fri, 25 Mar 2022 16:40:03 +0800 Subject: [PATCH 27/28] remove unneeded codes --- core/state/statedb.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 32ceb59da5..407f1ace5d 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -148,8 +148,6 @@ type StateDB struct { // New creates a new state from a given trie. func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) { - fmt.Printf("empty root = %x\n", emptyRoot) - fmt.Printf("dummy root = %x\n", dummyRoot) return newStateDB(root, db, snaps) } From 281c42e02cab525f387aa3736ae4f5f6ec510b18 Mon Sep 17 00:00:00 2001 From: forcodedancing Date: Mon, 28 Mar 2022 16:21:30 +0800 Subject: [PATCH 28/28] format comments --- core/block_validator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/block_validator.go b/core/block_validator.go index 78bff8db6b..c6a35f1fdf 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -142,7 +142,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } statedb.CorrectAccountsRoot() statedb.Finalise(v.config.IsEIP158(header.Number)) - //state verification pipeline - accounts root are not calculated here + // State verification pipeline - accounts root are not calculated here, just populate needed fields for process statedb.PopulateSnapAccountAndStorage() return nil })