mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 14:42:40 +01:00
askrene: calculate prob_cost_factor using ratio of typical mainnet channel.
During "test_real_data", then only successes with reduced fees were 92 on "mu=10", and only 1 on "mu=30": the rest went to mu=100 and failed. I tried numerous approaches, and in the end, opted for the simplest: The typical range of probability costs looks likes: min = 0, max = 924196240, mean = 10509.4, stddev = 1.9e+06 The typical range of linear fee costs looks like: min = 0, max = 101000000, mean = 81894.6, stddev = 2.6e+06 This implies a k factor of 8 makes the two comparable. This makes the two numbers comparable, and thus makes "mu" much more effective. Here are the number of different mu values we succeeded at: 87 mu=0 90 mu=10 42 mu=20 24 mu=30 17 mu=40 19 mu=50 19 mu=60 11 mu=70 95 mu=80 19 mu=90 Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
4897286c25
commit
6273adbe47
4 changed files with 27 additions and 95 deletions
|
@ -285,7 +285,7 @@ static const char *get_routes(const tal_t *ctx,
|
|||
const struct gossmap_node *srcnode, *dstnode;
|
||||
double delay_feefactor;
|
||||
double base_fee_penalty;
|
||||
u32 prob_cost_factor, mu;
|
||||
u32 mu;
|
||||
const char *ret;
|
||||
|
||||
if (gossmap_refresh(askrene->gossmap, NULL)) {
|
||||
|
@ -351,22 +351,10 @@ static const char *get_routes(const tal_t *ctx,
|
|||
delay_feefactor = 1.0/1000000;
|
||||
base_fee_penalty = 10.0;
|
||||
|
||||
/* From mcf.c: The input parameter `prob_cost_factor` in the function
|
||||
* `minflow` is defined as the PPM from the delivery amount `T` we are
|
||||
* *willing to pay* to increase the prob. of success by 0.1% */
|
||||
|
||||
/* This value is somewhat implied by our fee budget: say we would pay
|
||||
* the entire budget for 100% probability, that means prob_cost_factor
|
||||
* is (fee / amount) / 1000, or in PPM: (fee / amount) * 1000 */
|
||||
if (amount_msat_is_zero(amount))
|
||||
prob_cost_factor = 0;
|
||||
else
|
||||
prob_cost_factor = amount_msat_ratio(maxfee, amount) * 1000;
|
||||
|
||||
/* First up, don't care about fees. */
|
||||
mu = 0;
|
||||
flows = minflow(rq, rq, srcnode, dstnode, amount,
|
||||
mu, delay_feefactor, base_fee_penalty, prob_cost_factor);
|
||||
mu, delay_feefactor, base_fee_penalty);
|
||||
if (!flows) {
|
||||
ret = explain_failure(ctx, rq, srcnode, dstnode, amount);
|
||||
goto fail;
|
||||
|
@ -386,7 +374,7 @@ static const char *get_routes(const tal_t *ctx,
|
|||
"The worst flow delay is %"PRIu64" (> %i), retrying with delay_feefactor %f...",
|
||||
flows_worst_delay(flows), 2016 - finalcltv, delay_feefactor);
|
||||
flows = minflow(rq, rq, srcnode, dstnode, amount,
|
||||
mu, delay_feefactor, base_fee_penalty, prob_cost_factor);
|
||||
mu, delay_feefactor, base_fee_penalty);
|
||||
if (!flows || delay_feefactor > 10) {
|
||||
ret = rq_log(ctx, rq, LOG_UNUSUAL,
|
||||
"Could not find route without excessive delays");
|
||||
|
@ -404,8 +392,8 @@ too_expensive:
|
|||
fmt_amount_msat(tmpctx, maxfee),
|
||||
mu);
|
||||
flows = minflow(rq, rq, srcnode, dstnode, amount,
|
||||
mu, delay_feefactor, base_fee_penalty, prob_cost_factor);
|
||||
if (!flows || mu == 100) {
|
||||
mu > 100 ? 100 : mu, delay_feefactor, base_fee_penalty);
|
||||
if (!flows || mu >= 100) {
|
||||
ret = rq_log(ctx, rq, LOG_UNUSUAL,
|
||||
"Could not find route without excessive cost");
|
||||
goto fail;
|
||||
|
|
|
@ -134,38 +134,7 @@
|
|||
* However we propose to scale the prob. cost by a global factor k that
|
||||
* translates into the monetization of prob. cost.
|
||||
*
|
||||
* k/1000, for instance, becomes the equivalent monetary cost
|
||||
* of increasing the probability of success by 0.1% for P~100%.
|
||||
*
|
||||
* The input parameter `prob_cost_factor` in the function `minflow` is defined
|
||||
* as the PPM from the delivery amount `T` we are *willing to pay* to increase the
|
||||
* prob. of success by 0.1%:
|
||||
*
|
||||
* k_microsat = floor(1000*prob_cost_factor * T_sat)
|
||||
*
|
||||
* Is this enough to make integer prob. cost per unit flow?
|
||||
* For `prob_cost_factor=10`; i.e. we pay 10ppm for increasing the prob. by
|
||||
* 0.1%, we get that
|
||||
*
|
||||
* -> any arc with (b-a) > 10000 T, will have zero prob. cost, which is
|
||||
* reasonable because even if all the flow passes through that arc, we get
|
||||
* a 1.3 T/(b-a) ~ 0.01% prob. of failure at most.
|
||||
*
|
||||
* -> if (b-a) ~ 10000 T, then the arc will have unit cost, or just that we
|
||||
* pay 1 microsat for every sat we send through this arc.
|
||||
*
|
||||
* -> it would be desirable to have a high proportional fee when (b-a)~T,
|
||||
* because prob. of failure start to become very high.
|
||||
* In this case we get to pay 10000 microsats for every sat.
|
||||
*
|
||||
* Once `k` is fixed then we can combine the linear prob. and fee costs, both
|
||||
* are in monetary units.
|
||||
*
|
||||
* Note: with costs in microsats, because slopes represent ppm and flows are in
|
||||
* sats, then our integer bounds with 64 bits are such that we can move as many
|
||||
* as 10'000 BTC without overflow:
|
||||
*
|
||||
* 10^6 (max ppm) * 10^8 (sats per BTC) * 10^4 = 10^18
|
||||
* This was chosen empirically from examination of typical network values.
|
||||
*
|
||||
* # References
|
||||
*
|
||||
|
@ -324,7 +293,7 @@ struct pay_parameters {
|
|||
|
||||
double delay_feefactor;
|
||||
double base_fee_penalty;
|
||||
u32 prob_cost_factor;
|
||||
double k_factor;
|
||||
};
|
||||
|
||||
/* Representation of the linear MCF network.
|
||||
|
@ -470,7 +439,7 @@ static void linearize_channel(const struct pay_parameters *params,
|
|||
|
||||
cost[i] = params->cost_fraction[i]
|
||||
*params->amount.millisatoshis /* Raw: linearize_channel */
|
||||
*params->prob_cost_factor*1.0/(b-a);
|
||||
*params->k_factor/(b-a);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -561,17 +530,13 @@ static void linear_network_add_adjacenct_arc(
|
|||
* use `base_fee_penalty` to weight the base fee and `delay_feefactor` to
|
||||
* weight the CLTV delay.
|
||||
* */
|
||||
static s64 linear_fee_cost(
|
||||
const struct gossmap_chan *c,
|
||||
const int dir,
|
||||
double base_fee_penalty,
|
||||
double delay_feefactor)
|
||||
static s64 linear_fee_cost(u32 base_fee, u32 proportional_fee, u16 cltv_delta,
|
||||
double base_fee_penalty,
|
||||
double delay_feefactor)
|
||||
{
|
||||
assert(c);
|
||||
assert(dir==0 || dir==1);
|
||||
s64 pfee = c->half[dir].proportional_fee,
|
||||
bfee = c->half[dir].base_fee,
|
||||
delay = c->half[dir].delay;
|
||||
s64 pfee = proportional_fee,
|
||||
bfee = base_fee,
|
||||
delay = cltv_delta;
|
||||
|
||||
return pfee + bfee* base_fee_penalty+ delay*delay_feefactor;
|
||||
}
|
||||
|
@ -644,9 +609,12 @@ init_linear_network(const tal_t *ctx, const struct pay_parameters *params)
|
|||
// that are outgoing to `node`
|
||||
linearize_channel(params, c, half, capacity, prob_cost);
|
||||
|
||||
const s64 fee_cost = linear_fee_cost(c,half,
|
||||
params->base_fee_penalty,
|
||||
params->delay_feefactor);
|
||||
const s64 fee_cost = linear_fee_cost(
|
||||
c->half[half].base_fee,
|
||||
c->half[half].proportional_fee,
|
||||
c->half[half].delay,
|
||||
params->base_fee_penalty,
|
||||
params->delay_feefactor);
|
||||
|
||||
// let's subscribe the 4 parts of the channel direction
|
||||
// (c,half), the dual of these guys will be subscribed
|
||||
|
@ -1281,8 +1249,7 @@ struct flow **minflow(const tal_t *ctx,
|
|||
const struct gossmap_node *target,
|
||||
struct amount_msat amount,
|
||||
u32 mu,
|
||||
double delay_feefactor, double base_fee_penalty,
|
||||
u32 prob_cost_factor)
|
||||
double delay_feefactor, double base_fee_penalty)
|
||||
{
|
||||
struct flow **flow_paths;
|
||||
/* We allocate everything off this, and free it at the end,
|
||||
|
@ -1312,7 +1279,7 @@ struct flow **minflow(const tal_t *ctx,
|
|||
|
||||
params->delay_feefactor = delay_feefactor;
|
||||
params->base_fee_penalty = base_fee_penalty;
|
||||
params->prob_cost_factor = prob_cost_factor;
|
||||
params->k_factor = 8.0;
|
||||
|
||||
// build the uncertainty network with linearization and residual arcs
|
||||
struct linear_network *linear_network= init_linear_network(working_ctx, params);
|
||||
|
|
|
@ -8,22 +8,10 @@
|
|||
|
||||
struct route_query;
|
||||
|
||||
enum {
|
||||
RENEPAY_ERR_OK,
|
||||
// No feasible flow found, either there is not enough known liquidity (or capacity)
|
||||
// in the channels to complete the payment
|
||||
RENEPAY_ERR_NOFEASIBLEFLOW,
|
||||
// There is at least one feasible flow, but the the cheapest solution that we
|
||||
// found is too expensive, we return the result anyways.
|
||||
RENEPAY_ERR_NOCHEAPFLOW
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* optimal_payment_flow - API for min cost flow function(s).
|
||||
* @ctx: context to allocate returned flows from
|
||||
* @gossmap: the gossip map
|
||||
* @rq: the route_query we're processing (for logging)
|
||||
* @source: the source to start from
|
||||
* @target: the target to pay
|
||||
* @amount: the amount we want to reach @target
|
||||
|
@ -39,16 +27,6 @@ enum {
|
|||
*
|
||||
* effective_ppm = proportional_fee + base_fee_msat * base_fee_penalty
|
||||
*
|
||||
* @prob_cost_factor: factor used to monetize the probability cost. It is
|
||||
* defined as the number of ppm (parts per million of the total payment) we
|
||||
* are willing to pay to improve the probability of success by 0.1%.
|
||||
*
|
||||
* k_microsat = floor(1000*prob_cost_factor * payment_sat)
|
||||
*
|
||||
* this k is used to compute a prob. cost in units of microsats
|
||||
*
|
||||
* cost(payment) = - k_microsat * log Prob(payment)
|
||||
*
|
||||
* Return a series of subflows which deliver amount to target, or NULL.
|
||||
*/
|
||||
struct flow **minflow(const tal_t *ctx,
|
||||
|
@ -58,6 +36,5 @@ struct flow **minflow(const tal_t *ctx,
|
|||
struct amount_msat amount,
|
||||
u32 mu,
|
||||
double delay_feefactor,
|
||||
double base_fee_penalty,
|
||||
u32 prob_cost_factor);
|
||||
double base_fee_penalty);
|
||||
#endif /* LIGHTNING_PLUGINS_ASKRENE_MCF_H */
|
||||
|
|
|
@ -433,11 +433,11 @@ def test_getroutes(node_factory):
|
|||
10000000,
|
||||
[[{'short_channel_id_dir': '0x2x1/1',
|
||||
'next_node_id': nodemap[2],
|
||||
'amount_msat': 500000,
|
||||
'amount_msat': 4500004,
|
||||
'delay': 99 + 6}],
|
||||
[{'short_channel_id_dir': '0x2x3/1',
|
||||
'next_node_id': nodemap[2],
|
||||
'amount_msat': 9500009,
|
||||
'amount_msat': 5500005,
|
||||
'delay': 99 + 6}]])
|
||||
|
||||
|
||||
|
@ -992,4 +992,4 @@ def test_real_data(node_factory, bitcoind):
|
|||
if len(fees[n]) > len(fees[best]):
|
||||
best = n
|
||||
|
||||
assert (len(fees[best]), len(improved), total_first_fee, total_final_fee, percent_fee_reduction) == (9, 91, 20917688, 5254665, 75)
|
||||
assert (len(fees[best]), len(improved), total_first_fee, total_final_fee, percent_fee_reduction) == (10, 96, 19969585, 801613, 96)
|
||||
|
|
Loading…
Add table
Reference in a new issue