contractcourt: on startup, launch a goroutine to finalize co-op chan close if needed

This commit is contained in:
Olaoluwa Osuntokun 2018-01-19 17:24:48 -08:00
parent db2b9fd9ff
commit 91d45b37ba
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
1 changed files with 88 additions and 2 deletions

View File

@ -364,6 +364,18 @@ func (c *ChainArbitrator) Start() error {
// Next, for each channel is the closing state, we'll launch a
// corresponding more restricted resolver.
for _, closeChanInfo := range closingChannels {
// If this is a pending cooperative close channel then we'll
// simply launch a goroutine to wait until the closing
// transaction has been confirmed.
if closeChanInfo.CloseType == channeldb.CooperativeClose {
go c.watchForChannelClose(closeChanInfo)
// TODO(roasbeef): actually need arb to possibly
// recover from race condition broadcast?
// * if do, can't recover from multi-broadcast
continue
}
blockEpoch, err := c.cfg.Notifier.RegisterBlockEpochNtfn()
if err != nil {
return err
@ -456,6 +468,67 @@ func (c *ChainArbitrator) Stop() error {
return nil
}
// watchForChannelClose is used by the ChainArbitrator to watch for the
// ultimate on-chain conformation of an existing cooperative channel closure.
// This is needed if we started a co-op close, but it wasn't fully confirmed
// before we restarted.
//
// NOTE: This must be launched as a goroutine.
func (c *ChainArbitrator) watchForChannelClose(closeInfo *channeldb.ChannelCloseSummary) {
spendNtfn, err := c.cfg.Notifier.RegisterSpendNtfn(
&closeInfo.ChanPoint, closeInfo.CloseHeight,
)
if err != nil {
log.Errorf("unable to register for spend: %v", err)
return
}
var (
commitSpend *chainntnfs.SpendDetail
ok bool
)
select {
case commitSpend, ok = <-spendNtfn.Spend:
if !ok {
return
}
case <-c.quit:
return
}
confNtfn, err := c.cfg.Notifier.RegisterConfirmationsNtfn(
commitSpend.SpenderTxHash, 1,
uint32(commitSpend.SpendingHeight),
)
if err != nil {
log.Errorf("unable to register for "+
"conf: %v", err)
return
}
log.Infof("Waiting for txid=%v to close ChannelPoint(%v) on chain",
commitSpend.SpenderTxHash, closeInfo.ChanPoint)
select {
case confInfo, ok := <-confNtfn.Confirmed:
if !ok {
return
}
log.Infof("ChannelPoint(%v) is fully closed, at height: %v",
closeInfo.ChanPoint, confInfo.BlockHeight)
err := c.resolveContract(closeInfo.ChanPoint, nil)
if err != nil {
log.Errorf("unable to resolve contract: %v", err)
}
case <-c.quit:
return
}
}
// ContractSignals wraps the two signals that affect the state of a channel
// being watched by an arbitrator. The two signals we care about are: the
// channel has a new set of HTLC's, and the remote party has just broadcast
@ -603,8 +676,6 @@ func (c *ChainArbitrator) WatchNewChannel(newChan *channeldb.OpenChannel) error
// With the arbitrator created, we'll add it to our set of active
// arbitrators, then launch it.
c.activeChannels[chanPoint] = channelArb
return channelArb.Start()
}
if err := channelArb.Start(); err != nil {
return err
@ -636,5 +707,20 @@ func (c *ChainArbitrator) SubscribeChannelEvents(
return watcher.SubscribeChannelEvents(), nil
}
// BeginCoopChanClose allows the initiator or responder to a cooperative
// channel closure to signal to the ChainArbitrator that we're starting close
// negotiation. The caller can use this context to allow the underlying chain
// watcher to be prepared to act if *any* of the transactions that may
// potentially be signed off on during fee negotiation are confirmed.
func (c *ChainArbitrator) BeginCoopChanClose(chanPoint wire.OutPoint) (*CooperativeCloseCtx, error) {
watcher, ok := c.activeWatchers[chanPoint]
if !ok {
return nil, fmt.Errorf("unable to find watcher for: %v",
chanPoint)
}
return watcher.BeginCooperativeClose(), nil
}
// TODO(roasbeef): arbitration reports
// * types: contested, waiting for success conf, etc