routing/pathfind: validate final hop feature dependencies
This commit is contained in:
parent
cfa3fe2921
commit
acb7b83ead
@ -30,6 +30,11 @@ type ErrMissingFeatureDep struct {
|
|||||||
dep lnwire.FeatureBit
|
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.
|
// Error returns a human-readable description of the missing dep error.
|
||||||
func (e ErrMissingFeatureDep) Error() string {
|
func (e ErrMissingFeatureDep) Error() string {
|
||||||
return fmt.Sprintf("missing feature dependency: %v", e.dep)
|
return fmt.Sprintf("missing feature dependency: %v", e.dep)
|
||||||
@ -74,7 +79,7 @@ func validateDeps(features featureSet, supported supportedFeatures) error {
|
|||||||
// vector is invalid.
|
// vector is invalid.
|
||||||
checked, ok := supported[bit]
|
checked, ok := supported[bit]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrMissingFeatureDep{bit}
|
return NewErrMissingFeatureDep(bit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alternatively, if we know that this depdendency is valid, we
|
// Alternatively, if we know that this depdendency is valid, we
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/feature"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"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
|
// If the caller needs to send custom records, check that our
|
||||||
// destination feature vector supports TLV.
|
// destination feature vector supports TLV.
|
||||||
if len(r.DestCustomRecords) > 0 &&
|
if len(r.DestCustomRecords) > 0 &&
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/feature"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
@ -65,6 +66,19 @@ var (
|
|||||||
lnwire.TLVOnionPayloadOptional,
|
lnwire.TLVOnionPayloadOptional,
|
||||||
), lnwire.Features,
|
), lnwire.Features,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
payAddrFeatures = lnwire.NewFeatureVector(
|
||||||
|
lnwire.NewRawFeatureVector(
|
||||||
|
lnwire.PaymentAddrOptional,
|
||||||
|
), lnwire.Features,
|
||||||
|
)
|
||||||
|
|
||||||
|
tlvPayAddrFeatures = lnwire.NewFeatureVector(
|
||||||
|
lnwire.NewRawFeatureVector(
|
||||||
|
lnwire.TLVOnionPayloadOptional,
|
||||||
|
lnwire.PaymentAddrOptional,
|
||||||
|
), lnwire.Features,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -1446,6 +1460,78 @@ func TestDestTLVGraphFallback(t *testing.T) {
|
|||||||
assertExpectedPath(t, ctx.testGraphInstance.aliasMap, path, "luoji")
|
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) {
|
func TestPathInsufficientCapacity(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user