From 93cbfdbd60dfef04e7973476a7076f1f20b55585 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 28 Nov 2016 18:44:14 -0800 Subject: [PATCH] htlcswitch: add new CloseType enum to account for all closure types --- htlcswitch.go | 57 +++++++++++++++++++++++++++++++++++--------------- lnd.go | 2 +- peer.go | 58 ++++++++++++++++++++++++++++++++++++++++----------- rpcserver.go | 12 +++++++++-- 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/htlcswitch.go b/htlcswitch.go index 0e080eed..68329bb1 100644 --- a/htlcswitch.go +++ b/htlcswitch.go @@ -7,18 +7,18 @@ import ( "sync/atomic" "time" - "golang.org/x/crypto/ripemd160" - "github.com/lightningnetwork/lnd/routing" - "github.com/lightningnetwork/lnd/routing/rt/graph" "github.com/btcsuite/fastsha256" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing" + "github.com/lightningnetwork/lnd/routing/rt/graph" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" + "golang.org/x/crypto/ripemd160" ) const ( @@ -184,6 +184,8 @@ func (h *htlcSwitch) Start() error { return nil } + hswcLog.Tracef("Starting HTLC switch") + h.wg.Add(2) go h.networkAdmin() go h.htlcForwarder() @@ -198,6 +200,8 @@ func (h *htlcSwitch) Stop() error { return nil } + hswcLog.Infof("HLTC switch shutting down") + close(h.quit) h.wg.Wait() @@ -480,7 +484,7 @@ func (h *htlcSwitch) handleRegisterLink(req *registerLinkMsg) { // this link leaves the interface empty, then the interface entry itself is // also deleted. func (h *htlcSwitch) handleUnregisterLink(req *unregisterLinkMsg) { - hswcLog.Infof("unregistering active link, interface=%v, chan_point=%v", + hswcLog.Debugf("unregistering active link, interface=%v, chan_point=%v", hex.EncodeToString(req.chanInterface[:]), req.chanPoint) chanInterface := req.chanInterface @@ -493,7 +497,7 @@ func (h *htlcSwitch) handleUnregisterLink(req *unregisterLinkMsg) { // links for this channel should be cleared. chansRemoved := make([]*wire.OutPoint, 0, len(links)) if req.chanPoint == nil { - hswcLog.Infof("purging all active links for interface %v", + hswcLog.Debugf("purging all active links for interface %v", hex.EncodeToString(chanInterface[:])) for _, link := range links { @@ -550,7 +554,7 @@ func (h *htlcSwitch) handleUnregisterLink(req *unregisterLinkMsg) { // * just have the interfaces index be keyed on hash160? if len(links) == 0 { - hswcLog.Infof("interface %v has no active links, destroying", + hswcLog.Debugf("interface %v has no active links, destroying", hex.EncodeToString(chanInterface[:])) h.interfaceMtx.Lock() delete(h.interfaces, chanInterface) @@ -574,7 +578,7 @@ func (h *htlcSwitch) handleCloseLink(req *closeLinkReq) { return } - hswcLog.Infof("requesting interface %v to close link %v", + hswcLog.Debugf("requesting interface %v to close link %v", hex.EncodeToString(targetLink.peer.lightningID[:]), req.chanPoint) targetLink.peer.localCloseChanReqs <- req } @@ -650,11 +654,30 @@ func (h *htlcSwitch) UnregisterLink(remotePub *btcec.PublicKey, chanPoint *wire. <-done } -// closeChanReq represents a request to close a particular channel specified -// by its outpoint. +// LinkCloseType is a enum which signals the type of channel closure the switch +// should execute. +type LinkCloseType uint8 + +const ( + // CloseRegular indicates a regular cooperative channel closure should be attempted. + CloseRegular LinkCloseType = iota + + // CloseForce indicates that the channel should be forcefully closed. + // This entails the broadcast of the commitment transaction directly on + // chain unilaterally. + CloseForce + + // CloseBreach indicates that a channel breach has been dtected, and + // the link should immediately be marked as unavailable. + CloseBreach +) + +// closeChanReq represents a request to close a particular channel specified by +// its outpoint. type closeLinkReq struct { - chanPoint *wire.OutPoint - forceClose bool + CloseType LinkCloseType + + chanPoint *wire.OutPoint updates chan *lnrpc.CloseStatusUpdate err chan error @@ -663,18 +686,18 @@ type closeLinkReq struct { // CloseLink closes an active link targetted by it's channel point. Closing the // link initiates a cooperative channel closure iff forceClose is false. If // forceClose is true, then a unilateral channel closure is executed. -// TODO(roabeef): bool flag for timeout +// TODO(roasbeef): consolidate with UnregisterLink? func (h *htlcSwitch) CloseLink(chanPoint *wire.OutPoint, - forceClose bool) (chan *lnrpc.CloseStatusUpdate, chan error) { + closeType LinkCloseType) (chan *lnrpc.CloseStatusUpdate, chan error) { updateChan := make(chan *lnrpc.CloseStatusUpdate, 1) errChan := make(chan error, 1) h.linkControl <- &closeLinkReq{ - chanPoint: chanPoint, - forceClose: forceClose, - updates: updateChan, - err: errChan, + CloseType: closeType, + chanPoint: chanPoint, + updates: updateChan, + err: errChan, } return updateChan, errChan diff --git a/lnd.go b/lnd.go index 51f6e8a9..4e7b18b2 100644 --- a/lnd.go +++ b/lnd.go @@ -68,7 +68,7 @@ func lndMain() error { defer chanDB.Close() // Next load btcd's TLS cert for the RPC connection. If a raw cert was - // specified in the config, then we'll se that directly. Otherwise, we + // specified in the config, then we'll set that directly. Otherwise, we // attempt to read the cert from the path specified in the config. var rpcCert []byte if cfg.RawRPCCert != "" { diff --git a/peer.go b/peer.go index 56dc152c..fb8f50ac 100644 --- a/peer.go +++ b/peer.go @@ -11,7 +11,6 @@ import ( "sync/atomic" "time" - "github.com/lightningnetwork/lnd/routing/rt/graph" "github.com/btcsuite/fastsha256" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lightning-onion" @@ -19,6 +18,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/rt/graph" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" @@ -778,15 +778,35 @@ func (p *peer) handleLocalClose(req *closeLinkReq) { channel := p.activeChannels[*req.chanPoint] p.activeChanMtx.RUnlock() - if req.forceClose { + switch req.CloseType { + // A type of CloseForce indicates that the user has opted for + // unilaterally close the channel on-chain. + case CloseForce: closingTxid, err = p.executeForceClose(channel) peerLog.Infof("Force closing ChannelPoint(%v) with txid: %v", req.chanPoint, closingTxid) - } else { + + // A type of CloseRegular indicates that the user has opted to close + // out this channel on-chian, so we execute the cooperative channel + // closre workflow. + case CloseRegular: closingTxid, err = p.executeCooperativeClose(channel) peerLog.Infof("Attempting cooperative close of "+ "ChannelPoint(%v) with txid: %v", req.chanPoint, closingTxid) + + // A type of CloseBreach indicates that the counter-party has breached + // the cahnnel therefore we need to clean up our local state. + case CloseBreach: + peerLog.Infof("ChannelPoint(%v) has been breached, wiping "+ + "channel", req.chanPoint) + if err := wipeChannel(p, channel); err != nil { + peerLog.Infof("Unable to wipe channel after detected "+ + "breach: %v", err) + req.err <- err + return + } + return } if err != nil { req.err <- err @@ -817,11 +837,10 @@ func (p *peer) handleLocalClose(req *closeLinkReq) { select { case height, ok := <-confNtfn.Confirmed: - // In the case that the ChainNotifier is shutting - // down, all subscriber notification channels will be - // closed, generating a nil receive. + // In the case that the ChainNotifier is shutting down, + // all subscriber notification channels will be closed, + // generating a nil receive. if !ok { - // TODO(roasbeef): check for nil elsewhere return } @@ -877,10 +896,12 @@ func (p *peer) handleRemoteClose(req *lnwire.CloseRequest) { return } + peerLog.Infof("Broadcasting cooperative close tx: %v", + newLogClosure(func() string { + return spew.Sdump(closeTx) + })) + // Finally, broadcast the closure transaction, to the network. - peerLog.Infof("Broadcasting cooperative close tx: %v", newLogClosure(func() string { - return spew.Sdump(closeTx) - })) if err := p.server.lnwallet.PublishTransaction(closeTx); err != nil { peerLog.Errorf("channel close tx from "+ "ChannelPoint(%v) rejected: %v", @@ -910,20 +931,33 @@ func wipeChannel(p *peer, channel *lnwallet.LightningChannel) error { // longer active. p.server.htlcSwitch.UnregisterLink(p.addr.IdentityKey, chanID) + // Additionally, close up "down stream" link for the htlcManager which + // has been assigned to this channel. This servers the link between the + // htlcManager and the switch, signalling that the channel is no longer + // active. p.htlcManMtx.RLock() + + // If the channel can't be found in the map, then this channel has + // already been wiped. htlcWireLink, ok := p.htlcManagers[*chanID] if !ok { p.htlcManMtx.RUnlock() return nil } + + close(htlcWireLink) + p.htlcManMtx.RUnlock() + // Next, we remove the htlcManager from our internal map as the + // goroutine should have exited gracefully due to the channel closure + // above. p.htlcManMtx.RLock() delete(p.htlcManagers, *chanID) p.htlcManMtx.RUnlock() - close(htlcWireLink) - + // Finally, we purge the channel's state from the database, leaving a + // small summary for historical records. if err := channel.DeleteState(); err != nil { peerLog.Errorf("Unable to delete ChannelPoint(%v) "+ "from db %v", chanID, err) diff --git a/rpcserver.go b/rpcserver.go index 0a17b1d2..fb05087a 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -12,7 +12,6 @@ import ( "sync" "sync/atomic" - "github.com/lightningnetwork/lnd/routing/rt/graph" "github.com/btcsuite/fastsha256" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lightning-onion" @@ -20,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/rt/graph" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" @@ -367,7 +367,15 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, rpcsLog.Tracef("[closechannel] request for ChannelPoint(%v)", targetChannelPoint) - updateChan, errChan := r.server.htlcSwitch.CloseLink(targetChannelPoint, force) + var closeType LinkCloseType + switch force { + case true: + closeType = CloseForce + case false: + closeType = CloseRegular + } + + updateChan, errChan := r.server.htlcSwitch.CloseLink(targetChannelPoint, closeType) out: for {