// SendCoins attemps to send amt satoshis from the internal mining node to the // targetted lightning node. func (n *networkHarness) SendCoins(ctx context.Context, amt btcutil.Amount, target *lightningNode) error { balReq := &lnrpc.WalletBalanceRequest{} initialBalance, err := target.WalletBalance(ctx, balReq) if err != nil { return err } // First, obtain an address from the target lightning node, preferring // to receive a p2wkh address s.t the output can immediately be used as // an input to a funding transaction. addrReq := &lnrpc.NewAddressRequest{ Type: lnrpc.NewAddressRequest_WITNESS_PUBKEY_HASH, } resp, err := target.NewAddress(ctx, addrReq) if err != nil { return err } addr, err := btcutil.DecodeAddress(resp.Address, n.netParams) if err != nil { return err } addrScript, err := txscript.PayToAddrScript(addr) if err != nil { return err } // Generate a transaction which creates an output to the target // pkScript of the desired amount. output := &wire.TxOut{ PkScript: addrScript, Value: int64(amt), } if _, err := n.Miner.CoinbaseSpend([]*wire.TxOut{output}); err != nil { return err } // Finally, generate 6 new blocks to ensure the output gains a // sufficient number of confirmations. if _, err := n.Miner.Node.Generate(6); err != nil { return err } // Pause until the nodes current wallet balances reflects the amount // sent to it above. // TODO(roasbeef): factor out into helper func for { select { case <-time.Tick(time.Millisecond * 50): currentBal, err := target.WalletBalance(ctx, balReq) if err != nil { return err } if currentBal.Balance == initialBalance.Balance+amt.ToBTC() { return nil } case <-time.After(time.Second * 30): return fmt.Errorf("balances not synced after deadline") } } }
// 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() }