mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
Merge pull request #5156 from ellemouton/blockcache
blockcache: add blockcache pkg and pass it to all backends
This commit is contained in:
commit
2a2bc64a12
70
blockcache/blockcache.go
Normal file
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
|
||||
}
|
188
blockcache/blockcache_test.go
Normal file
188
blockcache/blockcache_test.go
Normal file
@ -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
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.
|
||||
|
Loading…
Reference in New Issue
Block a user