diff --git a/chainntnfs/neutrinonotify/driver.go b/chainntnfs/neutrinonotify/driver.go index 68a1b2f2..dbd89718 100644 --- a/chainntnfs/neutrinonotify/driver.go +++ b/chainntnfs/neutrinonotify/driver.go @@ -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 diff --git a/chainntnfs/neutrinonotify/neutrino.go b/chainntnfs/neutrinonotify/neutrino.go index 8842d4ce..2320c0e6 100644 --- a/chainntnfs/neutrinonotify/neutrino.go +++ b/chainntnfs/neutrinonotify/neutrino.go @@ -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 { diff --git a/chainntnfs/test/test_interface.go b/chainntnfs/test/test_interface.go index bfa4d23c..bd6b70ac 100644 --- a/chainntnfs/test/test_interface.go +++ b/chainntnfs/test/test_interface.go @@ -1967,6 +1967,7 @@ func TestInterfaces(t *testing.T, targetBackEnd string) { newNotifier = func() (chainntnfs.TestChainNotifier, error) { return neutrinonotify.New( spvNode, hintCache, hintCache, + blockCache, ), nil } } diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go index 5b6d59cf..ffe236ba 100644 --- a/chainreg/chainregistry.go +++ b/chainreg/chainregistry.go @@ -317,7 +317,7 @@ func NewChainControl(cfg *Config, blockCache *blockcache.BlockCache) ( // 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,