From a0109f169ba71bc8d253fd7af0d6b8c89cdc694c Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Fri, 20 Oct 2023 14:43:08 +0800 Subject: [PATCH 1/9] trie/pathdb: fix clean cache low hit rate issue; trie/trie: reuse node cache, prevent resolve again; --- core/blockchain.go | 5 +++++ core/state/trie_prefetcher.go | 17 ++++++----------- trie/tracer.go | 21 +++++++++++++++++++++ trie/trie.go | 10 ++++++++++ trie/triedb/pathdb/disklayer.go | 4 +++- 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 5e2c7fe7a8..b45a17cb1d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -182,6 +182,11 @@ func (c *CacheConfig) TriedbConfig() *trie.Config { CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, DirtyCacheSize: c.TrieDirtyLimit * 1024 * 1024, } + if config.EnableStateExpiry { + // state expiry need more cache for save epoch meta, but not exceed maxBuffer + config.PathDB.CleanCacheSize = 2 * config.PathDB.CleanCacheSize + config.PathDB.DirtyCacheSize = 2 * config.PathDB.DirtyCacheSize + } } return config } diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index 62c48bc45b..91307bd917 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -550,8 +550,6 @@ func (sf *subfetcher) loop() { sf.tasks = nil sf.lock.Unlock() - reviveKeys := make([]common.Hash, 0, len(tasks)) - revivePaths := make([][]byte, 0, len(tasks)) // Prefetch any tasks until the loop is interrupted for i, task := range tasks { select { @@ -577,9 +575,13 @@ func (sf *subfetcher) loop() { _, err := sf.trie.GetStorage(sf.addr, task) // handle expired state if sf.expiryMeta.enableStateExpiry { + // TODO(0xbundler): revert to single fetch, because tasks is a channel if exErr, match := err.(*trie2.ExpiredNodeError); match { - reviveKeys = append(reviveKeys, common.BytesToHash(task)) - revivePaths = append(revivePaths, exErr.Path) + key := common.BytesToHash(task) + _, err = fetchExpiredStorageFromRemote(sf.expiryMeta, sf.addr, sf.root, sf.trie, exErr.Path, key) + if err != nil { + log.Error("subfetcher fetchExpiredStorageFromRemote err", "addr", sf.addr, "path", exErr.Path, "err", err) + } } } } @@ -589,13 +591,6 @@ func (sf *subfetcher) loop() { } } - if len(reviveKeys) != 0 { - _, err = batchFetchExpiredFromRemote(sf.expiryMeta, sf.addr, sf.root, sf.trie, revivePaths, reviveKeys) - if err != nil { - log.Error("subfetcher batchFetchExpiredFromRemote err", "addr", sf.addr, "state", sf.state, "revivePaths", revivePaths, "reviveKeys", reviveKeys, "err", err) - } - } - case ch := <-sf.copy: // Somebody wants a copy of the current trie, grant them ch <- sf.db.CopyTrie(sf.trie) diff --git a/trie/tracer.go b/trie/tracer.go index d278fec39f..fc37f70698 100644 --- a/trie/tracer.go +++ b/trie/tracer.go @@ -46,6 +46,7 @@ type tracer struct { deleteEpochMetas map[string]struct{} // record for epoch meta accessList map[string][]byte accessEpochMetaList map[string][]byte + tagEpochMeta bool } // newTracer initializes the tracer for capturing trie changes. @@ -59,6 +60,10 @@ func newTracer() *tracer { } } +func (t *tracer) enableTagEpochMeta() { + t.tagEpochMeta = true +} + // onRead tracks the newly loaded trie node and caches the rlp-encoded // blob internally. Don't change the value outside of function since // it's not deep-copied. @@ -68,6 +73,9 @@ func (t *tracer) onRead(path []byte, val []byte) { // onReadEpochMeta tracks the newly loaded trie epoch meta func (t *tracer) onReadEpochMeta(path []byte, val []byte) { + if !t.tagEpochMeta { + return + } t.accessEpochMetaList[string(path)] = val } @@ -84,6 +92,9 @@ func (t *tracer) onInsert(path []byte) { // onExpandToBranchNode tracks the newly inserted trie branch node. func (t *tracer) onExpandToBranchNode(path []byte) { + if !t.tagEpochMeta { + return + } if _, present := t.deleteEpochMetas[string(path)]; present { delete(t.deleteEpochMetas, string(path)) } @@ -102,6 +113,9 @@ func (t *tracer) onDelete(path []byte) { // onDeleteBranchNode tracks the newly deleted trie branch node. func (t *tracer) onDeleteBranchNode(path []byte) { + if !t.tagEpochMeta { + return + } t.deleteEpochMetas[string(path)] = struct{}{} } @@ -144,6 +158,7 @@ func (t *tracer) copy() *tracer { deleteEpochMetas: deleteBranchNodes, accessList: accessList, accessEpochMetaList: accessEpochMetaList, + tagEpochMeta: t.tagEpochMeta, } } @@ -176,6 +191,12 @@ func (t *tracer) deletedBranchNodes() []string { return paths } +// cached check if cache the node. +func (t *tracer) cached(path []byte) ([]byte, bool) { + val, ok := t.accessList[string(path)] + return val, ok +} + // checkNodeChanged check if change for node. func (t *tracer) checkNodeChanged(path []byte, blob []byte) bool { val, ok := t.accessList[string(path)] diff --git a/trie/trie.go b/trie/trie.go index be5462cbe8..f19eb37d0e 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -110,6 +110,9 @@ func New(id *ID, db *Database) (*Trie, error) { } // resolve root epoch if trie.enableExpiry { + if trie.enableMetaDB { + trie.tracer.enableTagEpochMeta() + } if id.Root != (common.Hash{}) && id.Root != types.EmptyRootHash { trie.root = hashNode(id.Root[:]) meta, err := trie.resolveAccountMetaAndTrack() @@ -1028,6 +1031,13 @@ func (t *Trie) resolve(n node, prefix []byte, epoch types.StateEpoch) (node, err // node's original value. The rlp-encoded blob is preferred to be loaded from // database because it's easy to decode node while complex to encode node to blob. func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) { + if t.enableExpiry { + // when meet expired, the trie will skip the resolve path, but cache in tracer + blob, ok := t.tracer.cached(prefix) + if ok { + return mustDecodeNode(n, blob), nil + } + } blob, err := t.reader.node(prefix, common.BytesToHash(n)) if err != nil { return nil, err diff --git a/trie/triedb/pathdb/disklayer.go b/trie/triedb/pathdb/disklayer.go index af5b235ed9..6bb2e05616 100644 --- a/trie/triedb/pathdb/disklayer.go +++ b/trie/triedb/pathdb/disklayer.go @@ -19,6 +19,7 @@ package pathdb import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie/epochmeta" "sync" @@ -127,7 +128,8 @@ func (dl *diskLayer) Node(owner common.Hash, path []byte, hash common.Hash) ([]b h := newHasher() defer h.release() - got := h.hash(blob) + raw, _ := types.DecodeTypedTrieNodeRaw(blob) + got := h.hash(raw) if epochmeta.IsEpochMetaPath(path) || got == hash { cleanHitMeter.Mark(1) cleanReadMeter.Mark(int64(len(blob))) From a4775650bf0aefe6dac79cfb8fa46e52e0e5b8e7 Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Sun, 22 Oct 2023 14:05:48 +0800 Subject: [PATCH 2/9] prune: add more concurrent logics; --- cmd/geth/snapshot.go | 2 + cmd/utils/flags.go | 6 ++ core/state/pruner/pruner.go | 56 +++++------- core/state/state_object.go | 5 +- trie/trie.go | 174 ++++++++++++++++++++++++++++-------- 5 files changed, 168 insertions(+), 75 deletions(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 946173eaf6..91675e467d 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -62,6 +62,7 @@ var ( utils.BloomFilterSizeFlag, utils.TriesInMemoryFlag, utils.StateExpiryEnableFlag, + utils.StateExpiryMaxThreadFlag, configFileFlag, }, utils.NetworkFlags, utils.DatabasePathFlags), Description: ` @@ -450,6 +451,7 @@ func pruneState(ctx *cli.Context) error { EnableStateExpiry: cfg.Eth.StateExpiryCfg.EnableExpiry(), ChainConfig: chainConfig, CacheConfig: cacheConfig, + MaxExpireThreads: ctx.Uint64(utils.StateExpiryMaxThreadFlag.Name), } pruner, err := pruner.NewPruner(chaindb, prunerconfig, ctx.Uint64(utils.TriesInMemoryFlag.Name)) if err != nil { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5f61cd5566..e39252d20f 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1152,6 +1152,12 @@ var ( Usage: "if enable local revive", Category: flags.StateExpiryCategory, } + StateExpiryMaxThreadFlag = &cli.Uint64Flag{ + Name: "state-expiry.maxthread", + Usage: "set state expiry maxthread in prune", + Value: 10000, + Category: flags.StateExpiryCategory, + } ) func init() { diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index caeb1a2460..b2f7f02faa 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -30,7 +30,6 @@ import ( "path/filepath" "strings" "sync" - "sync/atomic" "time" "github.com/prometheus/tsdb/fileutil" @@ -66,6 +65,8 @@ const ( rangeCompactionThreshold = 100000 FixedPrefixAndAddrSize = 33 + + defaultReportDuration = 60 * time.Second ) // Config includes all the configurations for pruning. @@ -75,6 +76,7 @@ type Config struct { EnableStateExpiry bool ChainConfig *params.ChainConfig CacheConfig *core.CacheConfig + MaxExpireThreads uint64 } // Pruner is an offline tool to prune the stale state with the @@ -114,7 +116,7 @@ func NewPruner(db ethdb.Database, config Config, triesInMemory uint64) (*Pruner, } // Offline pruning is only supported in legacy hash based scheme. triedb := trie.NewDatabase(db, trie.HashDefaults) - log.Info("ChainConfig", "headBlock", headBlock.NumberU64(), "config", config.ChainConfig) + log.Info("ChainConfig", "headBlock", headBlock.NumberU64(), "config", config) snapconfig := snapshot.Config{ CacheSize: 256, @@ -201,7 +203,7 @@ func pruneAll(maindb ethdb.Database, g *core.Genesis) error { ) eta = time.Duration(left/speed) * time.Millisecond } - if time.Since(logged) > 8*time.Second { + if time.Since(logged) > defaultReportDuration { log.Info("Pruning state data", "nodes", count, "size", size, "elapsed", common.PrettyDuration(time.Since(pstart)), "eta", common.PrettyDuration(eta)) logged = time.Now() @@ -309,7 +311,7 @@ func prune(snaptree *snapshot.Tree, root common.Hash, maindb ethdb.Database, sta ) eta = time.Duration(left/speed) * time.Millisecond } - if time.Since(logged) > 8*time.Second { + if time.Since(logged) > defaultReportDuration { log.Info("Pruning state data", "nodes", count, "size", size, "elapsed", common.PrettyDuration(time.Since(pstart)), "eta", common.PrettyDuration(eta)) logged = time.Now() @@ -733,7 +735,7 @@ func (p *Pruner) ExpiredPrune(height *big.Int, root common.Hash) error { tasksWG.Add(2) go func() { defer tasksWG.Done() - rets[0] = asyncScanExpiredInTrie(trieDB, root, epoch, scanExpiredTrieCh, pruneExpiredInDiskCh) + rets[0] = asyncScanExpiredInTrie(trieDB, root, epoch, scanExpiredTrieCh, pruneExpiredInDiskCh, p.config.MaxExpireThreads) }() go func() { defer tasksWG.Done() @@ -783,7 +785,7 @@ func (p *Pruner) unExpiredBloomTag(trieDB *trie.Database, epoch types.StateEpoch tasksWG.Add(2) go func() { defer tasksWG.Done() - rets[0] = asyncScanUnExpiredInTrie(trieDB, root, epoch, scanUnExpiredTrieCh, tagUnExpiredInBloomCh) + rets[0] = asyncScanUnExpiredInTrie(trieDB, root, epoch, scanUnExpiredTrieCh, tagUnExpiredInBloomCh, p.config.MaxExpireThreads) }() go func() { defer tasksWG.Done() @@ -804,7 +806,7 @@ func asyncTagUnExpiredInBloom(tagUnExpiredInBloomCh chan *trie.NodeInfo, bloom * for info := range tagUnExpiredInBloomCh { trieCount++ bloom.Add(stateBloomHasher(info.Hash[:])) - if time.Since(logged) > 8*time.Second { + if time.Since(logged) > defaultReportDuration { log.Info("Tag unexpired states in bloom", "trieNodes", trieCount) logged = time.Now() } @@ -813,12 +815,12 @@ func asyncTagUnExpiredInBloom(tagUnExpiredInBloomCh chan *trie.NodeInfo, bloom * return nil } -func asyncScanUnExpiredInTrie(db *trie.Database, stateRoot common.Hash, epoch types.StateEpoch, scanUnExpiredTrieCh chan *snapshot.ContractItem, tagUnExpiredInBloomCh chan *trie.NodeInfo) error { - var ( - trieCount atomic.Uint64 - start = time.Now() - logged = time.Now() - ) +func asyncScanUnExpiredInTrie(db *trie.Database, stateRoot common.Hash, epoch types.StateEpoch, scanUnExpiredTrieCh chan *snapshot.ContractItem, tagUnExpiredInBloomCh chan *trie.NodeInfo, maxThreads uint64) error { + defer func() { + close(tagUnExpiredInBloomCh) + }() + st := trie.NewScanTask(tagUnExpiredInBloomCh, maxThreads, false) + go st.Report(defaultReportDuration) for item := range scanUnExpiredTrieCh { log.Info("start scan trie unexpired state", "addrHash", item.Addr, "root", item.Root) tr, err := trie.New(&trie.ID{ @@ -831,32 +833,24 @@ func asyncScanUnExpiredInTrie(db *trie.Database, stateRoot common.Hash, epoch ty return err } tr.SetEpoch(epoch) - if err = tr.ScanForPrune(tagUnExpiredInBloomCh, &trieCount, false); err != nil { + if err = tr.ScanForPrune(st); err != nil { log.Error("asyncScanExpiredInTrie, ScanForPrune err", "id", item, "err", err) return err } - if time.Since(logged) > 8*time.Second { - log.Info("Scan unexpired states", "trieNodes", trieCount.Load()) - logged = time.Now() - } } - log.Info("Scan unexpired states", "trieNodes", trieCount.Load(), "elapsed", common.PrettyDuration(time.Since(start))) - close(tagUnExpiredInBloomCh) + st.WaitThreads() return nil } // asyncScanExpiredInTrie prune trie expired state // here are some issues when just delete it from hash-based storage, because it's shared kv in hbss // but it's ok for pbss. -func asyncScanExpiredInTrie(db *trie.Database, stateRoot common.Hash, epoch types.StateEpoch, expireContractCh chan *snapshot.ContractItem, pruneExpiredInDisk chan *trie.NodeInfo) error { +func asyncScanExpiredInTrie(db *trie.Database, stateRoot common.Hash, epoch types.StateEpoch, expireContractCh chan *snapshot.ContractItem, pruneExpiredInDisk chan *trie.NodeInfo, maxThreads uint64) error { defer func() { close(pruneExpiredInDisk) }() - var ( - trieCount atomic.Uint64 - start = time.Now() - logged = time.Now() - ) + st := trie.NewScanTask(pruneExpiredInDisk, maxThreads, true) + go st.Report(defaultReportDuration) for item := range expireContractCh { log.Debug("start scan trie expired state", "addrHash", item.Addr, "root", item.Root) tr, err := trie.New(&trie.ID{ @@ -869,16 +863,12 @@ func asyncScanExpiredInTrie(db *trie.Database, stateRoot common.Hash, epoch type return err } tr.SetEpoch(epoch) - if err = tr.ScanForPrune(pruneExpiredInDisk, &trieCount, true); err != nil { + if err = tr.ScanForPrune(st); err != nil { log.Error("asyncScanExpiredInTrie, ScanForPrune err", "id", item, "err", err) return err } - if time.Since(logged) > 8*time.Second { - log.Info("Scan unexpired states", "trieNodes", trieCount.Load()) - logged = time.Now() - } } - log.Info("Scan unexpired states", "trieNodes", trieCount.Load(), "elapsed", common.PrettyDuration(time.Since(start))) + st.WaitThreads() return nil } @@ -953,7 +943,7 @@ func asyncPruneExpiredStorageInDisk(diskdb ethdb.Database, pruneExpiredInDisk ch } batch.Reset() } - if time.Since(logged) > 8*time.Second { + if time.Since(logged) > defaultReportDuration { log.Info("Pruning expired states", "items", itemCount, "trieNodes", trieCount, "trieSize", trieSize, "SnapKV", snapCount, "SnapKVSize", snapSize, "EpochMeta", epochMetaCount, "EpochMetaSize", epochMetaSize) diff --git a/core/state/state_object.go b/core/state/state_object.go index e4c39289a6..ef21b81e48 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -582,7 +582,7 @@ func (s *stateObject) updateTrie() (Trie, error) { } // reset trie as pending trie, will commit later if tr != nil { - s.trie = s.db.db.CopyTrie(tr) + s.trie = tr } } return tr, nil @@ -839,8 +839,7 @@ func (s *stateObject) futureReviveState(key common.Hash) { // TODO(0xbundler): add hash key cache later func (s *stateObject) queryFromReviveState(reviveState map[string]common.Hash, key common.Hash) (common.Hash, bool) { - khash := crypto.HashData(s.db.hasher, key[:]) - val, ok := reviveState[string(khash[:])] + val, ok := reviveState[string(crypto.Keccak256(key[:]))] return val, ok } diff --git a/trie/trie.go b/trie/trie.go index f19eb37d0e..b5889b67b6 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -27,7 +27,10 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/trie/epochmeta" "github.com/ethereum/go-ethereum/trie/trienode" + "runtime" + "sync" "sync/atomic" + "time" ) var ( @@ -1523,8 +1526,81 @@ type NodeInfo struct { IsBranch bool } +type ScanTask struct { + itemCh chan *NodeInfo + unexpiredStat *atomic.Uint64 + expiredStat *atomic.Uint64 + record *atomic.Uint64 + findExpired bool + maxThreads uint64 + wg *sync.WaitGroup + reportDone chan struct{} +} + +func NewScanTask(itemCh chan *NodeInfo, maxThreads uint64, findExpired bool) *ScanTask { + return &ScanTask{ + itemCh: itemCh, + unexpiredStat: &atomic.Uint64{}, + expiredStat: &atomic.Uint64{}, + record: &atomic.Uint64{}, + findExpired: findExpired, + maxThreads: maxThreads, + wg: &sync.WaitGroup{}, + reportDone: make(chan struct{}), + } +} + +func (st *ScanTask) Stat(expired bool) { + if expired { + st.expiredStat.Add(1) + } else { + st.unexpiredStat.Add(1) + } +} + +func (st *ScanTask) ExpiredStat() uint64 { + return st.expiredStat.Load() +} + +func (st *ScanTask) UnexpiredStat() uint64 { + return st.unexpiredStat.Load() +} + +func (st *ScanTask) WaitThreads() { + st.wg.Wait() + close(st.reportDone) +} + +func (st *ScanTask) Report(d time.Duration) { + start := time.Now() + timer := time.NewTimer(d) + defer timer.Stop() + for { + select { + case <-timer.C: + log.Info("Scan trie stats", "unexpired", st.UnexpiredStat(), "expired", st.ExpiredStat(), "go routine", runtime.NumGoroutine(), "elapsed", common.PrettyDuration(time.Since(start))) + timer.Reset(d) + case <-st.reportDone: + log.Info("Scan trie done", "unexpired", st.UnexpiredStat(), "expired", st.ExpiredStat(), "elapsed", common.PrettyDuration(time.Since(start))) + return + } + } +} + +func (st *ScanTask) moreThread() bool { + total := st.expiredStat.Load() + st.unexpiredStat.Load() + record := st.record.Load() + // every increase 10000, will add a new tread to handle other trie path + if total-record > 10000 && uint64(runtime.NumGoroutine()) < st.maxThreads { + st.record.Store(total) + return true + } + + return false +} + // ScanForPrune traverses the storage trie and prunes all expired or unexpired nodes. -func (t *Trie) ScanForPrune(itemCh chan *NodeInfo, stats *atomic.Uint64, findExpired bool) error { +func (t *Trie) ScanForPrune(st *ScanTask) error { if !t.enableExpiry { return nil @@ -1535,10 +1611,10 @@ func (t *Trie) ScanForPrune(itemCh chan *NodeInfo, stats *atomic.Uint64, findExp } err := t.findExpiredSubTree(t.root, nil, t.getRootEpoch(), func(n node, path []byte, epoch types.StateEpoch) { - if pruneErr := t.recursePruneExpiredNode(n, path, epoch, itemCh); pruneErr != nil { + if pruneErr := t.recursePruneExpiredNode(n, path, epoch, st); pruneErr != nil { log.Error("recursePruneExpiredNode err", "Path", path, "err", pruneErr) } - }, stats, itemCh, findExpired) + }, st) if err != nil { return err } @@ -1546,12 +1622,12 @@ func (t *Trie) ScanForPrune(itemCh chan *NodeInfo, stats *atomic.Uint64, findExp return nil } -func (t *Trie) findExpiredSubTree(n node, path []byte, epoch types.StateEpoch, pruner func(n node, path []byte, epoch types.StateEpoch), stats *atomic.Uint64, itemCh chan *NodeInfo, findExpired bool) error { +func (t *Trie) findExpiredSubTree(n node, path []byte, epoch types.StateEpoch, pruner func(n node, path []byte, epoch types.StateEpoch), st *ScanTask) error { // Upon reaching expired node, it will recursively traverse downwards to all the child nodes // and collect their hashes. Then, the corresponding key-value pairs will be deleted from the // database by batches. if t.epochExpired(n, epoch) { - if findExpired { + if st.findExpired { pruner(n, path, epoch) } return nil @@ -1559,32 +1635,28 @@ func (t *Trie) findExpiredSubTree(n node, path []byte, epoch types.StateEpoch, p switch n := n.(type) { case *shortNode: - if stats != nil { - stats.Add(1) - } - if !findExpired { - itemCh <- &NodeInfo{ + st.Stat(false) + if !st.findExpired { + st.itemCh <- &NodeInfo{ Hash: common.BytesToHash(n.flags.hash), } } - err := t.findExpiredSubTree(n.Val, append(path, n.Key...), epoch, pruner, stats, itemCh, findExpired) + err := t.findExpiredSubTree(n.Val, append(path, n.Key...), epoch, pruner, st) if err != nil { return err } return nil case *fullNode: - if stats != nil { - stats.Add(1) - } - if !findExpired { - itemCh <- &NodeInfo{ + st.Stat(false) + if !st.findExpired { + st.itemCh <- &NodeInfo{ Hash: common.BytesToHash(n.flags.hash), } } var err error // Go through every child and recursively delete expired nodes for i, child := range n.Children { - err = t.findExpiredSubTree(child, append(path, byte(i)), n.GetChildEpoch(i), pruner, stats, itemCh, findExpired) + err = t.findExpiredSubTree(child, append(path, byte(i)), n.GetChildEpoch(i), pruner, st) if err != nil { return err } @@ -1592,15 +1664,24 @@ func (t *Trie) findExpiredSubTree(n node, path []byte, epoch types.StateEpoch, p return nil case hashNode: - resolve, err := t.resolveAndTrack(n, path) + resolve, err := t.resolveHash(n, path) if err != nil { return err } - if err = t.resolveEpochMetaAndTrack(resolve, epoch, path); err != nil { + if err = t.resolveEpochMeta(resolve, epoch, path); err != nil { return err } + if st.moreThread() { + st.wg.Add(1) + path := common.CopyBytes(path) + go func() { + defer st.wg.Done() + t.findExpiredSubTree(resolve, path, epoch, pruner, st) + }() + return nil + } - return t.findExpiredSubTree(resolve, path, epoch, pruner, stats, itemCh, findExpired) + return t.findExpiredSubTree(resolve, path, epoch, pruner, st) case valueNode: return nil case nil: @@ -1610,40 +1691,46 @@ func (t *Trie) findExpiredSubTree(n node, path []byte, epoch types.StateEpoch, p } } -func (t *Trie) recursePruneExpiredNode(n node, path []byte, epoch types.StateEpoch, pruneItemCh chan *NodeInfo) error { +func (t *Trie) recursePruneExpiredNode(n node, path []byte, epoch types.StateEpoch, st *ScanTask) error { switch n := n.(type) { case *shortNode: + st.Stat(true) subPath := append(path, n.Key...) key := common.Hash{} _, isLeaf := n.Val.(valueNode) if isLeaf { key = common.BytesToHash(hexToKeybytes(subPath)) } - pruneItemCh <- &NodeInfo{ - Addr: t.owner, - Hash: common.BytesToHash(n.flags.hash), - Path: renewBytes(path), - Key: key, - Epoch: epoch, - IsLeaf: isLeaf, + if st.findExpired { + st.itemCh <- &NodeInfo{ + Addr: t.owner, + Hash: common.BytesToHash(n.flags.hash), + Path: renewBytes(path), + Key: key, + Epoch: epoch, + IsLeaf: isLeaf, + } } - err := t.recursePruneExpiredNode(n.Val, subPath, epoch, pruneItemCh) + err := t.recursePruneExpiredNode(n.Val, subPath, epoch, st) if err != nil { return err } return nil case *fullNode: - pruneItemCh <- &NodeInfo{ - Addr: t.owner, - Hash: common.BytesToHash(n.flags.hash), - Path: renewBytes(path), - Epoch: epoch, - IsBranch: true, + st.Stat(true) + if st.findExpired { + st.itemCh <- &NodeInfo{ + Addr: t.owner, + Hash: common.BytesToHash(n.flags.hash), + Path: renewBytes(path), + Epoch: epoch, + IsBranch: true, + } } // recurse child, and except valueNode for i := 0; i < BranchNodeLength-1; i++ { - err := t.recursePruneExpiredNode(n.Children[i], append(path, byte(i)), n.EpochMap[i], pruneItemCh) + err := t.recursePruneExpiredNode(n.Children[i], append(path, byte(i)), n.EpochMap[i], st) if err != nil { return err } @@ -1651,7 +1738,7 @@ func (t *Trie) recursePruneExpiredNode(n node, path []byte, epoch types.StateEpo return nil case hashNode: // hashNode is a index of trie node storage, need not prune. - rn, err := t.resolveAndTrack(n, path) + rn, err := t.resolveHash(n, path) // if touch miss node, just skip if _, ok := err.(*MissingNodeError); ok { return nil @@ -1659,10 +1746,19 @@ func (t *Trie) recursePruneExpiredNode(n node, path []byte, epoch types.StateEpo if err != nil { return err } - if err = t.resolveEpochMetaAndTrack(rn, epoch, path); err != nil { + if err = t.resolveEpochMeta(rn, epoch, path); err != nil { return err } - return t.recursePruneExpiredNode(rn, path, epoch, pruneItemCh) + if st.moreThread() { + st.wg.Add(1) + path := common.CopyBytes(path) + go func() { + defer st.wg.Done() + t.recursePruneExpiredNode(rn, path, epoch, st) + }() + return nil + } + return t.recursePruneExpiredNode(rn, path, epoch, st) case valueNode: // value node is not a single storage uint, so pass to prune. return nil From 51c48f2f18f26c2b3163abab269c6033eebb8258 Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:00:14 +0800 Subject: [PATCH 3/9] trie/typednode: opt encode/decode performance; pruner: opt concurrent logic; state/state_object: add access state in prefetch; --- core/blockchain.go | 1 - core/state/pruner/pruner.go | 20 +++++++--- core/state/state_expiry.go | 10 ++--- core/state/state_object.go | 31 +++++++++++----- core/state/statedb.go | 2 +- core/types/typed_trie_node.go | 67 ++++++++++++++++++++++++++++------ trie/committer.go | 6 ++- trie/node_enc.go | 26 +++++++++---- trie/trie.go | 69 +++++++++++++++++++++-------------- trie/typed_trie_node_test.go | 61 ++++++++++++++++++++----------- 10 files changed, 202 insertions(+), 91 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index b45a17cb1d..219065930e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -185,7 +185,6 @@ func (c *CacheConfig) TriedbConfig() *trie.Config { if config.EnableStateExpiry { // state expiry need more cache for save epoch meta, but not exceed maxBuffer config.PathDB.CleanCacheSize = 2 * config.PathDB.CleanCacheSize - config.PathDB.DirtyCacheSize = 2 * config.PathDB.DirtyCacheSize } } return config diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index b2f7f02faa..bf91553c3a 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -62,11 +62,13 @@ const ( // rangeCompactionThreshold is the minimal deleted entry number for // triggering range compaction. It's a quite arbitrary number but just // to avoid triggering range compaction because of small deletion. - rangeCompactionThreshold = 100000 + rangeCompactionThreshold = 1000000 FixedPrefixAndAddrSize = 33 defaultReportDuration = 60 * time.Second + + defaultChannelSize = 200000 ) // Config includes all the configurations for pruning. @@ -727,8 +729,8 @@ func (p *Pruner) ExpiredPrune(height *big.Int, root common.Hash) error { } var ( - scanExpiredTrieCh = make(chan *snapshot.ContractItem, 100000) - pruneExpiredInDiskCh = make(chan *trie.NodeInfo, 100000) + scanExpiredTrieCh = make(chan *snapshot.ContractItem, defaultChannelSize) + pruneExpiredInDiskCh = make(chan *trie.NodeInfo, defaultChannelSize) rets = make([]error, 3) tasksWG sync.WaitGroup ) @@ -771,8 +773,8 @@ func (p *Pruner) ExpiredPrune(height *big.Int, root common.Hash) error { func (p *Pruner) unExpiredBloomTag(trieDB *trie.Database, epoch types.StateEpoch, root common.Hash) (*bloomfilter.Filter, error) { var ( - scanUnExpiredTrieCh = make(chan *snapshot.ContractItem, 100000) - tagUnExpiredInBloomCh = make(chan *trie.NodeInfo, 100000) + scanUnExpiredTrieCh = make(chan *snapshot.ContractItem, defaultChannelSize) + tagUnExpiredInBloomCh = make(chan *trie.NodeInfo, defaultChannelSize) rets = make([]error, 3) tasksWG sync.WaitGroup ) @@ -833,6 +835,14 @@ func asyncScanUnExpiredInTrie(db *trie.Database, stateRoot common.Hash, epoch ty return err } tr.SetEpoch(epoch) + if st.MoreThread() { + st.Schedule(func() { + if err = tr.ScanForPrune(st); err != nil { + log.Error("asyncScanExpiredInTrie, ScanForPrune err", "id", item, "err", err) + } + }) + continue + } if err = tr.ScanForPrune(st); err != nil { log.Error("asyncScanExpiredInTrie, ScanForPrune err", "id", item, "err", err) return err diff --git a/core/state/state_expiry.go b/core/state/state_expiry.go index 8d1e3b9bbf..d0742ff65f 100644 --- a/core/state/state_expiry.go +++ b/core/state/state_expiry.go @@ -35,12 +35,12 @@ func defaultStateExpiryMeta() *stateExpiryMeta { // fetchExpiredStorageFromRemote request expired state from remote full state node; func fetchExpiredStorageFromRemote(meta *stateExpiryMeta, addr common.Address, root common.Hash, tr Trie, prefixKey []byte, key common.Hash) (map[string][]byte, error) { - log.Debug("fetching expired storage from remoteDB", "addr", addr, "prefix", prefixKey, "key", key) + //log.Debug("fetching expired storage from remoteDB", "addr", addr, "prefix", prefixKey, "key", key) reviveTrieMeter.Mark(1) if meta.enableLocalRevive { // if there need revive expired state, try to revive locally, when the node is not being pruned, just renew the epoch val, err := tr.TryLocalRevive(addr, key.Bytes()) - log.Debug("fetchExpiredStorageFromRemote TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) + //log.Debug("fetchExpiredStorageFromRemote TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) if _, ok := err.(*trie.MissingNodeError); !ok { return nil, err } @@ -60,7 +60,7 @@ func fetchExpiredStorageFromRemote(meta *stateExpiryMeta, addr common.Address, r reviveFromRemoteMeter.Mark(1) // cannot revive locally, fetch remote proof proofs, err := meta.fullStateDB.GetStorageReviveProof(meta.originalRoot, addr, root, []string{common.Bytes2Hex(prefixKey)}, []string{common.Bytes2Hex(key[:])}) - log.Debug("fetchExpiredStorageFromRemote GetStorageReviveProof", "addr", addr, "key", key, "proofs", len(proofs), "err", err) + //log.Debug("fetchExpiredStorageFromRemote GetStorageReviveProof", "addr", addr, "key", key, "proofs", len(proofs), "err", err) if err != nil { return nil, err } @@ -85,7 +85,7 @@ func batchFetchExpiredFromRemote(expiryMeta *stateExpiryMeta, addr common.Addres var expiredPrefixKeys [][]byte for i, key := range keys { val, err := tr.TryLocalRevive(addr, key.Bytes()) - log.Debug("fetchExpiredStorageFromRemote TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) + //log.Debug("fetchExpiredStorageFromRemote TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) if _, ok := err.(*trie.MissingNodeError); !ok { return nil, err } @@ -125,7 +125,7 @@ func batchFetchExpiredFromRemote(expiryMeta *stateExpiryMeta, addr common.Addres // cannot revive locally, fetch remote proof reviveFromRemoteMeter.Mark(int64(len(keysStr))) proofs, err := expiryMeta.fullStateDB.GetStorageReviveProof(expiryMeta.originalRoot, addr, root, prefixKeysStr, keysStr) - log.Debug("fetchExpiredStorageFromRemote GetStorageReviveProof", "addr", addr, "keys", keysStr, "prefixKeys", prefixKeysStr, "proofs", len(proofs), "err", err) + //log.Debug("fetchExpiredStorageFromRemote GetStorageReviveProof", "addr", addr, "keys", keysStr, "prefixKeys", prefixKeysStr, "proofs", len(proofs), "err", err) if err != nil { return nil, err } diff --git a/core/state/state_object.go b/core/state/state_object.go index ef21b81e48..23f75d52c6 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -18,10 +18,8 @@ package state import ( "bytes" - "encoding/hex" "fmt" "github.com/ethereum/go-ethereum/core/state/snapshot" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" "io" "math/big" @@ -319,7 +317,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { // handle state expiry situation if s.db.EnableExpire() { if enErr, ok := err.(*trie.ExpiredNodeError); ok { - log.Debug("GetCommittedState expired in trie", "addr", s.address, "key", key, "err", err) + //log.Debug("GetCommittedState expired in trie", "addr", s.address, "key", key, "err", err) val, err = s.fetchExpiredFromRemote(enErr.Path, key, false) } // TODO(0xbundler): add epoch record cache for prevent frequency access epoch update, may implement later @@ -394,6 +392,19 @@ func (s *stateObject) finalise(prefetch bool) { slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure } + // try prefetch future update state + for key := range s.pendingAccessedState { + if val, ok := s.dirtyStorage[key]; ok { + if val != s.originStorage[key] { + continue + } + } + if _, ok := s.pendingFutureReviveState[key]; ok { + continue + } + slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure + } + if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash { s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch) } @@ -463,7 +474,7 @@ func (s *stateObject) updateTrie() (Trie, error) { // it must hit in cache value := s.GetState(key) dirtyStorage[key] = common.TrimLeftZeroes(value[:]) - log.Debug("updateTrie access state", "contract", s.address, "key", key, "epoch", s.db.Epoch()) + //log.Debug("updateTrie access state", "contract", s.address, "key", key, "epoch", s.db.Epoch()) } } @@ -486,7 +497,7 @@ func (s *stateObject) updateTrie() (Trie, error) { if _, err = fetchExpiredStorageFromRemote(s.db.expiryMeta, s.address, s.data.Root, tr, enErr.Path, key); err != nil { s.db.setError(fmt.Errorf("state object pendingFutureReviveState fetchExpiredStorageFromRemote err, contract: %v, key: %v, path: %v, err: %v", s.address, key, enErr.Path, err)) } - log.Debug("updateTrie pendingFutureReviveState", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + //log.Debug("updateTrie pendingFutureReviveState", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) } } for key, value := range dirtyStorage { @@ -494,13 +505,13 @@ func (s *stateObject) updateTrie() (Trie, error) { if err := tr.DeleteStorage(s.address, key[:]); err != nil { s.db.setError(fmt.Errorf("state object update trie DeleteStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) } - log.Debug("updateTrie DeleteStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + //log.Debug("updateTrie DeleteStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) s.db.StorageDeleted += 1 } else { if err := tr.UpdateStorage(s.address, key[:], value); err != nil { s.db.setError(fmt.Errorf("state object update trie UpdateStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) } - log.Debug("updateTrie UpdateStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + //log.Debug("updateTrie UpdateStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) s.db.StorageUpdated += 1 } // Cache the items for preloading @@ -539,7 +550,7 @@ func (s *stateObject) updateTrie() (Trie, error) { } } storage[khash] = snapshotVal // snapshotVal will be nil if it's deleted - log.Debug("updateTrie UpdateSnapShot", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", snapshotVal, "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + //log.Debug("updateTrie UpdateSnapShot", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", snapshotVal, "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) // Track the original value of slot only if it's mutated first time prev := s.originStorage[key] @@ -906,11 +917,11 @@ func (s *stateObject) getExpirySnapStorage(key common.Hash) ([]byte, error, erro // if found value not been pruned, just return, local revive later if s.db.EnableLocalRevive() && len(val.GetVal()) > 0 { s.futureReviveState(key) - log.Debug("getExpirySnapStorage GetVal", "addr", s.address, "key", key, "val", hex.EncodeToString(val.GetVal())) + //log.Debug("getExpirySnapStorage GetVal", "addr", s.address, "key", key, "val", hex.EncodeToString(val.GetVal())) return val.GetVal(), nil, nil } - log.Debug("GetCommittedState expired in snapshot", "addr", s.address, "key", key, "val", val, "enc", enc, "err", err) + //log.Debug("GetCommittedState expired in snapshot", "addr", s.address, "key", key, "val", val, "enc", enc, "err", err) // handle from remoteDB, if got err just setError, or return to revert in consensus version. valRaw, err := s.fetchExpiredFromRemote(nil, key, true) if err != nil { diff --git a/core/state/statedb.go b/core/state/statedb.go index a7eb0aa300..96c94b45f5 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -259,7 +259,7 @@ func (s *StateDB) InitStateExpiryFeature(config *types.StateExpiryConfig, remote originalRoot: s.originalRoot, originalHash: startAtBlockHash, } - log.Debug("StateDB enable state expiry feature", "expectHeight", expectHeight, "startAtBlockHash", startAtBlockHash, "epoch", epoch) + //log.Debug("StateDB enable state expiry feature", "expectHeight", expectHeight, "startAtBlockHash", startAtBlockHash, "epoch", epoch) return s } diff --git a/core/types/typed_trie_node.go b/core/types/typed_trie_node.go index f7ac2ea044..01eb120edf 100644 --- a/core/types/typed_trie_node.go +++ b/core/types/typed_trie_node.go @@ -1,9 +1,10 @@ package types import ( - "bytes" "errors" + "fmt" "github.com/ethereum/go-ethereum/rlp" + "io" ) const ( @@ -39,13 +40,54 @@ func (n *TrieBranchNodeWithEpoch) Type() uint8 { } func (n *TrieBranchNodeWithEpoch) EncodeToRLPBytes(buf *rlp.EncoderBuffer) { - rlp.Encode(buf, n) + offset := buf.List() + mapOffset := buf.List() + for _, item := range n.EpochMap { + if item == 0 { + buf.Write(rlp.EmptyString) + } else { + buf.WriteUint64(uint64(item)) + } + } + buf.ListEnd(mapOffset) + buf.Write(n.Blob) + buf.ListEnd(offset) } func DecodeTrieBranchNodeWithEpoch(enc []byte) (*TrieBranchNodeWithEpoch, error) { var n TrieBranchNodeWithEpoch - if err := rlp.DecodeBytes(enc, &n); err != nil { - return nil, err + if len(enc) == 0 { + return nil, io.ErrUnexpectedEOF + } + elems, _, err := rlp.SplitList(enc) + if err != nil { + return nil, fmt.Errorf("decode error: %v", err) + } + + maps, rest, err := rlp.SplitList(elems) + if err != nil { + return nil, fmt.Errorf("decode epochmap error: %v", err) + } + for i := 0; i < len(n.EpochMap); i++ { + var c uint64 + c, maps, err = rlp.SplitUint64(maps) + if err != nil { + return nil, fmt.Errorf("decode epochmap val error: %v", err) + } + n.EpochMap[i] = StateEpoch(c) + } + + k, content, _, err := rlp.Split(rest) + if err != nil { + return nil, fmt.Errorf("decode raw error: %v", err) + } + switch k { + case rlp.String: + n.Blob = content + case rlp.List: + n.Blob = rest + default: + return nil, fmt.Errorf("decode wrong raw type error: %v", err) } return &n, nil } @@ -54,15 +96,16 @@ func EncodeTypedTrieNode(val TypedTrieNode) []byte { switch raw := val.(type) { case TrieNodeRaw: return raw + case *TrieBranchNodeWithEpoch: + // encode with type prefix + w := rlp.NewEncoderBuffer(nil) + w.Write([]byte{val.Type()}) + val.EncodeToRLPBytes(&w) + result := w.ToBytes() + w.Flush() + return result } - // encode with type prefix - buf := bytes.NewBuffer(make([]byte, 0, 40)) - buf.WriteByte(val.Type()) - encoder := rlp.NewEncoderBuffer(buf) - val.EncodeToRLPBytes(&encoder) - // it cannot be error here. - encoder.Flush() - return buf.Bytes() + return nil } func DecodeTypedTrieNode(enc []byte) (TypedTrieNode, error) { diff --git a/trie/committer.go b/trie/committer.go index 1980475408..2ad8d9f4f5 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -144,9 +144,11 @@ func (c *committer) store(path []byte, n node) node { } // Collect the dirty node to nodeset for return. nhash := common.BytesToHash(hash) - blob := nodeToBytes(n) + var blob []byte if c.enableStateExpiry && !c.enableMetaDB { - blob = nodeToBytesWithEpoch(n, blob) + blob = nodeToBytesWithEpoch(n) + } else { + blob = nodeToBytes(n) } changed := c.tracer.checkNodeChanged(path, blob) if changed { diff --git a/trie/node_enc.go b/trie/node_enc.go index 6485169ac2..98b17bca73 100644 --- a/trie/node_enc.go +++ b/trie/node_enc.go @@ -28,7 +28,8 @@ func nodeToBytes(n node) []byte { w.Flush() return result } -func nodeToBytesWithEpoch(n node, raw []byte) []byte { + +func nodeToBytesWithEpoch(n node) []byte { switch n := n.(type) { case *fullNode: withEpoch := false @@ -39,15 +40,26 @@ func nodeToBytesWithEpoch(n node, raw []byte) []byte { } } if withEpoch { - tn := types.TrieBranchNodeWithEpoch{ - EpochMap: n.EpochMap, - Blob: raw, + w := rlp.NewEncoderBuffer(nil) + w.Write([]byte{types.TrieBranchNodeWithEpochType}) + offset := w.List() + mapOffset := w.List() + for _, item := range n.EpochMap { + if item == 0 { + w.Write(rlp.EmptyString) + } else { + w.WriteUint64(uint64(item)) + } } - rn := types.EncodeTypedTrieNode(&tn) - return rn + w.ListEnd(mapOffset) + n.encode(w) + w.ListEnd(offset) + result := w.ToBytes() + w.Flush() + return result } } - return raw + return nodeToBytes(n) } func (n *fullNode) encode(w rlp.EncoderBuffer) { diff --git a/trie/trie.go b/trie/trie.go index b5889b67b6..75f0ef5e4d 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -1530,9 +1530,8 @@ type ScanTask struct { itemCh chan *NodeInfo unexpiredStat *atomic.Uint64 expiredStat *atomic.Uint64 - record *atomic.Uint64 findExpired bool - maxThreads uint64 + routineCh chan struct{} wg *sync.WaitGroup reportDone chan struct{} } @@ -1542,9 +1541,8 @@ func NewScanTask(itemCh chan *NodeInfo, maxThreads uint64, findExpired bool) *Sc itemCh: itemCh, unexpiredStat: &atomic.Uint64{}, expiredStat: &atomic.Uint64{}, - record: &atomic.Uint64{}, findExpired: findExpired, - maxThreads: maxThreads, + routineCh: make(chan struct{}, maxThreads), wg: &sync.WaitGroup{}, reportDone: make(chan struct{}), } @@ -1578,25 +1576,42 @@ func (st *ScanTask) Report(d time.Duration) { for { select { case <-timer.C: - log.Info("Scan trie stats", "unexpired", st.UnexpiredStat(), "expired", st.ExpiredStat(), "go routine", runtime.NumGoroutine(), "elapsed", common.PrettyDuration(time.Since(start))) + log.Info("Scan trie stats", "total", st.TotalScan(), "unexpired", st.UnexpiredStat(), "expired", st.ExpiredStat(), "go routine", runtime.NumGoroutine(), "elapsed", common.PrettyDuration(time.Since(start))) timer.Reset(d) case <-st.reportDone: - log.Info("Scan trie done", "unexpired", st.UnexpiredStat(), "expired", st.ExpiredStat(), "elapsed", common.PrettyDuration(time.Since(start))) + log.Info("Scan trie done", "total", st.TotalScan(), "unexpired", st.UnexpiredStat(), "expired", st.ExpiredStat(), "elapsed", common.PrettyDuration(time.Since(start))) return } } } -func (st *ScanTask) moreThread() bool { - total := st.expiredStat.Load() + st.unexpiredStat.Load() - record := st.record.Load() - // every increase 10000, will add a new tread to handle other trie path - if total-record > 10000 && uint64(runtime.NumGoroutine()) < st.maxThreads { - st.record.Store(total) +func (st *ScanTask) Schedule(f func()) { + log.Debug("schedule", "total", st.TotalScan(), "go routine", runtime.NumGoroutine()) + st.wg.Add(1) + go func() { + defer func() { + st.wg.Done() + select { + case <-st.routineCh: + log.Debug("task Schedule done", "routine", len(st.routineCh)) + default: + } + }() + f() + }() +} + +func (st *ScanTask) TotalScan() uint64 { + return st.expiredStat.Load() + st.unexpiredStat.Load() +} + +func (st *ScanTask) MoreThread() bool { + select { + case st.routineCh <- struct{}{}: return true + default: + return false } - - return false } // ScanForPrune traverses the storage trie and prunes all expired or unexpired nodes. @@ -1671,16 +1686,15 @@ func (t *Trie) findExpiredSubTree(n node, path []byte, epoch types.StateEpoch, p if err = t.resolveEpochMeta(resolve, epoch, path); err != nil { return err } - if st.moreThread() { - st.wg.Add(1) + if st.TotalScan()%1000 == 0 && st.MoreThread() { path := common.CopyBytes(path) - go func() { - defer st.wg.Done() - t.findExpiredSubTree(resolve, path, epoch, pruner, st) - }() + st.Schedule(func() { + if err := t.findExpiredSubTree(resolve, path, epoch, pruner, st); err != nil { + log.Error("recursePruneExpiredNode err", "addr", t.owner, "path", path, "epoch", epoch, "err", err) + } + }) return nil } - return t.findExpiredSubTree(resolve, path, epoch, pruner, st) case valueNode: return nil @@ -1749,13 +1763,14 @@ func (t *Trie) recursePruneExpiredNode(n node, path []byte, epoch types.StateEpo if err = t.resolveEpochMeta(rn, epoch, path); err != nil { return err } - if st.moreThread() { - st.wg.Add(1) + + if st.TotalScan()%1000 == 0 && st.MoreThread() { path := common.CopyBytes(path) - go func() { - defer st.wg.Done() - t.recursePruneExpiredNode(rn, path, epoch, st) - }() + st.Schedule(func() { + if err := t.recursePruneExpiredNode(rn, path, epoch, st); err != nil { + log.Error("recursePruneExpiredNode err", "addr", t.owner, "path", path, "epoch", epoch, "err", err) + } + }) return nil } return t.recursePruneExpiredNode(rn, path, epoch, st) diff --git a/trie/typed_trie_node_test.go b/trie/typed_trie_node_test.go index 603b18b665..dc88be1958 100644 --- a/trie/typed_trie_node_test.go +++ b/trie/typed_trie_node_test.go @@ -3,7 +3,6 @@ package trie import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/assert" "math/rand" "testing" @@ -11,6 +10,7 @@ import ( var ( fullNode1 = fullNode{ + EpochMap: randomEpochMap(), Children: [17]node{ &shortNode{ Key: common.FromHex("0x2e2"), @@ -25,6 +25,7 @@ var ( }, } fullNode2 = fullNode{ + EpochMap: randomEpochMap(), Children: [17]node{ hashNode(common.FromHex("0xac51f786e6cee2f4575d19789c1e7ae91da54f2138f415c0f95f127c2893eff9")), hashNode(common.FromHex("0x83254958a3640af7a740dfcb32a02edfa1224e0ef65c28b1ff60c0b17eacb5d1")), @@ -55,45 +56,39 @@ func TestSimpleTypedNode_Encode_Decode(t *testing.T) { err: true, }, { - n: types.TrieNodeRaw(encodeNode(&shortNode1)), + n: types.TrieNodeRaw(nodeToBytes(&shortNode1)), }, { - n: types.TrieNodeRaw(encodeNode(&shortNode2)), + n: types.TrieNodeRaw(nodeToBytes(&shortNode2)), }, { - n: types.TrieNodeRaw(encodeNode(&fullNode1)), + n: types.TrieNodeRaw(nodeToBytes(&fullNode1)), }, { - n: types.TrieNodeRaw(encodeNode(&fullNode2)), + n: types.TrieNodeRaw(nodeToBytes(&fullNode2)), }, { n: &types.TrieBranchNodeWithEpoch{ - EpochMap: randomEpochMap(), - Blob: common.FromHex("0x2465176C461AfB316ebc773C61fAEe85A6515DAA"), - }, - }, - { - n: &types.TrieBranchNodeWithEpoch{ - EpochMap: randomEpochMap(), - Blob: encodeNode(&fullNode1), + EpochMap: fullNode1.EpochMap, + Blob: nodeToBytes(&fullNode1), }, }, { n: &types.TrieBranchNodeWithEpoch{ - EpochMap: randomEpochMap(), - Blob: encodeNode(&fullNode2), + EpochMap: fullNode2.EpochMap, + Blob: nodeToBytes(&fullNode2), }, }, { n: &types.TrieBranchNodeWithEpoch{ EpochMap: randomEpochMap(), - Blob: encodeNode(&shortNode1), + Blob: nodeToBytes(&shortNode1), }, }, { n: &types.TrieBranchNodeWithEpoch{ EpochMap: randomEpochMap(), - Blob: encodeNode(&shortNode2), + Blob: nodeToBytes(&shortNode2), }, }, } @@ -111,10 +106,34 @@ func TestSimpleTypedNode_Encode_Decode(t *testing.T) { } } -func encodeNode(n node) []byte { - buf := rlp.NewEncoderBuffer(nil) - n.encode(buf) - return buf.ToBytes() +func TestNode2Bytes_Encode(t *testing.T) { + tests := []struct { + tn types.TypedTrieNode + n node + err bool + }{ + { + tn: &types.TrieBranchNodeWithEpoch{ + EpochMap: fullNode1.EpochMap, + Blob: nodeToBytes(&fullNode1), + }, + n: &fullNode1, + }, + { + tn: &types.TrieBranchNodeWithEpoch{ + EpochMap: fullNode2.EpochMap, + Blob: nodeToBytes(&fullNode2), + }, + n: &fullNode2, + }, + } + + for i, item := range tests { + enc1 := nodeToBytesWithEpoch(item.n) + enc2 := types.EncodeTypedTrieNode(item.tn) + t.Log(common.Bytes2Hex(enc1), common.Bytes2Hex(enc2)) + assert.Equal(t, enc2, enc1, i) + } } func randomEpochMap() [16]types.StateEpoch { From 9ee134ef0837b52396b4a3c88099ee3d451273a3 Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Tue, 24 Oct 2023 18:05:13 +0800 Subject: [PATCH 4/9] state/state_object: add more expired metrics; state/state_object: add more expired metrics; --- consensus/parlia/parlia.go | 3 ++- core/state/state_object.go | 12 ++++++++---- core/state/statedb.go | 11 +++++++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 8ce71f76a1..bc8362bdb3 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1692,13 +1692,14 @@ func (p *Parlia) applyTransaction( } actualTx := (*receivedTxs)[0] if !bytes.Equal(p.signer.Hash(actualTx).Bytes(), expectedHash.Bytes()) { - return fmt.Errorf("expected tx hash %v, get %v, nonce %d:%d, to %s:%s, value %s:%s, gas %d:%d, gasPrice %s:%s, data %s:%s", expectedHash.String(), actualTx.Hash().String(), + return fmt.Errorf("expected tx hash %v, get %v, nonce %d:%d, to %s:%s, value %s:%s, gas %d:%d, gasPrice %s:%s, data %s:%s, dbErr: %v", expectedHash.String(), actualTx.Hash().String(), expectedTx.Nonce(), actualTx.Nonce(), expectedTx.To().String(), actualTx.To().String(), expectedTx.Value().String(), actualTx.Value().String(), expectedTx.Gas(), actualTx.Gas(), expectedTx.GasPrice().String(), actualTx.GasPrice().String(), hex.EncodeToString(expectedTx.Data()), hex.EncodeToString(actualTx.Data()), + state.Error(), ) } expectedTx = actualTx diff --git a/core/state/state_object.go b/core/state/state_object.go index 23f75d52c6..317a70495e 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -319,11 +319,12 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { if enErr, ok := err.(*trie.ExpiredNodeError); ok { //log.Debug("GetCommittedState expired in trie", "addr", s.address, "key", key, "err", err) val, err = s.fetchExpiredFromRemote(enErr.Path, key, false) + getCommittedStorageExpiredMeter.Mark(1) + } else if err != nil { + getCommittedStorageUnexpiredMeter.Mark(1) + // TODO(0xbundler): add epoch record cache for prevent frequency access epoch update, may implement later + //s.originStorageEpoch[key] = epoch } - // TODO(0xbundler): add epoch record cache for prevent frequency access epoch update, may implement later - //if err != nil { - // s.originStorageEpoch[key] = epoch - //} } if err != nil { s.db.setError(fmt.Errorf("state object get storage err, contract: %v, key: %v, err: %v", s.address, key, err)) @@ -911,12 +912,15 @@ func (s *stateObject) getExpirySnapStorage(key common.Hash) ([]byte, error, erro s.originStorageEpoch[key] = val.GetEpoch() if !types.EpochExpired(val.GetEpoch(), s.db.Epoch()) { + getCommittedStorageUnexpiredMeter.Mark(1) return val.GetVal(), nil, nil } + getCommittedStorageExpiredMeter.Mark(1) // if found value not been pruned, just return, local revive later if s.db.EnableLocalRevive() && len(val.GetVal()) > 0 { s.futureReviveState(key) + getCommittedStorageExpiredLocalReviveMeter.Mark(1) //log.Debug("getExpirySnapStorage GetVal", "addr", s.address, "key", key, "val", hex.EncodeToString(val.GetVal())) return val.GetVal(), nil, nil } diff --git a/core/state/statedb.go b/core/state/statedb.go index 96c94b45f5..252df08074 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -46,10 +46,13 @@ import ( const defaultNumOfSlots = 100 var ( - getCommittedStorageMeter = metrics.NewRegisteredMeter("state/contract/committed", nil) - getCommittedStorageSnapMeter = metrics.NewRegisteredMeter("state/contract/committed/snap", nil) - getCommittedStorageTrieMeter = metrics.NewRegisteredMeter("state/contract/committed/trie", nil) - getCommittedStorageRemoteMeter = metrics.NewRegisteredMeter("state/contract/committed/remote", nil) + getCommittedStorageMeter = metrics.NewRegisteredMeter("state/contract/committed", nil) + getCommittedStorageSnapMeter = metrics.NewRegisteredMeter("state/contract/committed/snap", nil) + getCommittedStorageTrieMeter = metrics.NewRegisteredMeter("state/contract/committed/trie", nil) + getCommittedStorageExpiredMeter = metrics.NewRegisteredMeter("state/contract/committed/expired", nil) + getCommittedStorageExpiredLocalReviveMeter = metrics.NewRegisteredMeter("state/contract/committed/expired/localrevive", nil) + getCommittedStorageUnexpiredMeter = metrics.NewRegisteredMeter("state/contract/committed/unexpired", nil) + getCommittedStorageRemoteMeter = metrics.NewRegisteredMeter("state/contract/committed/remote", nil) ) type revision struct { From e46c9146fd231de1dd5cff09ae6873d73feec9e3 Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:59:07 +0800 Subject: [PATCH 5/9] eth/downloader: state expiry remotedb keep behind the latest; --- cmd/geth/chaincmd.go | 21 +++++-------- cmd/geth/config.go | 2 +- cmd/geth/consolecmd.go | 2 +- cmd/geth/dbcmd.go | 48 ++++++++++++------------------ cmd/geth/main.go | 1 + cmd/geth/snapshot.go | 29 +++++++----------- cmd/geth/verkle.go | 4 +-- cmd/utils/flags.go | 13 ++++++++ core/state/statedb.go | 2 +- core/types/state_expiry.go | 51 ++++++++++++++++++++++++++++++-- eth/backend.go | 2 +- eth/downloader/downloader.go | 57 ++++++++++++++++++++++++------------ eth/fetcher/block_fetcher.go | 15 ++++++++++ eth/handler.go | 11 ++++--- 14 files changed, 164 insertions(+), 94 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 59246d2c37..816df6f2e0 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -56,8 +56,7 @@ var ( Flags: flags.Merge([]cli.Flag{ utils.CachePreimagesFlag, utils.StateSchemeFlag, - utils.StateExpiryEnableFlag, - }, utils.DatabasePathFlags), + }, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` The init command initializes a new genesis block and definition for the network. This is a destructive action and changes the network in which you will be @@ -87,8 +86,7 @@ It expects the genesis file as argument.`, Name: "dumpgenesis", Usage: "Dumps genesis block JSON configuration to stdout", ArgsUsage: "", - Flags: append([]cli.Flag{utils.DataDirFlag, - utils.StateExpiryEnableFlag}, utils.NetworkFlags...), + Flags: flags.Merge([]cli.Flag{utils.DataDirFlag}, utils.NetworkFlags, utils.StateExpiryBaseFlags), Description: ` The dumpgenesis command prints the genesis configuration of the network preset if one is set. Otherwise it prints the genesis from the datadir.`, @@ -123,8 +121,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`, utils.TransactionHistoryFlag, utils.StateSchemeFlag, utils.StateHistoryFlag, - utils.StateExpiryEnableFlag, - }, utils.DatabasePathFlags), + }, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` The import command imports blocks from an RLP-encoded form. The form can be one file with several RLP-encoded blocks, or several files can be used. @@ -141,8 +138,7 @@ processing will proceed even if an individual RLP-file import failure occurs.`, utils.CacheFlag, utils.SyncModeFlag, utils.StateSchemeFlag, - utils.StateExpiryEnableFlag, - }, utils.DatabasePathFlags), + }, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` Requires a first argument of the file to write to. Optional second and third arguments control the first and @@ -158,8 +154,7 @@ be gzipped.`, Flags: flags.Merge([]cli.Flag{ utils.CacheFlag, utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.DatabasePathFlags), + }, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` The import-preimages command imports hash preimages from an RLP encoded stream. It's deprecated, please use "geth db import" instead. @@ -173,8 +168,7 @@ It's deprecated, please use "geth db import" instead. Flags: flags.Merge([]cli.Flag{ utils.CacheFlag, utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.DatabasePathFlags), + }, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` The export-preimages command exports hash preimages to an RLP encoded stream. It's deprecated, please use "geth db export" instead. @@ -194,8 +188,7 @@ It's deprecated, please use "geth db export" instead. utils.StartKeyFlag, utils.DumpLimitFlag, utils.StateSchemeFlag, - utils.StateExpiryEnableFlag, - }, utils.DatabasePathFlags), + }, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` This command dumps out the state for a given block (or latest, if none provided). `, diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 7c0381b88f..1418ade4dc 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -49,7 +49,7 @@ var ( Name: "dumpconfig", Usage: "Export configuration values in a TOML format", ArgsUsage: "", - Flags: flags.Merge(nodeFlags, rpcFlags, []cli.Flag{utils.StateExpiryEnableFlag}), + Flags: flags.Merge(nodeFlags, rpcFlags, utils.StateExpiryBaseFlags), Description: `Export configuration values in TOML format (to stdout by default).`, } diff --git a/cmd/geth/consolecmd.go b/cmd/geth/consolecmd.go index e5d1e3503f..c6575d247e 100644 --- a/cmd/geth/consolecmd.go +++ b/cmd/geth/consolecmd.go @@ -33,7 +33,7 @@ var ( Action: localConsole, Name: "console", Usage: "Start an interactive JavaScript environment", - Flags: flags.Merge(nodeFlags, rpcFlags, consoleFlags, []cli.Flag{utils.StateExpiryEnableFlag}), + Flags: flags.Merge(nodeFlags, rpcFlags, consoleFlags, utils.StateExpiryBaseFlags), Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 729ea56257..2f176d14fd 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -50,7 +50,7 @@ var ( Name: "removedb", Usage: "Remove blockchain and state databases", ArgsUsage: "", - Flags: flags.Merge(utils.DatabasePathFlags, []cli.Flag{utils.StateExpiryEnableFlag}), + Flags: flags.Merge(utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` Remove blockchain and state databases`, } @@ -86,8 +86,7 @@ Remove blockchain and state databases`, ArgsUsage: " ", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Usage: "Inspect the storage size for each type of data in the database", Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`, } @@ -97,8 +96,7 @@ Remove blockchain and state databases`, ArgsUsage: " ", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.DatabasePathFlags), + }, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Usage: "Inspect the MPT tree of the account and contract.", Description: `This commands iterates the entrie WorldState.`, } @@ -106,7 +104,7 @@ Remove blockchain and state databases`, Action: checkStateContent, Name: "check-state-content", ArgsUsage: "", - Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, []cli.Flag{utils.StateExpiryEnableFlag}), + Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Usage: "Verify that state data is cryptographically correct", Description: `This command iterates the entire database for 32-byte keys, looking for rlp-encoded trie nodes. For each trie node encountered, it checks that the key corresponds to the keccak256(value). If this is not true, this indicates @@ -157,8 +155,7 @@ a data corruption.`, Usage: "Print leveldb statistics", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), } dbCompactCmd = &cli.Command{ Action: dbCompact, @@ -168,8 +165,7 @@ a data corruption.`, utils.SyncModeFlag, utils.CacheFlag, utils.CacheDatabaseFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: `This command performs a database compaction. WARNING: This operation may take a very long time to finish, and may cause database corruption if it is aborted during execution'!`, @@ -181,8 +177,7 @@ corruption if it is aborted during execution'!`, ArgsUsage: "", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: "This command looks up the specified database key from the database.", } dbDeleteCmd = &cli.Command{ @@ -192,8 +187,7 @@ corruption if it is aborted during execution'!`, ArgsUsage: "", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: `This command deletes the specified database key from the database. WARNING: This is a low-level operation which may cause database corruption!`, } @@ -204,8 +198,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, ArgsUsage: " ", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: `This command sets a given database key to the given value. WARNING: This is a low-level operation which may cause database corruption!`, } @@ -217,8 +210,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, utils.StateSchemeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: "This command looks up the specified database key from the database.", } dbDumpFreezerIndex = &cli.Command{ @@ -228,8 +220,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, ArgsUsage: " ", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: "This command displays information about the freezer index.", } dbImportCmd = &cli.Command{ @@ -239,8 +230,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, ArgsUsage: " ", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.", } dbMetadataCmd = &cli.Command{ @@ -260,17 +249,16 @@ WARNING: This is a low-level operation which may cause database corruption!`, Usage: "Shows metadata about the chain status.", Flags: flags.Merge([]cli.Flag{ utils.SyncModeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: "Shows metadata about the chain status.", } ancientInspectCmd = &cli.Command{ Action: ancientInspect, Name: "inspect-reserved-oldest-blocks", - Flags: []cli.Flag{ - utils.DataDirFlag, - utils.StateExpiryEnableFlag, - }, + Flags: flags.Merge( + []cli.Flag{utils.DataDirFlag}, + utils.StateExpiryBaseFlags, + ), Usage: "Inspect the ancientStore information", Description: `This commands will read current offset from kvdb, which is the current offset and starting BlockNumber of ancientStore, will also displays the reserved number of blocks in ancientStore `, diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 6332506689..d35016e2a8 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -223,6 +223,7 @@ var ( utils.StateExpiryStateEpoch2BlockFlag, utils.StateExpiryStateEpochPeriodFlag, utils.StateExpiryEnableLocalReviveFlag, + utils.StateExpiryEnableRemoteModeFlag, } ) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 91675e467d..2134418ca7 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -61,10 +61,9 @@ var ( Flags: flags.Merge([]cli.Flag{ utils.BloomFilterSizeFlag, utils.TriesInMemoryFlag, - utils.StateExpiryEnableFlag, utils.StateExpiryMaxThreadFlag, configFileFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` geth snapshot prune-state will prune historical state data with the help of the state snapshot. @@ -82,14 +81,13 @@ WARNING: it's only supported in hash mode(--state.scheme=hash)". Usage: "Prune block data offline", Action: pruneBlock, Category: "MISCELLANEOUS COMMANDS", - Flags: []cli.Flag{ + Flags: flags.Merge([]cli.Flag{ utils.DataDirFlag, utils.AncientFlag, utils.BlockAmountReserved, utils.TriesInMemoryFlag, utils.CheckSnapshotWithMPT, - utils.StateExpiryEnableFlag, - }, + }, utils.StateExpiryBaseFlags), Description: ` geth offline prune-block for block data in ancientdb. The amount of blocks expected for remaining after prune can be specified via block-amount-reserved in this command, @@ -109,8 +107,7 @@ so it's very necessary to do block data prune, this feature will handle it. Action: verifyState, Flags: flags.Merge([]cli.Flag{ utils.StateSchemeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` geth snapshot verify-state will traverse the whole accounts and storages set based on the specified @@ -125,11 +122,10 @@ In other words, this command does the snapshot to trie conversion. ArgsUsage: "", Action: pruneAllState, Category: "MISCELLANEOUS COMMANDS", - Flags: []cli.Flag{ + Flags: flags.Merge([]cli.Flag{ utils.DataDirFlag, utils.AncientFlag, - utils.StateExpiryEnableFlag, - }, + }, utils.StateExpiryBaseFlags), Description: ` will prune all historical trie state data except genesis block. All trie nodes will be deleted from the database. @@ -147,7 +143,7 @@ the trie clean cache with default directory will be deleted. Usage: "Check that there is no 'dangling' snap storage", ArgsUsage: "", Action: checkDanglingStorage, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, []cli.Flag{utils.StateExpiryEnableFlag}), + Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` geth snapshot check-dangling-storage traverses the snap storage data, and verifies that all snapshot storage data has a corresponding account. @@ -158,7 +154,7 @@ data, and verifies that all snapshot storage data has a corresponding account. Usage: "Check all snapshot layers for the a specific account", ArgsUsage: "
", Action: checkAccount, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, []cli.Flag{utils.StateExpiryEnableFlag}), + Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` geth snapshot inspect-account
checks all snapshot layers and prints out information about the specified address. @@ -171,8 +167,7 @@ information about the specified address. Action: traverseState, Flags: flags.Merge([]cli.Flag{ utils.StateSchemeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` geth snapshot traverse-state will traverse the whole state from the given state root and will abort if any @@ -189,8 +184,7 @@ It's also usable without snapshot enabled. Action: traverseRawState, Flags: flags.Merge([]cli.Flag{ utils.StateSchemeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` geth snapshot traverse-rawstate will traverse the whole state from the given root and will abort if any referenced @@ -213,8 +207,7 @@ It's also usable without snapshot enabled. utils.DumpLimitFlag, utils.TriesInMemoryFlag, utils.StateSchemeFlag, - utils.StateExpiryEnableFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + }, utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` This command is semantically equivalent to 'geth dump', but uses the snapshots as the backend data source, making this command a lot faster. diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index 97851b0f85..aebee0c1cd 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -45,7 +45,7 @@ var ( Usage: "verify the conversion of a MPT into a verkle tree", ArgsUsage: "", Action: verifyVerkle, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, []cli.Flag{utils.StateExpiryEnableFlag}), + Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` geth verkle verify This command takes a root commitment and attempts to rebuild the tree. @@ -56,7 +56,7 @@ This command takes a root commitment and attempts to rebuild the tree. Usage: "Dump a verkle tree to a DOT file", ArgsUsage: " [ ...]", Action: expandVerkle, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, []cli.Flag{utils.StateExpiryEnableFlag}), + Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags, utils.StateExpiryBaseFlags), Description: ` geth verkle dump [ ...] This command will produce a dot file representing the tree, rooted at . diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e39252d20f..7687cf87fd 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1118,6 +1118,10 @@ var ( RemoteDBFlag, HttpHeaderFlag, } + StateExpiryBaseFlags = []cli.Flag{ + StateExpiryEnableFlag, + StateExpiryEnableRemoteModeFlag, + } ) var ( @@ -1158,6 +1162,12 @@ var ( Value: 10000, Category: flags.StateExpiryCategory, } + StateExpiryEnableRemoteModeFlag = &cli.BoolFlag{ + Name: "state-expiry.remotemode", + Usage: "set state expiry in remotemode", + Value: false, + Category: flags.StateExpiryCategory, + } ) func init() { @@ -2573,6 +2583,9 @@ func ParseStateExpiryConfig(ctx *cli.Context, disk ethdb.Database, scheme string if ctx.IsSet(StateExpiryEnableFlag.Name) { newCfg.Enable = ctx.Bool(StateExpiryEnableFlag.Name) } + if ctx.IsSet(StateExpiryEnableRemoteModeFlag.Name) { + newCfg.EnableRemoteMode = ctx.Bool(StateExpiryEnableRemoteModeFlag.Name) + } if ctx.IsSet(StateExpiryFullStateEndpointFlag.Name) { newCfg.FullStateEndpoint = ctx.String(StateExpiryFullStateEndpointFlag.Name) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 252df08074..c703bb1b92 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -255,7 +255,7 @@ func (s *StateDB) InitStateExpiryFeature(config *types.StateExpiryConfig, remote } epoch := types.GetStateEpoch(config, expectHeight) s.expiryMeta = &stateExpiryMeta{ - enableStateExpiry: config.Enable, + enableStateExpiry: config.EnableExpiry(), enableLocalRevive: config.EnableLocalRevive, fullStateDB: remote, epoch: epoch, diff --git a/core/types/state_expiry.go b/core/types/state_expiry.go index 3945e4c6f1..35ce7cc8e4 100644 --- a/core/types/state_expiry.go +++ b/core/types/state_expiry.go @@ -22,13 +22,23 @@ type StateExpiryConfig struct { StateEpoch2Block uint64 StateEpochPeriod uint64 EnableLocalRevive bool + EnableRemoteMode bool // when enable remoteDB mode, it will register specific RPC for partial proof and keep sync behind for safety proof } +// EnableExpiry when enable remote mode, it just check param func (s *StateExpiryConfig) EnableExpiry() bool { if s == nil { return false } - return s.Enable + return s.Enable && !s.EnableRemoteMode +} + +// EnableRemote when enable remote mode, it just check param +func (s *StateExpiryConfig) EnableRemote() bool { + if s == nil { + return false + } + return s.Enable && s.EnableRemoteMode } func (s *StateExpiryConfig) Validation() error { @@ -63,6 +73,9 @@ func (s *StateExpiryConfig) CheckCompatible(newCfg *StateExpiryConfig) error { if s.Enable && !newCfg.Enable { return errors.New("disable state expiry is dangerous after enabled, expired state may pruned") } + if s.EnableRemoteMode && !newCfg.EnableRemoteMode { + return errors.New("disable state expiry EnableRemoteMode is dangerous after enabled") + } if err := s.CheckStateEpochCompatible(newCfg.StateEpoch1Block, newCfg.StateEpoch2Block, newCfg.StateEpochPeriod); err != nil { return err @@ -96,8 +109,40 @@ func (s *StateExpiryConfig) CheckStateEpochCompatible(StateEpoch1Block, StateEpo func (s *StateExpiryConfig) String() string { if !s.Enable { - return "State Expiry Disable" + return "State Expiry Disable." + } + if s.Enable && s.EnableRemoteMode { + return "State Expiry Enable in RemoteMode, it will not expired any state." } - return fmt.Sprintf("Enable State Expiry, RemoteEndpoint: %v, StateEpoch: [%v|%v|%v], StateScheme: %v, PruneLevel: %v, EnableLocalRevive: %v", + return fmt.Sprintf("Enable State Expiry, RemoteEndpoint: %v, StateEpoch: [%v|%v|%v], StateScheme: %v, PruneLevel: %v, EnableLocalRevive: %v.", s.FullStateEndpoint, s.StateEpoch1Block, s.StateEpoch2Block, s.StateEpochPeriod, s.StateScheme, s.PruneLevel, s.EnableLocalRevive) } + +// ShouldKeep1EpochBehind when enable state expiry, keep remoteDB behind the latest only 1 epoch blocks +func (s *StateExpiryConfig) ShouldKeep1EpochBehind(remote uint64, local uint64) (bool, uint64) { + if !s.EnableRemoteMode { + return false, remote + } + if remote <= local { + return false, remote + } + + // if in epoch0, just sync + if remote < s.StateEpoch1Block { + return false, remote + } + + // if in epoch1, behind StateEpoch2Block-StateEpoch1Block + if remote < s.StateEpoch2Block { + if remote-(s.StateEpoch2Block-s.StateEpoch1Block) <= local { + return true, 0 + } + return false, remote - (s.StateEpoch2Block - s.StateEpoch1Block) + } + + // if in >= epoch2, behind StateEpochPeriod + if remote-s.StateEpochPeriod <= local { + return true, 0 + } + return false, remote - s.StateEpochPeriod +} diff --git a/eth/backend.go b/eth/backend.go index 19c6c094ec..879757cf04 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -276,7 +276,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { DirectBroadcast: config.DirectBroadcast, DisablePeerTxBroadcast: config.DisablePeerTxBroadcast, PeerSet: peers, - EnableStateExpiry: config.StateExpiryCfg.EnableExpiry(), + expiryConfig: config.StateExpiryCfg, }); err != nil { return nil, err } diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 1fe92de837..860d85d540 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -103,6 +103,9 @@ type Downloader struct { stateDB ethdb.Database // Database to state sync into (and deduplicate via) + // state expiry + expiryConfig *types.StateExpiryConfig + // Statistics syncStatsChainOrigin uint64 // Origin block number where syncing started at syncStatsChainHeight uint64 // Highest block number known when syncing started @@ -131,8 +134,6 @@ type Downloader struct { SnapSyncer *snap.Syncer // TODO(karalabe): make private! hack for now stateSyncStart chan *stateSync - enableStateExpiry bool - // Cancellation and termination cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop) cancelCh chan struct{} // Channel to cancel mid-flight syncs @@ -241,24 +242,24 @@ func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchai return dl } -func NewWithExpiry(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, enableStateExpiry bool, dropPeer peerDropFn, options ...DownloadOption) *Downloader { +func NewWithExpiry(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, expiryConfig *types.StateExpiryConfig, dropPeer peerDropFn, options ...DownloadOption) *Downloader { if lightchain == nil { lightchain = chain } dl := &Downloader{ - stateDB: stateDb, - mux: mux, - queue: newQueue(blockCacheMaxItems, blockCacheInitialItems), - peers: newPeerSet(), - blockchain: chain, - lightchain: lightchain, - dropPeer: dropPeer, - enableStateExpiry: enableStateExpiry, - headerProcCh: make(chan *headerTask, 1), - quitCh: make(chan struct{}), - SnapSyncer: snap.NewSyncerWithStateExpiry(stateDb, chain.TrieDB().Scheme(), enableStateExpiry), - stateSyncStart: make(chan *stateSync), - syncStartBlock: chain.CurrentSnapBlock().Number.Uint64(), + stateDB: stateDb, + mux: mux, + queue: newQueue(blockCacheMaxItems, blockCacheInitialItems), + peers: newPeerSet(), + blockchain: chain, + lightchain: lightchain, + dropPeer: dropPeer, + expiryConfig: expiryConfig, + headerProcCh: make(chan *headerTask, 1), + quitCh: make(chan struct{}), + SnapSyncer: snap.NewSyncerWithStateExpiry(stateDb, chain.TrieDB().Scheme(), expiryConfig.EnableExpiry()), + stateSyncStart: make(chan *stateSync), + syncStartBlock: chain.CurrentSnapBlock().Number.Uint64(), } go dl.stateFetcher() @@ -524,6 +525,15 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * localHeight = d.lightchain.CurrentHeader().Number.Uint64() } + if d.expiryConfig.EnableRemote() { + var keep bool + keep, remoteHeight = d.expiryConfig.ShouldKeep1EpochBehind(remoteHeight, localHeight) + log.Debug("EnableRemote wait remote more blocks", "remoteHeight", remoteHeader.Number, "request", remoteHeight, "localHeight", localHeight, "config", d.expiryConfig) + if keep { + return errCanceled + } + } + origin, err := d.findAncestor(p, localHeight, remoteHeader) if err != nil { return err @@ -611,9 +621,9 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * } fetchers := []func() error{ - func() error { return d.fetchHeaders(p, origin+1, remoteHeader.Number.Uint64()) }, // Headers are always retrieved - func() error { return d.fetchBodies(origin+1, beaconMode) }, // Bodies are retrieved during normal and snap sync - func() error { return d.fetchReceipts(origin+1, beaconMode) }, // Receipts are retrieved during snap sync + func() error { return d.fetchHeaders(p, origin+1, remoteHeight) }, // Headers are always retrieved + func() error { return d.fetchBodies(origin+1, beaconMode) }, // Bodies are retrieved during normal and snap sync + func() error { return d.fetchReceipts(origin+1, beaconMode) }, // Receipts are retrieved during snap sync func() error { return d.processHeaders(origin+1, td, ttd, beaconMode) }, } if mode == SnapSync { @@ -1199,6 +1209,15 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, head uint64) e return errCanceled } from += uint64(len(headers)) + // if EnableRemote, just return if ahead the head + if d.expiryConfig.EnableRemote() && from > head { + select { + case d.headerProcCh <- nil: + return nil + case <-d.cancelCh: + return errCanceled + } + } } // If we're still skeleton filling snap sync, check pivot staleness // before continuing to the next skeleton filling diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go index b2879cd25f..652fa3c5b5 100644 --- a/eth/fetcher/block_fetcher.go +++ b/eth/fetcher/block_fetcher.go @@ -203,6 +203,9 @@ type BlockFetcher struct { fetchingHook func([]common.Hash) // Method to call upon starting a block (eth/61) or header (eth/62) fetch completingHook func([]common.Hash) // Method to call upon starting a block body fetch (eth/62) importedHook func(*types.Header, *types.Block) // Method to call upon successful header or block import (both eth/61 and eth/62) + + // state expiry + expiryConfig *types.StateExpiryConfig } // NewBlockFetcher creates a block fetcher to retrieve blocks based on hash announcements. @@ -383,6 +386,14 @@ func (f *BlockFetcher) loop() { f.forgetBlock(hash) continue } + + if f.expiryConfig.EnableRemote() { + if keep, _ := f.expiryConfig.ShouldKeep1EpochBehind(number, height); keep { + log.Debug("BlockFetcher EnableRemote wait remote more blocks", "remoteHeight", number, "localHeight", height, "config", f.expiryConfig) + break + } + } + if f.light { f.importHeaders(op) } else { @@ -994,3 +1005,7 @@ func (f *BlockFetcher) forgetBlock(hash common.Hash) { delete(f.queued, hash) } } + +func (f *BlockFetcher) InitExpiryConfig(config *types.StateExpiryConfig) { + f.expiryConfig = config +} diff --git a/eth/handler.go b/eth/handler.go index 72bfd8993e..f784629117 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -123,7 +123,7 @@ type handlerConfig struct { DirectBroadcast bool DisablePeerTxBroadcast bool PeerSet *peerSet - EnableStateExpiry bool + expiryConfig *types.StateExpiryConfig } type handler struct { @@ -135,7 +135,7 @@ type handler struct { acceptTxs atomic.Bool // Flag whether we're considered synchronised (enables transaction processing) directBroadcast bool - enableStateExpiry bool + expiryConfig *types.StateExpiryConfig database ethdb.Database txpool txPool @@ -199,7 +199,7 @@ func newHandler(config *handlerConfig) (*handler, error) { peersPerIP: make(map[string]int), requiredBlocks: config.RequiredBlocks, directBroadcast: config.DirectBroadcast, - enableStateExpiry: config.EnableStateExpiry, + expiryConfig: config.expiryConfig, quitSync: make(chan struct{}), handlerDoneCh: make(chan struct{}), handlerStartCh: make(chan struct{}), @@ -253,7 +253,7 @@ func newHandler(config *handlerConfig) (*handler, error) { downloadOptions = append(downloadOptions, success) */ - h.downloader = downloader.NewWithExpiry(config.Database, h.eventMux, h.chain, nil, config.EnableStateExpiry, h.removePeer, downloadOptions...) + h.downloader = downloader.NewWithExpiry(config.Database, h.eventMux, h.chain, nil, config.expiryConfig, h.removePeer, downloadOptions...) // Construct the fetcher (short sync) validator := func(header *types.Header) error { @@ -339,6 +339,9 @@ func newHandler(config *handlerConfig) (*handler, error) { } h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, finalizeHeighter, nil, inserter, h.removePeer) + if config.expiryConfig != nil { + h.blockFetcher.InitExpiryConfig(config.expiryConfig) + } fetchTx := func(peer string, hashes []common.Hash) error { p := h.peers.peer(peer) From 43ebb5c0542a82cf8d195b3b16851b7ed996ec01 Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Thu, 26 Oct 2023 11:21:28 +0800 Subject: [PATCH 6/9] ethapi: opt storage proof logic; --- core/types/revive_state.go | 25 ++++++++++++++---- core/types/state_expiry.go | 2 +- eth/downloader/downloader.go | 2 +- ethdb/fullstatedb.go | 9 ++++--- internal/ethapi/api.go | 51 +++++++++++------------------------- trie/errors.go | 6 +++-- trie/proof.go | 2 +- trie/trie.go | 8 +++--- 8 files changed, 52 insertions(+), 53 deletions(-) diff --git a/core/types/revive_state.go b/core/types/revive_state.go index 85da6f7cb9..684f1a06b1 100644 --- a/core/types/revive_state.go +++ b/core/types/revive_state.go @@ -1,9 +1,5 @@ package types -import ( - "github.com/ethereum/go-ethereum/common/hexutil" -) - type ReviveStorageProof struct { Key string `json:"key"` PrefixKey string `json:"prefixKey"` @@ -11,6 +7,25 @@ type ReviveStorageProof struct { } type ReviveResult struct { + Err string `json:"err"` StorageProof []ReviveStorageProof `json:"storageProof"` - BlockNum hexutil.Uint64 `json:"blockNum"` + BlockNum uint64 `json:"blockNum"` +} + +func NewReviveErrResult(err error, block uint64) *ReviveResult { + var errRet string + if err != nil { + errRet = err.Error() + } + return &ReviveResult{ + Err: errRet, + BlockNum: block, + } +} + +func NewReviveResult(proof []ReviveStorageProof, block uint64) *ReviveResult { + return &ReviveResult{ + StorageProof: proof, + BlockNum: block, + } } diff --git a/core/types/state_expiry.go b/core/types/state_expiry.go index 35ce7cc8e4..ed2bb18e43 100644 --- a/core/types/state_expiry.go +++ b/core/types/state_expiry.go @@ -22,7 +22,7 @@ type StateExpiryConfig struct { StateEpoch2Block uint64 StateEpochPeriod uint64 EnableLocalRevive bool - EnableRemoteMode bool // when enable remoteDB mode, it will register specific RPC for partial proof and keep sync behind for safety proof + EnableRemoteMode bool `rlp:"optional"` // when enable remoteDB mode, it will register specific RPC for partial proof and keep sync behind for safety proof } // EnableExpiry when enable remote mode, it just check param diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 860d85d540..0c388dd9c8 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -528,7 +528,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * if d.expiryConfig.EnableRemote() { var keep bool keep, remoteHeight = d.expiryConfig.ShouldKeep1EpochBehind(remoteHeight, localHeight) - log.Debug("EnableRemote wait remote more blocks", "remoteHeight", remoteHeader.Number, "request", remoteHeight, "localHeight", localHeight, "config", d.expiryConfig) + log.Debug("EnableRemote wait remote more blocks", "remoteHeight", remoteHeader.Number, "request", remoteHeight, "localHeight", localHeight, "keep", keep, "config", d.expiryConfig) if keep { return errCanceled } diff --git a/ethdb/fullstatedb.go b/ethdb/fullstatedb.go index 373c2d734b..500beac1fb 100644 --- a/ethdb/fullstatedb.go +++ b/ethdb/fullstatedb.go @@ -93,15 +93,16 @@ func (f *FullStateRPCServer) GetStorageReviveProof(stateRoot common.Hash, accoun if err != nil { return nil, fmt.Errorf("failed to get storage revive proof, err: %v, remote's block number: %v", err, result.BlockNum) } - - proofs := result.StorageProof + if len(result.Err) > 0 { + return nil, fmt.Errorf("failed to get storage revive proof, err: %v, remote's block number: %v", result.Err, result.BlockNum) + } // add to cache - for _, proof := range proofs { + for _, proof := range result.StorageProof { f.cache.Add(ProofCacheKey(account, root, proof.PrefixKey, proof.Key), proof) } - ret = append(ret, proofs...) + ret = append(ret, result.StorageProof...) return ret, err } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d0ceb2c55a..49148619fe 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -51,7 +51,6 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/trie" lru "github.com/hashicorp/golang-lru" "github.com/tyler-smith/go-bip39" ) @@ -789,57 +788,42 @@ func (s *BlockChainAPI) GetStorageReviveProof(ctx context.Context, stateRoot com } var ( - blockNum hexutil.Uint64 - err error - stateDb *state.StateDB - header *types.Header - storageTrie state.Trie + blockNum uint64 keys = make([]common.Hash, len(storageKeys)) keyLengths = make([]int, len(storageKeys)) prefixKeys = make([][]byte, len(storagePrefixKeys)) storageProof = make([]types.ReviveStorageProof, len(storageKeys)) ) - openStorageTrie := func(stateDb *state.StateDB, header *types.Header, address common.Address) (state.Trie, error) { - id := trie.StorageTrieID(header.Root, crypto.Keccak256Hash(address.Bytes()), root) - tr, err := trie.NewStateTrie(id, stateDb.Database().TrieDB()) + // try open target state root trie + storageTrie, err := s.b.StorageTrie(stateRoot, address, root) + if err != nil { + // if there cannot find target state root, try latest trie + stateDb, header, err := s.b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber) if err != nil { return nil, err } - return tr, nil - } - - stateDb, header, _ = s.b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber) - blockNum = hexutil.Uint64(header.Number.Uint64()) - - storageTrie, err = s.b.StorageTrie(stateRoot, address, root) - if (err != nil || storageTrie == nil) && stateDb != nil { - storageTrie, err = openStorageTrie(stateDb, header, address) - log.Info("GetStorageReviveProof from latest block number", "blockNum", blockNum, "blockHash", header.Hash().Hex()) - } - - if err != nil || storageTrie == nil { - return &types.ReviveResult{ - StorageProof: nil, - BlockNum: blockNum, - }, err + blockNum = header.Number.Uint64() + storageTrie, err = stateDb.StorageTrie(address) + if err != nil { + return types.NewReviveErrResult(err, blockNum), nil + } + log.Debug("GetStorageReviveProof from latest block number", "blockNum", blockNum, "blockHash", header.Hash()) } // Deserialize all keys. This prevents state access on invalid input. for i, hexKey := range storageKeys { - var err error keys[i], keyLengths[i], err = decodeHash(hexKey) if err != nil { - return nil, err + return types.NewReviveErrResult(err, blockNum), nil } } // Decode prefix keys for i, prefixKey := range storagePrefixKeys { - var err error prefixKeys[i], err = hex.DecodeString(prefixKey) if err != nil { - return nil, err + return types.NewReviveErrResult(err, blockNum), nil } } @@ -867,7 +851,7 @@ func (s *BlockChainAPI) GetStorageReviveProof(ctx context.Context, stateRoot com } if err := storageTrie.ProveByPath(crypto.Keccak256(key.Bytes()), prefixKey, &proof); err != nil { - return nil, err + return types.NewReviveErrResult(err, blockNum), nil } storageProof[i] = types.ReviveStorageProof{ Key: outputKey, @@ -877,10 +861,7 @@ func (s *BlockChainAPI) GetStorageReviveProof(ctx context.Context, stateRoot com s.cache.Add(ethdb.ProofCacheKey(address, root, storagePrefixKeys[i], storageKeys[i]), storageProof[i]) } - return &types.ReviveResult{ - StorageProof: storageProof, - BlockNum: blockNum, - }, nil + return types.NewReviveResult(storageProof, blockNum), nil } // decodeHash parses a hex-encoded 32-byte hash. The input may optionally diff --git a/trie/errors.go b/trie/errors.go index 0c1b785bf0..0be1ac0778 100644 --- a/trie/errors.go +++ b/trie/errors.go @@ -71,15 +71,17 @@ func (e *ReviveNotExpiredError) Error() string { type ExpiredNodeError struct { Path []byte // hex-encoded path to the expired node Epoch types.StateEpoch + Node node } -func NewExpiredNodeError(path []byte, epoch types.StateEpoch) error { +func NewExpiredNodeError(path []byte, epoch types.StateEpoch, n node) error { return &ExpiredNodeError{ Path: path, Epoch: epoch, + Node: n, } } func (err *ExpiredNodeError) Error() string { - return fmt.Sprintf("expired trie node, path: %v, epoch: %v", err.Path, err.Epoch) + return fmt.Sprintf("expired trie node, path: %v, epoch: %v, node: %v", err.Path, err.Epoch, err.Node.fstring("")) } diff --git a/trie/proof.go b/trie/proof.go index 6b8a080a1e..cc19b84e37 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -57,7 +57,7 @@ func (t *Trie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error { } for len(key) > 0 && tn != nil { if t.enableExpiry && t.epochExpired(tn, nodeEpoch) { - return NewExpiredNodeError(prefix, nodeEpoch) + return NewExpiredNodeError(prefix, nodeEpoch, tn) } switch n := tn.(type) { case *shortNode: diff --git a/trie/trie.go b/trie/trie.go index 75f0ef5e4d..39b1dbd4ca 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -260,7 +260,7 @@ func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode no func (t *Trie) getWithEpoch(origNode node, key []byte, pos int, epoch types.StateEpoch, updateEpoch bool) (value []byte, newnode node, didResolve bool, err error) { if t.epochExpired(origNode, epoch) { - return nil, nil, false, NewExpiredNodeError(key[:pos], epoch) + return nil, nil, false, NewExpiredNodeError(key[:pos], epoch, origNode) } switch n := (origNode).(type) { case nil: @@ -596,7 +596,7 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error func (t *Trie) insertWithEpoch(n node, prefix, key []byte, value node, epoch types.StateEpoch) (bool, node, error) { if t.epochExpired(n, epoch) { - return false, nil, NewExpiredNodeError(prefix, epoch) + return false, nil, NewExpiredNodeError(prefix, epoch, n) } if len(key) == 0 { @@ -865,7 +865,7 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { func (t *Trie) deleteWithEpoch(n node, prefix, key []byte, epoch types.StateEpoch) (bool, node, error) { if t.epochExpired(n, epoch) { - return false, nil, NewExpiredNodeError(prefix, epoch) + return false, nil, NewExpiredNodeError(prefix, epoch, n) } switch n := n.(type) { @@ -1352,7 +1352,7 @@ func (t *Trie) tryRevive(n node, key []byte, targetPrefixKey []byte, nub MPTProo } if isExpired { - return nil, false, NewExpiredNodeError(key[:pos], epoch) + return nil, false, NewExpiredNodeError(key[:pos], epoch, n) } switch n := n.(type) { From 72c3415fda6a3967dd3b06fbe49197c47b34053e Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:11:06 +0800 Subject: [PATCH 7/9] state/stateobject: add dirtystorage touch & revive logic; trie: fix local review leaf expand bug; --- core/state/state_expiry.go | 6 ------ core/state/state_object.go | 18 ++++++++++++++++++ trie/trie_expiry.go | 8 +++++++- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/core/state/state_expiry.go b/core/state/state_expiry.go index d0742ff65f..75b1b9ec60 100644 --- a/core/state/state_expiry.go +++ b/core/state/state_expiry.go @@ -41,9 +41,6 @@ func fetchExpiredStorageFromRemote(meta *stateExpiryMeta, addr common.Address, r // if there need revive expired state, try to revive locally, when the node is not being pruned, just renew the epoch val, err := tr.TryLocalRevive(addr, key.Bytes()) //log.Debug("fetchExpiredStorageFromRemote TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) - if _, ok := err.(*trie.MissingNodeError); !ok { - return nil, err - } switch err.(type) { case *trie.MissingNodeError: // cannot revive locally, request from remote @@ -86,9 +83,6 @@ func batchFetchExpiredFromRemote(expiryMeta *stateExpiryMeta, addr common.Addres for i, key := range keys { val, err := tr.TryLocalRevive(addr, key.Bytes()) //log.Debug("fetchExpiredStorageFromRemote TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) - if _, ok := err.(*trie.MissingNodeError); !ok { - return nil, err - } switch err.(type) { case *trie.MissingNodeError: expiredKeys = append(expiredKeys, key) diff --git a/core/state/state_object.go b/core/state/state_object.go index 317a70495e..e137eb4a36 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -500,6 +500,24 @@ func (s *stateObject) updateTrie() (Trie, error) { } //log.Debug("updateTrie pendingFutureReviveState", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) } + // TODO(0xbundler): find some trie node with wrong epoch, temporary add get op, will fix later + //for key, val := range dirtyStorage { + // _, err = tr.GetStorage(s.address, key.Bytes()) + // if err == nil { + // continue + // } + // log.Error("EnableExpire GetStorage error", "addr", s.address, "key", key, "val", val, "origin", s.originStorage[key], "err", err) + // enErr, ok := err.(*trie.ExpiredNodeError) + // if !ok { + // s.db.setError(fmt.Errorf("state object dirtyStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) + // continue + // } + // if _, err = fetchExpiredStorageFromRemote(s.db.expiryMeta, s.address, s.data.Root, tr, enErr.Path, key); err != nil { + // log.Error("EnableExpire GetStorage fetchExpiredStorageFromRemote error", "addr", s.address, "key", key, "val", val, "origin", s.originStorage[key], "err", err) + // s.db.setError(fmt.Errorf("state object dirtyStorage fetchExpiredStorageFromRemote err, contract: %v, key: %v, path: %v, err: %v", s.address, key, enErr.Path, err)) + // } + // //log.Debug("updateTrie dirtyStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + //} } for key, value := range dirtyStorage { if len(value) == 0 { diff --git a/trie/trie_expiry.go b/trie/trie_expiry.go index 94ee7927ee..4a31086574 100644 --- a/trie/trie_expiry.go +++ b/trie/trie_expiry.go @@ -30,7 +30,13 @@ func (t *Trie) tryLocalRevive(origNode node, key []byte, pos int, epoch types.St return n, n, expired, nil case *shortNode: if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { - // key not found in trie + // key not found in trie, but just revive for expand + if t.renewNode(epoch, false, expired) { + n = n.copy() + n.setEpoch(t.currentEpoch) + n.flags = t.newFlag() + return nil, n, true, nil + } return nil, n, false, nil } value, newnode, didResolve, err := t.tryLocalRevive(n.Val, key, pos+len(n.Key), epoch) From 0670ccea5a5040e0828b8417f20df37221f12518 Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:52:43 +0800 Subject: [PATCH 8/9] state/stateobject: fix revive in diff & delete shrink miss node bug; trie: support revive from prefix; --- core/state/state_expiry.go | 21 +++--- core/state/state_object.go | 70 +++++++++++++------ core/state/trie_prefetcher.go | 6 +- eth/protocols/snap/handler.go | 8 +-- ethclient/gethclient/gethclient.go | 2 +- trie/errors.go | 14 ++++ trie/trie.go | 108 ++++++++++++----------------- 7 files changed, 124 insertions(+), 105 deletions(-) diff --git a/core/state/state_expiry.go b/core/state/state_expiry.go index 75b1b9ec60..2779e13e41 100644 --- a/core/state/state_expiry.go +++ b/core/state/state_expiry.go @@ -33,22 +33,23 @@ func defaultStateExpiryMeta() *stateExpiryMeta { return &stateExpiryMeta{enableStateExpiry: false} } -// fetchExpiredStorageFromRemote request expired state from remote full state node; -func fetchExpiredStorageFromRemote(meta *stateExpiryMeta, addr common.Address, root common.Hash, tr Trie, prefixKey []byte, key common.Hash) (map[string][]byte, error) { +// tryReviveState request expired state from remote full state node; +func tryReviveState(meta *stateExpiryMeta, addr common.Address, root common.Hash, tr Trie, prefixKey []byte, key common.Hash, force bool) (map[string][]byte, error) { + if !meta.enableStateExpiry { + return nil, nil + } //log.Debug("fetching expired storage from remoteDB", "addr", addr, "prefix", prefixKey, "key", key) reviveTrieMeter.Mark(1) - if meta.enableLocalRevive { + if meta.enableLocalRevive && !force { // if there need revive expired state, try to revive locally, when the node is not being pruned, just renew the epoch val, err := tr.TryLocalRevive(addr, key.Bytes()) - //log.Debug("fetchExpiredStorageFromRemote TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) + //log.Debug("tryReviveState TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) switch err.(type) { case *trie.MissingNodeError: // cannot revive locally, request from remote case nil: reviveFromLocalMeter.Mark(1) - ret := make(map[string][]byte, 1) - ret[key.String()] = val - return ret, nil + return map[string][]byte{key.String(): val}, nil default: return nil, err } @@ -57,7 +58,7 @@ func fetchExpiredStorageFromRemote(meta *stateExpiryMeta, addr common.Address, r reviveFromRemoteMeter.Mark(1) // cannot revive locally, fetch remote proof proofs, err := meta.fullStateDB.GetStorageReviveProof(meta.originalRoot, addr, root, []string{common.Bytes2Hex(prefixKey)}, []string{common.Bytes2Hex(key[:])}) - //log.Debug("fetchExpiredStorageFromRemote GetStorageReviveProof", "addr", addr, "key", key, "proofs", len(proofs), "err", err) + //log.Debug("tryReviveState GetStorageReviveProof", "addr", addr, "key", key, "proofs", len(proofs), "err", err) if err != nil { return nil, err } @@ -82,7 +83,7 @@ func batchFetchExpiredFromRemote(expiryMeta *stateExpiryMeta, addr common.Addres var expiredPrefixKeys [][]byte for i, key := range keys { val, err := tr.TryLocalRevive(addr, key.Bytes()) - //log.Debug("fetchExpiredStorageFromRemote TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) + //log.Debug("tryReviveState TryLocalRevive", "addr", addr, "key", key, "val", val, "err", err) switch err.(type) { case *trie.MissingNodeError: expiredKeys = append(expiredKeys, key) @@ -119,7 +120,7 @@ func batchFetchExpiredFromRemote(expiryMeta *stateExpiryMeta, addr common.Addres // cannot revive locally, fetch remote proof reviveFromRemoteMeter.Mark(int64(len(keysStr))) proofs, err := expiryMeta.fullStateDB.GetStorageReviveProof(expiryMeta.originalRoot, addr, root, prefixKeysStr, keysStr) - //log.Debug("fetchExpiredStorageFromRemote GetStorageReviveProof", "addr", addr, "keys", keysStr, "prefixKeys", prefixKeysStr, "proofs", len(proofs), "err", err) + //log.Debug("tryReviveState GetStorageReviveProof", "addr", addr, "keys", keysStr, "prefixKeys", prefixKeysStr, "proofs", len(proofs), "err", err) if err != nil { return nil, err } diff --git a/core/state/state_object.go b/core/state/state_object.go index e137eb4a36..aa70191a4c 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -316,9 +316,9 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { } // handle state expiry situation if s.db.EnableExpire() { - if enErr, ok := err.(*trie.ExpiredNodeError); ok { + if path, ok := trie.ParseExpiredNodeErr(err); ok { //log.Debug("GetCommittedState expired in trie", "addr", s.address, "key", key, "err", err) - val, err = s.fetchExpiredFromRemote(enErr.Path, key, false) + val, err = s.tryReviveState(path, key, false) getCommittedStorageExpiredMeter.Mark(1) } else if err != nil { getCommittedStorageUnexpiredMeter.Mark(1) @@ -490,13 +490,14 @@ func (s *stateObject) updateTrie() (Trie, error) { if err == nil { continue } - enErr, ok := err.(*trie.ExpiredNodeError) + path, ok := trie.ParseExpiredNodeErr(err) if !ok { - s.db.setError(fmt.Errorf("state object pendingFutureReviveState err, contract: %v, key: %v, err: %v", s.address, key, err)) + s.db.setError(fmt.Errorf("updateTrie pendingFutureReviveState err, contract: %v, key: %v, err: %v", s.address, key, err)) + //log.Debug("updateTrie pendingFutureReviveState", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s), "err", err) continue } - if _, err = fetchExpiredStorageFromRemote(s.db.expiryMeta, s.address, s.data.Root, tr, enErr.Path, key); err != nil { - s.db.setError(fmt.Errorf("state object pendingFutureReviveState fetchExpiredStorageFromRemote err, contract: %v, key: %v, path: %v, err: %v", s.address, key, enErr.Path, err)) + if _, err = tryReviveState(s.db.expiryMeta, s.address, s.data.Root, tr, path, key, false); err != nil { + s.db.setError(fmt.Errorf("updateTrie pendingFutureReviveState tryReviveState err, contract: %v, key: %v, path: %v, err: %v", s.address, key, path, err)) } //log.Debug("updateTrie pendingFutureReviveState", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) } @@ -507,35 +508,57 @@ func (s *stateObject) updateTrie() (Trie, error) { // continue // } // log.Error("EnableExpire GetStorage error", "addr", s.address, "key", key, "val", val, "origin", s.originStorage[key], "err", err) - // enErr, ok := err.(*trie.ExpiredNodeError) + // path, ok := trie.ParseExpiredNodeErr(err) // if !ok { // s.db.setError(fmt.Errorf("state object dirtyStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) // continue // } - // if _, err = fetchExpiredStorageFromRemote(s.db.expiryMeta, s.address, s.data.Root, tr, enErr.Path, key); err != nil { - // log.Error("EnableExpire GetStorage fetchExpiredStorageFromRemote error", "addr", s.address, "key", key, "val", val, "origin", s.originStorage[key], "err", err) - // s.db.setError(fmt.Errorf("state object dirtyStorage fetchExpiredStorageFromRemote err, contract: %v, key: %v, path: %v, err: %v", s.address, key, enErr.Path, err)) + // if _, err = tryReviveState(s.db.expiryMeta, s.address, s.data.Root, tr, path key); err != nil { + // log.Error("EnableExpire GetStorage tryReviveState error", "addr", s.address, "key", key, "val", val, "origin", s.originStorage[key], "err", err) + // s.db.setError(fmt.Errorf("state object dirtyStorage tryReviveState err, contract: %v, key: %v, path: %v, err: %v", s.address, key, enErr.Path, err)) // } // //log.Debug("updateTrie dirtyStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) //} } + touchExpiredStorage := make(map[common.Hash][]byte) for key, value := range dirtyStorage { if len(value) == 0 { - if err := tr.DeleteStorage(s.address, key[:]); err != nil { - s.db.setError(fmt.Errorf("state object update trie DeleteStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) + err := tr.DeleteStorage(s.address, key[:]) + if path, ok := trie.ParseExpiredNodeErr(err); ok { + touchExpiredStorage[key] = value + if _, err = tryReviveState(s.db.expiryMeta, s.address, s.data.Root, tr, path, key, true); err != nil { + s.db.setError(fmt.Errorf("updateTrie DeleteStorage tryReviveState err, contract: %v, key: %v, path: %v, err: %v", s.address, key, path, err)) + } + } else if err != nil { + s.db.setError(fmt.Errorf("updateTrie DeleteStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) } - //log.Debug("updateTrie DeleteStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + //log.Debug("updateTrie DeleteStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "err", err, "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) s.db.StorageDeleted += 1 } else { if err := tr.UpdateStorage(s.address, key[:], value); err != nil { - s.db.setError(fmt.Errorf("state object update trie UpdateStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) + s.db.setError(fmt.Errorf("updateTrie UpdateStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) } - //log.Debug("updateTrie UpdateStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + //log.Debug("updateTrie UpdateStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "err", err, "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) s.db.StorageUpdated += 1 } // Cache the items for preloading usedStorage = append(usedStorage, common.CopyBytes(key[:])) } + + // re-execute touched expired storage + for key, value := range touchExpiredStorage { + if len(value) == 0 { + if err := tr.DeleteStorage(s.address, key[:]); err != nil { + s.db.setError(fmt.Errorf("updateTrie DeleteStorage in touchExpiredStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) + } + //log.Debug("updateTrie DeleteStorage in touchExpiredStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "err", err, "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + } else { + if err := tr.UpdateStorage(s.address, key[:], value); err != nil { + s.db.setError(fmt.Errorf("updateTrie UpdateStorage in touchExpiredStorage err, contract: %v, key: %v, err: %v", s.address, key, err)) + } + //log.Debug("updateTrie UpdateStorage in touchExpiredStorage", "contract", s.address, "key", key, "epoch", s.db.Epoch(), "value", value, "tr.epoch", tr.Epoch(), "err", err, "tr", fmt.Sprintf("%p", tr), "ins", fmt.Sprintf("%p", s)) + } + } }() // If state snapshotting is active, cache the data til commit wg.Add(1) @@ -873,8 +896,8 @@ func (s *stateObject) queryFromReviveState(reviveState map[string]common.Hash, k return val, ok } -// fetchExpiredStorageFromRemote request expired state from remote full state node; -func (s *stateObject) fetchExpiredFromRemote(prefixKey []byte, key common.Hash, resolvePath bool) ([]byte, error) { +// tryReviveState request expired state from remote full state node; +func (s *stateObject) tryReviveState(prefixKey []byte, key common.Hash, resolvePath bool) ([]byte, error) { tr, err := s.getPendingReviveTrie() if err != nil { return nil, err @@ -883,19 +906,19 @@ func (s *stateObject) fetchExpiredFromRemote(prefixKey []byte, key common.Hash, // if no prefix, query from revive trie, got the newest expired info if resolvePath { val, err := tr.GetStorage(s.address, key.Bytes()) - // TODO(asyukii): temporary fix snap expired, but trie not expire, may investigate more later. - if val != nil { + if err == nil { + // TODO(asyukii): temporary fix snap expired, but trie not expire, may investigate more later. s.pendingReviveState[string(crypto.Keccak256(key[:]))] = common.BytesToHash(val) return val, nil } - enErr, ok := err.(*trie.ExpiredNodeError) + path, ok := trie.ParseExpiredNodeErr(err) if !ok { return nil, fmt.Errorf("cannot find expired state from trie, err: %v", err) } - prefixKey = enErr.Path + prefixKey = path } - kvs, err := fetchExpiredStorageFromRemote(s.db.expiryMeta, s.address, s.data.Root, tr, prefixKey, key) + kvs, err := tryReviveState(s.db.expiryMeta, s.address, s.data.Root, tr, prefixKey, key, false) if err != nil { return nil, err } @@ -924,6 +947,7 @@ func (s *stateObject) getExpirySnapStorage(key common.Hash) ([]byte, error, erro if val == nil { // record access empty kv, try touch in updateTrie for duplication + //log.Debug("getExpirySnapStorage nil val", "addr", s.address, "key", key, "val", val) s.futureReviveState(key) return nil, nil, nil } @@ -945,7 +969,7 @@ func (s *stateObject) getExpirySnapStorage(key common.Hash) ([]byte, error, erro //log.Debug("GetCommittedState expired in snapshot", "addr", s.address, "key", key, "val", val, "enc", enc, "err", err) // handle from remoteDB, if got err just setError, or return to revert in consensus version. - valRaw, err := s.fetchExpiredFromRemote(nil, key, true) + valRaw, err := s.tryReviveState(nil, key, true) if err != nil { return nil, nil, err } diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index 91307bd917..ccd62c395b 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -576,11 +576,11 @@ func (sf *subfetcher) loop() { // handle expired state if sf.expiryMeta.enableStateExpiry { // TODO(0xbundler): revert to single fetch, because tasks is a channel - if exErr, match := err.(*trie2.ExpiredNodeError); match { + if path, match := trie2.ParseExpiredNodeErr(err); match { key := common.BytesToHash(task) - _, err = fetchExpiredStorageFromRemote(sf.expiryMeta, sf.addr, sf.root, sf.trie, exErr.Path, key) + _, err = tryReviveState(sf.expiryMeta, sf.addr, sf.root, sf.trie, path, key, false) if err != nil { - log.Error("subfetcher fetchExpiredStorageFromRemote err", "addr", sf.addr, "path", exErr.Path, "err", err) + log.Error("subfetcher tryReviveState err", "addr", sf.addr, "path", path, "err", err) } } } diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go index 0eaf6a28e4..e60fc0fafc 100644 --- a/eth/protocols/snap/handler.go +++ b/eth/protocols/snap/handler.go @@ -445,8 +445,8 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP } proof := light.NewNodeSet() if err := stTrie.Prove(origin[:], proof); err != nil { - if enErr, ok := err.(*trie.ExpiredNodeError); ok { - err := reviveAndGetProof(chain.FullStateDB(), stTrie, req.Root, common.BytesToAddress(account[:]), acc.Root, enErr.Path, origin, proof) + if path, ok := trie.ParseExpiredNodeErr(err); ok { + err := reviveAndGetProof(chain.FullStateDB(), stTrie, req.Root, common.BytesToAddress(account[:]), acc.Root, path, origin, proof) if err != nil { log.Warn("Failed to prove storage range", "origin", origin, "err", err) return nil, nil @@ -455,8 +455,8 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP } if last != (common.Hash{}) { if err := stTrie.Prove(last[:], proof); err != nil { - if enErr, ok := err.(*trie.ExpiredNodeError); ok { - err := reviveAndGetProof(chain.FullStateDB(), stTrie, req.Root, common.BytesToAddress(account[:]), acc.Root, enErr.Path, last, proof) + if path, ok := trie.ParseExpiredNodeErr(err); ok { + err := reviveAndGetProof(chain.FullStateDB(), stTrie, req.Root, common.BytesToAddress(account[:]), acc.Root, path, last, proof) if err != nil { log.Warn("Failed to prove storage range", "origin", origin, "err", err) return nil, nil diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 7b9c8587fd..87f93d1625 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -140,7 +140,7 @@ func (ec *Client) GetStorageReviveProof(ctx context.Context, stateRoot common.Ha return &types.ReviveResult{ StorageProof: res.StorageProof, - BlockNum: res.BlockNum, + BlockNum: uint64(res.BlockNum), }, err } diff --git a/trie/errors.go b/trie/errors.go index 0be1ac0778..0ef3187ebd 100644 --- a/trie/errors.go +++ b/trie/errors.go @@ -85,3 +85,17 @@ func NewExpiredNodeError(path []byte, epoch types.StateEpoch, n node) error { func (err *ExpiredNodeError) Error() string { return fmt.Sprintf("expired trie node, path: %v, epoch: %v, node: %v", err.Path, err.Epoch, err.Node.fstring("")) } + +func ParseExpiredNodeErr(err error) ([]byte, bool) { + var path []byte + switch enErr := err.(type) { + case *ExpiredNodeError: + path = enErr.Path + case *MissingNodeError: // when meet MissingNodeError, try revive or fail + path = enErr.Path + default: + return nil, false + } + + return path, true +} diff --git a/trie/trie.go b/trie/trie.go index 39b1dbd4ca..d7149b23a5 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -314,38 +314,29 @@ func (t *Trie) getWithEpoch(origNode node, key []byte, pos int, epoch types.Stat } } -// updateChildNodeEpoch traverses the trie and update the node epoch for each accessed trie node. +// refreshNubEpoch traverses the trie and update the node epoch for each accessed trie node. // Under an expiry scheme where a hash node is accessed, its parent node's epoch will not be updated. -func (t *Trie) updateChildNodeEpoch(origNode node, key []byte, pos int, epoch types.StateEpoch) (newnode node, updateEpoch bool, err error) { +func refreshNubEpoch(origNode node, epoch types.StateEpoch) node { switch n := (origNode).(type) { case nil: - return nil, false, nil + return nil case valueNode: - return n, true, nil + return n case *shortNode: - if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { - return n, false, nil - } - newnode, updateEpoch, err = t.updateChildNodeEpoch(n.Val, key, pos+len(n.Key), epoch) - if err == nil && updateEpoch { - n = n.copy() - n.Val = newnode - n.setEpoch(t.currentEpoch) - n.flags = t.newFlag() - } - return n, true, err + n.Val = refreshNubEpoch(n.Val, epoch) + n.setEpoch(epoch) + n.flags = nodeFlag{dirty: true} + return n case *fullNode: - newnode, updateEpoch, err = t.updateChildNodeEpoch(n.Children[key[pos]], key, pos+1, epoch) - if err == nil && updateEpoch { - n = n.copy() - n.Children[key[pos]] = newnode - n.setEpoch(t.currentEpoch) - n.UpdateChildEpoch(int(key[pos]), t.currentEpoch) - n.flags = t.newFlag() + for i := 0; i < BranchNodeLength-1; i++ { + n.Children[i] = refreshNubEpoch(n.Children[i], epoch) + n.UpdateChildEpoch(i, epoch) } - return n, true, err + n.setEpoch(epoch) + n.flags = nodeFlag{dirty: true} + return n case hashNode: - return n, false, err + return n default: panic(fmt.Sprintf("%T: invalid node: %v", origNode, origNode)) } @@ -954,6 +945,7 @@ func (t *Trie) deleteWithEpoch(n node, prefix, key []byte, epoch types.StateEpoc // shortNode{..., shortNode{...}}. Since the entry // might not be loaded yet, resolve it just for this // check. + // Attention: if Children[pos] has pruned, just fetch from remote cnode, err := t.resolve(n.Children[pos], append(prefix, byte(pos)), n.GetChildEpoch(pos)) if err != nil { return false, nil, err @@ -1283,6 +1275,7 @@ func (t *Trie) TryRevive(key []byte, proof []*MPTProofNub) ([]*MPTProofNub, erro for _, nub := range proof { rootExpired := types.EpochExpired(t.getRootEpoch(), t.currentEpoch) newNode, didRevive, err := t.tryRevive(t.root, key, nub.n1PrefixKey, *nub, 0, t.currentEpoch, rootExpired) + //log.Debug("tryRevive", "key", key, "nub.n1PrefixKey", nub.n1PrefixKey, "nub", nub, "err", err) if _, ok := err.(*ReviveNotExpiredError); ok { reviveNotExpiredMeter.Mark(1) continue @@ -1300,6 +1293,7 @@ func (t *Trie) TryRevive(key []byte, proof []*MPTProofNub) ([]*MPTProofNub, erro return successNubs, nil } +// tryRevive it just revive from targetPrefixKey func (t *Trie) tryRevive(n node, key []byte, targetPrefixKey []byte, nub MPTProofNub, pos int, epoch types.StateEpoch, isExpired bool) (node, bool, error) { if pos > len(targetPrefixKey) { @@ -1307,9 +1301,8 @@ func (t *Trie) tryRevive(n node, key []byte, targetPrefixKey []byte, nub MPTProo } if pos == len(targetPrefixKey) { - - if !isExpired { - return nil, false, NewReviveNotExpiredErr(key[:pos], epoch) + if !t.isExpiredNode(n, targetPrefixKey, epoch, isExpired) { + return nil, false, NewReviveNotExpiredErr(targetPrefixKey[:pos], epoch) } hn, ok := n.(hashNode) if !ok { @@ -1326,39 +1319,20 @@ func (t *Trie) tryRevive(n node, key []byte, targetPrefixKey []byte, nub MPTProo if !ok { return nil, false, fmt.Errorf("invalid node type") } - tryUpdateNodeEpoch(nub.n2, t.currentEpoch) - newnode, _, err := t.updateChildNodeEpoch(nub.n2, key, pos+len(n1.Key), t.currentEpoch) - if err != nil { - return nil, false, fmt.Errorf("update child node epoch while reviving failed, err: %v", err) - } - n1 = n1.copy() - n1.Val = newnode - n1.flags = t.newFlag() - tryUpdateNodeEpoch(n1, t.currentEpoch) - renew, _, err := t.updateChildNodeEpoch(n1, key, pos, t.currentEpoch) - if err != nil { - return nil, false, fmt.Errorf("update child node epoch while reviving failed, err: %v", err) - } - return renew, true, nil - } - - tryUpdateNodeEpoch(nub.n1, t.currentEpoch) - newnode, _, err := t.updateChildNodeEpoch(nub.n1, key, pos, t.currentEpoch) - if err != nil { - return nil, false, fmt.Errorf("update child node epoch while reviving failed, err: %v", err) + n1.Val = nub.n2 + return refreshNubEpoch(n1, t.currentEpoch), true, nil } - - return newnode, true, nil + return refreshNubEpoch(nub.n1, t.currentEpoch), true, nil } if isExpired { - return nil, false, NewExpiredNodeError(key[:pos], epoch, n) + return nil, false, NewExpiredNodeError(targetPrefixKey[:pos], epoch, n) } switch n := n.(type) { case *shortNode: - if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { - return nil, false, fmt.Errorf("key %v not found", key) + if len(targetPrefixKey)-pos < len(n.Key) || !bytes.Equal(n.Key, targetPrefixKey[pos:pos+len(n.Key)]) { + return nil, false, fmt.Errorf("key %v not found", targetPrefixKey) } newNode, didRevive, err := t.tryRevive(n.Val, key, targetPrefixKey, nub, pos+len(n.Key), epoch, isExpired) if didRevive && err == nil { @@ -1369,7 +1343,7 @@ func (t *Trie) tryRevive(n node, key []byte, targetPrefixKey []byte, nub MPTProo } return n, didRevive, err case *fullNode: - childIndex := int(key[pos]) + childIndex := int(targetPrefixKey[pos]) isExpired, _ := n.ChildExpired(nil, childIndex, t.currentEpoch) newNode, didRevive, err := t.tryRevive(n.Children[childIndex], key, targetPrefixKey, nub, pos+1, epoch, isExpired) if didRevive && err == nil { @@ -1387,11 +1361,11 @@ func (t *Trie) tryRevive(n node, key []byte, targetPrefixKey []byte, nub MPTProo return n, didRevive, err case hashNode: - child, err := t.resolveAndTrack(n, key[:pos]) + child, err := t.resolveAndTrack(n, targetPrefixKey[:pos]) if err != nil { return nil, false, err } - if err = t.resolveEpochMetaAndTrack(child, epoch, key[:pos]); err != nil { + if err = t.resolveEpochMetaAndTrack(child, epoch, targetPrefixKey[:pos]); err != nil { return nil, false, err } @@ -1419,15 +1393,6 @@ func (t *Trie) ExpireByPrefix(prefixKeyHex []byte) error { return nil } -func tryUpdateNodeEpoch(origin node, epoch types.StateEpoch) { - switch n := origin.(type) { - case *shortNode: - n.setEpoch(epoch) - case *fullNode: - n.setEpoch(epoch) - } -} - func (t *Trie) expireByPrefix(n node, prefixKeyHex []byte) (node, bool, error) { // Loop through prefix key // When prefix key is empty, generate the hash node of the current node @@ -1791,3 +1756,18 @@ func (t *Trie) UpdateRootEpoch(epoch types.StateEpoch) { func (t *Trie) UpdateCurrentEpoch(epoch types.StateEpoch) { t.currentEpoch = epoch } + +// isExpiredNode check if expired or missed, it may prune by old state snap +func (t *Trie) isExpiredNode(n node, targetPrefixKey []byte, epoch types.StateEpoch, expired bool) bool { + if expired { + return true + } + + // check if there miss the trie node + _, err := t.resolve(n, targetPrefixKey, epoch) + if _, ok := err.(*MissingNodeError); ok { + return true + } + + return false +} From 7da1af70d58c993dfb16cda622a1c9bc6e13d034 Mon Sep 17 00:00:00 2001 From: 0xbundler <124862913+0xbundler@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:03:41 +0800 Subject: [PATCH 9/9] ut: fix some broken ut; lint: fix some golang lint; --- cmd/geth/dbcmd.go | 3 +++ cmd/geth/snapshot.go | 3 ++- cmd/utils/flags.go | 5 +++-- core/rawdb/accessors_trie.go | 3 ++- core/state/database.go | 3 ++- core/state/pruner/pruner.go | 7 ++++--- core/state/snapshot/conversion.go | 2 +- core/state/snapshot/generate_test.go | 1 - core/state/snapshot/snapshot_expire_test.go | 10 ++++------ core/state/snapshot/snapshot_value.go | 1 + core/state/snapshot/snapshot_value_test.go | 6 +++--- core/state/state_expiry.go | 4 ++-- core/state/state_object.go | 21 +++------------------ core/state/statedb.go | 14 ++------------ core/txpool/blobpool/interface.go | 3 ++- core/types/gen_account_rlp.go | 7 +++++-- core/types/gen_header_rlp.go | 7 +++++-- core/types/gen_log_rlp.go | 7 +++++-- core/types/gen_withdrawal_rlp.go | 7 +++++-- core/types/meta_test.go | 3 ++- core/types/state_expiry.go | 3 ++- core/types/typed_trie_node.go | 3 ++- eth/ethconfig/config.go | 3 ++- ethclient/gethclient/gethclient.go | 4 ++-- ethdb/fullstatedb.go | 5 +++-- internal/ethapi/api.go | 3 ++- miner/worker.go | 3 ++- trie/committer.go | 1 + trie/database.go | 3 ++- trie/epochmeta/database.go | 9 +++------ trie/epochmeta/difflayer.go | 7 ++++--- trie/epochmeta/difflayer_test.go | 20 ++++++++------------ trie/epochmeta/disklayer.go | 5 +++-- trie/epochmeta/snapshot.go | 7 ++++--- trie/epochmeta/snapshot_test.go | 5 +++-- trie/inspect_trie.go | 7 ++----- trie/node.go | 8 -------- trie/proof.go | 3 +-- trie/tracer.go | 5 ++--- trie/trie.go | 12 +++++------- trie/trie_expiry.go | 1 + trie/trie_reader.go | 5 +++-- trie/trie_test.go | 2 -- trie/triedb/pathdb/difflayer.go | 3 ++- trie/triedb/pathdb/disklayer.go | 3 ++- trie/triedb/pathdb/layertree.go | 3 ++- trie/triedb/pathdb/nodebuffer.go | 3 ++- trie/triedb/pathdb/testutils.go | 3 +++ trie/trienode/node.go | 3 ++- trie/typed_trie_node_test.go | 5 +++-- 50 files changed, 129 insertions(+), 135 deletions(-) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 2f176d14fd..cb54c7694d 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -429,6 +429,9 @@ func inspectTrie(ctx *cli.Context) error { return err } theInspect, err := trie.NewInspector(trieDB, theTrie, blockNumber, jobnum) + if err != nil { + return err + } theInspect.Run() theInspect.DisplayResult() } diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 2134418ca7..99b9bc5161 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -21,11 +21,12 @@ import ( "encoding/json" "errors" "fmt" - "github.com/ethereum/go-ethereum/core" "os" "path/filepath" "time" + "github.com/ethereum/go-ethereum/core" + "github.com/prometheus/tsdb/fileutil" "github.com/ethereum/go-ethereum/cmd/utils" diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 7687cf87fd..de013fbe5a 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -23,8 +23,6 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" "math" "math/big" "net" @@ -37,6 +35,9 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/fatih/structs" pcsclite "github.com/gballet/go-libpcsclite" gopsutil "github.com/shirou/gopsutil/mem" diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index 3cbffc64df..2911901ab4 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -18,9 +18,10 @@ package rawdb import ( "fmt" - "github.com/ethereum/go-ethereum/core/types" "sync" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" diff --git a/core/state/database.go b/core/state/database.go index 58366be0ad..ab17685f84 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -19,9 +19,10 @@ package state import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/log" "time" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/core/rawdb" diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index bf91553c3a..7550e4743b 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -22,8 +22,6 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/ethereum/go-ethereum/params" - bloomfilter "github.com/holiman/bloomfilter/v2" "math" "math/big" "os" @@ -32,6 +30,9 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/params" + bloomfilter "github.com/holiman/bloomfilter/v2" + "github.com/prometheus/tsdb/fileutil" "github.com/ethereum/go-ethereum/common" @@ -683,7 +684,7 @@ func (p *Pruner) Prune(root common.Hash) error { // find target header header := p.chainHeader - for header != nil && header.Number.Uint64() >= 0 && header.Root != root { + for header != nil && header.Root != root { header = rawdb.ReadHeader(p.db, header.ParentHash, header.Number.Uint64()-1) } if header == nil || header.Root != root { diff --git a/core/state/snapshot/conversion.go b/core/state/snapshot/conversion.go index 9e272a4d98..0c61e168d6 100644 --- a/core/state/snapshot/conversion.go +++ b/core/state/snapshot/conversion.go @@ -142,7 +142,7 @@ func TraverseContractTrie(snaptree *Tree, root common.Hash, pruneExpiredTrieCh c // Start to feed leaves for acctIt.Next() { // Fetch the next account and process it concurrently - account, err = types.FullAccount(acctIt.(AccountIterator).Account()) + account, err = types.FullAccount(acctIt.Account()) if err != nil { break } diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index dc86636488..40a73f5bd8 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -582,7 +582,6 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) { rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("b-key-2")), val) val, _ = rlp.EncodeToBytes([]byte("b-val-3")) rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("b-key-3")), val) - } root := helper.Commit() diff --git a/core/state/snapshot/snapshot_expire_test.go b/core/state/snapshot/snapshot_expire_test.go index ac20e9c90f..0bb2dd762c 100644 --- a/core/state/snapshot/snapshot_expire_test.go +++ b/core/state/snapshot/snapshot_expire_test.go @@ -1,20 +1,18 @@ package snapshot import ( + "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/stretchr/testify/assert" - "testing" ) var ( - accountHash = common.HexToHash("0x31b67165f56d0ac50814cafa06748fb3b8fccd3c611a8117350e7a49b44ce130") - storageHash1 = common.HexToHash("0x0bb2f3e66816c6fd12513f053d5ee034b1fa2d448a1dc8ee7f56e4c87d6c53fe") - storageHash2 = common.HexToHash("0x0bb2f3e66816c93e142cd336c411ebd5576a90739bad7ec1ec0d4a63ea0ec1dc") - storageShrink1 = common.FromHex("0x0bb2f3e66816c") - storageNodeHash1 = common.HexToHash("0xcf0e24cb9417a38ff61d11def23fb0ec041257c81c04dec6156d5e6b30f4ec28") + accountHash = common.HexToHash("0x31b67165f56d0ac50814cafa06748fb3b8fccd3c611a8117350e7a49b44ce130") + storageHash1 = common.HexToHash("0x0bb2f3e66816c6fd12513f053d5ee034b1fa2d448a1dc8ee7f56e4c87d6c53fe") ) func TestShrinkExpiredLeaf(t *testing.T) { diff --git a/core/state/snapshot/snapshot_value.go b/core/state/snapshot/snapshot_value.go index e487889b70..b9086476d2 100644 --- a/core/state/snapshot/snapshot_value.go +++ b/core/state/snapshot/snapshot_value.go @@ -3,6 +3,7 @@ package snapshot import ( "bytes" "errors" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" ) diff --git a/core/state/snapshot/snapshot_value_test.go b/core/state/snapshot/snapshot_value_test.go index d316c95fbb..cb5088b20b 100644 --- a/core/state/snapshot/snapshot_value_test.go +++ b/core/state/snapshot/snapshot_value_test.go @@ -2,16 +2,16 @@ package snapshot import ( "encoding/hex" + "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/assert" - "testing" ) var ( - val, _ = hex.DecodeString("0000f9eef0150e074b32e3b3b6d34d2534222292e3953019a41d714d135763a6") - hash, _ = hex.DecodeString("2b6fad2e1335b0b4debd3de01c91f3f45d2b88465ff42ae2c53900f2f702101d") + val, _ = hex.DecodeString("0000f9eef0150e074b32e3b3b6d34d2534222292e3953019a41d714d135763a6") ) func TestRawValueEncode(t *testing.T) { diff --git a/core/state/state_expiry.go b/core/state/state_expiry.go index 2779e13e41..203ed2fa2e 100644 --- a/core/state/state_expiry.go +++ b/core/state/state_expiry.go @@ -3,13 +3,14 @@ package state import ( "bytes" "fmt" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/trie" - "time" ) var ( @@ -103,7 +104,6 @@ func batchFetchExpiredFromRemote(expiryMeta *stateExpiryMeta, addr common.Addres for i, key := range expiredKeys { keysStr[i] = common.Bytes2Hex(key[:]) } - } else { for i, prefix := range prefixKeys { prefixKeysStr[i] = common.Bytes2Hex(prefix) diff --git a/core/state/state_object.go b/core/state/state_object.go index aa70191a4c..7146146907 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -19,13 +19,14 @@ package state import ( "bytes" "fmt" - "github.com/ethereum/go-ethereum/core/state/snapshot" - "github.com/ethereum/go-ethereum/trie" "io" "math/big" "sync" "time" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -336,22 +337,6 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { return value } -// needLoadFromTrie If not found in snap when EnableExpire(), need check insert duplication from trie. -func (s *stateObject) needLoadFromTrie(err error, sv snapshot.SnapValue) bool { - if s.db.snap == nil { - return true - } - if !s.db.EnableExpire() { - return err != nil - } - - if err != nil || sv == nil { - return true - } - - return false -} - // SetState updates a value in account storage. func (s *stateObject) SetState(key, value common.Hash) { // If the new value is the same as old, don't set diff --git a/core/state/statedb.go b/core/state/statedb.go index c703bb1b92..24afa2256b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -18,16 +18,16 @@ package state import ( - "bytes" "errors" "fmt" - "github.com/ethereum/go-ethereum/ethdb" "math/big" "runtime" "sort" "sync" "time" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/gopool" "github.com/ethereum/go-ethereum/core/rawdb" @@ -1829,16 +1829,6 @@ func (s *StateDB) Commit(block uint64, failPostCommitFunc func(), postCommitFunc return root, diffLayer, nil } -func stringfyEpochMeta(meta map[common.Hash]map[string][]byte) string { - buf := bytes.NewBuffer(nil) - for hash, m := range meta { - for k, v := range m { - buf.WriteString(fmt.Sprintf("%v: %v|%v, ", hash, []byte(k), common.Bytes2Hex(v))) - } - } - return buf.String() -} - func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []types.DiffStorage) { destructs := make([]common.Address, 0, len(s.stateObjectsDestruct)) for account := range s.stateObjectsDestruct { diff --git a/core/txpool/blobpool/interface.go b/core/txpool/blobpool/interface.go index f3c0658fc2..a1852a5bd8 100644 --- a/core/txpool/blobpool/interface.go +++ b/core/txpool/blobpool/interface.go @@ -17,11 +17,12 @@ package blobpool import ( + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" - "math/big" ) // BlockChain defines the minimal set of methods needed to back a blob pool with diff --git a/core/types/gen_account_rlp.go b/core/types/gen_account_rlp.go index 5181d88411..9d07200e33 100644 --- a/core/types/gen_account_rlp.go +++ b/core/types/gen_account_rlp.go @@ -5,8 +5,11 @@ package types -import "github.com/ethereum/go-ethereum/rlp" -import "io" +import ( + "io" + + "github.com/ethereum/go-ethereum/rlp" +) func (obj *StateAccount) EncodeRLP(_w io.Writer) error { w := rlp.NewEncoderBuffer(_w) diff --git a/core/types/gen_header_rlp.go b/core/types/gen_header_rlp.go index a5ed5cd150..e05bde09f6 100644 --- a/core/types/gen_header_rlp.go +++ b/core/types/gen_header_rlp.go @@ -5,8 +5,11 @@ package types -import "github.com/ethereum/go-ethereum/rlp" -import "io" +import ( + "io" + + "github.com/ethereum/go-ethereum/rlp" +) func (obj *Header) EncodeRLP(_w io.Writer) error { w := rlp.NewEncoderBuffer(_w) diff --git a/core/types/gen_log_rlp.go b/core/types/gen_log_rlp.go index 4a6c6b0094..78fa783cee 100644 --- a/core/types/gen_log_rlp.go +++ b/core/types/gen_log_rlp.go @@ -5,8 +5,11 @@ package types -import "github.com/ethereum/go-ethereum/rlp" -import "io" +import ( + "io" + + "github.com/ethereum/go-ethereum/rlp" +) func (obj *rlpLog) EncodeRLP(_w io.Writer) error { w := rlp.NewEncoderBuffer(_w) diff --git a/core/types/gen_withdrawal_rlp.go b/core/types/gen_withdrawal_rlp.go index d0b4e0147a..e3fa001eb6 100644 --- a/core/types/gen_withdrawal_rlp.go +++ b/core/types/gen_withdrawal_rlp.go @@ -5,8 +5,11 @@ package types -import "github.com/ethereum/go-ethereum/rlp" -import "io" +import ( + "io" + + "github.com/ethereum/go-ethereum/rlp" +) func (obj *Withdrawal) EncodeRLP(_w io.Writer) error { w := rlp.NewEncoderBuffer(_w) diff --git a/core/types/meta_test.go b/core/types/meta_test.go index e03a5b5097..ac03a5595f 100644 --- a/core/types/meta_test.go +++ b/core/types/meta_test.go @@ -2,8 +2,9 @@ package types import ( "encoding/hex" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestMetaEncodeDecode(t *testing.T) { diff --git a/core/types/state_expiry.go b/core/types/state_expiry.go index ed2bb18e43..bb83a31f2c 100644 --- a/core/types/state_expiry.go +++ b/core/types/state_expiry.go @@ -3,8 +3,9 @@ package types import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/log" "strings" + + "github.com/ethereum/go-ethereum/log" ) const ( diff --git a/core/types/typed_trie_node.go b/core/types/typed_trie_node.go index 01eb120edf..dedb94eddd 100644 --- a/core/types/typed_trie_node.go +++ b/core/types/typed_trie_node.go @@ -3,8 +3,9 @@ package types import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/rlp" "io" + + "github.com/ethereum/go-ethereum/rlp" ) const ( diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 091e7c65ca..a8278c4ebf 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -19,9 +19,10 @@ package ethconfig import ( "errors" - "github.com/ethereum/go-ethereum/core/types" "time" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 87f93d1625..b219d0a9bc 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -130,7 +130,7 @@ func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []s func (ec *Client) GetStorageReviveProof(ctx context.Context, stateRoot common.Hash, account common.Address, root common.Hash, keys []string, prefixKeys []string) (*types.ReviveResult, error) { type reviveResult struct { StorageProof []types.ReviveStorageProof `json:"storageProof"` - BlockNum hexutil.Uint64 `json:"blockNum"` + BlockNum uint64 `json:"blockNum"` } var err error @@ -140,7 +140,7 @@ func (ec *Client) GetStorageReviveProof(ctx context.Context, stateRoot common.Ha return &types.ReviveResult{ StorageProof: res.StorageProof, - BlockNum: uint64(res.BlockNum), + BlockNum: res.BlockNum, }, err } diff --git a/ethdb/fullstatedb.go b/ethdb/fullstatedb.go index 500beac1fb..367351f5e4 100644 --- a/ethdb/fullstatedb.go +++ b/ethdb/fullstatedb.go @@ -5,14 +5,15 @@ import ( "context" "errors" "fmt" + "strings" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rpc" lru "github.com/hashicorp/golang-lru" - "strings" - "time" ) var ( diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 49148619fe..496502b14e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -21,11 +21,12 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/ethereum/go-ethereum/metrics" "math/big" "strings" "time" + "github.com/ethereum/go-ethereum/metrics" + "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/accounts" diff --git a/miner/worker.go b/miner/worker.go index d641eb899a..6892da7d35 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -19,12 +19,13 @@ package miner import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/metrics" "math/big" "sync" "sync/atomic" "time" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" diff --git a/trie/committer.go b/trie/committer.go index 2ad8d9f4f5..f43f58419b 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -18,6 +18,7 @@ package trie import ( "fmt" + "github.com/ethereum/go-ethereum/trie/epochmeta" "github.com/ethereum/go-ethereum/common" diff --git a/trie/database.go b/trie/database.go index 904b485119..d41056b687 100644 --- a/trie/database.go +++ b/trie/database.go @@ -19,10 +19,11 @@ package trie import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/trie/epochmeta" "math/big" "strings" + "github.com/ethereum/go-ethereum/trie/epochmeta" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" diff --git a/trie/epochmeta/database.go b/trie/epochmeta/database.go index 8feb858991..566c33915c 100644 --- a/trie/epochmeta/database.go +++ b/trie/epochmeta/database.go @@ -3,9 +3,10 @@ package epochmeta import ( "bytes" "fmt" + "math/big" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" - "math/big" "github.com/ethereum/go-ethereum/rlp" @@ -91,9 +92,5 @@ func AccountMeta2Bytes(meta types.StateMeta) ([]byte, error) { // IsEpochMetaPath add some skip hash check rule func IsEpochMetaPath(path []byte) bool { - if bytes.Equal(AccountMetadataPath, path) { - return true - } - - return false + return bytes.Equal(AccountMetadataPath, path) } diff --git a/trie/epochmeta/difflayer.go b/trie/epochmeta/difflayer.go index 2838e8a9d0..2e2f73050a 100644 --- a/trie/epochmeta/difflayer.go +++ b/trie/epochmeta/difflayer.go @@ -4,13 +4,14 @@ import ( "bytes" "encoding/binary" "errors" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/rlp" - bloomfilter "github.com/holiman/bloomfilter/v2" "math" "math/big" "math/rand" "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" + bloomfilter "github.com/holiman/bloomfilter/v2" ) const ( diff --git a/trie/epochmeta/difflayer_test.go b/trie/epochmeta/difflayer_test.go index 54cea5f3c3..ebe1c99818 100644 --- a/trie/epochmeta/difflayer_test.go +++ b/trie/epochmeta/difflayer_test.go @@ -1,9 +1,10 @@ package epochmeta import ( - "github.com/ethereum/go-ethereum/core/types" "testing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/stretchr/testify/assert" @@ -12,17 +13,12 @@ import ( const hashLen = len(common.Hash{}) var ( - blockRoot0 = makeHash("b0") - blockRoot1 = makeHash("b1") - blockRoot2 = makeHash("b2") - blockRoot3 = makeHash("b3") - storageRoot0 = makeHash("s0") - storageRoot1 = makeHash("s1") - storageRoot2 = makeHash("s2") - storageRoot3 = makeHash("s3") - contract1 = makeHash("c1") - contract2 = makeHash("c2") - contract3 = makeHash("c3") + blockRoot0 = makeHash("b0") + blockRoot1 = makeHash("b1") + blockRoot2 = makeHash("b2") + contract1 = makeHash("c1") + contract2 = makeHash("c2") + contract3 = makeHash("c3") ) func TestEpochMetaDiffLayer_whenGenesis(t *testing.T) { diff --git a/trie/epochmeta/disklayer.go b/trie/epochmeta/disklayer.go index cc7894736c..4d71de4c3b 100644 --- a/trie/epochmeta/disklayer.go +++ b/trie/epochmeta/disklayer.go @@ -3,14 +3,15 @@ package epochmeta import ( "bytes" "errors" + "math/big" + "sync" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" lru "github.com/hashicorp/golang-lru" - "math/big" - "sync" ) const ( diff --git a/trie/epochmeta/snapshot.go b/trie/epochmeta/snapshot.go index 71201f961d..decbdaaf87 100644 --- a/trie/epochmeta/snapshot.go +++ b/trie/epochmeta/snapshot.go @@ -4,15 +4,16 @@ import ( "bytes" "errors" "fmt" + "io" + "math/big" + "sync" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" - "io" - "math/big" - "sync" ) // snapshot record diff layer and disk layer of shadow nodes, support mini reorg diff --git a/trie/epochmeta/snapshot_test.go b/trie/epochmeta/snapshot_test.go index 35099d5ba2..0cc4145eee 100644 --- a/trie/epochmeta/snapshot_test.go +++ b/trie/epochmeta/snapshot_test.go @@ -1,11 +1,12 @@ package epochmeta import ( - "github.com/ethereum/go-ethereum/ethdb/memorydb" - "github.com/stretchr/testify/assert" "math/big" "strconv" "testing" + + "github.com/ethereum/go-ethereum/ethdb/memorydb" + "github.com/stretchr/testify/assert" ) func TestEpochMetaDiffLayer_capDiffLayers(t *testing.T) { diff --git a/trie/inspect_trie.go b/trie/inspect_trie.go index a6a9df7b7d..5d4cc00d04 100644 --- a/trie/inspect_trie.go +++ b/trie/inspect_trie.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "fmt" - "github.com/ethereum/go-ethereum/core/types" "math/big" "os" "runtime" @@ -14,6 +13,8 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" @@ -32,7 +33,6 @@ type Inspector struct { trie *Trie // traverse trie blocknum uint64 root node // root of triedb - num uint64 // block number result *TotalTrieTreeStat // inspector result totalNum uint64 concurrentQueue chan struct{} @@ -206,7 +206,6 @@ func (inspect *Inspector) SubConcurrentTraversal(theTrie *Trie, theTrieTreeStat inspect.ConcurrentTraversal(theTrie, theTrieTreeStat, theNode, height, path) <-inspect.concurrentQueue inspect.wg.Done() - return } func (inspect *Inspector) ConcurrentTraversal(theTrie *Trie, theTrieTreeStat *TrieTreeStat, theNode node, height uint32, path []byte) { @@ -274,7 +273,6 @@ func (inspect *Inspector) ConcurrentTraversal(theTrie *Trie, theTrieTreeStat *Tr panic(errors.New("Invalid node type to traverse.")) } theTrieTreeStat.AtomicAdd(theNode, height) - return } func (inspect *Inspector) DisplayResult() { @@ -312,6 +310,5 @@ func (inspect *Inspector) DisplayResult() { } stat, _ := inspect.result.theTrieTreeStats.Get(cntHash[1]) stat.Display(cntHash[1], "ContractTrie") - i++ } } diff --git a/trie/node.go b/trie/node.go index 235344bcca..1ec7f00855 100644 --- a/trie/node.go +++ b/trie/node.go @@ -89,14 +89,6 @@ func (n *shortNode) setEpoch(epoch types.StateEpoch) { n.epoch = epoch } -func (n *fullNode) getEpoch() types.StateEpoch { - return n.epoch -} - -func (n *shortNode) getEpoch() types.StateEpoch { - return n.epoch -} - func (n *fullNode) GetChildEpoch(index int) types.StateEpoch { if index < 16 { return n.EpochMap[index] diff --git a/trie/proof.go b/trie/proof.go index cc19b84e37..5abee92cf9 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -21,6 +21,7 @@ import ( "encoding/hex" "errors" "fmt" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/common" @@ -37,7 +38,6 @@ import ( // nodes of the longest existing prefix of the key (at least the root node), ending // with the node that proves the absence of the key. func (t *Trie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error { - var nodeEpoch types.StateEpoch // Short circuit if the trie is already committed and not usable. @@ -201,7 +201,6 @@ func (t *Trie) traverseNodes(tn node, prefixKey, suffixKey []byte, nodes *[]node } func (t *Trie) ProveByPath(key []byte, prefixKeyHex []byte, proofDb ethdb.KeyValueWriter) error { - if t.committed { return ErrCommitted } diff --git a/trie/tracer.go b/trie/tracer.go index fc37f70698..a4321e5345 100644 --- a/trie/tracer.go +++ b/trie/tracer.go @@ -18,6 +18,7 @@ package trie import ( "bytes" + "github.com/ethereum/go-ethereum/common" ) @@ -95,9 +96,7 @@ func (t *tracer) onExpandToBranchNode(path []byte) { if !t.tagEpochMeta { return } - if _, present := t.deleteEpochMetas[string(path)]; present { - delete(t.deleteEpochMetas, string(path)) - } + delete(t.deleteEpochMetas, string(path)) } // onDelete tracks the newly deleted trie node. If it's already diff --git a/trie/trie.go b/trie/trie.go index d7149b23a5..0e5928673d 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -21,16 +21,17 @@ import ( "bytes" "errors" "fmt" + "runtime" + "sync" + "sync/atomic" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/trie/epochmeta" "github.com/ethereum/go-ethereum/trie/trienode" - "runtime" - "sync" - "sync/atomic" - "time" ) var ( @@ -997,7 +998,6 @@ func (t *Trie) deleteWithEpoch(n node, prefix, key []byte, epoch types.StateEpoc default: panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key)) } - } func concat(s1 []byte, s2 ...byte) []byte { @@ -1295,7 +1295,6 @@ func (t *Trie) TryRevive(key []byte, proof []*MPTProofNub) ([]*MPTProofNub, erro // tryRevive it just revive from targetPrefixKey func (t *Trie) tryRevive(n node, key []byte, targetPrefixKey []byte, nub MPTProofNub, pos int, epoch types.StateEpoch, isExpired bool) (node, bool, error) { - if pos > len(targetPrefixKey) { return nil, false, fmt.Errorf("target revive node not found") } @@ -1581,7 +1580,6 @@ func (st *ScanTask) MoreThread() bool { // ScanForPrune traverses the storage trie and prunes all expired or unexpired nodes. func (t *Trie) ScanForPrune(st *ScanTask) error { - if !t.enableExpiry { return nil } diff --git a/trie/trie_expiry.go b/trie/trie_expiry.go index 4a31086574..8c7c8e32f4 100644 --- a/trie/trie_expiry.go +++ b/trie/trie_expiry.go @@ -3,6 +3,7 @@ package trie import ( "bytes" "fmt" + "github.com/ethereum/go-ethereum/core/types" ) diff --git a/trie/trie_reader.go b/trie/trie_reader.go index 4ec9ca27d0..3cad34c8de 100644 --- a/trie/trie_reader.go +++ b/trie/trie_reader.go @@ -19,14 +19,15 @@ package trie import ( "errors" "fmt" + "math/big" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/trie/epochmeta" "github.com/ethereum/go-ethereum/trie/triestate" - "math/big" - "time" ) var ( diff --git a/trie/trie_test.go b/trie/trie_test.go index aa704f8166..cb80fa8af2 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -1038,7 +1038,6 @@ func TestRevive(t *testing.T) { } func TestReviveCustom(t *testing.T) { - data := map[string]string{ "abcd": "A", "abce": "B", "abde": "C", "abdf": "D", "defg": "E", "defh": "F", "degh": "G", "degi": "H", @@ -1083,7 +1082,6 @@ func TestReviveCustom(t *testing.T) { // TestReviveBadProof tests that a trie cannot be revived from a bad proof func TestReviveBadProof(t *testing.T) { - dataA := map[string]string{ "abcd": "A", "abce": "B", "abde": "C", "abdf": "D", "defg": "E", "defh": "F", "degh": "G", "degi": "H", diff --git a/trie/triedb/pathdb/difflayer.go b/trie/triedb/pathdb/difflayer.go index 0ffa8a6d90..74ff69fedd 100644 --- a/trie/triedb/pathdb/difflayer.go +++ b/trie/triedb/pathdb/difflayer.go @@ -18,9 +18,10 @@ package pathdb import ( "fmt" - "github.com/ethereum/go-ethereum/trie/epochmeta" "sync" + "github.com/ethereum/go-ethereum/trie/epochmeta" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie/trienode" diff --git a/trie/triedb/pathdb/disklayer.go b/trie/triedb/pathdb/disklayer.go index 6bb2e05616..145f8a5e4f 100644 --- a/trie/triedb/pathdb/disklayer.go +++ b/trie/triedb/pathdb/disklayer.go @@ -19,9 +19,10 @@ package pathdb import ( "errors" "fmt" + "sync" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie/epochmeta" - "sync" "github.com/VictoriaMetrics/fastcache" "github.com/ethereum/go-ethereum/common" diff --git a/trie/triedb/pathdb/layertree.go b/trie/triedb/pathdb/layertree.go index f7fc4930bc..3590691d4c 100644 --- a/trie/triedb/pathdb/layertree.go +++ b/trie/triedb/pathdb/layertree.go @@ -19,9 +19,10 @@ package pathdb import ( "errors" "fmt" - "github.com/ethereum/go-ethereum/log" "sync" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie/trienode" diff --git a/trie/triedb/pathdb/nodebuffer.go b/trie/triedb/pathdb/nodebuffer.go index 851264cd54..3f6a17c078 100644 --- a/trie/triedb/pathdb/nodebuffer.go +++ b/trie/triedb/pathdb/nodebuffer.go @@ -18,9 +18,10 @@ package pathdb import ( "fmt" - "github.com/ethereum/go-ethereum/trie/epochmeta" "time" + "github.com/ethereum/go-ethereum/trie/epochmeta" + "github.com/VictoriaMetrics/fastcache" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" diff --git a/trie/triedb/pathdb/testutils.go b/trie/triedb/pathdb/testutils.go index d6fdacb421..070e13dea3 100644 --- a/trie/triedb/pathdb/testutils.go +++ b/trie/triedb/pathdb/testutils.go @@ -20,6 +20,8 @@ import ( "bytes" "fmt" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -130,6 +132,7 @@ func hash(states map[common.Hash][]byte) (common.Hash, []byte) { if len(input) == 0 { return types.EmptyRootHash, nil } + input, _ = rlp.EncodeToBytes(input) return crypto.Keccak256Hash(input), input } diff --git a/trie/trienode/node.go b/trie/trienode/node.go index 6c1660ff79..38bd67cfe5 100644 --- a/trie/trienode/node.go +++ b/trie/trienode/node.go @@ -18,10 +18,11 @@ package trienode import ( "fmt" - "github.com/ethereum/go-ethereum/trie/epochmeta" "sort" "strings" + "github.com/ethereum/go-ethereum/trie/epochmeta" + "github.com/ethereum/go-ethereum/common" ) diff --git a/trie/typed_trie_node_test.go b/trie/typed_trie_node_test.go index dc88be1958..490b101c4d 100644 --- a/trie/typed_trie_node_test.go +++ b/trie/typed_trie_node_test.go @@ -1,11 +1,12 @@ package trie import ( + "math/rand" + "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/assert" - "math/rand" - "testing" ) var (