routing: add time_pref parameter to queryroutes and sendpayment

This commit is contained in:
Joost Jager committed 2022-04-13 20:26:46 +02:00
1 parent 269a8e74d3
commit ba5abdc090
17 files changed
+2034 -1918

No files matched your search

+17 -5
View File
@@ -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
View File
@@ -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,
)
}
+1 -1
View File
@@ -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.
+1
View File
@@ -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
View File
@@ -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
+4 -4
View File
@@ -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)