From e4563ca13ba32835507c94686ddfbdb99fb27903 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 9 Jun 2017 12:12:01 -0700 Subject: [PATCH] routing/chainview: make filter updates synchronous for neutrino This commit fixes a possible race condition wherein a call to FilterBlock after a call to UpdateFilter would result in the call to FilterBlock not yet using the updated filter. We fix this by ensuring the internal chain filter is updated by the time the call to FilterBlock returns. --- routing/chainview/btcd.go | 1 + routing/chainview/neutrino.go | 29 +++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/routing/chainview/btcd.go b/routing/chainview/btcd.go index 35922240f..336623b84 100644 --- a/routing/chainview/btcd.go +++ b/routing/chainview/btcd.go @@ -361,6 +361,7 @@ func (b *BtcdFilteredChainView) chainFilterer() { type filterUpdate struct { newUtxos []wire.OutPoint updateHeight uint32 + done chan struct{} } // UpdateFilter updates the UTXO filter which is to be consulted when creating diff --git a/routing/chainview/neutrino.go b/routing/chainview/neutrino.go index 49f129bf7..84dd6996b 100644 --- a/routing/chainview/neutrino.go +++ b/routing/chainview/neutrino.go @@ -225,6 +225,10 @@ func (c *CfFilteredChainView) chainFilterer() { log.Errorf("unable to update rescan: %v", err) } + if update.done != nil { + close(update.done) + } + case <-c.quit: return } @@ -253,11 +257,11 @@ func (c *CfFilteredChainView) FilterBlock(blockHash *chainhash.Hash) (*FilteredB // If we don't have any items within our current chain filter, then we // can exit early as we don't need to fetch the filter. c.filterMtx.RLock() - numPoints := len(c.chainFilter) - c.filterMtx.RUnlock() - if numPoints == 0 { + if len(c.chainFilter) == 0 { + c.filterMtx.RUnlock() return filteredBlock, nil } + c.filterMtx.RUnlock() // Next, using the block, hash, we'll fetch the compact filter for this // block. We only require the regular filter as we're just looking for @@ -338,17 +342,26 @@ func (c *CfFilteredChainView) FilterBlock(blockHash *chainhash.Hash) (*FilteredB // // NOTE: This is part of the FilteredChainView interface. func (c *CfFilteredChainView) UpdateFilter(ops []wire.OutPoint, updateHeight uint32) error { - select { - - case c.filterUpdates <- filterUpdate{ + doneChan := make(chan struct{}) + update := filterUpdate{ newUtxos: ops, updateHeight: updateHeight, - }: - return nil + done: doneChan, + } + select { + case c.filterUpdates <- update: case <-c.quit: return fmt.Errorf("chain filter shutting down") } + + select { + case <-doneChan: + return nil + case <-c.quit: + return fmt.Errorf("chain filter shutting down") + } + } // FilteredBlocks returns the channel that filtered blocks are to be sent over.