mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 14:42:40 +01:00
I used `amount_msat_eq(x, AMOUNT_MSAT(0))` because I forgot this function existed. I probably missed it because the name is surprising, so add "is" in there to make it clear it's a boolean function. You'll note almost all the places which did use it are Eduardo's and Lisa's code, so maybe it's just me. Fix up a few places which I could use it, too. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
489 lines
13 KiB
C
489 lines
13 KiB
C
#include "config.h"
|
|
#include <assert.h>
|
|
#include <ccan/tal/str/str.h>
|
|
#include <ccan/tal/tal.h>
|
|
#include <common/fp16.h>
|
|
#include <common/overflows.h>
|
|
#include <math.h>
|
|
#include <plugins/renepay/flow.h>
|
|
#include <stdio.h>
|
|
|
|
#ifndef SUPERVERBOSE
|
|
#define SUPERVERBOSE(...)
|
|
#else
|
|
#define SUPERVERBOSE_ENABLED 1
|
|
#endif
|
|
|
|
struct amount_msat *tal_flow_amounts(const tal_t *ctx, const struct flow *flow,
|
|
bool compute_fees)
|
|
{
|
|
const size_t pathlen = tal_count(flow->path);
|
|
struct amount_msat *amounts = tal_arr(ctx, struct amount_msat, pathlen);
|
|
amounts[pathlen - 1] = flow->amount;
|
|
|
|
for (int i = (int)pathlen - 2; i >= 0; i--) {
|
|
const struct half_chan *h = flow_edge(flow, i + 1);
|
|
amounts[i] = amounts[i + 1];
|
|
if (compute_fees &&
|
|
!amount_msat_add_fee(&amounts[i], h->base_fee,
|
|
h->proportional_fee))
|
|
goto function_fail;
|
|
}
|
|
|
|
return amounts;
|
|
|
|
function_fail:
|
|
return tal_free(amounts);
|
|
}
|
|
|
|
const char *fmt_flows(const tal_t *ctx, const struct gossmap *gossmap,
|
|
struct chan_extra_map *chan_extra_map,
|
|
struct flow **flows)
|
|
{
|
|
tal_t *this_ctx = tal(ctx, tal_t);
|
|
char *buff = tal_fmt(ctx, "%zu subflows\n", tal_count(flows));
|
|
for (size_t i = 0; i < tal_count(flows); i++) {
|
|
struct amount_msat fee, delivered;
|
|
tal_append_fmt(&buff, " ");
|
|
for (size_t j = 0; j < tal_count(flows[i]->path); j++) {
|
|
struct short_channel_id scid =
|
|
gossmap_chan_scid(gossmap, flows[i]->path[j]);
|
|
tal_append_fmt(&buff, "%s%s", j ? "->" : "",
|
|
fmt_short_channel_id(this_ctx, scid));
|
|
}
|
|
delivered = flows[i]->amount;
|
|
if (!flow_fee(&fee, flows[i])) {
|
|
abort();
|
|
}
|
|
tal_append_fmt(&buff, " prob %.2f, %s delivered with fee %s\n",
|
|
flows[i]->success_prob,
|
|
fmt_amount_msat(this_ctx, delivered),
|
|
fmt_amount_msat(this_ctx, fee));
|
|
}
|
|
|
|
tal_free(this_ctx);
|
|
return buff;
|
|
}
|
|
|
|
/* Returns the greatest amount we can deliver to the destination using this
|
|
* route. It takes into account the current knowledge, pending HTLC,
|
|
* htlc_max and fees.
|
|
*
|
|
* It fails if the maximum that we can
|
|
* deliver at node i is smaller than the minimum required to forward the least
|
|
* amount greater than zero to the next node. */
|
|
enum renepay_errorcode
|
|
flow_maximum_deliverable(struct amount_msat *max_deliverable,
|
|
const struct flow *flow,
|
|
const struct gossmap *gossmap,
|
|
struct chan_extra_map *chan_extra_map,
|
|
const struct gossmap_chan **bad_channel)
|
|
{
|
|
assert(tal_count(flow->path) > 0);
|
|
assert(tal_count(flow->dirs) > 0);
|
|
assert(tal_count(flow->path) == tal_count(flow->dirs));
|
|
struct amount_msat x;
|
|
enum renepay_errorcode err;
|
|
|
|
err = channel_liquidity(&x, gossmap, chan_extra_map, flow->path[0],
|
|
flow->dirs[0]);
|
|
if(err){
|
|
if(bad_channel)*bad_channel = flow->path[0];
|
|
return err;
|
|
}
|
|
x = amount_msat_min(x, gossmap_chan_htlc_max(flow->path[0], flow->dirs[0]));
|
|
|
|
if(amount_msat_is_zero(x))
|
|
{
|
|
if(bad_channel)*bad_channel = flow->path[0];
|
|
return RENEPAY_BAD_CHANNEL;
|
|
}
|
|
|
|
for (size_t i = 1; i < tal_count(flow->path); ++i) {
|
|
// ith node can forward up to 'liquidity_cap' because of the ith
|
|
// channel liquidity bound
|
|
struct amount_msat liquidity_cap;
|
|
|
|
err = channel_liquidity(&liquidity_cap, gossmap, chan_extra_map,
|
|
flow->path[i], flow->dirs[i]);
|
|
if(err) {
|
|
if(bad_channel)*bad_channel = flow->path[i];
|
|
return err;
|
|
}
|
|
|
|
/* ith node can receive up to 'x', therefore he will not forward
|
|
* more than 'forward_cap' that we compute below inverting the
|
|
* fee equation. */
|
|
struct amount_msat forward_cap;
|
|
err = channel_maximum_forward(&forward_cap, flow->path[i],
|
|
flow->dirs[i], x);
|
|
if(err)
|
|
{
|
|
if(bad_channel)*bad_channel = flow->path[i];
|
|
return err;
|
|
}
|
|
struct amount_msat x_new =
|
|
amount_msat_min(forward_cap, liquidity_cap);
|
|
x_new = amount_msat_min(
|
|
x_new, gossmap_chan_htlc_max(flow->path[i], flow->dirs[i]));
|
|
|
|
/* safety check: amounts decrease along the route */
|
|
assert(amount_msat_less_eq(x_new, x));
|
|
|
|
if(amount_msat_is_zero(x_new))
|
|
{
|
|
if(bad_channel)*bad_channel = flow->path[i];
|
|
return RENEPAY_BAD_CHANNEL;
|
|
}
|
|
|
|
/* safety check: the max liquidity in the next hop + fees cannot
|
|
be greater than the max liquidity in the current hop, IF the
|
|
next hop is non-zero. */
|
|
struct amount_msat x_check = x_new;
|
|
assert(
|
|
amount_msat_add_fee(&x_check, flow_edge(flow, i)->base_fee,
|
|
flow_edge(flow, i)->proportional_fee));
|
|
assert(amount_msat_less_eq(x_check, x));
|
|
|
|
x = x_new;
|
|
}
|
|
assert(!amount_msat_is_zero(x));
|
|
*max_deliverable = x;
|
|
return RENEPAY_NOERROR;
|
|
}
|
|
|
|
/* Returns the smallest amount we can send so that the destination can get one
|
|
* HTLC of any size. It takes into account htlc_min and fees.
|
|
* */
|
|
// static enum renepay_errorcode
|
|
// flow_minimum_sendable(struct amount_msat *min_sendable UNUSED,
|
|
// const struct flow *flow UNUSED,
|
|
// const struct gossmap *gossmap UNUSED,
|
|
// struct chan_extra_map *chan_extra_map UNUSED)
|
|
// {
|
|
// // TODO
|
|
// return RENEPAY_NOERROR;
|
|
// }
|
|
|
|
/* How much do we deliver to destination using this set of routes */
|
|
bool flowset_delivers(struct amount_msat *delivers, struct flow **flows)
|
|
{
|
|
struct amount_msat final = AMOUNT_MSAT(0);
|
|
for (size_t i = 0; i < tal_count(flows); i++) {
|
|
if (!amount_msat_accumulate(&final, flows[i]->amount))
|
|
return false;
|
|
}
|
|
*delivers = final;
|
|
return true;
|
|
}
|
|
|
|
/* FIXME: pass a pointer to const here */
|
|
size_t flowset_size(struct flow **flows)
|
|
{
|
|
size_t size = 0;
|
|
for (size_t i = 0; i < tal_count(flows); i++)
|
|
size += tal_count(flows[i]->path);
|
|
return size;
|
|
}
|
|
|
|
/* Checks if the flows satisfy the liquidity bounds imposed by the known maximum
|
|
* liquidity and pending HTLCs.
|
|
*
|
|
* FIXME The function returns false even in the case of failure. The caller has
|
|
* no way of knowing the difference between a failure of evaluation and a
|
|
* negative answer. */
|
|
// static bool check_liquidity_bounds(struct flow **flows,
|
|
// const struct gossmap *gossmap,
|
|
// struct chan_extra_map *chan_extra_map)
|
|
// {
|
|
// bool check = true;
|
|
// for (size_t i = 0; i < tal_count(flows); ++i) {
|
|
// struct amount_msat max_deliverable;
|
|
// if (!flow_maximum_deliverable(&max_deliverable, flows[i],
|
|
// gossmap, chan_extra_map))
|
|
// return false;
|
|
// struct amount_msat delivers = flow_delivers(flows[i]);
|
|
// check &= amount_msat_less_eq(delivers, max_deliverable);
|
|
// }
|
|
// return check;
|
|
// }
|
|
|
|
/* Compute the prob. of success of a set of concurrent set of flows.
|
|
*
|
|
* IMPORTANT: this is not simply the multiplication of the prob. of success of
|
|
* all of them, because they're not independent events. A flow that passes
|
|
* through a channel c changes that channel's liquidity and then if another flow
|
|
* passes through that same channel the previous liquidity change must be taken
|
|
* into account.
|
|
*
|
|
* P(A and B) != P(A) * P(B),
|
|
*
|
|
* but
|
|
*
|
|
* P(A and B) = P(A) * P(B | A)
|
|
*
|
|
* also due to the linear form of P() we have
|
|
*
|
|
* P(A and B) = P(A + B)
|
|
* */
|
|
struct chan_inflight_flow
|
|
{
|
|
struct amount_msat half[2];
|
|
};
|
|
|
|
/* @ctx: allocator
|
|
* @flows: flows for which the probability is computed
|
|
* @gossmap: gossip
|
|
* @chan_extra_map: knowledge
|
|
* @compute_fees: compute fees along the way or not
|
|
* @fail: if a failure occurs, returns a message to the caller
|
|
* */
|
|
// TODO(eduardo): here chan_extra_map should be const
|
|
// TODO(eduardo): here flows should be const
|
|
double flowset_probability(const tal_t *ctx, struct flow **flows,
|
|
const struct gossmap *const gossmap,
|
|
struct chan_extra_map *chan_extra_map,
|
|
bool compute_fees,
|
|
char **fail)
|
|
{
|
|
assert(flows);
|
|
assert(gossmap);
|
|
assert(chan_extra_map);
|
|
tal_t *this_ctx = tal(ctx, tal_t);
|
|
double prob = 1.0;
|
|
|
|
// TODO(eduardo): should it be better to use a map instead of an array
|
|
// here?
|
|
const size_t max_num_chans = gossmap_max_chan_idx(gossmap);
|
|
struct chan_inflight_flow *in_flight =
|
|
tal_arr(this_ctx, struct chan_inflight_flow, max_num_chans);
|
|
|
|
if (!in_flight) {
|
|
if (fail)
|
|
*fail = tal_fmt(ctx, "failed to allocate memory");
|
|
goto function_fail;
|
|
}
|
|
|
|
for (size_t i = 0; i < max_num_chans; ++i) {
|
|
in_flight[i].half[0] = in_flight[i].half[1] = AMOUNT_MSAT(0);
|
|
}
|
|
|
|
for (size_t i = 0; i < tal_count(flows); ++i) {
|
|
const struct flow *f = flows[i];
|
|
const size_t pathlen = tal_count(f->path);
|
|
struct amount_msat *amounts =
|
|
tal_flow_amounts(this_ctx, f, compute_fees);
|
|
if (!amounts)
|
|
{
|
|
if (fail)
|
|
*fail = tal_fmt(
|
|
ctx,
|
|
"failed to compute amounts along the path");
|
|
goto function_fail;
|
|
}
|
|
|
|
for (size_t j = 0; j < pathlen; ++j) {
|
|
const struct chan_extra_half *h =
|
|
get_chan_extra_half_by_chan(gossmap, chan_extra_map,
|
|
f->path[j], f->dirs[j]);
|
|
if (!h) {
|
|
if (fail)
|
|
*fail = tal_fmt(
|
|
ctx,
|
|
"channel not found in chan_extra_map");
|
|
goto function_fail;
|
|
}
|
|
const u32 c_idx = gossmap_chan_idx(gossmap, f->path[j]);
|
|
const int c_dir = f->dirs[j];
|
|
|
|
const struct amount_msat deliver = amounts[j];
|
|
|
|
struct amount_msat prev_flow, all_inflight;
|
|
if (!amount_msat_add(&prev_flow, h->htlc_total,
|
|
in_flight[c_idx].half[c_dir]) ||
|
|
!amount_msat_add(&all_inflight, prev_flow,
|
|
deliver)) {
|
|
if (fail)
|
|
*fail = tal_fmt(
|
|
ctx,
|
|
"in-flight amount_msat overflow");
|
|
goto function_fail;
|
|
}
|
|
|
|
if (!amount_msat_less_eq(all_inflight, h->known_max)) {
|
|
if (fail)
|
|
*fail = tal_fmt(
|
|
ctx,
|
|
"in-flight (%s) exceeds known_max "
|
|
"(%s)",
|
|
fmt_amount_msat(ctx, all_inflight),
|
|
fmt_amount_msat(ctx, h->known_max));
|
|
goto function_fail;
|
|
}
|
|
|
|
double edge_prob = edge_probability(
|
|
h->known_min, h->known_max, prev_flow, deliver);
|
|
|
|
if (edge_prob < 0) {
|
|
if (fail)
|
|
*fail = tal_fmt(ctx,
|
|
"edge_probability failed");
|
|
goto function_fail;
|
|
}
|
|
prob *= edge_prob;
|
|
|
|
if (!amount_msat_add(&in_flight[c_idx].half[c_dir],
|
|
in_flight[c_idx].half[c_dir],
|
|
deliver)) {
|
|
if (fail)
|
|
*fail = tal_fmt(
|
|
ctx, "in-flight amount_msat overflow");
|
|
goto function_fail;
|
|
}
|
|
}
|
|
}
|
|
tal_free(this_ctx);
|
|
return prob;
|
|
|
|
function_fail:
|
|
tal_free(this_ctx);
|
|
return -1;
|
|
}
|
|
|
|
bool flow_spend(struct amount_msat *ret, struct flow *flow)
|
|
{
|
|
assert(ret);
|
|
assert(flow);
|
|
const size_t pathlen = tal_count(flow->path);
|
|
struct amount_msat spend = flow->amount;
|
|
|
|
for (int i = (int)pathlen - 2; i >= 0; i--) {
|
|
const struct half_chan *h = flow_edge(flow, i + 1);
|
|
if (!amount_msat_add_fee(&spend, h->base_fee,
|
|
h->proportional_fee))
|
|
goto function_fail;
|
|
}
|
|
|
|
*ret = spend;
|
|
return true;
|
|
|
|
function_fail:
|
|
return false;
|
|
}
|
|
|
|
bool flow_fee(struct amount_msat *ret, struct flow *flow)
|
|
{
|
|
assert(ret);
|
|
assert(flow);
|
|
struct amount_msat fee;
|
|
struct amount_msat spend;
|
|
if (!flow_spend(&spend, flow))
|
|
goto function_fail;
|
|
if (!amount_msat_sub(&fee, spend, flow->amount))
|
|
goto function_fail;
|
|
|
|
*ret = fee;
|
|
return true;
|
|
|
|
function_fail:
|
|
return false;
|
|
}
|
|
|
|
bool flowset_fee(struct amount_msat *ret, struct flow **flows)
|
|
{
|
|
assert(ret);
|
|
assert(flows);
|
|
struct amount_msat fee = AMOUNT_MSAT(0);
|
|
for (size_t i = 0; i < tal_count(flows); i++) {
|
|
struct amount_msat this_fee;
|
|
if (!flow_fee(&this_fee, flows[i]))
|
|
return false;
|
|
if (!amount_msat_add(&fee, this_fee, fee))
|
|
return false;
|
|
}
|
|
*ret = fee;
|
|
return true;
|
|
}
|
|
|
|
/* Helper to access the half chan at flow index idx */
|
|
const struct half_chan *flow_edge(const struct flow *flow, size_t idx)
|
|
{
|
|
assert(flow);
|
|
assert(idx < tal_count(flow->path));
|
|
return &flow->path[idx]->half[flow->dirs[idx]];
|
|
}
|
|
|
|
/* Assign the delivered amount to the flow if it fits
|
|
the path maximum capacity. */
|
|
bool flow_assign_delivery(struct flow *flow, const struct gossmap *gossmap,
|
|
struct chan_extra_map *chan_extra_map,
|
|
struct amount_msat requested_amount)
|
|
{
|
|
struct amount_msat max_deliverable = AMOUNT_MSAT(0);
|
|
if (flow_maximum_deliverable(&max_deliverable, flow, gossmap,
|
|
chan_extra_map, NULL))
|
|
return false;
|
|
assert(!amount_msat_is_zero(max_deliverable));
|
|
flow->amount = amount_msat_min(requested_amount, max_deliverable);
|
|
return true;
|
|
}
|
|
|
|
/* Helper function to find the success_prob for a single flow
|
|
*
|
|
* IMPORTANT: flow->success_prob is misleading, because that's the prob. of
|
|
* success provided that there are no other flows in the current MPP flow set.
|
|
* */
|
|
double flow_probability(struct flow *flow, const struct gossmap *gossmap,
|
|
struct chan_extra_map *chan_extra_map,
|
|
bool compute_fees)
|
|
{
|
|
assert(flow);
|
|
assert(gossmap);
|
|
assert(chan_extra_map);
|
|
const size_t pathlen = tal_count(flow->path);
|
|
struct amount_msat spend = flow->amount;
|
|
double prob = 1.0;
|
|
|
|
for (int i = (int)pathlen - 1; i >= 0; i--) {
|
|
const struct half_chan *h = flow_edge(flow, i);
|
|
const struct chan_extra_half *eh = get_chan_extra_half_by_chan(
|
|
gossmap, chan_extra_map, flow->path[i], flow->dirs[i]);
|
|
|
|
prob *= edge_probability(eh->known_min, eh->known_max,
|
|
eh->htlc_total, spend);
|
|
|
|
if (prob < 0)
|
|
goto function_fail;
|
|
if (compute_fees && !amount_msat_add_fee(&spend, h->base_fee,
|
|
h->proportional_fee))
|
|
goto function_fail;
|
|
}
|
|
|
|
return prob;
|
|
|
|
function_fail:
|
|
return -1.;
|
|
}
|
|
|
|
u64 flow_delay(const struct flow *flow)
|
|
{
|
|
u64 delay = 0;
|
|
for (size_t i = 0; i < tal_count(flow->path); i++)
|
|
delay += flow_edge(flow, i)->delay;
|
|
return delay;
|
|
}
|
|
|
|
u64 flows_worst_delay(struct flow **flows)
|
|
{
|
|
u64 maxdelay = 0;
|
|
for (size_t i = 0; i < tal_count(flows); i++) {
|
|
u64 delay = flow_delay(flows[i]);
|
|
if (delay > maxdelay)
|
|
maxdelay = delay;
|
|
}
|
|
return maxdelay;
|
|
}
|
|
|
|
#ifndef SUPERVERBOSE_ENABLED
|
|
#undef SUPERVERBOSE
|
|
#endif
|