From eedd5ce1b60174439d66b12ba23974b42ec7b890 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 5 Jun 2019 16:12:18 -0700 Subject: [PATCH] chain: prevent deadlock while notifying RescanFinished for NeutrinoClient --- chain/neutrino.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/chain/neutrino.go b/chain/neutrino.go index fb109b4..c87e89e 100644 --- a/chain/neutrino.go +++ b/chain/neutrino.go @@ -322,16 +322,17 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre outPoints map[wire.OutPoint]btcutil.Address) error { s.clientMtx.Lock() - defer s.clientMtx.Unlock() if !s.started { + s.clientMtx.Unlock() return fmt.Errorf("can't do a rescan when the chain client " + "is not started") } if s.scanning { // Restart the rescan by killing the existing rescan. close(s.rescanQuit) + rescan := s.rescan s.clientMtx.Unlock() - s.rescan.WaitForShutdown() + rescan.WaitForShutdown() s.clientMtx.Lock() s.rescan = nil s.rescanErr = nil @@ -342,6 +343,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre s.lastProgressSent = false s.lastFilteredBlockHeader = nil s.isRescan = true + s.clientMtx.Unlock() bestBlock, err := s.CS.BestBlock() if err != nil { @@ -357,7 +359,14 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre // with state that indicates a "fresh" wallet, we'll send a // notification indicating the rescan has "finished". if header.BlockHash() == *startHash { + s.clientMtx.Lock() s.finished = true + rescanQuit := s.rescanQuit + s.clientMtx.Unlock() + + // Release the lock while dispatching the notification since + // it's possible for the notificationHandler to be waiting to + // acquire it before receiving the notification. select { case s.enqueueNotification <- &RescanFinished{ Hash: startHash, @@ -366,7 +375,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre }: case <-s.quit: return nil - case <-s.rescanQuit: + case <-rescanQuit: return nil } } @@ -375,6 +384,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre for op, addr := range outPoints { addrScript, err := txscript.PayToAddrScript(addr) if err != nil { + return err } inputsToWatch = append(inputsToWatch, neutrino.InputWithScript{ @@ -383,6 +393,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre }) } + s.clientMtx.Lock() newRescan := neutrino.NewRescan( &neutrino.RescanChainSource{ ChainService: s.CS, @@ -400,6 +411,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre ) s.rescan = newRescan s.rescanErr = s.rescan.Start() + s.clientMtx.Unlock() return nil }