contractcourt: on startup, launch a goroutine to finalize co-op chan close if needed
This commit is contained in:
parent
db2b9fd9ff
commit
91d45b37ba
@ -364,6 +364,18 @@ func (c *ChainArbitrator) Start() error {
|
|||||||
// Next, for each channel is the closing state, we'll launch a
|
// Next, for each channel is the closing state, we'll launch a
|
||||||
// corresponding more restricted resolver.
|
// corresponding more restricted resolver.
|
||||||
for _, closeChanInfo := range closingChannels {
|
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()
|
blockEpoch, err := c.cfg.Notifier.RegisterBlockEpochNtfn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -456,6 +468,67 @@ func (c *ChainArbitrator) Stop() error {
|
|||||||
return nil
|
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
|
// 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
|
// 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
|
// 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
|
// With the arbitrator created, we'll add it to our set of active
|
||||||
// arbitrators, then launch it.
|
// arbitrators, then launch it.
|
||||||
c.activeChannels[chanPoint] = channelArb
|
c.activeChannels[chanPoint] = channelArb
|
||||||
return channelArb.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := channelArb.Start(); err != nil {
|
if err := channelArb.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -636,5 +707,20 @@ func (c *ChainArbitrator) SubscribeChannelEvents(
|
|||||||
return watcher.SubscribeChannelEvents(), nil
|
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
|
// TODO(roasbeef): arbitration reports
|
||||||
// * types: contested, waiting for success conf, etc
|
// * types: contested, waiting for success conf, etc
|
||||||
|
Loading…
Reference in New Issue
Block a user