mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-12-05 10:21:20 +01:00
routing: add basic route pruning in response to HTLC onion errors
This commit adds basic route pruning in response to HTLC onion errors. With this new change, the router will now prune routes in response to HTLC errors, which will reduce the time to payment success, and also avoid a bunch of unnecessary network traffic. We now respond to two errors lnwire.FailTemporaryChannelFailure and lnwire.FailUnknownNextPeer. In response to the first error, we’ll prune all routes that contain the channel which was unable to be routed over. In response to the second error we’ll prune all routes that contain the node which couldn’t be found.
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/chainview"
|
"github.com/lightningnetwork/lnd/routing/chainview"
|
||||||
@@ -202,7 +203,8 @@ type ChannelRouter struct {
|
|||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// A compile time check to ensure ChannelRouter implements the ChannelGraphSource interface.
|
// A compile time check to ensure ChannelRouter implements the
|
||||||
|
// ChannelGraphSource interface.
|
||||||
var _ ChannelGraphSource = (*ChannelRouter)(nil)
|
var _ ChannelGraphSource = (*ChannelRouter)(nil)
|
||||||
|
|
||||||
// New creates a new instance of the ChannelRouter with the specified
|
// New creates a new instance of the ChannelRouter with the specified
|
||||||
@@ -845,7 +847,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
|
|||||||
// fetchChanPoint retrieves the original outpoint which is encoded within the
|
// fetchChanPoint retrieves the original outpoint which is encoded within the
|
||||||
// channelID.
|
// channelID.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): replace iwth call to GetBlockTransaction? (woudl allow to
|
// TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to
|
||||||
// later use getblocktxn)
|
// later use getblocktxn)
|
||||||
func (r *ChannelRouter) fetchChanPoint(chanID *lnwire.ShortChannelID) (*wire.OutPoint, error) {
|
func (r *ChannelRouter) fetchChanPoint(chanID *lnwire.ShortChannelID) (*wire.OutPoint, error) {
|
||||||
// First fetch the block hash by the block number encoded, then use
|
// First fetch the block hash by the block number encoded, then use
|
||||||
@@ -1030,7 +1032,7 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
|
|||||||
return validRoutes[i].TotalFees < validRoutes[j].TotalFees
|
return validRoutes[i].TotalFees < validRoutes[j].TotalFees
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Debugf("Obtained %v paths sending %v to %x: %v", len(validRoutes),
|
log.Tracef("Obtained %v paths sending %v to %x: %v", len(validRoutes),
|
||||||
amt, dest, newLogClosure(func() string {
|
amt, dest, newLogClosure(func() string {
|
||||||
return spew.Sdump(validRoutes)
|
return spew.Sdump(validRoutes)
|
||||||
}),
|
}),
|
||||||
@@ -1157,7 +1159,9 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
|
|||||||
// target payment using the multi-hop route. We'll try each route
|
// target payment using the multi-hop route. We'll try each route
|
||||||
// serially until either once succeeds, or we've exhausted our set of
|
// serially until either once succeeds, or we've exhausted our set of
|
||||||
// available paths.
|
// available paths.
|
||||||
for _, route := range routes {
|
sendLoop:
|
||||||
|
for i := 0; i < len(routes); i++ {
|
||||||
|
route := routes[i]
|
||||||
log.Tracef("Attempting to send payment %x, using route: %v",
|
log.Tracef("Attempting to send payment %x, using route: %v",
|
||||||
payment.PaymentHash, newLogClosure(func() string {
|
payment.PaymentHash, newLogClosure(func() string {
|
||||||
return spew.Sdump(route)
|
return spew.Sdump(route)
|
||||||
@@ -1197,7 +1201,14 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
|
|||||||
log.Errorf("Attempt to send payment %x failed: %v",
|
log.Errorf("Attempt to send payment %x failed: %v",
|
||||||
payment.PaymentHash, sendError)
|
payment.PaymentHash, sendError)
|
||||||
|
|
||||||
switch onionErr := sendError.(type) {
|
fErr, ok := sendError.(*htlcswitch.ForwardingError)
|
||||||
|
if !ok {
|
||||||
|
return preImage, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
errSource := fErr.ErrorSource
|
||||||
|
|
||||||
|
switch onionErr := fErr.FailureMessage.(type) {
|
||||||
// If the end destination didn't know they payment
|
// If the end destination didn't know they payment
|
||||||
// hash, then we'll terminate immediately.
|
// hash, then we'll terminate immediately.
|
||||||
case *lnwire.FailUnknownPaymentHash:
|
case *lnwire.FailUnknownPaymentHash:
|
||||||
@@ -1282,12 +1293,31 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
case *lnwire.FailTemporaryChannelFailure:
|
case *lnwire.FailTemporaryChannelFailure:
|
||||||
// TODO(roasbeef): remove channel from timeout period
|
|
||||||
update := onionErr.Update
|
update := onionErr.Update
|
||||||
if err := r.applyChannelUpdate(update); err != nil {
|
if err := r.applyChannelUpdate(update); err != nil {
|
||||||
return preImage, nil, err
|
return preImage, nil, err
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
|
// As this error indicates that the target
|
||||||
|
// channel was unable to carry this HTLC (for
|
||||||
|
// w/e reason), we'll query the index to find
|
||||||
|
// the _outgoign_ channel the source of the
|
||||||
|
// error was meant to pass the HTLC along to.
|
||||||
|
badChan, ok := route.nextHopChannel(errSource)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the channel was found, then we'll filter
|
||||||
|
// the rest of the routes to exclude any path
|
||||||
|
// that includes this channel, and restart the
|
||||||
|
// loop.
|
||||||
|
//
|
||||||
|
// TODO(roasbeef): should actually be
|
||||||
|
// directional?
|
||||||
|
routes = pruneChannelFromRoutes(routes,
|
||||||
|
badChan)
|
||||||
|
goto sendLoop
|
||||||
|
|
||||||
// If the send fail due to a node not having the
|
// If the send fail due to a node not having the
|
||||||
// required features, then we'll note this error and
|
// required features, then we'll note this error and
|
||||||
@@ -1305,12 +1335,24 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
// If the next hop in the route wasn't known or
|
// If the next hop in the route wasn't known or
|
||||||
// offline, we'll note this and continue with the rest
|
// offline, we'll prune the _next_ hop from the set of
|
||||||
// of the routes.
|
// routes and retry.
|
||||||
//
|
|
||||||
// TODO(roasbeef): remove unknown vertex
|
|
||||||
case *lnwire.FailUnknownNextPeer:
|
case *lnwire.FailUnknownNextPeer:
|
||||||
continue
|
// This failure indicates that the node _after_
|
||||||
|
// the source of the error was not found. As a
|
||||||
|
// result, we'll locate the vertex for that
|
||||||
|
// node itself.
|
||||||
|
missingNode, ok := route.nextHopVertex(errSource)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we've located the vertex, we'll prune
|
||||||
|
// it out fromthe rest of the routes, and
|
||||||
|
// restart path finding.
|
||||||
|
routes = pruneNodeFromRoutes(routes,
|
||||||
|
missingNode)
|
||||||
|
goto sendLoop
|
||||||
|
|
||||||
// If the node wasn't able to forward for which ever
|
// If the node wasn't able to forward for which ever
|
||||||
// reason, then we'll note this and continue with the
|
// reason, then we'll note this and continue with the
|
||||||
@@ -1393,8 +1435,8 @@ func (r *ChannelRouter) AddNode(node *channeldb.LightningNode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddEdge is used to add edge/channel to the topology of the router, after all
|
// AddEdge is used to add edge/channel to the topology of the router, after all
|
||||||
// information about channel will be gathered this
|
// information about channel will be gathered this edge/channel might be used
|
||||||
// edge/channel might be used in construction of payment path.
|
// in construction of payment path.
|
||||||
//
|
//
|
||||||
// NOTE: This method is part of the ChannelGraphSource interface.
|
// NOTE: This method is part of the ChannelGraphSource interface.
|
||||||
func (r *ChannelRouter) AddEdge(edge *channeldb.ChannelEdgeInfo) error {
|
func (r *ChannelRouter) AddEdge(edge *channeldb.ChannelEdgeInfo) error {
|
||||||
|
|||||||
Reference in New Issue
Block a user