Merge pull request #5156 from ellemouton/blockcache

blockcache: add blockcache pkg and pass it to all backends
This commit is contained in:
Olaoluwa Osuntokun 2021-04-29 13:52:39 -07:00 committed by GitHub
commit 2a2bc64a12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 538 additions and 55 deletions

70
blockcache/blockcache.go Normal file

@ -0,0 +1,70 @@
package blockcache
import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/neutrino/cache"
"github.com/lightninglabs/neutrino/cache/lru"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/multimutex"
)
// BlockCache is an lru cache for blocks.
type BlockCache struct {
Cache *lru.Cache
HashMutex *multimutex.HashMutex
}
// NewBlockCache creates a new BlockCache with the given maximum capacity.
func NewBlockCache(capacity uint64) *BlockCache {
return &BlockCache{
Cache: lru.NewCache(capacity),
HashMutex: multimutex.NewHashMutex(),
}
}
// GetBlock first checks to see if the BlockCache already contains the block
// with the given hash. If it does then the block is fetched from the cache and
// returned. Otherwise the getBlockImpl function is used in order to fetch the
// new block and then it is stored in the block cache and returned.
func (bc *BlockCache) GetBlock(hash *chainhash.Hash,
getBlockImpl func(hash *chainhash.Hash) (*wire.MsgBlock,
error)) (*wire.MsgBlock, error) {
bc.HashMutex.Lock(lntypes.Hash(*hash))
defer bc.HashMutex.Unlock(lntypes.Hash(*hash))
// Create an inv vector for getting the block.
inv := wire.NewInvVect(wire.InvTypeWitnessBlock, hash)
// Check if the block corresponding to the given hash is already
// stored in the blockCache and return it if it is.
cacheBlock, err := bc.Cache.Get(*inv)
if err != nil && err != cache.ErrElementNotFound {
return nil, err
}
if cacheBlock != nil {
return cacheBlock.(*cache.CacheableBlock).MsgBlock(), nil
}
// Fetch the block from the chain backends.
block, err := getBlockImpl(hash)
if err != nil {
return nil, err
}
// Add the new block to blockCache. If the Cache is at its maximum
// capacity then the LFU item will be evicted in favour of this new
// block.
_, err = bc.Cache.Put(
*inv, &cache.CacheableBlock{
Block: btcutil.NewBlock(block),
},
)
if err != nil {
return nil, err
}
return block, nil
}

@ -0,0 +1,188 @@
package blockcache
import (
"errors"
"fmt"
"sync"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/neutrino/cache"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type mockChainBackend struct {
blocks map[chainhash.Hash]*wire.MsgBlock
chainCallCount int
sync.RWMutex
}
func (m *mockChainBackend) addBlock(block *wire.MsgBlock, nonce uint32) {
m.Lock()
defer m.Unlock()
block.Header.Nonce = nonce
hash := block.Header.BlockHash()
m.blocks[hash] = block
}
func (m *mockChainBackend) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
m.RLock()
defer m.RUnlock()
m.chainCallCount++
block, ok := m.blocks[*blockHash]
if !ok {
return nil, fmt.Errorf("block not found")
}
return block, nil
}
func newMockChain() *mockChainBackend {
return &mockChainBackend{
blocks: make(map[chainhash.Hash]*wire.MsgBlock),
}
}
func (m *mockChainBackend) resetChainCallCount() {
m.RLock()
defer m.RUnlock()
m.chainCallCount = 0
}
// TestBlockCacheGetBlock tests that the block Cache works correctly as a LFU block
// Cache for the given max capacity.
func TestBlockCacheGetBlock(t *testing.T) {
mc := newMockChain()
getBlockImpl := mc.GetBlock
block1 := &wire.MsgBlock{Header: wire.BlockHeader{Nonce: 1}}
block2 := &wire.MsgBlock{Header: wire.BlockHeader{Nonce: 2}}
block3 := &wire.MsgBlock{Header: wire.BlockHeader{Nonce: 3}}
blockhash1 := block1.BlockHash()
blockhash2 := block2.BlockHash()
blockhash3 := block3.BlockHash()
inv1 := wire.NewInvVect(wire.InvTypeWitnessBlock, &blockhash1)
inv2 := wire.NewInvVect(wire.InvTypeWitnessBlock, &blockhash2)
inv3 := wire.NewInvVect(wire.InvTypeWitnessBlock, &blockhash3)
// Determine the size of one of the blocks.
sz, _ := (&cache.CacheableBlock{Block: btcutil.NewBlock(block1)}).Size()
// A new Cache is set up with a capacity of 2 blocks
bc := NewBlockCache(2 * sz)
mc.addBlock(&wire.MsgBlock{}, 1)
mc.addBlock(&wire.MsgBlock{}, 2)
mc.addBlock(&wire.MsgBlock{}, 3)
// We expect the initial Cache to be empty
require.Equal(t, 0, bc.Cache.Len())
// After calling getBlock for block1, it is expected that the Cache
// will have a size of 1 and will contain block1. One chain backends
// call is expected to fetch the block.
_, err := bc.GetBlock(&blockhash1, getBlockImpl)
require.NoError(t, err)
require.Equal(t, 1, bc.Cache.Len())
require.Equal(t, 1, mc.chainCallCount)
mc.resetChainCallCount()
_, err = bc.Cache.Get(*inv1)
require.NoError(t, err)
// After calling getBlock for block2, it is expected that the Cache
// will have a size of 2 and will contain both block1 and block2.
// One chain backends call is expected to fetch the block.
_, err = bc.GetBlock(&blockhash2, getBlockImpl)
require.NoError(t, err)
require.Equal(t, 2, bc.Cache.Len())
require.Equal(t, 1, mc.chainCallCount)
mc.resetChainCallCount()
_, err = bc.Cache.Get(*inv1)
require.NoError(t, err)
_, err = bc.Cache.Get(*inv2)
require.NoError(t, err)
// getBlock is called again for block1 to make block2 the LFU block.
// No call to the chain backend is expected since block 1 is already
// in the Cache.
_, err = bc.GetBlock(&blockhash1, getBlockImpl)
require.NoError(t, err)
require.Equal(t, 2, bc.Cache.Len())
require.Equal(t, 0, mc.chainCallCount)
mc.resetChainCallCount()
// Since the Cache is now at its max capacity, it is expected that when
// getBlock is called for a new block then the LFU block will be
// evicted. It is expected that block2 will be evicted. After calling
// Getblock for block3, it is expected that the Cache will have a
// length of 2 and will contain block 1 and 3.
_, err = bc.GetBlock(&blockhash3, getBlockImpl)
require.NoError(t, err)
require.Equal(t, 2, bc.Cache.Len())
require.Equal(t, 1, mc.chainCallCount)
mc.resetChainCallCount()
_, err = bc.Cache.Get(*inv1)
require.NoError(t, err)
_, err = bc.Cache.Get(*inv2)
require.True(t, errors.Is(err, cache.ErrElementNotFound))
_, err = bc.Cache.Get(*inv3)
require.NoError(t, err)
}
// TestBlockCacheMutexes is used to test that concurrent calls to GetBlock with
// the same block hash does not result in multiple calls to the chain backend.
// In other words this tests the HashMutex.
func TestBlockCacheMutexes(t *testing.T) {
mc := newMockChain()
getBlockImpl := mc.GetBlock
block1 := &wire.MsgBlock{Header: wire.BlockHeader{Nonce: 1}}
block2 := &wire.MsgBlock{Header: wire.BlockHeader{Nonce: 2}}
blockhash1 := block1.BlockHash()
blockhash2 := block2.BlockHash()
// Determine the size of the block.
sz, _ := (&cache.CacheableBlock{Block: btcutil.NewBlock(block1)}).Size()
// A new Cache is set up with a capacity of 2 blocks
bc := NewBlockCache(2 * sz)
mc.addBlock(&wire.MsgBlock{}, 1)
mc.addBlock(&wire.MsgBlock{}, 2)
// Spin off multiple go routines and ensure that concurrent calls to the
// GetBlock method does not result in multiple calls to the chain
// backend.
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(e int) {
if e%2 == 0 {
_, err := bc.GetBlock(&blockhash1, getBlockImpl)
assert.NoError(t, err)
} else {
_, err := bc.GetBlock(&blockhash2, getBlockImpl)
assert.NoError(t, err)
}
wg.Done()
}(i)
}
wg.Wait()
require.Equal(t, 2, mc.chainCallCount)
}

@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/queue"
)
@ -50,6 +51,9 @@ type BitcoindNotifier struct {
bestBlock chainntnfs.BlockEpoch
// blockCache is a LRU block cache.
blockCache *blockcache.BlockCache
// spendHintCache is a cache used to query and update the latest height
// hints for an outpoint. Each height hint represents the earliest
// height at which the outpoint could have been spent within the chain.
@ -73,7 +77,8 @@ var _ chainntnfs.ChainNotifier = (*BitcoindNotifier)(nil)
// willing to accept RPC requests and new zmq clients.
func New(chainConn *chain.BitcoindConn, chainParams *chaincfg.Params,
spendHintCache chainntnfs.SpendHintCache,
confirmHintCache chainntnfs.ConfirmHintCache) *BitcoindNotifier {
confirmHintCache chainntnfs.ConfirmHintCache,
blockCache *blockcache.BlockCache) *BitcoindNotifier {
notifier := &BitcoindNotifier{
chainParams: chainParams,
@ -86,6 +91,8 @@ func New(chainConn *chain.BitcoindConn, chainParams *chaincfg.Params,
spendHintCache: spendHintCache,
confirmHintCache: confirmHintCache,
blockCache: blockCache,
quit: make(chan struct{}),
}
@ -522,7 +529,7 @@ func (b *BitcoindNotifier) confDetailsManually(confRequest chainntnfs.ConfReques
"with height %d", height)
}
block, err := b.chainConn.GetBlock(blockHash)
block, err := b.GetBlock(blockHash)
if err != nil {
return nil, chainntnfs.TxNotFoundManually,
fmt.Errorf("unable to get block with hash "+
@ -558,7 +565,7 @@ func (b *BitcoindNotifier) handleBlockConnected(block chainntnfs.BlockEpoch) err
// First, we'll fetch the raw block as we'll need to gather all the
// transactions to determine whether any are relevant to our registered
// clients.
rawBlock, err := b.chainConn.GetBlock(block.Hash)
rawBlock, err := b.GetBlock(block.Hash)
if err != nil {
return fmt.Errorf("unable to get block: %v", err)
}
@ -777,7 +784,7 @@ func (b *BitcoindNotifier) historicalSpendDetails(
return nil, fmt.Errorf("unable to retrieve hash for "+
"block with height %d: %v", height, err)
}
block, err := b.chainConn.GetBlock(blockHash)
block, err := b.GetBlock(blockHash)
if err != nil {
return nil, fmt.Errorf("unable to retrieve block "+
"with hash %v: %v", blockHash, err)
@ -955,3 +962,11 @@ func (b *BitcoindNotifier) RegisterBlockEpochNtfn(
}, nil
}
}
// GetBlock is used to retrieve the block with the given hash. This function
// wraps the blockCache's GetBlock function.
func (b *BitcoindNotifier) GetBlock(hash *chainhash.Hash) (*wire.MsgBlock,
error) {
return b.blockCache.GetBlock(hash, b.chainConn.GetBlock)
}

@ -11,6 +11,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/integration/rpctest"
"github.com/btcsuite/btcwallet/chain"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
)
@ -55,13 +56,14 @@ func initHintCache(t *testing.T) *chainntnfs.HeightHintCache {
// bitcoind driver.
func setUpNotifier(t *testing.T, bitcoindConn *chain.BitcoindConn,
spendHintCache chainntnfs.SpendHintCache,
confirmHintCache chainntnfs.ConfirmHintCache) *BitcoindNotifier {
confirmHintCache chainntnfs.ConfirmHintCache,
blockCache *blockcache.BlockCache) *BitcoindNotifier {
t.Helper()
notifier := New(
bitcoindConn, chainntnfs.NetParams, spendHintCache,
confirmHintCache,
confirmHintCache, blockCache,
)
if err := notifier.Start(); err != nil {
t.Fatalf("unable to start notifier: %v", err)
@ -116,8 +118,11 @@ func TestHistoricalConfDetailsTxIndex(t *testing.T) {
defer cleanUp()
hintCache := initHintCache(t)
blockCache := blockcache.NewBlockCache(10000)
notifier := setUpNotifier(t, bitcoindConn, hintCache, hintCache)
notifier := setUpNotifier(
t, bitcoindConn, hintCache, hintCache, blockCache,
)
defer notifier.Stop()
syncNotifierWithMiner(t, notifier, miner)
@ -209,8 +214,11 @@ func TestHistoricalConfDetailsNoTxIndex(t *testing.T) {
defer cleanUp()
hintCache := initHintCache(t)
blockCache := blockcache.NewBlockCache(10000)
notifier := setUpNotifier(t, bitcoindConn, hintCache, hintCache)
notifier := setUpNotifier(
t, bitcoindConn, hintCache, hintCache, blockCache,
)
defer notifier.Stop()
// Since the node has its txindex disabled, we fall back to scanning the

@ -6,15 +6,16 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcwallet/chain"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
)
// createNewNotifier creates a new instance of the ChainNotifier interface
// implemented by BitcoindNotifier.
func createNewNotifier(args ...interface{}) (chainntnfs.ChainNotifier, error) {
if len(args) != 4 {
if len(args) != 5 {
return nil, fmt.Errorf("incorrect number of arguments to "+
".New(...), expected 4, instead passed %v", len(args))
".New(...), expected 5, instead passed %v", len(args))
}
chainConn, ok := args[0].(*chain.BitcoindConn)
@ -41,7 +42,14 @@ func createNewNotifier(args ...interface{}) (chainntnfs.ChainNotifier, error) {
"is incorrect, expected a chainntnfs.ConfirmHintCache")
}
return New(chainConn, chainParams, spendHintCache, confirmHintCache), nil
blockCache, ok := args[4].(*blockcache.BlockCache)
if !ok {
return nil, errors.New("fifth argument to bitcoindnotify.New " +
"is incorrect, expected a *blockcache.BlockCache")
}
return New(chainConn, chainParams, spendHintCache,
confirmHintCache, blockCache), nil
}
// init registers a driver for the BtcdNotifier concrete implementation of the

@ -14,6 +14,7 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/queue"
)
@ -69,6 +70,9 @@ type BtcdNotifier struct {
bestBlock chainntnfs.BlockEpoch
// blockCache is a LRU block cache.
blockCache *blockcache.BlockCache
chainUpdates *queue.ConcurrentQueue
txUpdates *queue.ConcurrentQueue
@ -94,7 +98,8 @@ var _ chainntnfs.ChainNotifier = (*BtcdNotifier)(nil)
// accept new websockets clients.
func New(config *rpcclient.ConnConfig, chainParams *chaincfg.Params,
spendHintCache chainntnfs.SpendHintCache,
confirmHintCache chainntnfs.ConfirmHintCache) (*BtcdNotifier, error) {
confirmHintCache chainntnfs.ConfirmHintCache,
blockCache *blockcache.BlockCache) (*BtcdNotifier, error) {
notifier := &BtcdNotifier{
chainParams: chainParams,
@ -110,6 +115,8 @@ func New(config *rpcclient.ConnConfig, chainParams *chaincfg.Params,
spendHintCache: spendHintCache,
confirmHintCache: confirmHintCache,
blockCache: blockCache,
quit: make(chan struct{}),
}
@ -578,7 +585,7 @@ func (b *BtcdNotifier) confDetailsManually(confRequest chainntnfs.ConfRequest,
}
// TODO: fetch the neutrino filters instead.
block, err := b.chainConn.GetBlock(blockHash)
block, err := b.GetBlock(blockHash)
if err != nil {
return nil, chainntnfs.TxNotFoundManually,
fmt.Errorf("unable to get block with hash "+
@ -616,7 +623,7 @@ func (b *BtcdNotifier) handleBlockConnected(epoch chainntnfs.BlockEpoch) error {
// First, we'll fetch the raw block as we'll need to gather all the
// transactions to determine whether any are relevant to our registered
// clients.
rawBlock, err := b.chainConn.GetBlock(epoch.Hash)
rawBlock, err := b.GetBlock(epoch.Hash)
if err != nil {
return fmt.Errorf("unable to get block: %v", err)
}
@ -1012,3 +1019,11 @@ func (b *BtcdNotifier) RegisterBlockEpochNtfn(
}, nil
}
}
// GetBlock is used to retrieve the block with the given hash. This function
// wraps the blockCache's GetBlock function.
func (b *BtcdNotifier) GetBlock(hash *chainhash.Hash) (*wire.MsgBlock,
error) {
return b.blockCache.GetBlock(hash, b.chainConn.GetBlock)
}

@ -9,6 +9,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/integration/rpctest"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
)
@ -53,9 +54,12 @@ func initHintCache(t *testing.T) *chainntnfs.HeightHintCache {
// driver.
func setUpNotifier(t *testing.T, h *rpctest.Harness) *BtcdNotifier {
hintCache := initHintCache(t)
blockCache := blockcache.NewBlockCache(10000)
rpcCfg := h.RPCConfig()
notifier, err := New(&rpcCfg, chainntnfs.NetParams, hintCache, hintCache)
notifier, err := New(
&rpcCfg, chainntnfs.NetParams, hintCache, hintCache, blockCache,
)
if err != nil {
t.Fatalf("unable to create notifier: %v", err)
}

@ -6,15 +6,16 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/rpcclient"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
)
// createNewNotifier creates a new instance of the ChainNotifier interface
// implemented by BtcdNotifier.
func createNewNotifier(args ...interface{}) (chainntnfs.ChainNotifier, error) {
if len(args) != 4 {
if len(args) != 5 {
return nil, fmt.Errorf("incorrect number of arguments to "+
".New(...), expected 4, instead passed %v", len(args))
".New(...), expected 5, instead passed %v", len(args))
}
config, ok := args[0].(*rpcclient.ConnConfig)
@ -41,7 +42,15 @@ func createNewNotifier(args ...interface{}) (chainntnfs.ChainNotifier, error) {
"is incorrect, expected a chainntnfs.ConfirmHintCache")
}
return New(config, chainParams, spendHintCache, confirmHintCache)
blockCache, ok := args[4].(*blockcache.BlockCache)
if !ok {
return nil, errors.New("fifth argument to btcdnotify.New " +
"is incorrect, expected a *blockcache.BlockCache")
}
return New(
config, chainParams, spendHintCache, confirmHintCache, blockCache,
)
}
// init registers a driver for the BtcdNotifier concrete implementation of the

@ -5,15 +5,16 @@ import (
"fmt"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
)
// createNewNotifier creates a new instance of the ChainNotifier interface
// implemented by NeutrinoNotifier.
func createNewNotifier(args ...interface{}) (chainntnfs.ChainNotifier, error) {
if len(args) != 3 {
if len(args) != 4 {
return nil, fmt.Errorf("incorrect number of arguments to "+
".New(...), expected 3, instead passed %v", len(args))
".New(...), expected 4, instead passed %v", len(args))
}
config, ok := args[0].(*neutrino.ChainService)
@ -34,7 +35,13 @@ func createNewNotifier(args ...interface{}) (chainntnfs.ChainNotifier, error) {
"is incorrect, expected a chainntfs.ConfirmHintCache")
}
return New(config, spendHintCache, confirmHintCache), nil
blockCache, ok := args[3].(*blockcache.BlockCache)
if !ok {
return nil, errors.New("fourth argument to neutrinonotify.New " +
"is incorrect, expected a *blockcache.BlockCache")
}
return New(config, spendHintCache, confirmHintCache, blockCache), nil
}
// init registers a driver for the NeutrinoNotify concrete implementation of

@ -17,7 +17,9 @@ import (
"github.com/btcsuite/btcutil/gcs/builder"
"github.com/lightninglabs/neutrino"
"github.com/lightninglabs/neutrino/headerfs"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/queue"
)
@ -73,6 +75,9 @@ type NeutrinoNotifier struct {
// which the transaction could have confirmed within the chain.
confirmHintCache chainntnfs.ConfirmHintCache
// blockCache is an LRU block cache.
blockCache *blockcache.BlockCache
wg sync.WaitGroup
quit chan struct{}
}
@ -86,7 +91,8 @@ var _ chainntnfs.ChainNotifier = (*NeutrinoNotifier)(nil)
// NOTE: The passed neutrino node should already be running and active before
// being passed into this function.
func New(node *neutrino.ChainService, spendHintCache chainntnfs.SpendHintCache,
confirmHintCache chainntnfs.ConfirmHintCache) *NeutrinoNotifier {
confirmHintCache chainntnfs.ConfirmHintCache,
blockCache *blockcache.BlockCache) *NeutrinoNotifier {
return &NeutrinoNotifier{
notificationCancels: make(chan interface{}),
@ -105,6 +111,8 @@ func New(node *neutrino.ChainService, spendHintCache chainntnfs.SpendHintCache,
spendHintCache: spendHintCache,
confirmHintCache: confirmHintCache,
blockCache: blockCache,
quit: make(chan struct{}),
}
}
@ -571,7 +579,7 @@ func (n *NeutrinoNotifier) historicalConfDetails(confRequest chainntnfs.ConfRequ
// In the case that we do have a match, we'll fetch the block
// from the network so we can find the positional data required
// to send the proper response.
block, err := n.p2pNode.GetBlock(*blockHash)
block, err := n.GetBlock(*blockHash)
if err != nil {
return nil, fmt.Errorf("unable to get block from network: %v", err)
}
@ -628,7 +636,7 @@ func (n *NeutrinoNotifier) handleBlockConnected(newBlock *filteredBlock) error {
// getFilteredBlock is a utility to retrieve the full filtered block from a block epoch.
func (n *NeutrinoNotifier) getFilteredBlock(epoch chainntnfs.BlockEpoch) (*filteredBlock, error) {
rawBlock, err := n.p2pNode.GetBlock(*epoch.Hash)
rawBlock, err := n.GetBlock(*epoch.Hash)
if err != nil {
return nil, fmt.Errorf("unable to get block: %v", err)
}
@ -908,6 +916,21 @@ func (n *NeutrinoNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
return ntfn.Event, nil
}
// GetBlock is used to retrieve the block with the given hash. Since the block
// cache used by neutrino will be the same as that used by LND (since it is
// passed to neutrino on initialisation), the neutrino GetBlock method can be
// called directly since it already uses the block cache. However, neutrino
// does not lock the block cache mutex for the given block hash and so that is
// done here.
func (n *NeutrinoNotifier) GetBlock(hash chainhash.Hash) (
*btcutil.Block, error) {
n.blockCache.HashMutex.Lock(lntypes.Hash(hash))
defer n.blockCache.HashMutex.Unlock(lntypes.Hash(hash))
return n.p2pNode.GetBlock(hash)
}
// blockEpochRegistration represents a client's intent to receive a
// notification with each newly connected block.
type blockEpochRegistration struct {

@ -19,6 +19,7 @@ import (
"github.com/btcsuite/btcwallet/chain"
_ "github.com/btcsuite/btcwallet/walletdb/bdb" // Required to auto-register the boltdb walletdb implementation.
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify"
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
@ -1930,6 +1931,8 @@ func TestInterfaces(t *testing.T, targetBackEnd string) {
t.Fatalf("unable to create height hint cache: %v", err)
}
blockCache := blockcache.NewBlockCache(10000)
var (
cleanUp func()
newNotifier func() (chainntnfs.TestChainNotifier, error)
@ -1944,7 +1947,7 @@ func TestInterfaces(t *testing.T, targetBackEnd string) {
newNotifier = func() (chainntnfs.TestChainNotifier, error) {
return bitcoindnotify.New(
bitcoindConn, chainntnfs.NetParams,
hintCache, hintCache,
hintCache, hintCache, blockCache,
), nil
}
@ -1952,7 +1955,7 @@ func TestInterfaces(t *testing.T, targetBackEnd string) {
newNotifier = func() (chainntnfs.TestChainNotifier, error) {
return btcdnotify.New(
&rpcConfig, chainntnfs.NetParams,
hintCache, hintCache,
hintCache, hintCache, blockCache,
)
}
@ -1964,6 +1967,7 @@ func TestInterfaces(t *testing.T, targetBackEnd string) {
newNotifier = func() (chainntnfs.TestChainNotifier, error) {
return neutrinonotify.New(
spvNode, hintCache, hintCache,
blockCache,
), nil
}
}

@ -19,6 +19,7 @@ import (
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/wallet"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify"
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
@ -73,6 +74,9 @@ type Config struct {
// RemoteChanDB is a pointer to the remote backing channel database.
RemoteChanDB *channeldb.DB
// BlockCacheSize is the size (in bytes) of blocks kept in memory.
BlockCacheSize uint64
// PrivateWalletPw is the private wallet password to the underlying
// btcwallet instance.
PrivateWalletPw []byte
@ -231,7 +235,8 @@ type ChainControl struct {
// full-node, another backed by a running bitcoind full-node, and the other
// backed by a running neutrino light client instance. When running with a
// neutrino light client instance, `neutrinoCS` must be non-nil.
func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
func NewChainControl(cfg *Config, blockCache *blockcache.BlockCache) (
*ChainControl, func(), error) {
// Set the RPC config from the "home" chain. Multi-chain isn't yet
// active, so we'll restrict usage to a particular chain for now.
@ -312,9 +317,11 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
// along with the wallet's ChainSource, which are all backed by
// the neutrino light client.
cc.ChainNotifier = neutrinonotify.New(
cfg.NeutrinoCS, hintCache, hintCache,
cfg.NeutrinoCS, hintCache, hintCache, blockCache,
)
cc.ChainView, err = chainview.NewCfFilteredChainView(
cfg.NeutrinoCS, blockCache,
)
cc.ChainView, err = chainview.NewCfFilteredChainView(cfg.NeutrinoCS)
if err != nil {
return nil, nil, err
}
@ -409,9 +416,12 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
}
cc.ChainNotifier = bitcoindnotify.New(
bitcoindConn, cfg.ActiveNetParams.Params, hintCache, hintCache,
bitcoindConn, cfg.ActiveNetParams.Params, hintCache,
hintCache, blockCache,
)
cc.ChainView = chainview.NewBitcoindFilteredChainView(
bitcoindConn, blockCache,
)
cc.ChainView = chainview.NewBitcoindFilteredChainView(bitcoindConn)
walletConfig.ChainSource = bitcoindConn.NewBitcoindClient()
// If we're not in regtest mode, then we'll attempt to use a
@ -538,7 +548,8 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
DisableAutoReconnect: false,
}
cc.ChainNotifier, err = btcdnotify.New(
rpcConfig, cfg.ActiveNetParams.Params, hintCache, hintCache,
rpcConfig, cfg.ActiveNetParams.Params, hintCache,
hintCache, blockCache,
)
if err != nil {
return nil, nil, err
@ -546,7 +557,9 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
// Finally, we'll create an instance of the default chain view to be
// used within the routing layer.
cc.ChainView, err = chainview.NewBtcdFilteredChainView(*rpcConfig)
cc.ChainView, err = chainview.NewBtcdFilteredChainView(
*rpcConfig, blockCache,
)
if err != nil {
log.Errorf("unable to create chain view: %v", err)
return nil, nil, err
@ -638,7 +651,7 @@ func NewChainControl(cfg *Config) (*ChainControl, func(), error) {
return nil, nil, err
}
wc, err := btcwallet.New(*walletConfig)
wc, err := btcwallet.New(*walletConfig, blockCache)
if err != nil {
fmt.Printf("unable to create wallet controller: %v\n", err)
return nil, ccCleanup, err

@ -101,6 +101,10 @@ const (
// initiated the channel closure.
defaultCoopCloseTargetConfs = 6
// defaultBlockCacheSize is the size (in bytes) of blocks that will be
// keep in memory if no size is specified.
defaultBlockCacheSize uint64 = 20 * 1024 * 1024 // 20 MB
// defaultHostSampleInterval is the default amount of time that the
// HostAnnouncer will wait between DNS resolutions to check if the
// backing IP of a host has changed.
@ -273,6 +277,8 @@ type Config struct {
LtcdMode *lncfg.Btcd `group:"ltcd" namespace:"ltcd"`
LitecoindMode *lncfg.Bitcoind `group:"litecoind" namespace:"litecoind"`
BlockCacheSize uint64 `long:"blockcachesize" description:"The maximum capacity of the block cache"`
Autopilot *lncfg.AutoPilot `group:"Autopilot" namespace:"autopilot"`
Tor *lncfg.Tor `group:"Tor" namespace:"tor"`
@ -434,6 +440,7 @@ func DefaultConfig() Config {
UserAgentName: neutrino.UserAgentName,
UserAgentVersion: neutrino.UserAgentVersion,
},
BlockCacheSize: defaultBlockCacheSize,
UnsafeDisconnect: true,
MaxPendingChannels: lncfg.DefaultMaxPendingChannels,
NoSeedBackup: defaultNoSeedBackup,

15
lnd.go

@ -34,6 +34,7 @@ import (
"gopkg.in/macaroon.v2"
"github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/cert"
"github.com/lightningnetwork/lnd/chainreg"
@ -254,6 +255,9 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
defer cleanUp()
// Initialize a new block cache.
blockCache := blockcache.NewBlockCache(cfg.BlockCacheSize)
// Before starting the wallet, we'll create and start our Neutrino
// light client instance, if enabled, in order to allow it to sync
// while the rest of the daemon continues startup.
@ -264,7 +268,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
var neutrinoCS *neutrino.ChainService
if mainChain.Node == "neutrino" {
neutrinoBackend, neutrinoCleanUp, err := initNeutrinoBackend(
cfg, mainChain.ChainDir,
cfg, mainChain.ChainDir, blockCache,
)
if err != nil {
err := fmt.Errorf("unable to initialize neutrino "+
@ -546,9 +550,12 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
Dialer: func(addr string) (net.Conn, error) {
return cfg.net.Dial("tcp", addr, cfg.ConnectionTimeout)
},
BlockCacheSize: cfg.BlockCacheSize,
}
activeChainControl, cleanup, err := chainreg.NewChainControl(chainControlCfg)
activeChainControl, cleanup, err := chainreg.NewChainControl(
chainControlCfg, blockCache,
)
if cleanup != nil {
defer cleanup()
}
@ -1553,7 +1560,8 @@ func initializeDatabases(ctx context.Context,
// initNeutrinoBackend inits a new instance of the neutrino light client
// backend given a target chain directory to store the chain state.
func initNeutrinoBackend(cfg *Config, chainDir string) (*neutrino.ChainService,
func initNeutrinoBackend(cfg *Config, chainDir string,
blockCache *blockcache.BlockCache) (*neutrino.ChainService,
func(), error) {
// Both channel validation flags are false by default but their meaning
@ -1661,6 +1669,7 @@ func initNeutrinoBackend(cfg *Config, chainDir string) (*neutrino.ChainService,
return ips, nil
},
AssertFilterHeader: headerStateAssertion,
BlockCache: blockCache.Cache,
}
neutrino.MaxPeers = 8

@ -8,10 +8,10 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain"
"github.com/lightninglabs/neutrino"
"github.com/lightninglabs/neutrino/headerfs"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet"
)
@ -127,10 +127,25 @@ func (b *BtcWallet) GetUtxo(op *wire.OutPoint, pkScript []byte,
}
}
// GetBlock returns a raw block from the server given its hash.
// GetBlock returns a raw block from the server given its hash. For the Neutrino
// implementation of the lnwallet.BlockChainIO interface, the Neutrino GetBlock
// method is called directly. For other implementations, the block cache is used
// to wrap the call to GetBlock.
//
// This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
_, ok := b.chain.(*chain.NeutrinoClient)
if !ok {
return b.blockCache.GetBlock(blockHash, b.chain.GetBlock)
}
// For the neutrino implementation of lnwallet.BlockChainIO the neutrino
// GetBlock function can be called directly since it uses the same block
// cache. However, it does not lock the block cache mutex for the given
// block hash and so that is done here.
b.blockCache.HashMutex.Lock(lntypes.Hash(*blockHash))
defer b.blockCache.HashMutex.Unlock(lntypes.Hash(*blockHash))
return b.chain.GetBlock(blockHash)
}

@ -24,6 +24,7 @@ import (
"github.com/btcsuite/btcwallet/wallet/txrules"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
@ -74,6 +75,8 @@ type BtcWallet struct {
netParams *chaincfg.Params
chainKeyScope waddrmgr.KeyScope
blockCache *blockcache.BlockCache
}
// A compile time check to ensure that BtcWallet implements the
@ -83,7 +86,7 @@ var _ lnwallet.BlockChainIO = (*BtcWallet)(nil)
// New returns a new fully initialized instance of BtcWallet given a valid
// configuration struct.
func New(cfg Config) (*BtcWallet, error) {
func New(cfg Config, blockCache *blockcache.BlockCache) (*BtcWallet, error) {
// Ensure the wallet exists or create it when the create flag is set.
netDir := NetworkDir(cfg.DataDir, cfg.NetParams)
@ -142,6 +145,7 @@ func New(cfg Config) (*BtcWallet, error) {
chain: cfg.ChainSource,
netParams: cfg.NetParams,
chainKeyScope: chainKeyScope,
blockCache: blockCache,
}, nil
}

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/btcsuite/btcwallet/chain"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/lnwallet"
)
@ -16,9 +17,9 @@ const (
// properly create an instance of the lnwallet.WalletDriver struct for
// BtcWallet.
func createNewWallet(args ...interface{}) (lnwallet.WalletController, error) {
if len(args) != 1 {
if len(args) != 2 {
return nil, fmt.Errorf("incorrect number of arguments to .New(...), "+
"expected 1, instead passed %v", len(args))
"expected 2, instead passed %v", len(args))
}
config, ok := args[0].(*Config)
@ -27,7 +28,13 @@ func createNewWallet(args ...interface{}) (lnwallet.WalletController, error) {
"incorrect, expected a *rpcclient.ConnConfig")
}
return New(*config)
blockCache, ok := args[1].(*blockcache.BlockCache)
if !ok {
return nil, fmt.Errorf("second argument to btcdnotifier.New is " +
"incorrect, expected a *blockcache.BlockCache")
}
return New(*config, blockCache)
}
// init registers a driver for the BtcWallet concrete implementation of the

@ -32,6 +32,7 @@ import (
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
"github.com/davecgh/go-spew/spew"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainntnfs/btcdnotify"
"github.com/lightningnetwork/lnd/channeldb"
@ -3204,8 +3205,9 @@ func TestLightningWallet(t *testing.T, targetBackEnd string) {
if err != nil {
t.Fatalf("unable to create height hint cache: %v", err)
}
blockCache := blockcache.NewBlockCache(10000)
chainNotifier, err := btcdnotify.New(
&rpcConfig, netParams, hintCache, hintCache,
&rpcConfig, netParams, hintCache, hintCache, blockCache,
)
if err != nil {
t.Fatalf("unable to create notifier: %v", err)
@ -3262,6 +3264,8 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
}
defer os.RemoveAll(tempTestDirBob)
blockCache := blockcache.NewBlockCache(10000)
walletType := walletDriver.WalletType
switch walletType {
case "btcwallet":
@ -3430,7 +3434,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
// wallet starts in recovery mode
RecoveryWindow: 2,
}
aliceWalletController, err = walletDriver.New(aliceWalletConfig)
aliceWalletController, err = walletDriver.New(
aliceWalletConfig, blockCache,
)
if err != nil {
t.Fatalf("unable to create btcwallet: %v", err)
}
@ -3455,7 +3461,9 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
// wallet starts without recovery mode
RecoveryWindow: 0,
}
bobWalletController, err = walletDriver.New(bobWalletConfig)
bobWalletController, err = walletDriver.New(
bobWalletConfig, blockCache,
)
if err != nil {
t.Fatalf("unable to create btcwallet: %v", err)
}

@ -12,6 +12,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/channeldb"
)
@ -37,6 +38,9 @@ type BitcoindFilteredChainView struct {
// chainView.
blockQueue *blockEventQueue
// blockCache is an LRU block cache.
blockCache *blockcache.BlockCache
// filterUpdates is a channel in which updates to the utxo filter
// attached to this instance are sent over.
filterUpdates chan filterUpdate
@ -61,12 +65,14 @@ var _ FilteredChainView = (*BitcoindFilteredChainView)(nil)
// NewBitcoindFilteredChainView creates a new instance of a FilteredChainView
// from RPC credentials and a ZMQ socket address for a bitcoind instance.
func NewBitcoindFilteredChainView(
chainConn *chain.BitcoindConn) *BitcoindFilteredChainView {
chainConn *chain.BitcoindConn,
blockCache *blockcache.BlockCache) *BitcoindFilteredChainView {
chainView := &BitcoindFilteredChainView{
chainFilter: make(map[wire.OutPoint]struct{}),
filterUpdates: make(chan filterUpdate),
filterBlockReqs: make(chan *filterBlockReq),
blockCache: blockCache,
quit: make(chan struct{}),
}
@ -390,7 +396,7 @@ func (b *BitcoindFilteredChainView) chainFilterer() {
case req := <-b.filterBlockReqs:
// First we'll fetch the block itself as well as some
// additional information including its height.
block, err := b.chainClient.GetBlock(req.blockHash)
block, err := b.GetBlock(req.blockHash)
if err != nil {
req.err <- err
req.resp <- nil
@ -479,3 +485,11 @@ func (b *BitcoindFilteredChainView) FilteredBlocks() <-chan *FilteredBlock {
func (b *BitcoindFilteredChainView) DisconnectedBlocks() <-chan *FilteredBlock {
return b.blockQueue.staleBlocks
}
// GetBlock is used to retrieve the block with the given hash. This function
// wraps the blockCache's GetBlock function.
func (b *BitcoindFilteredChainView) GetBlock(hash *chainhash.Hash) (
*wire.MsgBlock, error) {
return b.blockCache.GetBlock(hash, b.chainClient.GetBlock)
}

@ -12,6 +12,7 @@ import (
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/channeldb"
)
@ -35,6 +36,9 @@ type BtcdFilteredChainView struct {
// chainView.
blockQueue *blockEventQueue
// blockCache is an LRU block cache.
blockCache *blockcache.BlockCache
// filterUpdates is a channel in which updates to the utxo filter
// attached to this instance are sent over.
filterUpdates chan filterUpdate
@ -58,11 +62,14 @@ var _ FilteredChainView = (*BtcdFilteredChainView)(nil)
// NewBtcdFilteredChainView creates a new instance of a FilteredChainView from
// RPC credentials for an active btcd instance.
func NewBtcdFilteredChainView(config rpcclient.ConnConfig) (*BtcdFilteredChainView, error) {
func NewBtcdFilteredChainView(config rpcclient.ConnConfig,
blockCache *blockcache.BlockCache) (*BtcdFilteredChainView, error) {
chainView := &BtcdFilteredChainView{
chainFilter: make(map[wire.OutPoint]struct{}),
filterUpdates: make(chan filterUpdate),
filterBlockReqs: make(chan *filterBlockReq),
blockCache: blockCache,
quit: make(chan struct{}),
}
@ -404,7 +411,7 @@ func (b *BtcdFilteredChainView) chainFilterer() {
case req := <-b.filterBlockReqs:
// First we'll fetch the block itself as well as some
// additional information including its height.
block, err := b.btcdConn.GetBlock(req.blockHash)
block, err := b.GetBlock(req.blockHash)
if err != nil {
req.err <- err
req.resp <- nil
@ -486,3 +493,11 @@ func (b *BtcdFilteredChainView) FilteredBlocks() <-chan *FilteredBlock {
func (b *BtcdFilteredChainView) DisconnectedBlocks() <-chan *FilteredBlock {
return b.blockQueue.staleBlocks
}
// GetBlock is used to retrieve the block with the given hash. This function
// wraps the blockCache's GetBlock function.
func (b *BtcdFilteredChainView) GetBlock(hash *chainhash.Hash) (
*wire.MsgBlock, error) {
return b.blockCache.GetBlock(hash, b.btcdConn.GetBlock)
}

@ -26,6 +26,7 @@ import (
_ "github.com/btcsuite/btcwallet/walletdb/bdb" // Required to register the boltdb walletdb implementation.
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/kvdb"
)
@ -844,7 +845,11 @@ var interfaceImpls = []struct {
cleanUp2()
}
chainView := NewBitcoindFilteredChainView(chainConn)
blockCache := blockcache.NewBlockCache(10000)
chainView := NewBitcoindFilteredChainView(
chainConn, blockCache,
)
return cleanUp3, chainView, nil
},
@ -890,7 +895,11 @@ var interfaceImpls = []struct {
os.RemoveAll(spvDir)
}
chainView, err := NewCfFilteredChainView(spvNode)
blockCache := blockcache.NewBlockCache(10000)
chainView, err := NewCfFilteredChainView(
spvNode, blockCache,
)
if err != nil {
return nil, nil, err
}
@ -901,7 +910,10 @@ var interfaceImpls = []struct {
{
name: "btcd_websockets",
chainViewInit: func(config rpcclient.ConnConfig, _ string) (func(), FilteredChainView, error) {
chainView, err := NewBtcdFilteredChainView(config)
blockCache := blockcache.NewBlockCache(10000)
chainView, err := NewBtcdFilteredChainView(
config, blockCache,
)
if err != nil {
return nil, nil, err
}

@ -11,7 +11,9 @@ import (
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/gcs/builder"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/blockcache"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lntypes"
)
// CfFilteredChainView is an implementation of the FilteredChainView interface
@ -40,6 +42,9 @@ type CfFilteredChainView struct {
// chainView.
blockQueue *blockEventQueue
// blockCache is an LRU block cache.
blockCache *blockcache.BlockCache
// chainFilter is the
filterMtx sync.RWMutex
chainFilter map[wire.OutPoint][]byte
@ -57,13 +62,15 @@ var _ FilteredChainView = (*CfFilteredChainView)(nil)
//
// NOTE: The node should already be running and syncing before being passed into
// this function.
func NewCfFilteredChainView(node *neutrino.ChainService) (*CfFilteredChainView, error) {
func NewCfFilteredChainView(node *neutrino.ChainService,
blockCache *blockcache.BlockCache) (*CfFilteredChainView, error) {
return &CfFilteredChainView{
blockQueue: newBlockEventQueue(),
quit: make(chan struct{}),
rescanErrChan: make(chan error),
chainFilter: make(map[wire.OutPoint][]byte),
p2pNode: node,
blockCache: blockCache,
}, nil
}
@ -269,7 +276,7 @@ func (c *CfFilteredChainView) FilterBlock(blockHash *chainhash.Hash) (*FilteredB
// If we reach this point, then there was a match, so we'll need to
// fetch the block itself so we can scan it for any actual matches (as
// there's a fp rate).
block, err := c.p2pNode.GetBlock(*blockHash)
block, err := c.GetBlock(*blockHash)
if err != nil {
return nil, err
}
@ -364,3 +371,18 @@ func (c *CfFilteredChainView) FilteredBlocks() <-chan *FilteredBlock {
func (c *CfFilteredChainView) DisconnectedBlocks() <-chan *FilteredBlock {
return c.blockQueue.staleBlocks
}
// GetBlock is used to retrieve the block with the given hash. Since the block
// cache used by neutrino will be the same as that used by LND (since it is
// passed to neutrino on initialisation), the neutrino GetBlock method can be
// called directly since it already uses the block cache. However, neutrino
// does not lock the block cache mutex for the given block hash and so that is
// done here.
func (c *CfFilteredChainView) GetBlock(hash chainhash.Hash) (
*btcutil.Block, error) {
c.blockCache.HashMutex.Lock(lntypes.Hash(hash))
defer c.blockCache.HashMutex.Unlock(lntypes.Hash(hash))
return c.p2pNode.GetBlock(hash)
}

@ -224,6 +224,12 @@
; The target location of the channel backup file.
; backupfilepath=~/.lnd/data/chain/bitcoin/simnet/channel.backup
; The maximum capacity of the block cache in bytes. Increasing this will result
; in more blocks being kept in memory but will increase performance when the
; same block is required multiple times.
; The example value below is 40 MB (1024 * 1024 * 40)
; blockcachesize=41943040
; Optional URL for external fee estimation. If no URL is specified, the method
; for fee estimation will depend on the chosen backend and network. Must be set
; for neutrino on mainnet.