mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-10-11 09:53:14 +02:00
routing: implement route failure fallback in SendPayment
This commit adds payment route failure fallback to SendPayment. By this, we mean that we now take all the possible routes found during path finding and try them in series. Either a route fails and we move onto the next one, or the route is successful and we terminate early. With this commit, sending payments using lnd is now much more robust as if there exists an eligible route with sufficient capacity, it will be utilized.
This commit is contained in:
@@ -3,7 +3,6 @@ package routing
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
@@ -1221,43 +1220,70 @@ type LightningPayment struct {
|
||||
// within the network to reach the destination. Additionally, the payment
|
||||
// preimage will also be returned.
|
||||
func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route, error) {
|
||||
var (
|
||||
err error
|
||||
preImage [32]byte
|
||||
log.Tracef("Dispatching route for lightning payment: %v",
|
||||
newLogClosure(func() string {
|
||||
return spew.Sdump(payment)
|
||||
}),
|
||||
)
|
||||
|
||||
// Query the graph for a potential path to the destination node that
|
||||
// can support our payment amount. If a path is ultimately unavailable,
|
||||
// then an error will be returned.
|
||||
route, err := r.FindRoute(payment.Target, payment.Amount)
|
||||
if err != nil {
|
||||
return preImage, nil, err
|
||||
}
|
||||
log.Tracef("Selected route for payment: %#v", route)
|
||||
// TODO(roasbeef): consult KSP cache before dispatching
|
||||
|
||||
// Generate the raw encoded sphinx packet to be included along with the
|
||||
// htlcAdd message that we send directly to the switch.
|
||||
sphinxPacket, err := generateSphinxPacket(route, payment.PaymentHash[:])
|
||||
var (
|
||||
sendError error
|
||||
preImage [32]byte
|
||||
)
|
||||
|
||||
// Query the graph for a set of potential routes to the destination
|
||||
// node that can support our payment amount. If no such routes can be
|
||||
// found then an error will be returned.
|
||||
routes, err := r.FindRoutes(payment.Target, payment.Amount)
|
||||
if err != nil {
|
||||
return preImage, nil, err
|
||||
}
|
||||
|
||||
// Craft an HTLC packet to send to the layer 2 switch. The metadata
|
||||
// within this packet will be used to route the payment through the
|
||||
// network, starting with the first-hop.
|
||||
htlcAdd := &lnwire.UpdateAddHTLC{
|
||||
Amount: route.TotalAmount,
|
||||
PaymentHash: payment.PaymentHash,
|
||||
}
|
||||
copy(htlcAdd.OnionBlob[:], sphinxPacket)
|
||||
// For each eligible path, we'll attempt to successfully send our
|
||||
// target payment using the multi-hop route. We'll try each route
|
||||
// serially until either once succeeds, or we've exhausted our set of
|
||||
// available paths.
|
||||
for _, route := range routes {
|
||||
log.Tracef("Attempting to send payment %x, using route: %#v",
|
||||
payment.PaymentHash, newLogClosure(func() string {
|
||||
return spew.Sdump(route)
|
||||
}),
|
||||
)
|
||||
|
||||
// Attempt to send this payment through the network to complete the
|
||||
// payment. If this attempt fails, then we'll bail our early.
|
||||
firstHop := route.Hops[0].Channel.Node.PubKey
|
||||
preImage, err = r.cfg.SendToSwitch(firstHop, htlcAdd)
|
||||
if err != nil {
|
||||
return preImage, nil, err
|
||||
// Generate the raw encoded sphinx packet to be included along
|
||||
// with the htlcAdd message that we send directly to the
|
||||
// switch.
|
||||
sphinxPacket, err := generateSphinxPacket(route, payment.PaymentHash[:])
|
||||
if err != nil {
|
||||
return preImage, nil, err
|
||||
}
|
||||
|
||||
// Craft an HTLC packet to send to the layer 2 switch. The
|
||||
// metadata within this packet will be used to route the
|
||||
// payment through the network, starting with the first-hop.
|
||||
htlcAdd := &lnwire.UpdateAddHTLC{
|
||||
Amount: route.TotalAmount,
|
||||
PaymentHash: payment.PaymentHash,
|
||||
}
|
||||
copy(htlcAdd.OnionBlob[:], sphinxPacket)
|
||||
|
||||
// Attempt to send this payment through the network to complete
|
||||
// the payment. If this attempt fails, then we'll continue on
|
||||
// to the next available route.
|
||||
firstHop := route.Hops[0].Channel.Node.PubKey
|
||||
preImage, sendError = r.cfg.SendToSwitch(firstHop, htlcAdd)
|
||||
if sendError != nil {
|
||||
log.Errorf("Attempt to send payment %x failed: %v",
|
||||
payment.PaymentHash, err)
|
||||
continue
|
||||
}
|
||||
|
||||
return preImage, route, nil
|
||||
}
|
||||
|
||||
return preImage, route, nil
|
||||
// If we're unable to successfully make a payment using any of the
|
||||
// routes we've found, then return an error.
|
||||
return [32]byte{}, nil, sendError
|
||||
}
|
||||
|
Reference in New Issue
Block a user