routing: add proportional attempt cost

This commit is contained in:
Joost Jager 2020-09-08 13:02:33 +02:00
parent 60a22ff09d
commit bbfeeec8b1
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
6 changed files with 69 additions and 13 deletions

@ -47,6 +47,7 @@ func DefaultConfig() *Config {
MinRouteProbability: routing.DefaultMinRouteProbability, MinRouteProbability: routing.DefaultMinRouteProbability,
PenaltyHalfLife: routing.DefaultPenaltyHalfLife, PenaltyHalfLife: routing.DefaultPenaltyHalfLife,
AttemptCost: routing.DefaultAttemptCost.ToSatoshis(), AttemptCost: routing.DefaultAttemptCost.ToSatoshis(),
AttemptCostPPM: routing.DefaultAttemptCostPPM,
MaxMcHistory: routing.DefaultMaxMcHistory, MaxMcHistory: routing.DefaultMaxMcHistory,
} }
@ -62,6 +63,7 @@ func GetRoutingConfig(cfg *Config) *RoutingConfig {
AprioriWeight: cfg.AprioriWeight, AprioriWeight: cfg.AprioriWeight,
MinRouteProbability: cfg.MinRouteProbability, MinRouteProbability: cfg.MinRouteProbability,
AttemptCost: cfg.AttemptCost, AttemptCost: cfg.AttemptCost,
AttemptCostPPM: cfg.AttemptCostPPM,
PenaltyHalfLife: cfg.PenaltyHalfLife, PenaltyHalfLife: cfg.PenaltyHalfLife,
MaxMcHistory: cfg.MaxMcHistory, MaxMcHistory: cfg.MaxMcHistory,
} }

@ -32,7 +32,13 @@ type RoutingConfig struct {
// AttemptCost is the fixed virtual cost in path finding of a failed // AttemptCost is the fixed virtual cost in path finding of a failed
// payment attempt. It is used to trade off potentially better routes // payment attempt. It is used to trade off potentially better routes
// against their probability of succeeding. // against their probability of succeeding.
AttemptCost btcutil.Amount `long:"attemptcost" description:"The (virtual) cost in sats of a failed payment attempt"` AttemptCost btcutil.Amount `long:"attemptcost" description:"The fixed (virtual) cost in sats of a failed payment attempt"`
// AttemptCostPPM is the proportional virtual cost in path finding of a
// failed payment attempt. It is used to trade off potentially better
// routes against their probability of succeeding. This parameter is
// expressed in parts per million of the total payment amount.
AttemptCostPPM int64 `long:"attemptcostppm" description:"The proportional (virtual) cost in sats of a failed payment attempt expressed in parts per million of the total payment amount"`
// MaxMcHistory defines the maximum number of payment results that // MaxMcHistory defines the maximum number of payment results that
// are held on disk by mission control. // are held on disk by mission control.

@ -45,11 +45,23 @@ type pathFinder = func(g *graphParams, r *RestrictParams,
[]*channeldb.ChannelEdgePolicy, error) []*channeldb.ChannelEdgePolicy, error)
var ( var (
// DefaultAttemptCost is the default virtual cost in path finding of a // DefaultAttemptCost is the default fixed virtual cost in path finding
// failed payment attempt. It is used to trade off potentially better // of a failed payment attempt. It is used to trade off potentially
// routes against their probability of succeeding. // better routes against their probability of succeeding.
DefaultAttemptCost = lnwire.NewMSatFromSatoshis(100) DefaultAttemptCost = lnwire.NewMSatFromSatoshis(100)
// DefaultAttemptCostPPM is the default proportional virtual cost in
// path finding weight units of executing a payment attempt that fails.
// It is used to trade off potentially better routes against their
// probability of succeeding. This parameter is expressed in parts per
// million of the payment amount.
//
// It is impossible to pick a perfect default value. The current value
// of 0.1% is based on the idea that a transaction fee of 1% is within
// reasonable territory and that a payment shouldn't need more than 10
// attempts.
DefaultAttemptCostPPM = int64(1000)
// DefaultMinRouteProbability is the default minimum probability for routes // DefaultMinRouteProbability is the default minimum probability for routes
// returned from findPath. // returned from findPath.
DefaultMinRouteProbability = float64(0.01) DefaultMinRouteProbability = float64(0.01)
@ -314,11 +326,17 @@ type RestrictParams struct {
// PathFindingConfig defines global parameters that control the trade-off in // PathFindingConfig defines global parameters that control the trade-off in
// path finding between fees and probabiity. // path finding between fees and probabiity.
type PathFindingConfig struct { type PathFindingConfig struct {
// AttemptCost is the virtual cost in path finding of a failed // AttemptCost is the fixed virtual cost in path finding of a failed
// payment attempt. It is used to trade off potentially better routes // payment attempt. It is used to trade off potentially better routes
// against their probability of succeeding. // against their probability of succeeding.
AttemptCost lnwire.MilliSatoshi AttemptCost lnwire.MilliSatoshi
// AttemptCostPPM is the proportional virtual cost in path finding of a
// failed payment attempt. It is used to trade off potentially better
// routes against their probability of succeeding. This parameter is
// expressed in parts per million of the total payment amount.
AttemptCostPPM int64
// MinProbability defines the minimum success probability of the // MinProbability defines the minimum success probability of the
// returned route. // returned route.
MinProbability float64 MinProbability float64
@ -548,6 +566,14 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// if the cltv limit is MaxUint32. // if the cltv limit is MaxUint32.
absoluteCltvLimit := uint64(r.CltvLimit) + uint64(finalHtlcExpiry) absoluteCltvLimit := uint64(r.CltvLimit) + uint64(finalHtlcExpiry)
// Calculate the absolute attempt cost that is used for probability
// estimation.
absoluteAttemptCost := int64(cfg.AttemptCost) +
int64(amt)*cfg.AttemptCostPPM/1000000
log.Debugf("Pathfinding absolute attempt cost: %v sats",
float64(absoluteAttemptCost)/1000)
// processEdge is a helper closure that will be used to make sure edges // processEdge is a helper closure that will be used to make sure edges
// satisfy our specific requirements. // satisfy our specific requirements.
processEdge := func(fromVertex route.Vertex, processEdge := func(fromVertex route.Vertex,
@ -642,7 +668,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// probability. // probability.
tempDist := getProbabilityBasedDist( tempDist := getProbabilityBasedDist(
tempWeight, probability, tempWeight, probability,
int64(cfg.AttemptCost), absoluteAttemptCost,
) )
// If there is already a best route stored, compare this // If there is already a best route stored, compare this

@ -2467,9 +2467,9 @@ func TestProbabilityRouting(t *testing.T) {
// to the same total route probability. In both cases the three // to the same total route probability. In both cases the three
// hop route should be the best route. The three hop route has a // hop route should be the best route. The three hop route has a
// probability of 0.5 * 0.8 = 0.4. The fee is 5 (chan 10) + 8 // probability of 0.5 * 0.8 = 0.4. The fee is 5 (chan 10) + 8
// (chan 11) = 13. Path finding distance should work out to: 13 // (chan 11) = 13. The attempt cost is 9 + 1% * 100 = 10. Path
// + 10 (attempt penalty) / 0.4 = 38. The two hop route is 25 + // finding distance should work out to: 13 + 10 (attempt
// 10 / 0.7 = 39. // penalty) / 0.4 = 38. The two hop route is 25 + 10 / 0.7 = 39.
{ {
name: "three hop 1", name: "three hop 1",
p10: 0.8, p11: 0.5, p20: 0.7, p10: 0.8, p11: 0.5, p20: 0.7,
@ -2485,6 +2485,21 @@ func TestProbabilityRouting(t *testing.T) {
amount: 100, amount: 100,
}, },
// 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
// three hop path finding distance should work out to: 13 + 12
// (attempt penalty) / 0.4 = 43. The two hop route is 25 + 12 /
// 0.7 = 42. For this higher amount, the two hop route is
// expected to be selected.
{
name: "two hop high amount",
p10: 0.8, p11: 0.5, p20: 0.7,
minProbability: 0.1,
expectedChan: 20,
amount: 300,
},
// If the probability of the two hop route is increased, its // If the probability of the two hop route is increased, its
// distance becomes 25 + 10 / 0.85 = 37. This is less than the // distance becomes 25 + 10 / 0.85 = 37. This is less than the
// three hop route with its distance 38. So with an attempt // three hop route with its distance 38. So with an attempt
@ -2589,7 +2604,8 @@ func testProbabilityRouting(t *testing.T, paymentAmt btcutil.Amount,
} }
ctx.pathFindingConfig = PathFindingConfig{ ctx.pathFindingConfig = PathFindingConfig{
AttemptCost: lnwire.NewMSatFromSatoshis(10), AttemptCost: lnwire.NewMSatFromSatoshis(9),
AttemptCostPPM: 10000,
MinProbability: minProbability, MinProbability: minProbability,
} }

@ -619,8 +619,12 @@ litecoin.node=ltcd
; probability (default: 1h0m0s) ; probability (default: 1h0m0s)
; routerrpc.penaltyhalflife=2h ; routerrpc.penaltyhalflife=2h
; The (virtual) cost in sats of a failed payment attempt (default: 100) ; The (virtual) fixed cost in sats of a failed payment attempt (default: 100)
; routerrpc.attemptcost=90 ; routerrpc.attemptcost=90
; The (virtual) proportional cost in ppm of the total amount of a failed payment
; attempt (default: 1000)
; routerrpc.attemptcostppm=900
; The maximum number of payment results that are held on disk by mission control ; The maximum number of payment results that are held on disk by mission control
; (default: 1000) ; (default: 1000)

@ -724,14 +724,16 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
} }
srvrLog.Debugf("Instantiating payment session source with config: "+ srvrLog.Debugf("Instantiating payment session source with config: "+
"AttemptCost=%v, MinRouteProbability=%v", "AttemptCost=%v + %v%%, MinRouteProbability=%v",
int64(routingConfig.AttemptCost), int64(routingConfig.AttemptCost),
float64(routingConfig.AttemptCostPPM)/10000,
routingConfig.MinRouteProbability) routingConfig.MinRouteProbability)
pathFindingConfig := routing.PathFindingConfig{ pathFindingConfig := routing.PathFindingConfig{
AttemptCost: lnwire.NewMSatFromSatoshis( AttemptCost: lnwire.NewMSatFromSatoshis(
routingConfig.AttemptCost, routingConfig.AttemptCost,
), ),
AttemptCostPPM: routingConfig.AttemptCostPPM,
MinProbability: routingConfig.MinRouteProbability, MinProbability: routingConfig.MinRouteProbability,
} }