routing: add time_pref parameter to queryroutes and sendpayment
This commit is contained in:
17 files changed
+2034
-1918
No files matched your search
+17
-5
@@ -41,7 +41,7 @@ const (
|
||||
// pathFinder defines the interface of a path finding algorithm.
|
||||
type pathFinder = func(g *graphParams, r *RestrictParams,
|
||||
cfg *PathFindingConfig, source, target route.Vertex,
|
||||
amt lnwire.MilliSatoshi, finalHtlcExpiry int32) (
|
||||
amt lnwire.MilliSatoshi, timePref float64, finalHtlcExpiry int32) (
|
||||
[]*channeldb.CachedEdgePolicy, error)
|
||||
|
||||
var (
|
||||
@@ -413,7 +413,7 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
|
||||
// path and accurately check the amount to forward at every node against the
|
||||
// available bandwidth.
|
||||
func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
||||
source, target route.Vertex, amt lnwire.MilliSatoshi,
|
||||
source, target route.Vertex, amt lnwire.MilliSatoshi, timePref float64,
|
||||
finalHtlcExpiry int32) ([]*channeldb.CachedEdgePolicy, error) {
|
||||
|
||||
// Pathfinding can be a significant portion of the total payment
|
||||
@@ -573,13 +573,25 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
||||
// if the cltv limit is MaxUint32.
|
||||
absoluteCltvLimit := uint64(r.CltvLimit) + uint64(finalHtlcExpiry)
|
||||
|
||||
// Calculate the absolute attempt cost that is used for probability
|
||||
// estimation.
|
||||
absoluteAttemptCost := float64(
|
||||
// Calculate the default attempt cost as configured globally.
|
||||
defaultAttemptCost := float64(
|
||||
cfg.AttemptCost +
|
||||
amt*lnwire.MilliSatoshi(cfg.AttemptCostPPM)/1000000,
|
||||
)
|
||||
|
||||
// Validate time preference value.
|
||||
if math.Abs(timePref) > 1 {
|
||||
return nil, fmt.Errorf("time preference %v out of range [-1, 1]",
|
||||
timePref)
|
||||
}
|
||||
|
||||
// Scale to avoid the extremes -1 and 1 which run into infinity issues.
|
||||
timePref *= 0.9
|
||||
|
||||
// Apply time preference. At 0, the default attempt cost will
|
||||
// be used.
|
||||
absoluteAttemptCost := defaultAttemptCost * (1/(0.5-timePref/2) - 1)
|
||||
|
||||
log.Debugf("Pathfinding absolute attempt cost: %v sats",
|
||||
absoluteAttemptCost/1000)
|
||||
|
||||
|
||||
+38
-20
@@ -1059,7 +1059,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
|
||||
CltvLimit: math.MaxUint32,
|
||||
},
|
||||
testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, paymentAmt,
|
||||
sourceNode.PubKeyBytes, target, paymentAmt, 0,
|
||||
startingHeight+finalHopCLTV,
|
||||
)
|
||||
if test.expectFailureNoPath {
|
||||
@@ -1248,7 +1248,7 @@ func runPathFindingWithAdditionalEdges(t *testing.T, useCache bool) {
|
||||
graph.graph, additionalEdges, &mockBandwidthHints{},
|
||||
r, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, doge.PubKeyBytes, paymentAmt,
|
||||
0,
|
||||
0, 0,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1669,7 +1669,7 @@ func runPathNotAvailable(t *testing.T, useCache bool) {
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, unknownNode, 100, 0,
|
||||
sourceNode.PubKeyBytes, unknownNode, 100, 0, 0,
|
||||
)
|
||||
if err != errNoPathFound {
|
||||
t.Fatalf("path shouldn't have been found: %v", err)
|
||||
@@ -1722,7 +1722,7 @@ func runDestTLVGraphFallback(t *testing.T, useCache bool) {
|
||||
return dbFindPath(
|
||||
ctx.graph, nil, &mockBandwidthHints{},
|
||||
r, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, 100, 0,
|
||||
sourceNode.PubKeyBytes, target, 100, 0, 0,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1983,7 +1983,7 @@ func runPathInsufficientCapacity(t *testing.T, useCache bool) {
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != errInsufficientBalance {
|
||||
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
||||
@@ -2012,7 +2012,7 @@ func runRouteFailMinHTLC(t *testing.T, useCache bool) {
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != errNoPathFound {
|
||||
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
||||
@@ -2105,7 +2105,7 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to find path: %v", err)
|
||||
@@ -2131,7 +2131,7 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to find path: %v", err)
|
||||
@@ -2154,7 +2154,7 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != errNoPathFound {
|
||||
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
||||
@@ -2184,7 +2184,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
path, err := dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to find path: %v", err)
|
||||
@@ -2207,7 +2207,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, bandwidths,
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != errNoPathFound {
|
||||
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
||||
@@ -2222,7 +2222,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
path, err = dbFindPath(
|
||||
graph.graph, nil, bandwidths,
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to find path: %v", err)
|
||||
@@ -2250,7 +2250,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
path, err = dbFindPath(
|
||||
graph.graph, nil, bandwidths,
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to find path: %v", err)
|
||||
@@ -2296,7 +2296,7 @@ func TestPathFindSpecExample(t *testing.T) {
|
||||
carol := ctx.aliases["C"]
|
||||
const amt lnwire.MilliSatoshi = 4999999
|
||||
route, err := ctx.router.FindRoute(
|
||||
bobNode.PubKeyBytes, carol, amt, noRestrictions, nil, nil,
|
||||
bobNode.PubKeyBytes, carol, amt, 0, noRestrictions, nil, nil,
|
||||
MinCLTVDelta,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -2352,7 +2352,7 @@ func TestPathFindSpecExample(t *testing.T) {
|
||||
|
||||
// We'll now request a route from A -> B -> C.
|
||||
route, err = ctx.router.FindRoute(
|
||||
source.PubKeyBytes, carol, amt, noRestrictions, nil, nil,
|
||||
source.PubKeyBytes, carol, amt, 0, noRestrictions, nil, nil,
|
||||
MinCLTVDelta,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -2686,6 +2686,7 @@ func runProbabilityRouting(t *testing.T, useCache bool) {
|
||||
minProbability float64
|
||||
expectedChan uint64
|
||||
amount btcutil.Amount
|
||||
timePref float64
|
||||
}{
|
||||
// Test two variations with probabilities that should multiply
|
||||
// to the same total route probability. In both cases the three
|
||||
@@ -2709,6 +2710,18 @@ func runProbabilityRouting(t *testing.T, useCache bool) {
|
||||
amount: 100,
|
||||
},
|
||||
|
||||
// If we increase the time preference, we expect the algorithm
|
||||
// to choose - with everything else being equal - the more
|
||||
// expensive, more reliable route.
|
||||
{
|
||||
name: "three hop timepref",
|
||||
p10: 0.5, p11: 0.8, p20: 0.7,
|
||||
minProbability: 0.1,
|
||||
expectedChan: 20,
|
||||
amount: 100,
|
||||
timePref: 1,
|
||||
},
|
||||
|
||||
// If a larger amount is sent, the effect of the proportional
|
||||
// attempt cost becomes more noticeable. This amount in this
|
||||
// test brings the attempt cost to 9 + 1% * 300 = 12 sat. The
|
||||
@@ -2765,7 +2778,8 @@ func runProbabilityRouting(t *testing.T, useCache bool) {
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
testProbabilityRouting(
|
||||
t, useCache, tc.amount, tc.p10, tc.p11, tc.p20,
|
||||
t, useCache, tc.amount, tc.timePref,
|
||||
tc.p10, tc.p11, tc.p20,
|
||||
tc.minProbability, tc.expectedChan,
|
||||
)
|
||||
})
|
||||
@@ -2773,7 +2787,8 @@ func runProbabilityRouting(t *testing.T, useCache bool) {
|
||||
}
|
||||
|
||||
func testProbabilityRouting(t *testing.T, useCache bool,
|
||||
paymentAmt btcutil.Amount, p10, p11, p20, minProbability float64,
|
||||
paymentAmt btcutil.Amount, timePref float64,
|
||||
p10, p11, p20, minProbability float64,
|
||||
expectedChan uint64) {
|
||||
|
||||
t.Parallel()
|
||||
@@ -2834,6 +2849,8 @@ func testProbabilityRouting(t *testing.T, useCache bool,
|
||||
MinProbability: minProbability,
|
||||
}
|
||||
|
||||
ctx.timePref = timePref
|
||||
|
||||
path, err := ctx.findPath(
|
||||
target, lnwire.NewMSatFromSatoshis(paymentAmt),
|
||||
)
|
||||
@@ -3044,6 +3061,7 @@ type pathFindingTestContext struct {
|
||||
pathFindingConfig PathFindingConfig
|
||||
testGraphInstance *testGraphInstance
|
||||
source route.Vertex
|
||||
timePref float64
|
||||
}
|
||||
|
||||
func newPathFindingTestContext(t *testing.T, useCache bool,
|
||||
@@ -3097,7 +3115,7 @@ func (c *pathFindingTestContext) findPath(target route.Vertex,
|
||||
|
||||
return dbFindPath(
|
||||
c.graph, nil, c.bandwidthHints, &c.restrictParams,
|
||||
&c.pathFindingConfig, c.source, target, amt, 0,
|
||||
&c.pathFindingConfig, c.source, target, amt, c.timePref, 0,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3123,7 +3141,7 @@ func dbFindPath(graph *channeldb.ChannelGraph,
|
||||
additionalEdges map[route.Vertex][]*channeldb.CachedEdgePolicy,
|
||||
bandwidthHints bandwidthHints,
|
||||
r *RestrictParams, cfg *PathFindingConfig,
|
||||
source, target route.Vertex, amt lnwire.MilliSatoshi,
|
||||
source, target route.Vertex, amt lnwire.MilliSatoshi, timePref float64,
|
||||
finalHtlcExpiry int32) ([]*channeldb.CachedEdgePolicy, error) {
|
||||
|
||||
sourceNode, err := graph.SourceNode()
|
||||
@@ -3148,6 +3166,6 @@ func dbFindPath(graph *channeldb.ChannelGraph,
|
||||
bandwidthHints: bandwidthHints,
|
||||
graph: routingGraph,
|
||||
},
|
||||
r, cfg, source, target, amt, finalHtlcExpiry,
|
||||
r, cfg, source, target, amt, timePref, finalHtlcExpiry,
|
||||
)
|
||||
}
|
||||
@@ -304,7 +304,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
||||
},
|
||||
restrictions, &p.pathFindingConfig,
|
||||
sourceVertex, p.payment.Target,
|
||||
maxAmt, finalHtlcExpiry,
|
||||
maxAmt, p.payment.TimePref, finalHtlcExpiry,
|
||||
)
|
||||
|
||||
// Close routing graph.
|
||||
|
||||
@@ -212,6 +212,7 @@ func TestRequestRoute(t *testing.T) {
|
||||
session.pathFinder = func(
|
||||
g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
||||
source, target route.Vertex, amt lnwire.MilliSatoshi,
|
||||
timePref float64,
|
||||
finalHtlcExpiry int32) ([]*channeldb.CachedEdgePolicy, error) {
|
||||
|
||||
// We expect find path to receive a cltv limit excluding the
|
||||
|
||||
+13
-2
@@ -1732,7 +1732,8 @@ type routingMsg struct {
|
||||
// particular target destination to which it is able to send `amt` after
|
||||
// factoring in channel capacities and cumulative fees along the route.
|
||||
func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
||||
amt lnwire.MilliSatoshi, restrictions *RestrictParams,
|
||||
amt lnwire.MilliSatoshi, timePref float64,
|
||||
restrictions *RestrictParams,
|
||||
destCustomRecords record.CustomSet,
|
||||
routeHints map[route.Vertex][]*channeldb.CachedEdgePolicy,
|
||||
finalExpiry uint16) (*route.Route, error) {
|
||||
@@ -1759,6 +1760,11 @@ func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
||||
// execute our path finding algorithm.
|
||||
finalHtlcExpiry := currentHeight + int32(finalExpiry)
|
||||
|
||||
// Validate time preference.
|
||||
if timePref < -1 || timePref > 1 {
|
||||
return nil, errors.New("time preference out of range")
|
||||
}
|
||||
|
||||
path, err := findPath(
|
||||
&graphParams{
|
||||
additionalEdges: routeHints,
|
||||
@@ -1767,7 +1773,7 @@ func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
||||
},
|
||||
restrictions,
|
||||
&r.cfg.PathFindingConfig,
|
||||
source, target, amt, finalHtlcExpiry,
|
||||
source, target, amt, timePref, finalHtlcExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1960,6 +1966,11 @@ type LightningPayment struct {
|
||||
//
|
||||
// NOTE: This field is _optional_.
|
||||
MaxShardAmt *lnwire.MilliSatoshi
|
||||
|
||||
// TimePref is the time preference for this payment. Set to -1 to
|
||||
// optimize for fees only, to 1 to optimize for reliability only or a
|
||||
// value in between for a mix.
|
||||
TimePref float64
|
||||
}
|
||||
|
||||
// AMPOptions houses information that must be known in order to send an AMP
|
||||
|
||||
@@ -268,7 +268,7 @@ func TestFindRoutesWithFeeLimit(t *testing.T) {
|
||||
|
||||
route, err := ctx.router.FindRoute(
|
||||
ctx.router.selfNode.PubKeyBytes,
|
||||
target, paymentAmt, restrictions, nil, nil,
|
||||
target, paymentAmt, 0, restrictions, nil, nil,
|
||||
MinCLTVDelta,
|
||||
)
|
||||
require.NoError(t, err, "unable to find any routes")
|
||||
@@ -1525,7 +1525,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
copy(targetPubKeyBytes[:], targetNode.SerializeCompressed())
|
||||
_, err = ctx.router.FindRoute(
|
||||
ctx.router.selfNode.PubKeyBytes,
|
||||
targetPubKeyBytes, paymentAmt, noRestrictions, nil, nil,
|
||||
targetPubKeyBytes, paymentAmt, 0, noRestrictions, nil, nil,
|
||||
MinCLTVDelta,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -1568,7 +1568,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
// updated.
|
||||
_, err = ctx.router.FindRoute(
|
||||
ctx.router.selfNode.PubKeyBytes,
|
||||
targetPubKeyBytes, paymentAmt, noRestrictions, nil, nil,
|
||||
targetPubKeyBytes, paymentAmt, 0, noRestrictions, nil, nil,
|
||||
MinCLTVDelta,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -2465,7 +2465,7 @@ func TestFindPathFeeWeighting(t *testing.T) {
|
||||
ctx.graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions,
|
||||
testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, amt, 0,
|
||||
sourceNode.PubKeyBytes, target, amt, 0, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to find path: %v", err)
|
||||
|
||||
Reference in new issue
Block a user