// executeCooperativeClose executes the initial phase of a user-executed // cooperative channel close. The channel state machine is transitioned to the // closing phase, then our half of the closing witness is sent over to the // remote peer. func (p *peer) executeCooperativeClose(channel *lnwallet.LightningChannel) (*wire.ShaHash, error) { // Shift the channel state machine into a 'closing' state. This // generates a signature for the closing tx, as well as a txid of the // closing tx itself, allowing us to watch the network to determine // when the remote node broadcasts the fully signed closing // transaction. sig, txid, err := channel.InitCooperativeClose() if err != nil { return nil, err } chanPoint := channel.ChannelPoint() peerLog.Infof("Executing cooperative closure of "+ "ChanPoint(%v) with peerID(%v), txid=%v", chanPoint, p.id, txid) // With our signature for the close tx generated, send the signature to // the remote peer instructing it to close this particular channel // point. // TODO(roasbeef): remove encoding redundancy closeSig, err := btcec.ParseSignature(sig, btcec.S256()) if err != nil { return nil, err } closeReq := lnwire.NewCloseRequest(chanPoint, closeSig) p.queueMsg(closeReq, nil) return txid, nil }
// executeForceClose executes a unilateral close of the target channel by // broadcasting the current commitment state directly on-chain. Once the // commitment transaction has been broadcast, a struct describing the final // state of the channel is sent to the utxoNursery in order to ultimatley sweep // the immature outputs. func (p *peer) executeForceClose(channel *lnwallet.LightningChannel) (*wire.ShaHash, error) { // Execute a unilateral close shutting down all further channel // operation. closeSummary, err := channel.ForceClose() if err != nil { return nil, err } closeTx := closeSummary.CloseTx txid := closeTx.TxSha() // With the close transaction in hand, broadcast the transaction to the // network, thereby entering the psot channel resolution state. peerLog.Infof("Broadcasting force close transaction: %v", channel.ChannelPoint(), newLogClosure(func() string { return spew.Sdump(closeTx) })) if err := p.server.lnwallet.PublishTransaction(closeTx); err != nil { return nil, err } // Send the closed channel summary over to the utxoNursery in order to // have its outputs swept back into the wallet once they're mature. p.server.utxoNursery.incubateOutputs(closeSummary) return &txid, nil }
// wipeChannel removes the passed channel from all indexes associated with the // peer, and deletes the channel from the database. func wipeChannel(p *peer, channel *lnwallet.LightningChannel) error { chanID := channel.ChannelPoint() delete(p.activeChannels, *chanID) // Instruct the Htlc Switch to close this link as the channel is no // longer active. p.server.htlcSwitch.UnregisterLink(p.addr.IdentityKey, chanID) htlcWireLink, ok := p.htlcManagers[*chanID] if !ok { return nil } delete(p.htlcManagers, *chanID) close(htlcWireLink) if err := channel.DeleteState(); err != nil { peerLog.Errorf("Unable to delete ChannelPoint(%v) "+ "from db %v", chanID, err) return err } return nil }
// htlcManager is the primary goroutine which drives a channel's commitment // update state-machine in response to messages received via several channels. // The htlcManager reads messages from the upstream (remote) peer, and also // from several possible downstream channels managed by the htlcSwitch. In the // event that an htlc needs to be forwarded, then send-only htlcPlex chan is // used which sends htlc packets to the switch for forwarding. Additionally, // the htlcManager handles acting upon all timeouts for any active HTLC's, // manages the channel's revocation window, and also the htlc trickle // queue+timer for this active channels. func (p *peer) htlcManager(channel *lnwallet.LightningChannel, htlcPlex chan<- *htlcPacket, downstreamLink <-chan *htlcPacket, upstreamLink <-chan lnwire.Message) { chanStats := channel.StateSnapshot() peerLog.Infof("HTLC manager for ChannelPoint(%v) started, "+ "our_balance=%v, their_balance=%v, chain_height=%v", channel.ChannelPoint(), chanStats.LocalBalance, chanStats.RemoteBalance, chanStats.NumUpdates) // A new session for this active channel has just started, therefore we // need to send our initial revocation window to the remote peer. for i := 0; i < lnwallet.InitialRevocationWindow; i++ { rev, err := channel.ExtendRevocationWindow() if err != nil { peerLog.Errorf("unable to expand revocation window: %v", err) continue } p.queueMsg(rev, nil) } state := &commitmentState{ channel: channel, chanPoint: channel.ChannelPoint(), clearedHTCLs: make(map[uint32]*pendingPayment), htlcsToSettle: make(map[uint32]*channeldb.Invoice), pendingCircuits: make(map[uint32]*sphinx.ProcessedPacket), sphinx: p.server.sphinx, switchChan: htlcPlex, } // TODO(roasbeef): check to see if able to settle any currently pending // HTLC's // * also need signals when new invoices are added by the invoiceRegistry batchTimer := time.Tick(10 * time.Millisecond) out: for { select { case <-channel.UnilateralCloseSignal: // TODO(roasbeef): eliminate false positive via local close peerLog.Warnf("Remote peer has closed ChannelPoint(%v) on-chain", state.chanPoint) if err := wipeChannel(p, channel); err != nil { peerLog.Errorf("unable to wipe channel %v", err) } break out case <-channel.ForceCloseSignal: peerLog.Warnf("ChannelPoint(%v) has been force "+ "closed, disconnecting from peerID(%x)", state.chanPoint, p.id) break out //p.Disconnect() // TODO(roasbeef): prevent leaking ticker? case <-state.logCommitTimer: // If we haven't sent or received a new commitment // update in some time, check to see if we have any // pending updates we need to commit. If so, then send // an update incrementing the unacked counter is // successfully. if !state.channel.PendingUpdates() && len(state.htlcsToSettle) == 0 { continue } if sent, err := p.updateCommitTx(state); err != nil { peerLog.Errorf("unable to update "+ "commitment: %v", err) p.Disconnect() break out } else if sent { state.numUnAcked += 1 } case <-batchTimer: // If the current batch is empty, then we have no work // here. if len(state.pendingBatch) == 0 { continue } // Otherwise, attempt to extend the remote commitment // chain including all the currently pending entries. // If the send was unsuccesful, then abaondon the // update, waiting for the revocation window to open // up. if sent, err := p.updateCommitTx(state); err != nil { peerLog.Errorf("unable to update "+ "commitment: %v", err) p.Disconnect() break out } else if !sent { continue } state.numUnAcked += 1 case pkt := <-downstreamLink: p.handleDownStreamPkt(state, pkt) case msg, ok := <-upstreamLink: // If the upstream message link is closed, this signals // that the channel itself is being closed, therefore // we exit. if !ok { break out } p.handleUpstreamMsg(state, msg) case <-p.quit: break out } } p.wg.Done() peerLog.Tracef("htlcManager for peer %v done", p) }