rpc: when force closing, properly manage on-disk channel close state

This commit is contained in:
Olaoluwa Osuntokun 2017-05-04 16:05:34 -07:00
parent 7e8c840f29
commit 3fd161d527
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2

View File

@ -470,73 +470,29 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
} }
r.server.breachArbiter.settledContracts <- chanPoint r.server.breachArbiter.settledContracts <- chanPoint
// With the necessary indexes cleaned up, we'll now force close
// the channel.
closingTxid, err := r.forceCloseChan(channel) closingTxid, err := r.forceCloseChan(channel)
if err != nil { if err != nil {
rpcsLog.Errorf("unable to force close transaction: %v", err) rpcsLog.Errorf("unable to force close transaction: %v", err)
// If the transaction we broadcast is detected as a
// double spend, the this indicates that the remote
// party has broadcast their commitment transaction be
// we didn't notice.
if strings.Contains(err.Error(), "fully-spent") ||
strings.Contains(err.Error(), "double spend") {
// In this case, we'll clean up the channel
// state.
// TODO(roasbeef): check close summary to see
// if we need to sweep any HTLC's
if err := channel.DeleteState(); err != nil {
return err
}
// TODO(roasbeef): also unregister link?
return fmt.Errorf("channel has been closed by remote party")
}
return err return err
} }
updateChan = make(chan *lnrpc.CloseStatusUpdate) // With the transaction broadcast, we send our first update to
errChan = make(chan error) // the client.
go func() { updateChan = make(chan *lnrpc.CloseStatusUpdate, 1)
// With the transaction broadcast, we send our first updateChan <- &lnrpc.CloseStatusUpdate{
// update to the client. Update: &lnrpc.CloseStatusUpdate_ClosePending{
updateChan <- &lnrpc.CloseStatusUpdate{ ClosePending: &lnrpc.PendingUpdate{
Update: &lnrpc.CloseStatusUpdate_ClosePending{ Txid: closingTxid[:],
ClosePending: &lnrpc.PendingUpdate{
Txid: closingTxid[:],
},
}, },
} },
}
// Next, we enter the second phase, waiting for the
// channel to be confirmed before we finalize the force
// closure.
notifier := r.server.chainNotifier
confNtfn, err := notifier.RegisterConfirmationsNtfn(closingTxid, 1)
if err != nil {
errChan <- err
return
}
select {
case txConf, ok := <-confNtfn.Confirmed:
if !ok {
return
}
// As the channel has been closed, we can now
// delete it's state from the database.
rpcsLog.Infof("ChannelPoint(%v) is now "+
"closed at height %v", chanPoint,
txConf.BlockHeight)
if err := channel.DeleteState(); err != nil {
errChan <- err
return
}
case <-r.quit:
return
}
errChan = make(chan error, 1)
notifier := r.server.chainNotifier
go waitForChanToClose(notifier, errChan, chanPoint, closingTxid, func() {
// Respond to the local subsystem which requested the // Respond to the local subsystem which requested the
// channel closure. // channel closure.
updateChan <- &lnrpc.CloseStatusUpdate{ updateChan <- &lnrpc.CloseStatusUpdate{
@ -547,12 +503,9 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
}, },
}, },
} }
})
// Finally, signal to the breachArbiter that it no // TODO(roasbeef): utxo nursery marks as fully closed
// longer needs to watch the channel as it's been
// closed.
r.server.breachArbiter.settledContracts <- chanPoint
}()
} else { } else {
// Otherwise, the caller has requested a regular interactive // Otherwise, the caller has requested a regular interactive
@ -641,7 +594,7 @@ func (r *rpcServer) forceCloseChan(channel *lnwallet.LightningChannel) (*chainha
txid := closeTx.TxHash() txid := closeTx.TxHash()
// With the close transaction in hand, broadcast the transaction to the // With the close transaction in hand, broadcast the transaction to the
// network, thereby entering the psot channel resolution state. // network, thereby entering the postk channel resolution state.
rpcsLog.Infof("Broadcasting force close transaction, ChannelPoint(%v): %v", rpcsLog.Infof("Broadcasting force close transaction, ChannelPoint(%v): %v",
channel.ChannelPoint(), newLogClosure(func() string { channel.ChannelPoint(), newLogClosure(func() string {
return spew.Sdump(closeTx) return spew.Sdump(closeTx)
@ -650,9 +603,28 @@ func (r *rpcServer) forceCloseChan(channel *lnwallet.LightningChannel) (*chainha
return nil, err return nil, err
} }
// Now that the closing transaction has been broadcast successfully,
// we'll mark this channel as being in the pending closed state. The
// UTXO nursery will mark the channel as fully closed once all the
// outputs have been swept.
chanPoint := channel.ChannelPoint()
chanInfo := channel.StateSnapshot()
closeInfo := &channeldb.ChannelCloseSummary{
ChanPoint: *chanPoint,
ClosingTXID: closeTx.TxHash(),
RemotePub: &chanInfo.RemoteIdentity,
Capacity: chanInfo.Capacity,
OurBalance: chanInfo.LocalBalance,
CloseType: channeldb.ForceClose,
IsPending: true,
}
if err := channel.DeleteState(closeInfo); err != nil {
return nil, err
}
// Send the closed channel summary over to the utxoNursery in order to // Send the closed channel summary over to the utxoNursery in order to
// have its outputs swept back into the wallet once they're mature. // have its outputs swept back into the wallet once they're mature.
r.server.utxoNursery.incubateOutputs(closeSummary) r.server.utxoNursery.IncubateOutputs(closeSummary)
return &txid, nil return &txid, nil
} }