Merge pull request #624 from wpaulino/neutrino-rescan-finished-deadlock

chain: prevent deadlock while notifying RescanFinished for NeutrinoClient
This commit is contained in:
Olaoluwa Osuntokun 2019-06-11 04:59:59 +02:00 committed by GitHub
commit 66a95921c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -322,16 +322,17 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre
outPoints map[wire.OutPoint]btcutil.Address) error { outPoints map[wire.OutPoint]btcutil.Address) error {
s.clientMtx.Lock() s.clientMtx.Lock()
defer s.clientMtx.Unlock()
if !s.started { if !s.started {
s.clientMtx.Unlock()
return fmt.Errorf("can't do a rescan when the chain client " + return fmt.Errorf("can't do a rescan when the chain client " +
"is not started") "is not started")
} }
if s.scanning { if s.scanning {
// Restart the rescan by killing the existing rescan. // Restart the rescan by killing the existing rescan.
close(s.rescanQuit) close(s.rescanQuit)
rescan := s.rescan
s.clientMtx.Unlock() s.clientMtx.Unlock()
s.rescan.WaitForShutdown() rescan.WaitForShutdown()
s.clientMtx.Lock() s.clientMtx.Lock()
s.rescan = nil s.rescan = nil
s.rescanErr = nil s.rescanErr = nil
@ -342,6 +343,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre
s.lastProgressSent = false s.lastProgressSent = false
s.lastFilteredBlockHeader = nil s.lastFilteredBlockHeader = nil
s.isRescan = true s.isRescan = true
s.clientMtx.Unlock()
bestBlock, err := s.CS.BestBlock() bestBlock, err := s.CS.BestBlock()
if err != nil { 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 // with state that indicates a "fresh" wallet, we'll send a
// notification indicating the rescan has "finished". // notification indicating the rescan has "finished".
if header.BlockHash() == *startHash { if header.BlockHash() == *startHash {
s.clientMtx.Lock()
s.finished = true 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 { select {
case s.enqueueNotification <- &RescanFinished{ case s.enqueueNotification <- &RescanFinished{
Hash: startHash, Hash: startHash,
@ -366,7 +375,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre
}: }:
case <-s.quit: case <-s.quit:
return nil return nil
case <-s.rescanQuit: case <-rescanQuit:
return nil return nil
} }
} }
@ -375,6 +384,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre
for op, addr := range outPoints { for op, addr := range outPoints {
addrScript, err := txscript.PayToAddrScript(addr) addrScript, err := txscript.PayToAddrScript(addr)
if err != nil { if err != nil {
return err
} }
inputsToWatch = append(inputsToWatch, neutrino.InputWithScript{ 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( newRescan := neutrino.NewRescan(
&neutrino.RescanChainSource{ &neutrino.RescanChainSource{
ChainService: s.CS, ChainService: s.CS,
@ -400,6 +411,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre
) )
s.rescan = newRescan s.rescan = newRescan
s.rescanErr = s.rescan.Start() s.rescanErr = s.rescan.Start()
s.clientMtx.Unlock()
return nil return nil
} }