diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index b3dfd929b..1fa32218d 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -16,6 +16,13 @@ import ( "github.com/roasbeef/btcutil" ) +// LocalUnilateralCloseInfo encapsulates all the informnation we need to act +// on a local force close that gets confirmed. +type LocalUnilateralCloseInfo struct { + *chainntnfs.SpendDetail + *lnwallet.LocalForceCloseSummary +} + // ChainEventSubscription is a struct that houses a subscription to be notified // for any on-chain events related to a channel. There are three types of // possible on-chain events: a cooperative channel closure, a unilateral @@ -25,13 +32,16 @@ type ChainEventSubscription struct { // ChanPoint is that channel that chain events will be dispatched for. ChanPoint wire.OutPoint - // UnilateralClosure is a channel that will be sent upon in the event that - // the remote party broadcasts their latest version of the commitment - // transaction. - UnilateralClosure chan *lnwallet.UnilateralCloseSummary + // RemoteUnilateralClosure is a channel that will be sent upon in the + // event that the remote party's commitment transaction is confirmed. + RemoteUnilateralClosure chan *lnwallet.UnilateralCloseSummary - // CooperativeClosure is a signal that will be sent upon once a cooperative - // channel closure has been detected. + // LocalUnilateralClosure is a channel that will be sent upon in the + // event that our commitment transaction is confirmed. + LocalUnilateralClosure chan *LocalUnilateralCloseInfo + + // CooperativeClosure is a signal that will be sent upon once a + // cooperative channel closure has been detected confirmed. // // TODO(roasbeef): or something else CooperativeClosure chan struct{} @@ -226,10 +236,11 @@ func (c *chainWatcher) SubscribeChannelEvents(syncDispatch bool) *ChainEventSubs clientID, c.chanState.FundingOutpoint) sub := &ChainEventSubscription{ - ChanPoint: c.chanState.FundingOutpoint, - UnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), - CooperativeClosure: make(chan struct{}, 1), - ContractBreach: make(chan *lnwallet.BreachRetribution, 1), + ChanPoint: c.chanState.FundingOutpoint, + RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), + LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1), + CooperativeClosure: make(chan struct{}, 1), + ContractBreach: make(chan *lnwallet.BreachRetribution, 1), Cancel: func() { c.Lock() delete(c.clientSubscriptions, clientID) @@ -307,6 +318,13 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) { &commitmentHash, ) if isOurCommitment { + if err := c.dispatchLocalForceClose( + commitSpend, *localCommit, + ); err != nil { + log.Errorf("unable to handle local"+ + "close for chan_point=%v: %v", + c.chanState.FundingOutpoint, err) + } return } @@ -349,7 +367,7 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) { // has a fail crash _after_ accepting the new state, // but _before_ sending their signature to us. case broadcastStateNum >= remoteStateNum: - if err := c.dispatchRemoteClose( + if err := c.dispatchRemoteForceClose( commitSpend, *remoteCommit, ); err != nil { log.Errorf("unable to handle remote "+ @@ -500,12 +518,45 @@ func (c *chainWatcher) dispatchCooperativeClose(commitSpend *chainntnfs.SpendDet } -// dispatchRemoteClose processes a detected unilateral channel closure by the +// dispatchLocalForceClose processes a unilateral close by us being confirmed. +func (c *chainWatcher) dispatchLocalForceClose( + commitSpend *chainntnfs.SpendDetail, + localCommit channeldb.ChannelCommitment) error { + + log.Infof("Local unilateral close of ChannelPoint(%v) "+ + "detected", c.chanState.FundingOutpoint) + + forceClose, err := lnwallet.NewLocalForceCloseSummary( + c.chanState, c.signer, c.pCache, commitSpend.SpendingTx, + localCommit, + ) + if err != nil { + return err + } + + // With the event processed, we'll now notify all subscribers of the + // event. + closeInfo := &LocalUnilateralCloseInfo{commitSpend, forceClose} + c.Lock() + for _, sub := range c.clientSubscriptions { + select { + case sub.LocalUnilateralClosure <- closeInfo: + case <-c.quit: + c.Unlock() + return fmt.Errorf("exiting") + } + } + c.Unlock() + + return nil +} + +// dispatchRemoteForceClose processes a detected unilateral channel closure by the // remote party. This function will prepare a UnilateralCloseSummary which will // then be sent to any subscribers allowing them to resolve all our funds in // the channel on chain. Once this close summary is prepared, all registered // subscribers will receive a notification of this event. -func (c *chainWatcher) dispatchRemoteClose(commitSpend *chainntnfs.SpendDetail, +func (c *chainWatcher) dispatchRemoteForceClose(commitSpend *chainntnfs.SpendDetail, remoteCommit channeldb.ChannelCommitment) error { log.Infof("Unilateral close of ChannelPoint(%v) "+ @@ -538,7 +589,7 @@ func (c *chainWatcher) dispatchRemoteClose(commitSpend *chainntnfs.SpendDetail, // * get ACK from the consumer of the ntfn before writing to disk? // * no harm in repeated ntfns: at least once semantics select { - case sub.UnilateralClosure <- uniClose: + case sub.RemoteUnilateralClosure <- uniClose: case <-c.quit: c.Unlock() return fmt.Errorf("exiting")