// htlcForwarder is responsible for optimally forwarding (and possibly // fragmenting) incoming/outgoing HTLC's amongst all active interfaces and // their links. The duties of the forwarder are similar to that of a network // switch, in that it facilitates multi-hop payments by acting as a central // messaging bus. The switch communicates will active links to create, manage, // and tearn down active onion routed payments.Each active channel is modeled // as networked device with meta-data such as the available payment bandwidth, // and total link capacity. func (h *htlcSwitch) htlcForwarder() { // TODO(roasbeef): track pending payments here instead of within each peer? // Examine settles/timeouts from htlcPlex. Add src to htlcPacket, key by // (src, htlcKey). // TODO(roasbeef): cleared vs settled distinction var numUpdates uint64 var satSent, satRecv btcutil.Amount logTicker := time.NewTicker(10 * time.Second) out: for { select { case htlcPkt := <-h.outgoingPayments: dest := htlcPkt.dest h.interfaceMtx.RLock() chanInterface, ok := h.interfaces[dest] h.interfaceMtx.RUnlock() if !ok { err := fmt.Errorf("Unable to locate link %x", dest[:]) hswcLog.Errorf(err.Error()) htlcPkt.err <- err continue } wireMsg := htlcPkt.msg.(*lnwire.HTLCAddRequest) amt := btcutil.Amount(wireMsg.Amount) // Handle this send request in a distinct goroutine in // order to avoid a possible deadlock between the htlc // switch and channel's htlc manager. for _, link := range chanInterface { // TODO(roasbeef): implement HTLC fragmentation // * avoid full channel depletion at higher // level (here) instead of within state // machine? if link.availableBandwidth < int64(amt) { continue } hswcLog.Tracef("Sending %v to %x", amt, dest[:]) go func() { link.linkChan <- htlcPkt }() n := atomic.AddInt64(&link.availableBandwidth, -int64(amt)) hswcLog.Tracef("Decrementing link %v bandwidth to %v", link.chanPoint, n) continue out } hswcLog.Errorf("Unable to send payment, insufficient capacity") htlcPkt.err <- fmt.Errorf("Insufficient capacity") case pkt := <-h.htlcPlex: // TODO(roasbeef): properly account with cleared vs settled numUpdates += 1 hswcLog.Tracef("plex packet: %v", newLogClosure(func() string { return spew.Sdump(pkt) })) switch wireMsg := pkt.msg.(type) { // A link has just forwarded us a new HTLC, therefore // we initiate the payment circuit within our internal // staate so we can properly forward the ultimate // settle message. case *lnwire.HTLCAddRequest: // Create the two ends of the payment circuit // required to ensure completion of this new // payment. nextHop := pkt.onion.NextHop h.onionMtx.RLock() clearLink, ok := h.onionIndex[nextHop] h.onionMtx.RUnlock() if !ok { hswcLog.Errorf("unable to find dest end of "+ "circuit: %x", nextHop) continue } h.chanIndexMtx.RLock() settleLink := h.chanIndex[pkt.srcLink] h.chanIndexMtx.RUnlock() // TODO(roasbeef): examine per-hop info to decide on link? // * check clear has enough available sat circuit := &paymentCircuit{ clear: clearLink[0], settle: settleLink, } cKey := circuitKey(wireMsg.RedemptionHashes[0]) h.paymentCircuits[cKey] = circuit hswcLog.Debugf("Creating onion circuit for %x: %v<->%v", cKey[:], clearLink[0].chanPoint, settleLink.chanPoint) // With the circuit initiated, send the htlcPkt // to the clearing link within the circuit to // continue propagating the HTLC accross the // network. circuit.clear.linkChan <- &htlcPacket{ msg: wireMsg, err: make(chan error, 1), } // Reduce the available bandwidth for the link // as it will clear the above HTLC, increasing // the limbo balance within the channel. n := atomic.AddInt64(&circuit.clear.availableBandwidth, -int64(pkt.amt)) hswcLog.Tracef("Decrementing link %v bandwidth to %v", circuit.clear.chanPoint, n) satRecv += pkt.amt // We've just received a settle message which means we // can finalize the payment circuit by forwarding the // settle msg to the link which initially created the // circuit. case *lnwire.HTLCSettleRequest: rHash := fastsha256.Sum256(wireMsg.RedemptionProofs[0][:]) var cKey circuitKey copy(cKey[:], rHash[:]) // If we initiated the payment then there won't // be an active circuit to continue propagating // the settle over. Therefore, we exit early. circuit, ok := h.paymentCircuits[cKey] if !ok { hswcLog.Debugf("No existing circuit "+ "for %x", rHash[:]) satSent += pkt.amt continue } hswcLog.Debugf("Closing completed onion "+ "circuit for %x: %v<->%v", rHash[:], circuit.clear.chanPoint, circuit.settle.chanPoint) circuit.settle.linkChan <- &htlcPacket{ msg: wireMsg, err: make(chan error, 1), } // Increase the available bandwidth for the // link as it will settle the above HTLC, // subtracting from the limbo balacne and // incrementing its local balance. n := atomic.AddInt64(&circuit.settle.availableBandwidth, int64(pkt.amt)) hswcLog.Tracef("Incrementing link %v bandwidth to %v", circuit.settle.chanPoint, n) satSent += pkt.amt } case <-logTicker.C: if numUpdates == 0 { continue } hswcLog.Infof("Sent %v satoshis, received %v satoshi in "+ "the last 10 seconds (%v tx/sec)", satSent.ToUnit(btcutil.AmountSatoshi), satRecv.ToUnit(btcutil.AmountSatoshi), float64(numUpdates)/10) satSent = 0 satRecv = 0 numUpdates = 0 case <-h.quit: break out } } h.wg.Done() }