htlcswitch: asynchronously handle channel close requests

This commit modifies how the htlcswitch handles close requests.
Previously it could be the case that a new channel was added, but at
the same time a channel was requested to be closed. This would result
in a circular waiting dependency: the peer contacts the switch, who
tries to contact the peer.

We eliminate this possibility by ensuring that the switch handles all
close requests asynchronously. With this, the switch won't block
indefinitely in the scenario described above.
This commit is contained in:
Olaoluwa Osuntokun 2017-08-03 16:57:56 -07:00
parent d39410cc01
commit 0d4ee08372
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2

@ -149,7 +149,7 @@ type Switch struct {
// the channel close handler.
chanCloseRequests chan *ChanClose
// linkControl is a channel used to propogate add/remove/get htlc
// linkControl is a channel used to propagate add/remove/get htlc
// switch handler commands.
linkControl chan interface{}
}
@ -512,7 +512,9 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
// If this is failure than we need to obfuscate the error.
if htlc, ok := htlc.(*lnwire.UpdateFailHTLC); ok && !packet.isObfuscated {
htlc.Reason = circuit.Obfuscator.BackwardObfuscate(htlc.Reason)
htlc.Reason = circuit.Obfuscator.BackwardObfuscate(
htlc.Reason,
)
}
// Propagating settle/fail htlc back to src of add htlc packet.
@ -542,7 +544,7 @@ func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
closeType ChannelCloseType) (chan *lnrpc.CloseStatusUpdate, chan error) {
// TODO(roasbeef) abstract out the close updates.
updateChan := make(chan *lnrpc.CloseStatusUpdate, 1)
updateChan := make(chan *lnrpc.CloseStatusUpdate, 2)
errChan := make(chan error, 1)
command := &ChanClose{
@ -564,34 +566,6 @@ func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
}
}
// handleCloseLink sends a message to the peer responsible for the target
// channel point, instructing it to initiate a cooperative channel closure.
func (s *Switch) handleChanelClose(req *ChanClose) {
targetChanID := lnwire.NewChanIDFromOutPoint(req.ChanPoint)
var link ChannelLink
for chanID, l := range s.linkIndex {
if chanID == targetChanID {
link = l
}
}
if link == nil {
req.Err <- errors.Errorf("channel with ChannelID(%v) not "+
"found", targetChanID)
return
}
log.Debugf("requesting local channel close, peer(%v) channel(%v)",
link.Peer(), targetChanID)
// TODO(roasbeef): if type was CloseBreach initiate force closure with
// all other channels (if any) we have with the remote peer.
peerPub := link.Peer().PubKey()
s.cfg.LocalChannelClose(peerPub[:], req)
return
}
// htlcForwarder is responsible for optimally forwarding (and possibly
// fragmenting) incoming/outgoing HTLCs amongst all active interfaces and their
// links. The duties of the forwarder are similar to that of a network switch,
@ -630,7 +604,19 @@ func (s *Switch) htlcForwarder() {
// relevant link (if it exists) so the channel can be
// cooperatively closed (if possible).
case req := <-s.chanCloseRequests:
s.handleChanelClose(req)
chanID := lnwire.NewChanIDFromOutPoint(req.ChanPoint)
link, ok := s.linkIndex[chanID]
if !ok {
req.Err <- errors.Errorf("channel with "+
"chan_id=%v not found", chanID[:])
continue
}
peerPub := link.Peer().PubKey()
log.Debugf("Requesting local channel close: peer=%v, "+
"chan_id=%x", link.Peer(), chanID[:])
go s.cfg.LocalChannelClose(peerPub[:], req)
// A new packet has arrived for forwarding, we'll interpret the
// packet concretely, then either forward it along, or