Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Renepay: htlc limits on mcf #6905

Merged
merged 2 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions plugins/renepay/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,16 @@ bool flows_fit_amount(const tal_t *ctx, struct amount_msat *amount_allocated,
const struct gossmap *gossmap,
struct chan_extra_map *chan_extra_map, char **fail);

/* Helpers to get the htlc_max and htlc_min of a channel. */
static inline struct amount_msat
channel_htlc_max(const struct gossmap_chan *chan, const int dir)
{
return amount_msat(fp16_to_u64(chan->half[dir].htlc_max));
}
static inline struct amount_msat
channel_htlc_min(const struct gossmap_chan *chan, const int dir)
{
return amount_msat(fp16_to_u64(chan->half[dir].htlc_min));
}

#endif /* LIGHTNING_PLUGINS_RENEPAY_FLOW_H */
55 changes: 49 additions & 6 deletions plugins/renepay/mcf.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <ccan/lqueue/lqueue.h>
#include <ccan/tal/str/str.h>
#include <ccan/tal/tal.h>
#include <common/pseudorand.h>
#include <common/type_to_string.h>
#include <common/utils.h>
#include <math.h>
Expand Down Expand Up @@ -191,7 +192,8 @@
static const double CHANNEL_PIVOTS[]={0,0.5,0.8,0.95};

static const s64 INFINITE = INT64_MAX;
static const u32 INVALID_INDEX=0xffffffff;
static const u64 INFINITE_MSAT = UINT64_MAX;
static const u32 INVALID_INDEX = 0xffffffff;
static const s64 MU_MAX = 128;

/* Let's try this encoding of arcs:
Expand Down Expand Up @@ -1226,6 +1228,12 @@ struct list_data
struct flow *flow_path;
};

static inline uint64_t pseudorand_interval(uint64_t a, uint64_t b)
{
assert(b > a);
return a + pseudorand(b - a);
}

/* Given a flow in the residual network, build a set of payment flows in the
* gossmap that corresponds to this flow. */
static struct flow **
Expand Down Expand Up @@ -1310,6 +1318,13 @@ get_flow_paths(const tal_t *ctx, const struct gossmap *gossmap,
u32 final_idx = find_positive_balance(gossmap,chan_flow,node_idx,balance,
prev_chan,prev_dir,prev_idx);

/* For each route we will compute the highest htlc_min
* and the smallest htlc_max and use those to constraint
* the flow we will allocate. */
struct amount_msat sup_htlc_min = AMOUNT_MSAT_INIT(0),
inf_htlc_max =
AMOUNT_MSAT_INIT(INFINITE_MSAT);

s64 delta=-balance[node_idx];
int length = 0;
delta = MIN(delta,balance[final_idx]);
Expand All @@ -1329,13 +1344,41 @@ get_flow_paths(const tal_t *ctx, const struct gossmap *gossmap,
delta=MIN(delta,chan_flow[c_idx].half[dir]);
length++;

// TODO(eduardo) does htlc_max has any relevance
// here?
// HINT: delta=MIN(delta,htlc_max);
// however this might not work because often we
// move delta+fees
/* obtain the supremum htlc_min along the route
*/
sup_htlc_min = amount_msat_max(
sup_htlc_min, channel_htlc_min(c, dir));

/* obtain the infimum htlc_max along the route
*/
inf_htlc_max = amount_msat_min(
inf_htlc_max, channel_htlc_max(c, dir));
}

s64 htlc_max=inf_htlc_max.millisatoshis/1000;/* Raw: need htlc_max in sats to do arithmetic operations.*/
s64 htlc_min=(sup_htlc_min.millisatoshis+999)/1000;/* Raw: need htlc_min in sats to do arithmetic operations.*/

if (htlc_min > htlc_max) {
/* htlc_min is too big or htlc_max is too small,
* we cannot send `delta` along this route.
*
* FIXME: We try anyways because failing
* channels will be blacklisted downstream. */
htlc_min = 0;
}

/* If we divide this route into different flows make it
* random to avoid routing nodes making correlations. */
if (delta > htlc_max) {
// FIXME: choosing a number in the range
// [htlc_min, htlc_max] or
// [0.5 htlc_max, htlc_max]
// The choice of the fraction was completely
// arbitrary.
delta = pseudorand_interval(
MAX(htlc_min, (htlc_max * 50) / 100),
htlc_max);
}

struct flow *fp = tal(this_ctx,struct flow);
fp->path = tal_arr(fp,const struct gossmap_chan *,length);
Expand Down
29 changes: 29 additions & 0 deletions tests/test_renepay.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,32 @@ def test_fee_allocation(node_factory):
l1.wait_for_htlcs()
invoice = only_one(l4.rpc.listinvoices('inv')['invoices'])
assert invoice['amount_received_msat'] >= Millisatoshi('1500000sat')


def test_htlc_max(node_factory):
'''
Topology:
1----2----4
| |
3----5----6
'''
opts = [
{'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 0},
{'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 0},
{'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 0},
{'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 0,
'htlc-maximum-msat': 500000000},
{'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 0,
'htlc-maximum-msat': 500000000},
{'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 0},
]
l1, l2, l3, l4, l5, l6 = node_factory.get_nodes(6, opts=opts)
start_channels([(l1, l2, 10000000), (l2, l4, 1000000), (l4, l6, 2000000),
(l1, l3, 10000000), (l3, l5, 1000000), (l5, l6, 2000000)])

inv = l6.rpc.invoice("1800000sat", "inv", 'description')

l1.rpc.call('renepay', {'invstring': inv['bolt11']})
l1.wait_for_htlcs()
invoice = only_one(l6.rpc.listinvoices('inv')['invoices'])
assert invoice['amount_received_msat'] >= Millisatoshi('1800000sat')
Loading