/ blockcache / blockcache.go
blockcache.go
 1  package blockcache
 2  
 3  import (
 4  	"github.com/btcsuite/btcd/btcutil"
 5  	"github.com/btcsuite/btcd/chaincfg/chainhash"
 6  	"github.com/btcsuite/btcd/wire"
 7  	"github.com/lightninglabs/neutrino"
 8  	"github.com/lightninglabs/neutrino/cache"
 9  	"github.com/lightninglabs/neutrino/cache/lru"
10  	"github.com/lightningnetwork/lnd/lntypes"
11  	"github.com/lightningnetwork/lnd/multimutex"
12  )
13  
14  // BlockCache is an lru cache for blocks.
15  type BlockCache struct {
16  	Cache     *lru.Cache[wire.InvVect, *neutrino.CacheableBlock]
17  	HashMutex *multimutex.Mutex[lntypes.Hash]
18  }
19  
20  // NewBlockCache creates a new BlockCache with the given maximum capacity.
21  func NewBlockCache(capacity uint64) *BlockCache {
22  	return &BlockCache{
23  		Cache: lru.NewCache[wire.InvVect, *neutrino.CacheableBlock](
24  			capacity,
25  		),
26  		HashMutex: multimutex.NewMutex[lntypes.Hash](),
27  	}
28  }
29  
30  // GetBlock first checks to see if the BlockCache already contains the block
31  // with the given hash. If it does then the block is fetched from the cache and
32  // returned. Otherwise the getBlockImpl function is used in order to fetch the
33  // new block and then it is stored in the block cache and returned.
34  func (bc *BlockCache) GetBlock(hash *chainhash.Hash,
35  	getBlockImpl func(hash *chainhash.Hash) (*wire.MsgBlock,
36  		error)) (*wire.MsgBlock, error) {
37  
38  	bc.HashMutex.Lock(lntypes.Hash(*hash))
39  	defer bc.HashMutex.Unlock(lntypes.Hash(*hash))
40  
41  	// Create an inv vector for getting the block.
42  	inv := wire.NewInvVect(wire.InvTypeWitnessBlock, hash)
43  
44  	// Check if the block corresponding to the given hash is already
45  	// stored in the blockCache and return it if it is.
46  	cacheBlock, err := bc.Cache.Get(*inv)
47  	if err != nil && err != cache.ErrElementNotFound {
48  		return nil, err
49  	}
50  	if cacheBlock != nil {
51  		return cacheBlock.MsgBlock(), nil
52  	}
53  
54  	// Fetch the block from the chain backends.
55  	msgBlock, err := getBlockImpl(hash)
56  	if err != nil {
57  		return nil, err
58  	}
59  
60  	// Make a copy of the block so it won't escape to the heap.
61  	msgBlockCopy := msgBlock.Copy()
62  	block := btcutil.NewBlock(msgBlockCopy)
63  
64  	// Add the new block to blockCache. If the Cache is at its maximum
65  	// capacity then the LFU item will be evicted in favour of this new
66  	// block.
67  	_, err = bc.Cache.Put(
68  		*inv, &neutrino.CacheableBlock{
69  			Block: block,
70  		},
71  	)
72  	if err != nil {
73  		return nil, err
74  	}
75  
76  	return msgBlockCopy, nil
77  }