routing/pathfind: ensure final hop supports payment addrs

This commit adds an optional PaymentAddr field to the RestrictParams, so
that we can verify the final hop can support it before doing an
expensive round of pathfindig.
This commit is contained in:
Conner Fromknecht 2019-12-18 23:54:31 -08:00
parent acb7b83ead
commit 990d55d08c
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
3 changed files with 88 additions and 1 deletions

@ -70,6 +70,11 @@ var (
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")
@ -298,6 +303,11 @@ type RestrictParams struct {
// supports. If none are provided, pathfinding will try to inspect any
// features on the node announcement instead.
DestFeatures *lnwire.FeatureVector
// PaymentAddr is a random 32-byte value generated by the receiver to
// mitigate probing vectors and payment sniping attacks on overpaid
// invoices.
PaymentAddr *[32]byte
}
// PathFindingConfig defines global parameters that control the trade-off in
@ -447,6 +457,14 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
return nil, errNoTlvPayload
}
// If the caller has a payment address to attach, check that our
// destination feature vector supports them.
if r.PaymentAddr != nil &&
!features.HasFeature(lnwire.PaymentAddrOptional) {
return nil, errNoPaymentAddr
}
// If we are routing from ourselves, check that we have enough local
// balance available.
if source == self {

@ -1532,6 +1532,71 @@ func TestMissingFeatureDep(t *testing.T) {
assertExpectedPath(t, ctx.testGraphInstance.aliasMap, path, "conner")
}
// TestDestPaymentAddr asserts that we properly detect when we can send a
// payment address to a receiver, and also that we fallback to the receiver's
// node announcement if we don't have an invoice features.
func TestDestPaymentAddr(t *testing.T) {
t.Parallel()
testChannels := []*testChannel{
symmetricTestChannel("roasbeef", "luoji", 100000,
&testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: 100000000,
},
),
}
ctx := newPathFindingTestContext(t, testChannels, "roasbeef")
defer ctx.cleanup()
sourceNode, err := ctx.graphParams.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
find := func(r *RestrictParams,
target route.Vertex) ([]*channeldb.ChannelEdgePolicy, error) {
return findPath(
&graphParams{
graph: ctx.graphParams.graph,
},
r, testPathFindingConfig,
sourceNode.PubKeyBytes, target, 100,
)
}
luoji := ctx.testGraphInstance.aliasMap["luoji"]
restrictions := *noRestrictions
// Add payment address w/o any invoice features.
restrictions.PaymentAddr = &[32]byte{1}
// Add empty destination features. This should cause us to fail, since
// this overrides anything in the graph.
restrictions.DestFeatures = lnwire.EmptyFeatureVector()
_, err = find(&restrictions, luoji)
if err != errNoPaymentAddr {
t.Fatalf("path shouldn't have been found: %v", err)
}
// Now, set the TLV and payment address features for the destination. We
// should succeed in finding a path to luoji.
restrictions.DestFeatures = tlvPayAddrFeatures
path, err := find(&restrictions, luoji)
if err != nil {
t.Fatalf("path should have been found: %v", err)
}
assertExpectedPath(t, ctx.testGraphInstance.aliasMap, path, "luoji")
}
func TestPathInsufficientCapacity(t *testing.T) {
t.Parallel()

@ -192,7 +192,11 @@ func (p *paymentLifecycle) resumePayment() ([32]byte, *route.Route, error) {
// payment-level failure.
func errorToPaymentFailure(err error) channeldb.FailureReason {
switch err {
case errNoTlvPayload, errNoPathFound, errMaxHopsExceeded,
case
errNoTlvPayload,
errNoPaymentAddr,
errNoPathFound,
errMaxHopsExceeded,
errPrebuiltRouteTried:
return channeldb.FailureReasonNoRoute