contractcourt/chain_watcher: don't delete syncDispatch clients on Cancel()

This commit makes clients subscribing to channel events that are marked
"sync dispatch" _not_ being deleted from the list of clients when they
call Cancel(). Instead a go routine will be launched that will send an
error on every read of the ProcessACK channel.

This fixes a race in handing off the breach info while lnd was shutting
down. The breach arbiter could end up being shut down (and calling
Cancel()) before while the ChainWatcher was in the process of
dispatching a breach. Since the breach arbiter no longer was among the
registered clients at this point, the ChainWatcher would assume the
breach was handed off successfully, and mark the channel as pending
closed. When lnd now was restarted, the breach arbiter would not know
about the breach, and the ChainWatcher wouldn't attempt to re-dispatch,
as it was already marked as pending closed.
This commit is contained in:
Johan T. Halseth 2018-04-16 15:21:21 +02:00
parent 8d5a33e349
commit 4320421110
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26

@ -251,6 +251,30 @@ func (c *chainWatcher) SubscribeChannelEvents(syncDispatch bool) *ChainEventSubs
if syncDispatch {
sub.ProcessACK = make(chan error, 1)
// If this client is syncDispatch, we cannot safely delete it
// from our list of clients. This is because of a potential
// race at shutdown, where the client shuts down and calls
// Cancel(). In this case we must make sure the ChainWatcher
// doesn't think it has successfully handed off a contract
// breach to the client. We start a goroutine that will send an
// error on the ProcessACK channel until the ChainWatcher is
// shutdown.
sub.Cancel = func() {
c.wg.Add(1)
go func() {
defer c.wg.Done()
err := fmt.Errorf("cancelled")
for {
select {
case sub.ProcessACK <- err:
case <-c.quit:
return
}
}
}()
}
}
c.Lock()