routing: exit on unexpected RequestRoute error

We whitelist a set of "expected" errors that can be returned from
RequestRoute, by converting them into a new type noRouteError. For any
other error returned by RequestRoute, we'll now exit immediately.
This commit is contained in:
Johan T. Halseth 2020-04-01 00:13:27 +02:00
parent fee5fd0093
commit 5e72a4b77c
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
4 changed files with 73 additions and 43 deletions

@ -141,7 +141,7 @@ func (m *mockPaymentSession) RequestRoute(_, _ lnwire.MilliSatoshi,
_, height uint32) (*route.Route, error) {
if len(m.routes) == 0 {
return nil, fmt.Errorf("no routes")
return nil, errNoPathFound
}
r := m.routes[0]

@ -58,24 +58,6 @@ var (
// DefaultAprioriHopProbability is the default a priori probability for
// a hop.
DefaultAprioriHopProbability = float64(0.6)
// errNoTlvPayload is returned when the destination hop does not support
// a tlv payload.
errNoTlvPayload = errors.New("destination hop doesn't " +
"understand new TLV payloads")
// errNoPaymentAddr is returned when the destination hop does not
// support payment addresses.
errNoPaymentAddr = errors.New("destination hop doesn't " +
"understand payment addresses")
// errNoPathFound is returned when a path to the target destination does
// not exist in the graph.
errNoPathFound = errors.New("unable to find a path to destination")
// errInsufficientLocalBalance is returned when none of the local
// channels have enough balance for the payment.
errInsufficientBalance = errors.New("insufficient local balance")
)
// edgePolicyWithSource is a helper struct to keep track of the source node

@ -210,11 +210,16 @@ func (p *paymentLifecycle) resumePayment() ([32]byte, *route.Route, error) {
log.Warnf("Failed to find route for payment %x: %v",
p.paymentHash, err)
routeErr, ok := err.(noRouteError)
if !ok {
return [32]byte{}, nil, err
}
// There is no route to try, and we have no active
// shards. This means that there is no way for us to
// send the payment, so mark it failed with no route.
if state.numShardsInFlight == 0 {
failureCode := errorToPaymentFailure(err)
failureCode := routeErr.FailureReason()
log.Debugf("Marking payment %v permanently "+
"failed with no route: %v",
p.paymentHash, failureCode)
@ -570,25 +575,6 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
}, nil
}
// errorToPaymentFailure takes a path finding error and converts it into a
// payment-level failure.
func errorToPaymentFailure(err error) channeldb.FailureReason {
switch err {
case
errNoTlvPayload,
errNoPaymentAddr,
errNoPathFound,
errEmptyPaySession:
return channeldb.FailureReasonNoRoute
case errInsufficientBalance:
return channeldb.FailureReasonInsufficientBalance
}
return channeldb.FailureReasonError
}
// createNewPaymentAttempt creates a new payment attempt from the given route.
func (p *shardHandler) createNewPaymentAttempt(rt *route.Route) (
lnwire.ShortChannelID, *lnwire.UpdateAddHTLC,

@ -1,8 +1,6 @@
package routing
import (
"errors"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
@ -12,12 +10,73 @@ import (
// to prevent an HTLC being failed if some blocks are mined while it's in-flight.
const BlockPadding uint16 = 3
var (
// noRouteError encodes a non-critical error encountered during path finding.
type noRouteError uint8
const (
// errNoTlvPayload is returned when the destination hop does not support
// a tlv payload.
errNoTlvPayload noRouteError = iota
// errNoPaymentAddr is returned when the destination hop does not
// support payment addresses.
errNoPaymentAddr
// errNoPathFound is returned when a path to the target destination does
// not exist in the graph.
errNoPathFound
// errInsufficientLocalBalance is returned when none of the local
// channels have enough balance for the payment.
errInsufficientBalance
// errEmptyPaySession is returned when the empty payment session is
// queried for a route.
errEmptyPaySession = errors.New("empty payment session")
errEmptyPaySession
)
// Error returns the string representation of the noRouteError
func (e noRouteError) Error() string {
switch e {
case errNoTlvPayload:
return "destination hop doesn't understand new TLV payloads"
case errNoPaymentAddr:
return "destination hop doesn't understand payment addresses"
case errNoPathFound:
return "unable to find a path to destination"
case errEmptyPaySession:
return "empty payment session"
case errInsufficientBalance:
return "insufficient local balance"
default:
return "unknown no-route error"
}
}
// FailureReason converts a path finding error into a payment-level failure.
func (e noRouteError) FailureReason() channeldb.FailureReason {
switch e {
case
errNoTlvPayload,
errNoPaymentAddr,
errNoPathFound,
errEmptyPaySession:
return channeldb.FailureReasonNoRoute
case errInsufficientBalance:
return channeldb.FailureReasonInsufficientBalance
default:
return channeldb.FailureReasonError
}
}
// PaymentSession is used during SendPayment attempts to provide routes to
// attempt. It also defines methods to give the PaymentSession additional
// information learned during the previous attempts.
@ -29,6 +88,9 @@ type PaymentSession interface {
// argument should be set to instruct the payment session about the
// number of in flight HTLCS for the payment, such that it can choose
// splitting strategy accordingly.
//
// A noRouteError is returned if a non-critical error is encountered
// during path finding.
RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
activeShards, height uint32) (*route.Route, error)
}