mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-07-04 04:21:12 +02:00
routing: limit capacity factor and tune parameters
* The maximal reduction in the probability is limited to 0.5 (previously ~0.05), such that we don't get too low apriori probabilities. Otherwise, this may lead to a too strong selection of large (and maybe expensive) channels. A two-hop path would get total probability penalties of: - 1000PPM/(0.6*0.6) = 2778 PPM in the unsaturated case - 1000PPM/(0.6*(0.6*0.5)) = 5556 PPM in the saturated case, where the second hop is saturated The difference in PPM of 2778 PPM should be enough to bias towards the first path. * The smearing factor is reduced. Previously we had to keep a higher smearing factor in order to make the capacity factor not go to zero for high amounts, to still give a fully saturated channel a chance. This is not needed anymore due to the capping to 0.5. A lower value of the smearing factor lets us more precisely choose a capacity fraction and the capacity factor is more neutral when it comes to intermediate amounts. We set a conservative default value for the capacity fraction, which still has the effect of discarding exhausted channels, giving a noticeable effect when about 90% of the capacity is being used.
This commit is contained in:
@ -16,31 +16,24 @@ const (
|
|||||||
// capacity-related probability reweighting works. CapacityFraction
|
// capacity-related probability reweighting works. CapacityFraction
|
||||||
// defines the fraction of the channel capacity at which the effect
|
// defines the fraction of the channel capacity at which the effect
|
||||||
// roughly sets in and capacitySmearingFraction defines over which range
|
// roughly sets in and capacitySmearingFraction defines over which range
|
||||||
// the factor changes from 1 to 0.
|
// the factor changes from 1 to minCapacityFactor.
|
||||||
//
|
|
||||||
// We may fall below the minimum required probability
|
|
||||||
// (DefaultMinRouteProbability) when the amount comes close to the
|
|
||||||
// available capacity of a single channel of the route in case of no
|
|
||||||
// prior knowledge about the channels. We want such routes still to be
|
|
||||||
// available and therefore a probability reduction should not completely
|
|
||||||
// drop the total probability below DefaultMinRouteProbability.
|
|
||||||
// For this to hold for a three-hop route we require:
|
|
||||||
// (DefaultAprioriHopProbability)^3 * minCapacityFactor >
|
|
||||||
// DefaultMinRouteProbability
|
|
||||||
//
|
|
||||||
// For DefaultAprioriHopProbability = 0.6 and
|
|
||||||
// DefaultMinRouteProbability = 0.01 this results in
|
|
||||||
// minCapacityFactor ~ 0.05. The following combination of parameters
|
|
||||||
// fulfill the requirement with capacityFactor(cap, cap) ~ 0.076 (see
|
|
||||||
// tests).
|
|
||||||
|
|
||||||
// DefaultCapacityFraction is the default value for CapacityFraction.
|
// DefaultCapacityFraction is the default value for CapacityFraction.
|
||||||
DefaultCapacityFraction = 0.75
|
// It is chosen such that the capacity factor is active but with a small
|
||||||
|
// effect. This value together with capacitySmearingFraction leads to a
|
||||||
|
// noticeable reduction in probability if the amount starts to come
|
||||||
|
// close to 90% of a channel's capacity.
|
||||||
|
DefaultCapacityFraction = 0.9999
|
||||||
|
|
||||||
// We don't want to have a sharp drop of the capacity factor to zero at
|
// capacitySmearingFraction defines how quickly the capacity factor
|
||||||
// capacityCutoffFraction, but a smooth smearing such that some residual
|
// drops from 1 to minCapacityFactor. This value results in about a
|
||||||
// probability is left when spending the whole amount, see above.
|
// variation over 20% of the capacity.
|
||||||
capacitySmearingFraction = 0.1
|
capacitySmearingFraction = 0.025
|
||||||
|
|
||||||
|
// minCapacityFactor is the minimal value the capacityFactor can take.
|
||||||
|
// Having a too low value can lead to discarding of paths due to the
|
||||||
|
// enforced minimal proability or to too high pathfinding weights.
|
||||||
|
minCapacityFactor = 0.5
|
||||||
|
|
||||||
// minCapacityFraction is the minimum allowed value for
|
// minCapacityFraction is the minimum allowed value for
|
||||||
// CapacityFraction. The success probability in the random balance model
|
// CapacityFraction. The success probability in the random balance model
|
||||||
@ -265,10 +258,13 @@ func (p *AprioriEstimator) getWeight(age time.Duration) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// capacityFactor is a multiplier that can be used to reduce the probability
|
// capacityFactor is a multiplier that can be used to reduce the probability
|
||||||
// depending on how much of the capacity is sent. The limits are 1 for amt == 0
|
// depending on how much of the capacity is sent. In other words, the factor
|
||||||
// and 0 for amt >> cutoffMsat. The function drops significantly when amt
|
// sorts out channels that don't provide enough liquidity. Effectively, this
|
||||||
// reaches cutoffMsat. smearingMsat determines over which scale the reduction
|
// leads to usage of larger channels in total to increase success probability,
|
||||||
// takes place.
|
// but it may also increase fees. The limits are 1 for amt == 0 and
|
||||||
|
// minCapacityFactor for amt >> capacityCutoffFraction. The function drops
|
||||||
|
// significantly when amt reaches cutoffMsat. smearingMsat determines over which
|
||||||
|
// scale the reduction takes place.
|
||||||
func capacityFactor(amt lnwire.MilliSatoshi, capacity btcutil.Amount,
|
func capacityFactor(amt lnwire.MilliSatoshi, capacity btcutil.Amount,
|
||||||
capacityCutoffFraction float64) float64 {
|
capacityCutoffFraction float64) float64 {
|
||||||
|
|
||||||
@ -299,7 +295,11 @@ func capacityFactor(amt lnwire.MilliSatoshi, capacity btcutil.Amount,
|
|||||||
// at cutoffMsat, decaying over the smearingMsat scale.
|
// at cutoffMsat, decaying over the smearingMsat scale.
|
||||||
denominator := 1 + math.Exp(-(amtMsat-cutoffMsat)/smearingMsat)
|
denominator := 1 + math.Exp(-(amtMsat-cutoffMsat)/smearingMsat)
|
||||||
|
|
||||||
return 1 - 1/denominator
|
// The numerator decides what the minimal value of this function will
|
||||||
|
// be. The minimal value is set by minCapacityFactor.
|
||||||
|
numerator := 1 - minCapacityFactor
|
||||||
|
|
||||||
|
return 1 - numerator/denominator
|
||||||
}
|
}
|
||||||
|
|
||||||
// PairProbability estimates the probability of successfully traversing to
|
// PairProbability estimates the probability of successfully traversing to
|
||||||
|
@ -28,11 +28,12 @@ const (
|
|||||||
|
|
||||||
// testCapacity is used to define a capacity for some channels.
|
// testCapacity is used to define a capacity for some channels.
|
||||||
testCapacity = btcutil.Amount(100_000)
|
testCapacity = btcutil.Amount(100_000)
|
||||||
testAmount = lnwire.MilliSatoshi(50_000_000)
|
testAmount = lnwire.MilliSatoshi(90_000_000)
|
||||||
testCapacityFraction = 0.75
|
testCapacityFraction = 0.9999
|
||||||
|
|
||||||
// Defines the capacityFactor for testAmount and testCapacity.
|
// capFactor is the capacityFactor for testAmount, testCapacity and
|
||||||
capFactor = 0.9241
|
// testCapacityFraction.
|
||||||
|
capFactor = 0.9909715
|
||||||
)
|
)
|
||||||
|
|
||||||
type estimatorTestContext struct {
|
type estimatorTestContext struct {
|
||||||
@ -244,13 +245,13 @@ func TestCapacityCutoff(t *testing.T) {
|
|||||||
name: "low amount",
|
name: "low amount",
|
||||||
capacityFraction: 0.75,
|
capacityFraction: 0.75,
|
||||||
amountMsat: capacityMSat / 10,
|
amountMsat: capacityMSat / 10,
|
||||||
expectedFactor: 0.998,
|
expectedFactor: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "half amount",
|
name: "half amount",
|
||||||
capacityFraction: 0.75,
|
capacityFraction: 0.75,
|
||||||
amountMsat: capacityMSat / 2,
|
amountMsat: capacityMSat / 2,
|
||||||
expectedFactor: 0.924,
|
expectedFactor: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "cutoff amount",
|
name: "cutoff amount",
|
||||||
@ -258,13 +259,13 @@ func TestCapacityCutoff(t *testing.T) {
|
|||||||
amountMsat: int(
|
amountMsat: int(
|
||||||
0.75 * float64(capacityMSat),
|
0.75 * float64(capacityMSat),
|
||||||
),
|
),
|
||||||
expectedFactor: 0.5,
|
expectedFactor: 0.75,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "high amount",
|
name: "high amount",
|
||||||
capacityFraction: 0.75,
|
capacityFraction: 0.75,
|
||||||
amountMsat: capacityMSat * 80 / 100,
|
amountMsat: capacityMSat * 80 / 100,
|
||||||
expectedFactor: 0.377,
|
expectedFactor: 0.560,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Even when we spend the full capacity, we still want
|
// Even when we spend the full capacity, we still want
|
||||||
@ -274,7 +275,7 @@ func TestCapacityCutoff(t *testing.T) {
|
|||||||
name: "full amount",
|
name: "full amount",
|
||||||
capacityFraction: 0.75,
|
capacityFraction: 0.75,
|
||||||
amountMsat: capacityMSat,
|
amountMsat: capacityMSat,
|
||||||
expectedFactor: 0.076,
|
expectedFactor: 0.5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "more than capacity",
|
name: "more than capacity",
|
||||||
@ -282,6 +283,28 @@ func TestCapacityCutoff(t *testing.T) {
|
|||||||
amountMsat: capacityMSat + 1,
|
amountMsat: capacityMSat + 1,
|
||||||
expectedFactor: 0.0,
|
expectedFactor: 0.0,
|
||||||
},
|
},
|
||||||
|
// Default CapacityFactor of 0.9999.
|
||||||
|
{
|
||||||
|
name: "zero amount",
|
||||||
|
capacityFraction: 0.9999,
|
||||||
|
amountMsat: 0,
|
||||||
|
expectedFactor: 1.00,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "90% of the channel capacity",
|
||||||
|
capacityFraction: 0.9999,
|
||||||
|
amountMsat: capacityMSat * 90 / 100,
|
||||||
|
expectedFactor: 0.990,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// We won't saturate at 0.5 as in the other case but at
|
||||||
|
// a higher value of 0.75 due to the smearing, this
|
||||||
|
// translates to a penalty increase of a factor of 1.33.
|
||||||
|
name: "full amount",
|
||||||
|
capacityFraction: 0.9999,
|
||||||
|
amountMsat: capacityMSat,
|
||||||
|
expectedFactor: 0.75,
|
||||||
|
},
|
||||||
// Inactive capacity factor.
|
// Inactive capacity factor.
|
||||||
{
|
{
|
||||||
name: "inactive capacity factor",
|
name: "inactive capacity factor",
|
||||||
|
Reference in New Issue
Block a user