routing/pathfind: validate final hop feature dependencies

This commit is contained in:
Conner Fromknecht 2019-12-18 23:54:11 -08:00
parent cfa3fe2921
commit acb7b83ead
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
3 changed files with 104 additions and 1 deletions

@ -30,6 +30,11 @@ type ErrMissingFeatureDep struct {
dep lnwire.FeatureBit
}
// NewErrMissingFeatureDep creates a new ErrMissingFeatureDep error.
func NewErrMissingFeatureDep(dep lnwire.FeatureBit) ErrMissingFeatureDep {
return ErrMissingFeatureDep{dep: dep}
}
// Error returns a human-readable description of the missing dep error.
func (e ErrMissingFeatureDep) Error() string {
return fmt.Sprintf("missing feature dependency: %v", e.dep)
@ -74,7 +79,7 @@ func validateDeps(features featureSet, supported supportedFeatures) error {
// vector is invalid.
checked, ok := supported[bit]
if !ok {
return ErrMissingFeatureDep{bit}
return NewErrMissingFeatureDep(bit)
}
// Alternatively, if we know that this depdendency is valid, we

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/btcec"
"github.com/coreos/bbolt"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/routing/route"
@ -427,6 +428,17 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
}
}
// With the destination's feature vector selected, ensure that all
// transitive depdencies are set.
err = feature.ValidateDeps(features)
if err != nil {
return nil, err
}
// Now that we know the feature vector is well formed, we'll proceed in
// checking that it supports the features we need, given our
// restrictions on the final hop.
// If the caller needs to send custom records, check that our
// destination feature vector supports TLV.
if len(r.DestCustomRecords) > 0 &&

@ -22,6 +22,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/routing/route"
@ -65,6 +66,19 @@ var (
lnwire.TLVOnionPayloadOptional,
), lnwire.Features,
)
payAddrFeatures = lnwire.NewFeatureVector(
lnwire.NewRawFeatureVector(
lnwire.PaymentAddrOptional,
), lnwire.Features,
)
tlvPayAddrFeatures = lnwire.NewFeatureVector(
lnwire.NewRawFeatureVector(
lnwire.TLVOnionPayloadOptional,
lnwire.PaymentAddrOptional,
), lnwire.Features,
)
)
var (
@ -1446,6 +1460,78 @@ func TestDestTLVGraphFallback(t *testing.T) {
assertExpectedPath(t, ctx.testGraphInstance.aliasMap, path, "luoji")
}
// TestMissingFeatureDep asserts that we fail path finding when the
// destination's features are broken, in that the feature vector doesn't signal
// all transitive dependencies.
func TestMissingFeatureDep(t *testing.T) {
t.Parallel()
testChannels := []*testChannel{
asymmetricTestChannel("roasbeef", "conner", 100000,
&testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: 100000000,
},
&testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: 100000000,
Features: payAddrFeatures,
}, 0,
),
}
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,
)
}
// Conner's node in the graph has a broken feature vector, since it
// signals payment addresses without signaling tlv onions. Pathfinding
// should fail since we validate transitive feature dependencies for the
// final node.
conner := ctx.testGraphInstance.aliasMap["conner"]
restrictions := *noRestrictions
_, err = find(&restrictions, conner)
if err != feature.NewErrMissingFeatureDep(
lnwire.TLVOnionPayloadOptional,
) {
t.Fatalf("path shouldn't have been found: %v", err)
}
// Now, set the TLV and payment addresses features to override the
// broken features found in the graph. We should succeed in finding a
// path to conner.
restrictions.DestFeatures = tlvPayAddrFeatures
path, err := find(&restrictions, conner)
if err != nil {
t.Fatalf("path should have been found: %v", err)
}
assertExpectedPath(t, ctx.testGraphInstance.aliasMap, path, "conner")
}
func TestPathInsufficientCapacity(t *testing.T) {
t.Parallel()