diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 5120a76ce0..4ecf4eda98 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -130,6 +130,7 @@ var ( utils.CacheGCFlag, utils.CacheSnapshotFlag, // utils.CacheNoPrefetchFlag, + utils.CacheEnableSharedStorageFlag, utils.CachePreimagesFlag, utils.MultiDataBaseFlag, utils.PruneAncientDataFlag, // deprecated diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 930d6c42ae..93c5419bd3 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -569,6 +569,12 @@ var ( Usage: "Disable heuristic state prefetch during block import (less CPU and disk IO, more time waiting for data)", Category: flags.PerfCategory, } + CacheEnableSharedStorageFlag = &cli.BoolFlag{ + Name: "cache.enablesharedpool", + Usage: "Enable shared storage pool cache in statedb, default is false", + Value: false, + Category: flags.PerfCategory, + } CachePreimagesFlag = &cli.BoolFlag{ Name: "cache.preimages", Usage: "Enable recording the SHA3/keccak preimages of trie keys", @@ -2130,6 +2136,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheSnapshotFlag.Name) { cfg.SnapshotCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheSnapshotFlag.Name) / 100 } + if ctx.IsSet(CacheEnableSharedStorageFlag.Name) { + cfg.EnableSharedStorage = true + log.Info("Enabled shared storage pool cache") + } else { + log.Info("Disabled shared storage pool cache") + } if ctx.IsSet(CacheLogSizeFlag.Name) { cfg.FilterLogCacheSize = ctx.Int(CacheLogSizeFlag.Name) } diff --git a/core/blockchain.go b/core/blockchain.go index 15a0dbfd59..4707d14392 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -156,6 +156,7 @@ const ( // CacheConfig contains the configuration values for the trie database // and state snapshot these are resident in a blockchain. type CacheConfig struct { + EnableSharedStorage bool // Whether to enable shared storage in statedb, improve execute stage performance ~6%. TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk @@ -2185,6 +2186,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness if err != nil { return nil, it.index, err } + statedb.EnableSharedStorage(bc.cacheConfig.EnableSharedStorage) + statedb.SetNeedBadSharedStorage(bc.chainConfig.NeedBadSharedStorage(block.Number())) bc.updateHighestVerifiedHeader(block.Header()) // If we are past Byzantium, enable prefetching to pull in trie node paths diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index b0df521fa7..1b6f2f8fdb 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -416,10 +416,11 @@ func (bc *BlockChain) State() (*state.StateDB, error) { // StateAt returns a new mutable state based on a particular point in time. func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { - stateDb, err := state.New(root, bc.statedb) + stateDb, err := state.NewWithSharedPool(root, bc.statedb) if err != nil { return nil, err } + stateDb.EnableSharedStorage(bc.cacheConfig.EnableSharedStorage) // If there's no trie and the specified snapshot is not available, getting // any state will by default return nil. diff --git a/core/state/state_object.go b/core/state/state_object.go index b164c7891d..5657c47780 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -161,10 +161,7 @@ func (s *stateObject) getPrefetchedTrie() Trie { return s.db.prefetcher.trie(s.addrHash, s.data.Root) } -func (s *stateObject) getOriginStorage(key common.Hash) (common.Hash, bool) { - if value, cached := s.originStorage[key]; cached { - return value, true - } +func (s *stateObject) tryGetFromSharedPool(key common.Hash) (common.Hash, bool) { // if L1 cache miss, try to get it from shared pool if s.sharedOriginStorage != nil { val, ok := s.sharedOriginStorage.Load(key) @@ -172,7 +169,6 @@ func (s *stateObject) getOriginStorage(key common.Hash) (common.Hash, bool) { return common.Hash{}, false } storage := val.(common.Hash) - s.originStorage[key] = storage return storage, true } return common.Hash{}, false @@ -211,9 +207,18 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { return value } - if value, cached := s.getOriginStorage(key); cached { + if value, cached := s.originStorage[key]; cached { return value } + + if s.db.needBadSharedStorage { + // keep compatible with old erroneous data(https://forum.bnbchain.org/t/about-the-hertzfix/2400). + if value, cached := s.tryGetFromSharedPool(key); cached { + s.originStorage[key] = value + return value + } + } + // If the object was destructed in *this* block (and potentially resurrected), // the storage has been cleared out, and we should *not* consult the previous // database about any storage values. The only possible alternatives are: @@ -224,6 +229,12 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { s.originStorage[key] = common.Hash{} // track the empty slot as origin value return common.Hash{} } + + if value, cached := s.tryGetFromSharedPool(key); cached { + s.originStorage[key] = value + return value + } + s.db.StorageLoaded++ var start time.Time diff --git a/core/state/statedb.go b/core/state/statedb.go index ef4f19cf82..983628ed6d 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -112,8 +112,12 @@ type StateDB struct { // perspective. This map is populated at the transaction boundaries. mutations map[common.Address]*mutation - storagePool *StoragePool // sharedPool to store L1 originStorage of stateObjects + // if needBadSharedStorage = true, try read from sharedPool firstly, compatible with old erroneous data(https://forum.bnbchain.org/t/about-the-hertzfix/2400). + // else read from sharedPool which is not in stateObjectsDestruct. + needBadSharedStorage bool writeOnSharedStorage bool // Write to the shared origin storage of a stateObject while reading from the underlying storage layer. + storagePool *StoragePool // sharedPool to store L1 originStorage of stateObjects + // DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs @@ -212,8 +216,15 @@ func New(root common.Hash, db Database) (*StateDB, error) { return sdb, nil } -func (s *StateDB) EnableWriteOnSharedStorage() { - s.writeOnSharedStorage = true +func (s *StateDB) EnableSharedStorage(enableSharedStorage bool) { + s.writeOnSharedStorage = enableSharedStorage +} + +func (s *StateDB) SetNeedBadSharedStorage(needBadSharedStorage bool) { + s.needBadSharedStorage = needBadSharedStorage + if needBadSharedStorage { + s.writeOnSharedStorage = true + } } // In mining mode, we will try multi-fillTransactions to get the most profitable one. @@ -792,14 +803,15 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB { stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)), mutations: make(map[common.Address]*mutation, len(s.mutations)), dbErr: s.dbErr, + needBadSharedStorage: s.needBadSharedStorage, + writeOnSharedStorage: s.writeOnSharedStorage, storagePool: s.storagePool, - // writeOnSharedStorage: s.writeOnSharedStorage, - refund: s.refund, - thash: s.thash, - txIndex: s.txIndex, - logs: make(map[common.Hash][]*types.Log, len(s.logs)), - logSize: s.logSize, - preimages: maps.Clone(s.preimages), + refund: s.refund, + thash: s.thash, + txIndex: s.txIndex, + logs: make(map[common.Hash][]*types.Log, len(s.logs)), + logSize: s.logSize, + preimages: maps.Clone(s.preimages), transientStorage: s.transientStorage.Copy(), journal: s.journal.copy(), diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index bb156a2da3..1139dc8a2b 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -54,10 +54,6 @@ func (p *statePrefetcher) Prefetch(transactions types.Transactions, header *type for i := 0; i < prefetchThread; i++ { go func() { newStatedb := statedb.CopyDoPrefetch() - if p.config.NeedBadSharedStorage(header.Number) { - newStatedb.EnableWriteOnSharedStorage() - } - gaspool := new(GasPool).AddGas(gasLimit) evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), newStatedb, p.config, *cfg) // Iterate over and process the individual transactions diff --git a/eth/backend.go b/eth/backend.go index 06fca4f52d..ad70609549 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -305,6 +305,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { EnablePreimageRecording: config.EnablePreimageRecording, } cacheConfig = &core.CacheConfig{ + EnableSharedStorage: config.EnableSharedStorage, TrieCleanLimit: config.TrieCleanCache, TrieCleanNoPrefetch: config.NoPrefetch, TrieDirtyLimit: config.TrieDirtyCache, diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 80c4e669a1..dfe89710e2 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -50,28 +50,29 @@ var FullNodeGPO = gasprice.Config{ // Defaults contains default settings for use on the BSC main net. var Defaults = Config{ - SyncMode: SnapSync, - NetworkId: 0, // enable auto configuration of networkID == chainID - TxLookupLimit: 2350000, - TransactionHistory: 2350000, - BlockHistory: 0, - StateHistory: params.FullImmutabilityThreshold, - DatabaseCache: 512, - TrieCleanCache: 154, - TrieDirtyCache: 256, - TrieTimeout: 10 * time.Minute, - TriesInMemory: 128, - TriesVerifyMode: core.LocalVerify, - SnapshotCache: 102, - FilterLogCacheSize: 32, - Miner: minerconfig.DefaultConfig, - TxPool: legacypool.DefaultConfig, - BlobPool: blobpool.DefaultConfig, - RPCGasCap: 50000000, - RPCEVMTimeout: 5 * time.Second, - GPO: FullNodeGPO, - RPCTxFeeCap: 1, // 1 ether - BlobExtraReserve: params.DefaultExtraReserveForBlobRequests, // Extra reserve threshold for blob, blob never expires when -1 is set, default 28800 + SyncMode: SnapSync, + NetworkId: 0, // enable auto configuration of networkID == chainID + TxLookupLimit: 2350000, + TransactionHistory: 2350000, + BlockHistory: 0, + StateHistory: params.FullImmutabilityThreshold, + DatabaseCache: 512, + EnableSharedStorage: false, + TrieCleanCache: 154, + TrieDirtyCache: 256, + TrieTimeout: 10 * time.Minute, + TriesInMemory: 128, + TriesVerifyMode: core.LocalVerify, + SnapshotCache: 102, + FilterLogCacheSize: 32, + Miner: minerconfig.DefaultConfig, + TxPool: legacypool.DefaultConfig, + BlobPool: blobpool.DefaultConfig, + RPCGasCap: 50000000, + RPCEVMTimeout: 5 * time.Second, + GPO: FullNodeGPO, + RPCTxFeeCap: 1, // 1 ether + BlobExtraReserve: params.DefaultExtraReserveForBlobRequests, // Extra reserve threshold for blob, blob never expires when -1 is set, default 28800 } //go:generate go run github.com/fjl/gencodec -type Config -formats toml -out gen_config.go @@ -145,13 +146,14 @@ type Config struct { // !!Deprecated: use 'BlockHistory' instead. PruneAncientData bool - TrieCleanCache int - TrieDirtyCache int - TrieTimeout time.Duration - SnapshotCache int - TriesInMemory uint64 - TriesVerifyMode core.VerifyMode - Preimages bool + EnableSharedStorage bool + TrieCleanCache int + TrieDirtyCache int + TrieTimeout time.Duration + SnapshotCache int + TriesInMemory uint64 + TriesVerifyMode core.VerifyMode + Preimages bool // This is the number of blocks for which logs will be cached in the filter system. FilterLogCacheSize int diff --git a/params/config.go b/params/config.go index bb82d6c55a..a3eddfd9ab 100644 --- a/params/config.go +++ b/params/config.go @@ -968,7 +968,7 @@ func (c *ChainConfig) IsHertzfix(num *big.Int) bool { } func (c *ChainConfig) NeedBadSharedStorage(num *big.Int) bool { - if c.IsHertzfix(num) { + if c.IsHertzfix(num) || c.ChainID == nil { return false }