diff --git a/common/lru/basiclru.go b/common/lru/basiclru.go index b3369cf1f2..a429157fe5 100644 --- a/common/lru/basiclru.go +++ b/common/lru/basiclru.go @@ -34,7 +34,7 @@ type cacheItem[K any, V any] struct { // NewBasicLRU creates a new LRU cache. func NewBasicLRU[K comparable, V any](capacity int) BasicLRU[K, V] { - if capacity < 0 { + if capacity <= 0 { capacity = 1 } c := BasicLRU[K, V]{ diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index dcdfb20c63..53ccc34ccb 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + lru "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" @@ -41,7 +42,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" - lru "github.com/hashicorp/golang-lru" "golang.org/x/crypto/sha3" ) @@ -143,11 +143,11 @@ var ( type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error) // ecrecover extracts the Ethereum account address from a signed header. -func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { +func ecrecover(header *types.Header, sigcache *sigLRU) (common.Address, error) { // If the signature's already cached, return that hash := header.Hash() if address, known := sigcache.Get(hash); known { - return address.(common.Address), nil + return address, nil } // Retrieve the signature from the header extra-data if len(header.Extra) < extraSeal { @@ -173,8 +173,8 @@ type Clique struct { config *params.CliqueConfig // Consensus engine configuration parameters db ethdb.Database // Database to store and retrieve snapshot checkpoints - recents *lru.ARCCache // Snapshots for recent block to speed up reorgs - signatures *lru.ARCCache // Signatures of recent blocks to speed up mining + recents *lru.Cache[common.Hash, *Snapshot] // Snapshots for recent block to speed up reorgs + signatures *sigLRU // Signatures of recent blocks to speed up mining proposals map[common.Address]bool // Current list of proposals we are pushing @@ -195,8 +195,8 @@ func New(config *params.CliqueConfig, db ethdb.Database) *Clique { conf.Epoch = epochLength } // Allocate the snapshot caches and create the engine - recents, _ := lru.NewARC(inmemorySnapshots) - signatures, _ := lru.NewARC(inmemorySignatures) + recents := lru.NewCache[common.Hash, *Snapshot](inmemorySnapshots) + signatures := lru.NewCache[common.Hash, common.Address](inmemorySignatures) return &Clique{ config: &conf, @@ -375,7 +375,7 @@ func (c *Clique) snapshot(chain consensus.ChainHeaderReader, number uint64, hash for snap == nil { // If an in-memory snapshot was found, use that if s, ok := c.recents.Get(hash); ok { - snap = s.(*Snapshot) + snap = s break } // If an on-disk checkpoint snapshot can be found, use that diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go index 35eaf1eb77..e5efa5108f 100644 --- a/consensus/clique/snapshot.go +++ b/consensus/clique/snapshot.go @@ -23,12 +23,12 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" "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/params" - lru "github.com/hashicorp/golang-lru" ) // Vote represents a single vote that an authorized signer made to modify the @@ -47,10 +47,12 @@ type Tally struct { Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal } +type sigLRU = lru.Cache[common.Hash, common.Address] + // Snapshot is the state of the authorization voting at a given point in time. type Snapshot struct { config *params.CliqueConfig // Consensus engine parameters to fine tune behavior - sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover + sigcache *sigLRU // Cache of recent block signatures to speed up ecrecover Number uint64 `json:"number"` // Block number where the snapshot was created Hash common.Hash `json:"hash"` // Block hash where the snapshot was created @@ -70,7 +72,7 @@ func (s signersAscending) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // newSnapshot creates a new snapshot with the specified startup parameters. This // method does not initialize the set of recent signers, so only ever use if for // the genesis block. -func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot { +func newSnapshot(config *params.CliqueConfig, sigcache *sigLRU, number uint64, hash common.Hash, signers []common.Address) *Snapshot { snap := &Snapshot{ config: config, sigcache: sigcache, @@ -87,7 +89,7 @@ func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uin } // loadSnapshot loads an existing snapshot from the database. -func loadSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) { +func loadSnapshot(config *params.CliqueConfig, sigcache *sigLRU, db ethdb.Database, hash common.Hash) (*Snapshot, error) { blob, err := db.Get(append(rawdb.CliqueSnapshotPrefix, hash[:]...)) if err != nil { return nil, err diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index dfe00d4b93..6cb3124827 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -34,11 +34,11 @@ import ( "unsafe" "github.com/edsrzf/mmap-go" + lrupkg "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rpc" - "github.com/hashicorp/golang-lru/simplelru" ) var ErrInvalidDumpMagic = errors.New("invalid dump magic") @@ -165,34 +165,45 @@ func memoryMapAndGenerate(path string, size uint64, lock bool, generator func(bu return memoryMap(path, lock) } +type cacheOrDataset interface { + *cache | *dataset +} + // lru tracks caches or datasets by their last use time, keeping at most N of them. -type lru struct { +type lru[T cacheOrDataset] struct { what string - new func(epoch uint64) interface{} + new func(epoch uint64) T mu sync.Mutex // Items are kept in a LRU cache, but there is a special case: // We always keep an item for (highest seen epoch) + 1 as the 'future item'. - cache *simplelru.LRU + cache lrupkg.BasicLRU[uint64, T] future uint64 - futureItem interface{} + futureItem T } // newlru create a new least-recently-used cache for either the verification caches // or the mining datasets. -func newlru(what string, maxItems int, new func(epoch uint64) interface{}) *lru { - if maxItems <= 0 { - maxItems = 1 +func newlru[T cacheOrDataset](maxItems int, new func(epoch uint64) T) *lru[T] { + var what string + switch any(T(nil)).(type) { + case *cache: + what = "cache" + case *dataset: + what = "dataset" + default: + panic("unknown type") + } + return &lru[T]{ + what: what, + new: new, + cache: lrupkg.NewBasicLRU[uint64, T](maxItems), } - cache, _ := simplelru.NewLRU(maxItems, func(key, value interface{}) { - log.Trace("Evicted ethash "+what, "epoch", key) - }) - return &lru{what: what, new: new, cache: cache} } // get retrieves or creates an item for the given epoch. The first return value is always // non-nil. The second return value is non-nil if lru thinks that an item will be useful in // the near future. -func (lru *lru) get(epoch uint64) (item, future interface{}) { +func (lru *lru[T]) get(epoch uint64) (item, future T) { lru.mu.Lock() defer lru.mu.Unlock() @@ -226,9 +237,8 @@ type cache struct { once sync.Once // Ensures the cache is generated only once } -// newCache creates a new ethash verification cache and returns it as a plain Go -// interface to be usable in an LRU cache. -func newCache(epoch uint64) interface{} { +// newCache creates a new ethash verification cache. +func newCache(epoch uint64) *cache { return &cache{epoch: epoch} } @@ -308,7 +318,7 @@ type dataset struct { // newDataset creates a new ethash mining dataset and returns it as a plain Go // interface to be usable in an LRU cache. -func newDataset(epoch uint64) interface{} { +func newDataset(epoch uint64) *dataset { return &dataset{epoch: epoch} } @@ -439,8 +449,8 @@ type Config struct { type Ethash struct { config Config - caches *lru // In memory caches to avoid regenerating too often - datasets *lru // In memory datasets to avoid regenerating too often + caches *lru[*cache] // In memory caches to avoid regenerating too often + datasets *lru[*dataset] // In memory datasets to avoid regenerating too often // Mining related fields rand *rand.Rand // Properly seeded random source for nonces @@ -477,8 +487,8 @@ func New(config Config, notify []string, noverify bool) *Ethash { } ethash := &Ethash{ config: config, - caches: newlru("cache", config.CachesInMem, newCache), - datasets: newlru("dataset", config.DatasetsInMem, newDataset), + caches: newlru(config.CachesInMem, newCache), + datasets: newlru(config.DatasetsInMem, newDataset), update: make(chan struct{}), hashrate: metrics.NewMeterForced(), } @@ -573,15 +583,13 @@ func (ethash *Ethash) StopRemoteSealer() error { // stored on disk, and finally generating one if none can be found. func (ethash *Ethash) cache(block uint64) *cache { epoch := block / epochLength - currentI, futureI := ethash.caches.get(epoch) - current := currentI.(*cache) + current, future := ethash.caches.get(epoch) // Wait for generation finish. current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.CachesLockMmap, ethash.config.PowMode == ModeTest) // If we need a new future cache, now's a good time to regenerate it. - if futureI != nil { - future := futureI.(*cache) + if future != nil { go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.CachesLockMmap, ethash.config.PowMode == ModeTest) } return current @@ -596,25 +604,20 @@ func (ethash *Ethash) cache(block uint64) *cache { func (ethash *Ethash) dataset(block uint64, async bool) *dataset { // Retrieve the requested ethash dataset epoch := block / epochLength - currentI, futureI := ethash.datasets.get(epoch) - current := currentI.(*dataset) + current, future := ethash.datasets.get(epoch) // If async is specified, generate everything in a background thread if async && !current.generated() { go func() { current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest) - - if futureI != nil { - future := futureI.(*dataset) + if future != nil { future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest) } }() } else { // Either blocking generation was requested, or already done current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest) - - if futureI != nil { - future := futureI.(*dataset) + if future != nil { go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest) } } diff --git a/go.mod b/go.mod index 60afbfffb9..2012eda308 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,6 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/graph-gophers/graphql-go v1.3.0 github.com/hashicorp/go-bexpr v0.1.10 - github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.2.0 diff --git a/go.sum b/go.sum index 82818291cb..9e54fafbd6 100644 --- a/go.sum +++ b/go.sum @@ -226,7 +226,6 @@ github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpx github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= diff --git a/les/vflux/server/clientdb.go b/les/vflux/server/clientdb.go index 30cd9a6528..a39cbec36a 100644 --- a/les/vflux/server/clientdb.go +++ b/les/vflux/server/clientdb.go @@ -22,13 +22,13 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/utils" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" - lru "github.com/hashicorp/golang-lru" ) const ( @@ -57,7 +57,7 @@ var ( type nodeDB struct { db ethdb.KeyValueStore - cache *lru.Cache + cache *lru.Cache[string, utils.ExpiredValue] auxbuf []byte // 37-byte auxiliary buffer for key encoding verbuf [2]byte // 2-byte auxiliary buffer for db version evictCallBack func(mclock.AbsTime, bool, utils.ExpiredValue) bool // Callback to determine whether the balance can be evicted. @@ -67,10 +67,9 @@ type nodeDB struct { } func newNodeDB(db ethdb.KeyValueStore, clock mclock.Clock) *nodeDB { - cache, _ := lru.New(balanceCacheLimit) ndb := &nodeDB{ db: db, - cache: cache, + cache: lru.NewCache[string, utils.ExpiredValue](balanceCacheLimit), auxbuf: make([]byte, 37), clock: clock, closeCh: make(chan struct{}), @@ -125,8 +124,9 @@ func (db *nodeDB) getOrNewBalance(id []byte, neg bool) utils.ExpiredValue { key := db.key(id, neg) item, exist := db.cache.Get(string(key)) if exist { - return item.(utils.ExpiredValue) + return item } + var b utils.ExpiredValue enc, err := db.db.Get(key) if err != nil || len(enc) == 0 { diff --git a/light/lightchain.go b/light/lightchain.go index 84eee8ecaf..155f7b0c05 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -27,6 +27,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -37,7 +38,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - lru "github.com/hashicorp/golang-lru" ) var ( @@ -61,9 +61,9 @@ type LightChain struct { genesisBlock *types.Block forker *core.ForkChoice - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - blockCache *lru.Cache // Cache for the most recent entire blocks + bodyCache *lru.Cache[common.Hash, *types.Body] + bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] + blockCache *lru.Cache[common.Hash, *types.Block] chainmu sync.RWMutex // protects header inserts quit chan struct{} @@ -79,18 +79,14 @@ type LightChain struct { // available in the database. It initialises the default Ethereum header // validator. func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine, checkpoint *params.TrustedCheckpoint) (*LightChain, error) { - bodyCache, _ := lru.New(bodyCacheLimit) - bodyRLPCache, _ := lru.New(bodyCacheLimit) - blockCache, _ := lru.New(blockCacheLimit) - bc := &LightChain{ chainDb: odr.Database(), indexerConfig: odr.IndexerConfig(), odr: odr, quit: make(chan struct{}), - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - blockCache: blockCache, + bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), + bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit), + blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit), engine: engine, } bc.forker = core.NewForkChoice(bc, nil) @@ -233,8 +229,7 @@ func (lc *LightChain) StateCache() state.Database { func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) { // Short circuit if the body's already in the cache, retrieve otherwise if cached, ok := lc.bodyCache.Get(hash); ok { - body := cached.(*types.Body) - return body, nil + return cached, nil } number := lc.hc.GetBlockNumber(hash) if number == nil { @@ -254,7 +249,7 @@ func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Bod func (lc *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) { // Short circuit if the body's already in the cache, retrieve otherwise if cached, ok := lc.bodyRLPCache.Get(hash); ok { - return cached.(rlp.RawValue), nil + return cached, nil } number := lc.hc.GetBlockNumber(hash) if number == nil { @@ -281,7 +276,7 @@ func (lc *LightChain) HasBlock(hash common.Hash, number uint64) bool { func (lc *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) { // Short circuit if the block's already in the cache, retrieve otherwise if block, ok := lc.blockCache.Get(hash); ok { - return block.(*types.Block), nil + return block, nil } block, err := GetBlock(ctx, lc.odr, hash, number) if err != nil {