diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index 5300fecb0f04..dc736b046951 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -1,39 +1,27 @@ PLUGIN_RENEPAY_SRC := \ - plugins/renepay/main.c \ - plugins/renepay/flow.c \ - plugins/renepay/mcf.c \ - plugins/renepay/dijkstra.c \ - plugins/renepay/disabledmap.c \ + plugins/renepay/renepay.c \ plugins/renepay/payment.c \ - plugins/renepay/chan_extra.c \ plugins/renepay/route.c \ - plugins/renepay/routebuilder.c \ plugins/renepay/routetracker.c \ plugins/renepay/routefail.c \ plugins/renepay/sendpay.c \ - plugins/renepay/uncertainty.c \ plugins/renepay/mods.c \ plugins/renepay/errorcodes.c \ + plugins/renepay/utils.c \ plugins/renepay/json.c PLUGIN_RENEPAY_HDRS := \ - plugins/renepay/payplugin.h \ - plugins/renepay/flow.h \ - plugins/renepay/mcf.h \ - plugins/renepay/dijkstra.h \ - plugins/renepay/disabledmap.h \ + plugins/renepay/renepay.h \ plugins/renepay/payment.h \ plugins/renepay/payment_info.h \ - plugins/renepay/chan_extra.h \ plugins/renepay/renepayconfig.h \ plugins/renepay/route.h \ - plugins/renepay/routebuilder.h \ plugins/renepay/routetracker.h \ plugins/renepay/routefail.h \ plugins/renepay/sendpay.h \ - plugins/renepay/uncertainty.h \ plugins/renepay/mods.h \ plugins/renepay/errorcodes.h \ + plugins/renepay/utils.h \ plugins/renepay/json.c PLUGIN_RENEPAY_OBJS := $(PLUGIN_RENEPAY_SRC:.c=.o) diff --git a/plugins/renepay/chan_extra.c b/plugins/renepay/chan_extra.c deleted file mode 100644 index 931463ca53d2..000000000000 --- a/plugins/renepay/chan_extra.c +++ /dev/null @@ -1,677 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include - -bool chan_extra_is_busy(const struct chan_extra *const ce) -{ - if (ce == NULL) - return false; - return ce->half[0].num_htlcs || ce->half[1].num_htlcs; -} - -const char *fmt_chan_extra_map(const tal_t *ctx, - struct chan_extra_map *chan_extra_map) -{ - tal_t *this_ctx = tal(ctx, tal_t); - char *buff = tal_fmt(ctx, "Uncertainty network:\n"); - struct chan_extra_map_iter it; - for (struct chan_extra *ch = chan_extra_map_first(chan_extra_map, &it); - ch; ch = chan_extra_map_next(chan_extra_map, &it)) { - const char *scid_str = fmt_short_channel_id(this_ctx, ch->scid); - for (int dir = 0; dir < 2; ++dir) { - tal_append_fmt( - &buff, "%s[%d]:(%s,%s) htlc: %s\n", scid_str, dir, - fmt_amount_msat(this_ctx, ch->half[dir].known_min), - fmt_amount_msat(this_ctx, ch->half[dir].known_max), - fmt_amount_msat(this_ctx, ch->half[dir].htlc_total)); - } - } - tal_free(this_ctx); - return buff; -} - -const char *fmt_chan_extra_details(const tal_t *ctx, - const struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd) -{ - const tal_t *this_ctx = tal(ctx, tal_t); - const struct chan_extra *ce = - chan_extra_map_get(chan_extra_map, scidd->scid); - const struct chan_extra_half *ch; - char *str = tal_strdup(ctx, ""); - char sep = '('; - - if (!ce) { - // we have no information on this channel - tal_append_fmt(&str, "()"); - goto finished; - } - - ch = &ce->half[scidd->dir]; - if (ch->num_htlcs != 0) { - tal_append_fmt(&str, "%c%s in %zu htlcs", sep, - fmt_amount_msat(this_ctx, ch->htlc_total), - ch->num_htlcs); - sep = ','; - } - /* Happens with local channels, where we're certain. */ - if (amount_msat_eq(ch->known_min, ch->known_max)) { - tal_append_fmt(&str, "%cmin=max=%s", sep, - fmt_amount_msat(this_ctx, ch->known_min)); - sep = ','; - } else { - if (amount_msat_greater(ch->known_min, AMOUNT_MSAT(0))) { - tal_append_fmt( - &str, "%cmin=%s", sep, - fmt_amount_msat(this_ctx, ch->known_min)); - sep = ','; - } - if (!amount_msat_eq(ch->known_max, ce->capacity)) { - tal_append_fmt( - &str, "%cmax=%s", sep, - fmt_amount_msat(this_ctx, ch->known_max)); - sep = ','; - } - } - if (!streq(str, "")) - tal_append_fmt(&str, ")"); - -finished: - tal_free(this_ctx); - return str; -} - -struct chan_extra *new_chan_extra(struct chan_extra_map *chan_extra_map, - const struct short_channel_id scid, - struct amount_msat capacity) -{ - assert(chan_extra_map); - struct chan_extra *ce = tal(chan_extra_map, struct chan_extra); - if (!ce) - return ce; - - ce->scid = scid; - ce->capacity = capacity; - for (size_t i = 0; i <= 1; i++) { - ce->half[i].num_htlcs = 0; - ce->half[i].htlc_total = AMOUNT_MSAT(0); - ce->half[i].known_min = AMOUNT_MSAT(0); - ce->half[i].known_max = capacity; - } - if (!chan_extra_map_add(chan_extra_map, ce)) { - return tal_free(ce); - } - - /* Remove self from map when done */ - // TODO(eduardo): - // Is this desctructor really necessary? the chan_extra will deallocated - // when the chan_extra_map is freed. Anyways valgrind complains that the - // hash table is removing the element with a freed pointer. - // tal_add_destructor2(ce, destroy_chan_extra, chan_extra_map); - return ce; -} - -/* Based on the knowledge that we have and HTLCs, returns the greatest - * amount that we can send through this channel. */ -enum renepay_errorcode channel_liquidity(struct amount_msat *liquidity, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, - const int dir) -{ - const struct chan_extra_half *h = - get_chan_extra_half_by_chan(gossmap, chan_extra_map, chan, dir); - if (!h) - return RENEPAY_CHANNEL_NOT_FOUND; - struct amount_msat value_liquidity = h->known_max; - if (!amount_msat_sub(&value_liquidity, value_liquidity, h->htlc_total)) - return RENEPAY_AMOUNT_OVERFLOW; - *liquidity = value_liquidity; - return RENEPAY_NOERROR; -} - -/* Checks BOLT 7 HTLC fee condition: - * recv >= base_fee + (send*proportional_fee)/1000000 */ -bool check_fee_inequality(struct amount_msat recv, struct amount_msat send, - u64 base_fee, u64 proportional_fee) -{ - // nothing to forward, any incoming amount is good - if (amount_msat_is_zero(send)) - return true; - // FIXME If this addition fails we return false. The caller will not be - // able to know that there was an addition overflow, he will just assume - // that the fee inequality was not satisfied. - if (!amount_msat_add_fee(&send, base_fee, proportional_fee)) - return false; - return amount_msat_greater_eq(recv, send); -} - -/* Let `recv` be the maximum amount this channel can receive, this function - * computes the maximum amount this channel can forward `send`. - * From BOLT7 specification wee need to satisfy the following inequality: - * - * recv-send >= base_fee + floor(send*proportional_fee/1000000) - * - * That is equivalent to have - * - * send <= Bound(recv,send) - * - * where - * - * Bound(recv, send) = ((recv - base_fee)*1000000 + (send*proportional_fee) - *% 1000000)/(proportional_fee+1000000) - * - * However the quantity we want to determine, `send`, appears on both sides of - * the equation. However the term `send*proportional_fee) % 1000000` only - * contributes by increasing the bound by at most one so that we can neglect - * the extra term and use instead - * - * Bound_simple(recv) = ((recv - - *base_fee)*1000000)/(proportional_fee+1000000) - * - * as the upper bound for `send`. Formally one can check that - * - * Bound_simple(recv) <= Bound(recv, send) < Bound_simple(recv) + 2 - * - * So that if one wishes to find the very highest value of `send` that - * satisfies - * - * send <= Bound(recv, send) - * - * it is enough to compute - * - * send = Bound_simple(recv) - * - * which already satisfies the fee equation and then try to go higher - * with send+1, send+2, etc. But we know that it is enough to try up to - * send+1 because Bound(recv, send) < Bound_simple(recv) + 2. - * */ -enum renepay_errorcode channel_maximum_forward(struct amount_msat *max_forward, - const struct gossmap_chan *chan, - const int dir, - struct amount_msat recv) -{ - const u64 b = chan->half[dir].base_fee, - p = chan->half[dir].proportional_fee; - - const u64 one_million = 1000000; - u64 x_msat = - recv.millisatoshis; /* Raw: need to invert the fee equation */ - - // special case, when recv - base_fee <= 0, we cannot forward anything - if (x_msat <= b) { - *max_forward = amount_msat(0); - return RENEPAY_NOERROR; - } - - x_msat -= b; - - if (mul_overflows_u64(one_million, x_msat)) - return RENEPAY_AMOUNT_OVERFLOW; - - struct amount_msat best_send = - AMOUNT_MSAT_INIT((one_million * x_msat) / (one_million + p)); - - /* Try to increase the value we send (up tp the last millisat) until we - * fail to fulfill the fee inequality. It takes only one iteration - * though. */ - for (size_t i = 0; i < 10; ++i) { - struct amount_msat next_send; - if (!amount_msat_add(&next_send, best_send, amount_msat(1))) - return RENEPAY_AMOUNT_OVERFLOW; - - if (check_fee_inequality(recv, next_send, b, p)) - best_send = next_send; - else - break; - } - *max_forward = best_send; - return RENEPAY_NOERROR; -} - -/* This helper function preserves the uncertainty network invariant after the - * knowledge is updated. It assumes that the (channel,!dir) knowledge is - * correct. */ -static enum renepay_errorcode chan_extra_adjust_half(struct chan_extra *ce, - int dir) -{ - assert(ce); - assert(dir == 0 || dir == 1); - - struct amount_msat new_known_max, new_known_min; - - if (!amount_msat_sub(&new_known_max, ce->capacity, - ce->half[!dir].known_min) || - !amount_msat_sub(&new_known_min, ce->capacity, - ce->half[!dir].known_max)) - return RENEPAY_AMOUNT_OVERFLOW; - - ce->half[dir].known_max = new_known_max; - ce->half[dir].known_min = new_known_min; - return RENEPAY_NOERROR; -} - -/* Update the knowledge that this (channel,direction) can send x msat.*/ -static enum renepay_errorcode -chan_extra_can_send_(struct chan_extra *ce, int dir, struct amount_msat x) -{ - assert(ce); - assert(dir == 0 || dir == 1); - enum renepay_errorcode err; - - if (amount_msat_greater(x, ce->capacity)) - return RENEPAY_PRECONDITION_ERROR; - - struct amount_msat known_min, known_max; - - // in case we fail, let's remember the original state - known_min = ce->half[dir].known_min; - known_max = ce->half[dir].known_max; - - ce->half[dir].known_min = amount_msat_max(ce->half[dir].known_min, x); - ce->half[dir].known_max = amount_msat_max(ce->half[dir].known_max, x); - - err = chan_extra_adjust_half(ce, !dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - - return RENEPAY_NOERROR; - -restore_and_fail: - // we fail, thus restore the original state - ce->half[dir].known_min = known_min; - ce->half[dir].known_max = known_max; - return err; -} - -enum renepay_errorcode -chan_extra_can_send(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd) -{ - assert(scidd); - assert(chan_extra_map); - struct chan_extra *ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return RENEPAY_CHANNEL_NOT_FOUND; - return chan_extra_can_send_(ce, scidd->dir, - ce->half[scidd->dir].htlc_total); -} - -/* Update the knowledge that this (channel,direction) cannot send.*/ -enum renepay_errorcode -chan_extra_cannot_send(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd) -{ - assert(scidd); - assert(chan_extra_map); - struct amount_msat x; - enum renepay_errorcode err; - struct chan_extra *ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return RENEPAY_CHANNEL_NOT_FOUND; - - /* Note: sent is already included in htlc_total! */ - if (!amount_msat_sub(&x, ce->half[scidd->dir].htlc_total, - AMOUNT_MSAT(1))) - return RENEPAY_AMOUNT_OVERFLOW; - - struct amount_msat known_min, known_max; - // in case we fail, let's remember the original state - known_min = ce->half[scidd->dir].known_min; - known_max = ce->half[scidd->dir].known_max; - - /* If we "knew" the capacity was at least this, we just showed we're - * wrong! */ - if (amount_msat_less(x, ce->half[scidd->dir].known_min)) { - /* Skip to half of x, since we don't know (rounds down) */ - ce->half[scidd->dir].known_min = amount_msat_div(x, 2); - } - - ce->half[scidd->dir].known_max = - amount_msat_min(ce->half[scidd->dir].known_max, x); - - err = chan_extra_adjust_half(ce, !scidd->dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - return err; - -restore_and_fail: - // we fail, thus restore the original state - ce->half[scidd->dir].known_min = known_min; - ce->half[scidd->dir].known_max = known_max; - return err; -} - -/* Update the knowledge that this (channel,direction) has liquidity x.*/ -// FIXME for being this low level API, I thinkg it's too much to have verbose -// error messages -static enum renepay_errorcode chan_extra_set_liquidity_(struct chan_extra *ce, - int dir, - struct amount_msat min, - struct amount_msat max) -{ - assert(ce); - assert(dir == 0 || dir == 1); - enum renepay_errorcode err; - - if (amount_msat_greater(max, ce->capacity) || - amount_msat_greater(min, max)) - return RENEPAY_PRECONDITION_ERROR; - - // in case we fail, let's remember the original state - struct amount_msat known_min, known_max; - known_min = ce->half[dir].known_min; - known_max = ce->half[dir].known_max; - - ce->half[dir].known_min = min; - ce->half[dir].known_max = max; - - err = chan_extra_adjust_half(ce, !dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - return err; - -restore_and_fail: - // we fail, thus restore the original state - ce->half[dir].known_min = known_min; - ce->half[dir].known_max = known_max; - return err; -} - -enum renepay_errorcode -chan_extra_set_liquidity(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat min, - struct amount_msat max) -{ - assert(scidd); - assert(chan_extra_map); - struct chan_extra *ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return RENEPAY_CHANNEL_NOT_FOUND; - - return chan_extra_set_liquidity_(ce, scidd->dir, min, max); -} - -/* Update the knowledge that this (channel,direction) has sent x msat.*/ -enum renepay_errorcode -chan_extra_sent_success(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat x) -{ - assert(scidd); - assert(chan_extra_map); - - struct chan_extra *ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return RENEPAY_CHANNEL_NOT_FOUND; - - // if we sent amount x, it first means that all htlcs on this channel - // fit in the liquidity - enum renepay_errorcode err; - err = chan_extra_can_send(chan_extra_map, scidd); - if (err != RENEPAY_NOERROR) - return err; - - if (amount_msat_greater(x, ce->capacity)) - return RENEPAY_PRECONDITION_ERROR; - - // in case we fail, let's remember the original state - struct amount_msat known_min, known_max; - known_min = ce->half[scidd->dir].known_min; - known_max = ce->half[scidd->dir].known_max; - - struct amount_msat new_a, new_b; - - if (!amount_msat_sub(&new_a, ce->half[scidd->dir].known_min, x)) - new_a = AMOUNT_MSAT(0); - if (!amount_msat_sub(&new_b, ce->half[scidd->dir].known_max, x)) - new_b = AMOUNT_MSAT(0); - - ce->half[scidd->dir].known_min = new_a; - ce->half[scidd->dir].known_max = new_b; - - err = chan_extra_adjust_half(ce, !scidd->dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - - return err; - -// we fail, thus restore the original state -restore_and_fail: - ce->half[scidd->dir].known_min = known_min; - ce->half[scidd->dir].known_max = known_max; - return err; -} - -/* Forget a bit about this (channel,direction) state. */ -static enum renepay_errorcode chan_extra_relax(struct chan_extra *ce, int dir, - struct amount_msat down, - struct amount_msat up) -{ - assert(ce); - assert(dir == 0 || dir == 1); - struct amount_msat new_a, new_b; - enum renepay_errorcode err; - - if (!amount_msat_sub(&new_a, ce->half[dir].known_min, down)) - new_a = AMOUNT_MSAT(0); - if (!amount_msat_add(&new_b, ce->half[dir].known_max, up)) - new_b = ce->capacity; - new_b = amount_msat_min(new_b, ce->capacity); - - // in case we fail, let's remember the original state - struct amount_msat known_min, known_max; - known_min = ce->half[dir].known_min; - known_max = ce->half[dir].known_max; - - ce->half[dir].known_min = new_a; - ce->half[dir].known_max = new_b; - - err = chan_extra_adjust_half(ce, !dir); - if (err != RENEPAY_NOERROR) - goto restore_and_fail; - return err; - -// we fail, thus restore the original state -restore_and_fail: - ce->half[dir].known_min = known_min; - ce->half[dir].known_max = known_max; - return err; -} - -/* Forget the channel information by a fraction of the capacity. */ -enum renepay_errorcode chan_extra_relax_fraction(struct chan_extra *ce, - double fraction) -{ - assert(ce); - assert(fraction >= 0); - /* Allow to have values greater than 1 to indicate full relax. */ - // assert(fraction<=1); - fraction = fabs(fraction); // this number is always non-negative - fraction = MIN(1.0, fraction); // this number cannot be greater than 1. - struct amount_msat delta = - amount_msat(ce->capacity.millisatoshis*fraction); /* Raw: get a fraction of the capacity */ - - /* The direction here is not important because the 'down' and the 'up' - * limits are changed by the same amount. - * Notice that if chan[0] with capacity C changes from (a,b) to - * (a-d,b+d) then its counterpart chan[1] changes from (C-b,C-a) to - * (C-b-d,C-a+d), hence both dirs are applied the same transformation. - */ - return chan_extra_relax(ce, /*dir=*/0, delta, delta); -} - -/* Returns either NULL, or an entry from the hash */ -struct chan_extra_half * -get_chan_extra_half_by_scid(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd) -{ - assert(scidd); - assert(chan_extra_map); - struct chan_extra *ce; - - ce = chan_extra_map_get(chan_extra_map, scidd->scid); - if (!ce) - return NULL; - return &ce->half[scidd->dir]; -} -/* Helper if we have a gossmap_chan */ -struct chan_extra_half * -get_chan_extra_half_by_chan(const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, int dir) -{ - assert(chan); - assert(dir == 0 || dir == 1); - assert(gossmap); - assert(chan_extra_map); - struct short_channel_id_dir scidd; - - scidd.scid = gossmap_chan_scid(gossmap, chan); - scidd.dir = dir; - return get_chan_extra_half_by_scid(chan_extra_map, &scidd); -} - -// static void destroy_chan_extra(struct chan_extra *ce, -// struct chan_extra_map *chan_extra_map) -// { -// chan_extra_map_del(chan_extra_map, ce); -// } -/* Helper to get the chan_extra_half. If it doesn't exist create a new one. */ -struct chan_extra_half * -get_chan_extra_half_by_chan_verify(const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, int dir) -{ - assert(chan); - assert(dir == 0 || dir == 1); - assert(gossmap); - assert(chan_extra_map); - struct short_channel_id_dir scidd; - - scidd.scid = gossmap_chan_scid(gossmap, chan); - scidd.dir = dir; - struct chan_extra_half *h = - get_chan_extra_half_by_scid(chan_extra_map, &scidd); - if (!h) { - struct amount_msat cap_msat - = gossmap_chan_get_capacity(gossmap, chan); - h = &new_chan_extra(chan_extra_map, scidd.scid, cap_msat) - ->half[scidd.dir]; - } - return h; -} - -/* Assuming a uniform distribution, what is the chance this f gets through? - * Here we compute the conditional probability of success for a flow f, given - * the knowledge that the liquidity is in the range [a,b) and some amount - * x is already committed on another part of the payment. - * - * The probability equation for x=0 is: - * - * prob(f) = - * - * for f=f>=a: (b-f)/(b-a) - * for b0 the prob. of success for passing x and f is: - * - * prob(f and x) = prob(x) * prob(f|x) - * - * and it can be shown to be equal to - * - * prob(f and x) = prob(f+x) - * - * The purpose of this function is to obtain prob(f|x), i.e. the probability of - * getting f through provided that we already succeeded in getting x. - * This conditional probability comes with 4 cases: - * - * prob(f|x) = - * - * for x=a-x: (b-x-f)/(b-a) - * for x>=a: (b-x-f)/(b-x) - * for f>b-x: 0. - * - * This is the same as the probability of success of f when the bounds are - * shifted by x amount, the new bounds be [MAX(0,a-x),b-x). - */ -double edge_probability(struct amount_msat min, struct amount_msat max, - struct amount_msat in_flight, struct amount_msat f) -{ - assert(amount_msat_less_eq(min, max)); - assert(amount_msat_less_eq(in_flight, max)); - - const struct amount_msat one = AMOUNT_MSAT(1); - struct amount_msat B = max; // = max +1 - in_flight - - // one past the last known value, makes computations simpler - if (!amount_msat_accumulate(&B, one)) - goto function_fail; - - // in_flight cannot be greater than max - if (!amount_msat_sub(&B, B, in_flight)) - goto function_fail; - - struct amount_msat A = min; // = MAX(0,min-in_flight); - - if (!amount_msat_sub(&A, A, in_flight)) - A = AMOUNT_MSAT(0); - - struct amount_msat denominator; // = B-A - - // B cannot be smaller than or equal A - if (!amount_msat_sub(&denominator, B, A) || amount_msat_less_eq(B, A)) - goto function_fail; - - struct amount_msat numerator; // MAX(0,B-f) - - if (!amount_msat_sub(&numerator, B, f)) - numerator = AMOUNT_MSAT(0); - - return amount_msat_less_eq(f, A) - ? 1.0 - : amount_msat_ratio(numerator, denominator); - -function_fail: - return -1; -} - -enum renepay_errorcode -chan_extra_remove_htlc(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat amount) -{ - struct chan_extra_half *h = - get_chan_extra_half_by_scid(chan_extra_map, scidd); - if (!h) - return RENEPAY_CHANNEL_NOT_FOUND; - if (h->num_htlcs <= 0) - return RENEPAY_PRECONDITION_ERROR; - - if (!amount_msat_sub(&h->htlc_total, h->htlc_total, amount)) - return RENEPAY_AMOUNT_OVERFLOW; - h->num_htlcs--; - return RENEPAY_NOERROR; -} - -enum renepay_errorcode -chan_extra_commit_htlc(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat amount) -{ - struct chan_extra_half *h = - get_chan_extra_half_by_scid(chan_extra_map, scidd); - if (!h) - return RENEPAY_CHANNEL_NOT_FOUND; - if (!amount_msat_accumulate(&h->htlc_total, amount)) - return RENEPAY_AMOUNT_OVERFLOW; - h->num_htlcs++; - return RENEPAY_NOERROR; -} diff --git a/plugins/renepay/chan_extra.h b/plugins/renepay/chan_extra.h deleted file mode 100644 index d7213ad52cad..000000000000 --- a/plugins/renepay/chan_extra.h +++ /dev/null @@ -1,210 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_CHAN_EXTRA_H -#define LIGHTNING_PLUGINS_RENEPAY_CHAN_EXTRA_H - -#include "config.h" -#include -#include -#include -#include -#include - -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) - -/* Any implementation needs to keep some data on channels which are - * in-use (or about which we have extra information). We use a hash - * table here, since most channels are not in use. */ -// TODO(eduardo): if we know the liquidity of channel (X,dir) is [A,B] -// then we also know that the liquidity of channel (X,!dir) is [Cap-B,Cap-A]. -// This means that it is redundant to store known_min and known_max for both -// halves of the channel and it also means that once we update the knowledge of -// (X,dir) the knowledge of (X,!dir) is updated as well. -struct chan_extra { - struct short_channel_id scid; - struct amount_msat capacity; - - struct chan_extra_half { - /* How many htlcs we've directed through it */ - size_t num_htlcs; - - /* The total size of those HTLCs */ - struct amount_msat htlc_total; - - /* The known minimum / maximum capacity (if nothing known, - * 0/capacity */ - struct amount_msat known_min, known_max; - } half[2]; -}; - -bool chan_extra_is_busy(const struct chan_extra *const ce); - -static inline const struct short_channel_id -chan_extra_scid(const struct chan_extra *cd) -{ - return cd->scid; -} - -static inline bool chan_extra_eq_scid(const struct chan_extra *cd, - const struct short_channel_id scid) -{ - return short_channel_id_eq(scid, cd->scid); -} - -HTABLE_DEFINE_NODUPS_TYPE(struct chan_extra, chan_extra_scid, short_channel_id_hash, - chan_extra_eq_scid, - chan_extra_map); - -/* Helpers for chan_extra_map */ -/* Channel knowledge invariants: - * - * 0<=a<=b<=capacity - * - * a_inv = capacity-b - * b_inv = capacity-a - * - * where a,b are the known minimum and maximum liquidities, and a_inv and b_inv - * are the known minimum and maximum liquidities for the channel in the opposite - * direction. - * - * Knowledge update operations can be: - * - * 1. set liquidity (x) - * (a,b) -> (x,x) - * - * The entropy is minimum here (=0). - * - * 2. can send (x): - * xb = min(x,capacity) - * (a,b) -> (max(a,xb),max(b,xb)) - * - * If x<=a then there is no new knowledge and the entropy remains - * the same. - * If x>a the entropy decreases. - * - * - * 3. can't send (x): - * xb = max(0,x-1) - * (a,b) -> (min(a,xb),min(b,xb)) - * - * If x>b there is no new knowledge and the entropy remains. - * If x<=b then the entropy decreases. - * - * 4. sent success (x): - * (a,b) -> (max(0,a-x),max(0,b-x)) - * - * If x<=a there is no new knowledge and the entropy remains. - * If a (max(0,a-x),min(capacity,b+y)) - * - * Entropy increases unless it is already maximum. - * */ - -const char *fmt_chan_extra_map(const tal_t *ctx, - struct chan_extra_map *chan_extra_map); - -/* Returns "" if nothing useful known about channel, otherwise - * "(details)" */ -const char *fmt_chan_extra_details(const tal_t *ctx, - const struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd); - -/* Creates a new chan_extra and adds it to the chan_extra_map. */ -struct chan_extra *new_chan_extra(struct chan_extra_map *chan_extra_map, - const struct short_channel_id scid, - struct amount_msat capacity); - -/* Update the knowledge that this (channel,direction) can send x msat.*/ -enum renepay_errorcode -chan_extra_can_send(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd); - -/* Update the knowledge that this (channel,direction) cannot send x msat.*/ -enum renepay_errorcode -chan_extra_cannot_send(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd); - -enum renepay_errorcode -chan_extra_remove_htlc(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat amount); - -enum renepay_errorcode -chan_extra_commit_htlc(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat amount); - - -/* Update the knowledge that this (channel,direction) has liquidity x.*/ -enum renepay_errorcode -chan_extra_set_liquidity(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat min, - struct amount_msat max); - -/* Update the knowledge that this (channel,direction) has sent x msat.*/ -enum renepay_errorcode -chan_extra_sent_success(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd, - struct amount_msat x); - -/* Forget the channel information by a fraction of the capacity. */ -enum renepay_errorcode chan_extra_relax_fraction(struct chan_extra *ce, - double fraction); - -/* Returns either NULL, or an entry from the hash */ -struct chan_extra_half * -get_chan_extra_half_by_scid(struct chan_extra_map *chan_extra_map, - const struct short_channel_id_dir *scidd); -/* If the channel is not registered, then a new entry is created. scid must be - * present in the gossmap. */ -struct chan_extra_half * -get_chan_extra_half_by_chan_verify(const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, int dir); - -/* Helper if we have a gossmap_chan */ -struct chan_extra_half * -get_chan_extra_half_by_chan(const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, int dir); - -/* Based on the knowledge that we have and HTLCs, returns the greatest - * amount that we can send through this channel. */ -enum renepay_errorcode channel_liquidity(struct amount_msat *liquidity, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - const struct gossmap_chan *chan, - const int dir); - -/* inputs - * @chan: a channel - * @recv: how much can we send to this channels - * - * output - * @max_forward: how much can we ask this channel to forward to the next hop - * */ -enum renepay_errorcode channel_maximum_forward(struct amount_msat *max_forward, - const struct gossmap_chan *chan, - const int dir, - struct amount_msat recv); - -/* Assume a uniform distribution: - * @min, @max: the bounds of liquidity - * @in_flight: htlcs - * - * @f: the amount we want to forward - * - * returns the probability that this forward request gets through. - * */ -double edge_probability(struct amount_msat min, struct amount_msat max, - struct amount_msat in_flight, struct amount_msat f); - -/* Checks BOLT 7 HTLC fee condition: - * recv >= base_fee + (send*proportional_fee)/1000000 */ -bool check_fee_inequality(struct amount_msat recv, struct amount_msat send, - u64 base_fee, u64 proportional_fee); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_CHAN_EXTRA_H */ diff --git a/plugins/renepay/dijkstra.c b/plugins/renepay/dijkstra.c deleted file mode 100644 index db41ebaf902d..000000000000 --- a/plugins/renepay/dijkstra.c +++ /dev/null @@ -1,186 +0,0 @@ -#define NDEBUG 1 -#include "config.h" -#include - -/* In the heap we keep node idx, but in this structure we keep the distance - * value associated to every node, and their position in the heap as a pointer - * so that we can update the nodes inside the heap when the distance label is - * changed. - * - * Therefore this is no longer a multipurpose heap, the node_idx must be an - * index between 0 and less than max_num_nodes. */ -struct dijkstra { - // - s64 *distance; - u32 *base; - u32 **heapptr; - size_t heapsize; - struct gheap_ctx gheap_ctx; -}; - -static const s64 INFINITE = INT64_MAX; - -/* Required a global dijkstra for gheap. */ -static struct dijkstra *global_dijkstra; - -/* The heap comparer for Dijkstra search. Since the top element must be the one - * with the smallest distance, we use the operator >, rather than <. */ -static int dijkstra_less_comparer( - const void *const ctx UNUSED, - const void *const a, - const void *const b) -{ - return global_dijkstra->distance[*(u32*)a] - > global_dijkstra->distance[*(u32*)b]; -} - -/* The heap move operator for Dijkstra search. */ -static void dijkstra_item_mover(void *const dst, const void *const src) -{ - u32 src_idx = *(u32*)src; - *(u32*)dst = src_idx; - - // we keep track of the pointer position of each element in the heap, - // for easy update. - global_dijkstra->heapptr[src_idx] = dst; -} - -/* Allocation of resources for the heap. */ -struct dijkstra *dijkstra_new(const tal_t *ctx, size_t max_num_nodes) -{ - struct dijkstra *dijkstra = tal(ctx, struct dijkstra); - - dijkstra->distance = tal_arr(dijkstra,s64,max_num_nodes); - dijkstra->base = tal_arr(dijkstra,u32,max_num_nodes); - dijkstra->heapptr = tal_arrz(dijkstra,u32*,max_num_nodes); - - dijkstra->heapsize=0; - - dijkstra->gheap_ctx.fanout=2; - dijkstra->gheap_ctx.page_chunks=1024; - dijkstra->gheap_ctx.item_size=sizeof(dijkstra->base[0]); - dijkstra->gheap_ctx.less_comparer=dijkstra_less_comparer; - dijkstra->gheap_ctx.less_comparer_ctx=NULL; - dijkstra->gheap_ctx.item_mover=dijkstra_item_mover; - - return dijkstra; -} - - -void dijkstra_init(struct dijkstra *dijkstra) -{ - const size_t max_num_nodes = tal_count(dijkstra->distance); - dijkstra->heapsize=0; - for(size_t i=0;idistance[i]=INFINITE; - dijkstra->heapptr[i] = NULL; - } -} -size_t dijkstra_size(const struct dijkstra *dijkstra) -{ - return dijkstra->heapsize; -} - -size_t dijkstra_maxsize(const struct dijkstra *dijkstra) -{ - return tal_count(dijkstra->distance); -} - -static void dijkstra_append(struct dijkstra *dijkstra, u32 node_idx, s64 distance) -{ - assert(dijkstra_size(dijkstra) < dijkstra_maxsize(dijkstra)); - assert(node_idx < dijkstra_maxsize(dijkstra)); - - const size_t pos = dijkstra->heapsize; - - dijkstra->base[pos]=node_idx; - dijkstra->distance[node_idx]=distance; - dijkstra->heapptr[node_idx] = &(dijkstra->base[pos]); - dijkstra->heapsize++; -} - -void dijkstra_update(struct dijkstra *dijkstra, u32 node_idx, s64 distance) -{ - assert(node_idx < dijkstra_maxsize(dijkstra)); - - if(!dijkstra->heapptr[node_idx]) - { - // not in the heap - dijkstra_append(dijkstra, node_idx,distance); - global_dijkstra = dijkstra; - gheap_restore_heap_after_item_increase( - &dijkstra->gheap_ctx, - dijkstra->base, - dijkstra->heapsize, - dijkstra->heapptr[node_idx] - - dijkstra->base); - global_dijkstra = NULL; - return; - } - - if(dijkstra->distance[node_idx] > distance) - { - // distance decrease - dijkstra->distance[node_idx] = distance; - - global_dijkstra = dijkstra; - gheap_restore_heap_after_item_increase( - &dijkstra->gheap_ctx, - dijkstra->base, - dijkstra->heapsize, - dijkstra->heapptr[node_idx] - - dijkstra->base); - global_dijkstra = NULL; - }else - { - // distance increase - dijkstra->distance[node_idx] = distance; - - global_dijkstra = dijkstra; - gheap_restore_heap_after_item_decrease( - &dijkstra->gheap_ctx, - dijkstra->base, - dijkstra->heapsize, - dijkstra->heapptr[node_idx] - - dijkstra->base); - global_dijkstra = NULL; - - } - // assert(gheap_is_heap(&dijkstra->gheap_ctx, - // dijkstra->base, - // dijkstra_size())); -} - -u32 dijkstra_top(const struct dijkstra *dijkstra) -{ - return dijkstra->base[0]; -} - -bool dijkstra_empty(const struct dijkstra *dijkstra) -{ - return dijkstra->heapsize==0; -} - -void dijkstra_pop(struct dijkstra *dijkstra) -{ - if(dijkstra->heapsize==0) - return; - - const u32 top = dijkstra_top(dijkstra); - assert(dijkstra->heapptr[top]==dijkstra->base); - - global_dijkstra = dijkstra; - gheap_pop_heap( - &dijkstra->gheap_ctx, - dijkstra->base, - dijkstra->heapsize--); - global_dijkstra = NULL; - - dijkstra->heapptr[top]=NULL; -} - -const s64* dijkstra_distance_data(const struct dijkstra *dijkstra) -{ - return dijkstra->distance; -} diff --git a/plugins/renepay/dijkstra.h b/plugins/renepay/dijkstra.h deleted file mode 100644 index bfff1d1dd288..000000000000 --- a/plugins/renepay/dijkstra.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H -#define LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H -#include "config.h" -#include -#include -#include - -/* Allocation of resources for the heap. */ -struct dijkstra *dijkstra_new(const tal_t *ctx, size_t max_num_nodes); - -/* Initialization of the heap for a new Dijkstra search. */ -void dijkstra_init(struct dijkstra *dijkstra); - -/* Inserts a new element in the heap. If node_idx was already in the heap then - * its distance value is updated. */ -void dijkstra_update(struct dijkstra *dijkstra, u32 node_idx, s64 distance); - -u32 dijkstra_top(const struct dijkstra *dijkstra); -bool dijkstra_empty(const struct dijkstra *dijkstra); -void dijkstra_pop(struct dijkstra *dijkstra); - -const s64* dijkstra_distance_data(const struct dijkstra *dijkstra); - -/* Number of elements on the heap. */ -size_t dijkstra_size(const struct dijkstra *dijkstra); - -/* Maximum number of elements the heap can host */ -size_t dijkstra_maxsize(const struct dijkstra *dijkstra); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H */ diff --git a/plugins/renepay/disabledmap.c b/plugins/renepay/disabledmap.c deleted file mode 100644 index 68492eced369..000000000000 --- a/plugins/renepay/disabledmap.c +++ /dev/null @@ -1,125 +0,0 @@ -#include "config.h" -#include - -struct disabledmap *disabledmap_new(const tal_t *ctx) -{ - struct disabledmap *obj = tal(ctx, struct disabledmap); - if (!obj) - return NULL; - - obj->disabled_map = tal(obj, struct scidd_map); - obj->warned_map = tal(obj, struct scidd_map); - obj->disabled_ctx = tal(obj, tal_t); - obj->warned_ctx = tal(obj, tal_t); - obj->disabled_nodes = tal_arr(obj, struct node_id, 0); - - if (!obj->disabled_map || !obj->warned_map || !obj->disabled_nodes || - !obj->disabled_ctx || !obj->warned_ctx) - return tal_free(obj); - - scidd_map_init(obj->disabled_map); - scidd_map_init(obj->warned_map); - return obj; -} - -// FIXME: check success -void disabledmap_reset(struct disabledmap *p) -{ - /* This will remove and free every element in the maps. */ - p->warned_ctx = tal_free(p->warned_ctx); - p->disabled_ctx = tal_free(p->disabled_ctx); - - tal_resize(&p->disabled_nodes, 0); - - p->disabled_ctx = tal(p, tal_t); - p->warned_ctx = tal(p, tal_t); -} - -static void remove_scidd(struct short_channel_id_dir *scidd, - struct scidd_map *map) -{ - scidd_map_del(map, scidd); -} - -// FIXME: check success -void disabledmap_add_channel(struct disabledmap *p, - struct short_channel_id_dir scidd) -{ - struct short_channel_id_dir *ptr_scidd = - scidd_map_get(p->disabled_map, scidd); - if (ptr_scidd) { - /* htable allows for duplicates, but we don't want duplicates. - */ - return; - } - ptr_scidd = - tal_dup(p->disabled_ctx, struct short_channel_id_dir, &scidd); - scidd_map_add(p->disabled_map, ptr_scidd); - tal_add_destructor2(ptr_scidd, remove_scidd, p->disabled_map); -} - -// FIXME: check success -void disabledmap_warn_channel(struct disabledmap *p, - struct short_channel_id_dir scidd) -{ - struct short_channel_id_dir *ptr_scidd = - scidd_map_get(p->warned_map, scidd); - if (ptr_scidd) { - /* htable allows for duplicates, but we don't want duplicates. - */ - return; - } - ptr_scidd = tal_dup(p->warned_ctx, struct short_channel_id_dir, &scidd); - scidd_map_add(p->warned_map, ptr_scidd); - tal_add_destructor2(ptr_scidd, remove_scidd, p->warned_map); -} - -// FIXME: check success -void disabledmap_add_node(struct disabledmap *p, struct node_id node) -{ - tal_arr_expand(&p->disabled_nodes, node); -} - -bool disabledmap_channel_is_warned(struct disabledmap *p, - struct short_channel_id_dir scidd) -{ - return scidd_map_get(p->warned_map, scidd) != NULL; -} - -bitmap *tal_disabledmap_get_bitmap(const tal_t *ctx, struct disabledmap *p, - const struct gossmap *gossmap) -{ - bitmap *disabled = tal_arrz( - ctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); - if (!disabled) - return NULL; - - /* Disable every channel in the list of disabled scids. */ - struct scidd_map_iter it; - for(struct short_channel_id_dir *scidd = scidd_map_first(p->disabled_map,&it); - scidd; - scidd = scidd_map_next(p->disabled_map, &it)){ - - struct gossmap_chan *c = - gossmap_find_chan(gossmap, &scidd->scid); - if (c) - bitmap_set_bit(disabled, - gossmap_chan_idx(gossmap, c) * 2 + - scidd->dir); - } - - /* Disable all channels that lead to a disabled node. */ - for (size_t i = 0; i < tal_count(p->disabled_nodes); i++) { - const struct gossmap_node *node = - gossmap_find_node(gossmap, &p->disabled_nodes[i]); - - for (size_t j = 0; j < node->num_chans; j++) { - int half; - const struct gossmap_chan *c = - gossmap_nth_chan(gossmap, node, j, &half); - bitmap_set_bit(disabled, - gossmap_chan_idx(gossmap, c) * 2 + half); - } - } - return disabled; -} diff --git a/plugins/renepay/disabledmap.h b/plugins/renepay/disabledmap.h deleted file mode 100644 index 7bd0487bd23d..000000000000 --- a/plugins/renepay/disabledmap.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_DISABLEDMAP_H -#define LIGHTNING_PLUGINS_RENEPAY_DISABLEDMAP_H - -#include "config.h" -#include -#include -#include -#include -#include - -static inline size_t hash_scidd(const struct short_channel_id_dir scidd) -{ - /* scids cost money to generate, so simple hash works here. Letting same - * scid with two directions collide. */ - return (scidd.scid.u64 >> 32) ^ (scidd.scid.u64 >> 16) ^ scidd.scid.u64; -} - -static inline struct short_channel_id_dir -self_scidd(const struct short_channel_id_dir *self) -{ - return *self; -} - -static inline bool -my_short_channel_id_dir_eq(const struct short_channel_id_dir *scidd_a, - const struct short_channel_id_dir scidd_b) -{ - return short_channel_id_eq(scidd_a->scid, scidd_b.scid) && - scidd_a->dir == scidd_b.dir; -} - -/* A htable for short_channel_id_dir, the structure itself is the element key. - */ -HTABLE_DEFINE_NODUPS_TYPE(struct short_channel_id_dir, self_scidd, hash_scidd, - my_short_channel_id_dir_eq, scidd_map); - -struct disabledmap { - /* Channels we decided to disable for various reasons. */ - struct scidd_map *disabled_map; - tal_t *disabled_ctx; - - /* Channels that we flagged for failures. If warned two times we will - * disable it. */ - struct scidd_map *warned_map; - tal_t *warned_ctx; - - /* nodes we disable */ - // FIXME: use a map also for nodes - struct node_id *disabled_nodes; -}; - -void disabledmap_reset(struct disabledmap *p); -struct disabledmap *disabledmap_new(const tal_t *ctx); -void disabledmap_add_channel(struct disabledmap *p, - struct short_channel_id_dir scidd); -void disabledmap_warn_channel(struct disabledmap *p, - struct short_channel_id_dir scidd); -void disabledmap_add_node(struct disabledmap *p, struct node_id node); -bool disabledmap_channel_is_warned(struct disabledmap *p, - struct short_channel_id_dir scidd); -bitmap *tal_disabledmap_get_bitmap(const tal_t *ctx, struct disabledmap *p, - const struct gossmap *gossmap); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_DISABLEDMAP_H */ diff --git a/plugins/renepay/flow.c b/plugins/renepay/flow.c deleted file mode 100644 index 6d4d25aa1ccd..000000000000 --- a/plugins/renepay/flow.c +++ /dev/null @@ -1,489 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#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 diff --git a/plugins/renepay/flow.h b/plugins/renepay/flow.h deleted file mode 100644 index 959eb42f3f64..000000000000 --- a/plugins/renepay/flow.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_FLOW_H -#define LIGHTNING_PLUGINS_RENEPAY_FLOW_H -#include "config.h" -#include -#include -#include -#include -#include -#include - -/* An actual partial flow. */ -struct flow { - const struct gossmap_chan **path; - /* The directions to traverse. */ - int *dirs; - /* Amounts for this flow (fees mean this shrinks across path). */ - double success_prob; - struct amount_msat amount; -}; - -const char *fmt_flows(const tal_t *ctx, const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - struct flow **flows); - -/* Helper to access the half chan at flow index idx */ -const struct half_chan *flow_edge(const struct flow *flow, size_t idx); - -/* A big number, meaning "don't bother" (not infinite, since you may add) */ -#define FLOW_INF_COST 100000000.0 - -/* Cost function to send @f msat through @c in direction @dir, - * given we already have a flow of prev_flow. */ -double flow_edge_cost(const struct gossmap *gossmap, - const struct gossmap_chan *c, int dir, - const struct amount_msat known_min, - const struct amount_msat known_max, - struct amount_msat prev_flow, - struct amount_msat f, - double mu, - double basefee_penalty, - double delay_riskfactor); - -/* Compute the prob. of success of a set of concurrent set of flows. */ -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); - -/* How much do we need to send to make this flow arrive. */ -bool flow_spend(struct amount_msat *ret, struct flow *flow); - -/* How much do we pay in fees to make this flow arrive. */ -bool flow_fee(struct amount_msat *ret, struct flow *flow); - -bool flowset_fee(struct amount_msat *fee, struct flow **flows); - -bool flowset_delivers(struct amount_msat *delivers, struct flow **flows); - -/* how many channels are being used */ -size_t flowset_size(struct flow **flows); - -static inline struct amount_msat flow_delivers(const struct flow *flow) -{ - return flow->amount; -} - -struct amount_msat *tal_flow_amounts(const tal_t *ctx, const struct flow *flow, - bool compute_fees); - -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); - -/* 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); - -double flow_probability(struct flow *flow, const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - bool compute_fees); - -u64 flow_delay(const struct flow *flow); -u64 flows_worst_delay(struct flow **flows); - -struct flow ** -flows_ensure_liquidity_constraints(const tal_t *ctx, struct flow **flows TAKES, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_FLOW_H */ diff --git a/plugins/renepay/json.c b/plugins/renepay/json.c index e48388fb308e..eb63ea6acf42 100644 --- a/plugins/renepay/json.c +++ b/plugins/renepay/json.c @@ -393,3 +393,58 @@ void json_myadd_blinded_path(struct json_stream *s, json_array_end(s); json_object_end(s); } + +bool json_to_myroute(const char *buf, + const jsmntok_t *tok, + struct route *route) +{ + u64 probability_ppm; + u32 final_delay; + const char *err = json_scan( + tmpctx, buf, tok, "{probability_ppm:%,amount_msat:%,final_cltv:%}", + JSON_SCAN(json_to_u64, &probability_ppm), + JSON_SCAN(json_to_msat, &route->amount_deliver), + JSON_SCAN(json_to_u32, &final_delay)); + + if (err) + return false; + route->success_prob = probability_ppm * 1e-6; + const jsmntok_t *pathtok = json_get_member(buf, tok, "path"); + if (!pathtok || pathtok->type != JSMN_ARRAY) + return false; + + assert(route->hops == NULL); + route->hops = tal_arr(route, struct route_hop, pathtok->size); + size_t i; + const jsmntok_t *hoptok; + json_for_each_arr(i, hoptok, pathtok) + { + struct route_hop *hop = &route->hops[i]; + struct short_channel_id_dir scidd; + struct amount_msat amount; + u32 delay; + err = json_scan(tmpctx, buf, hoptok, + "{short_channel_id_dir:%,next_node_id:%,amount_msat:%,delay:%}", + JSON_SCAN(json_to_short_channel_id_dir, &scidd), + JSON_SCAN(json_to_node_id, &hop->node_id), + JSON_SCAN(json_to_msat, &amount), + JSON_SCAN(json_to_u32, &delay)); + if (err) { + route->hops = tal_free(route->hops); + return false; + } + hop->scid = scidd.scid; + hop->direction = scidd.dir; + + /* FIXME: this convention is so weird. If we ever get to merge + * PR 7639, remember to remove this index adjustment. */ + if (i > 0) { + route->hops[i - 1].amount = amount; + route->hops[i - 1].delay = delay; + } + } + route->hops[i - 1].amount = route->amount_deliver; + route->hops[i - 1].delay = final_delay; + route->amount_sent = route->hops[0].amount; + return true; +} diff --git a/plugins/renepay/json.h b/plugins/renepay/json.h index 5abaa8348997..234c4f3daa74 100644 --- a/plugins/renepay/json.h +++ b/plugins/renepay/json.h @@ -25,4 +25,8 @@ void json_myadd_blinded_path(struct json_stream *s, const char *fieldname, const struct blinded_path *blinded_path); +bool json_to_myroute(const char *buf, + const jsmntok_t *tok, + struct route *route); + #endif /* LIGHTNING_PLUGINS_RENEPAY_JSON_H */ diff --git a/plugins/renepay/mcf.c b/plugins/renepay/mcf.c deleted file mode 100644 index 7d2f1490300a..000000000000 --- a/plugins/renepay/mcf.c +++ /dev/null @@ -1,1860 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* # Optimal payments - * - * In this module we reduce the routing optimization problem to a linear - * cost optimization problem and find a solution using MCF algorithms. - * The optimization of the routing itself doesn't need a precise numerical - * solution, since we can be happy near optimal results; e.g. paying 100 msat or - * 101 msat for fees doesn't make any difference if we wish to deliver 1M sats. - * On the other hand, we are now also considering Pickhard's - * [1] model to improve payment reliability, - * hence our optimization moves to a 2D space: either we like to maximize the - * probability of success of a payment or minimize the routing fees, or - * alternatively we construct a function of the two that gives a good compromise. - * - * Therefore from now own, the definition of optimal is a matter of choice. - * To simplify the API of this module, we think the best way to state the - * problem is: - * - * Find a routing solution that pays the least of fees while keeping - * the probability of success above a certain value `min_probability`. - * - * - * # Fee Cost - * - * Routing fees is non-linear function of the payment flow x, that's true even - * without the base fee: - * - * fee_msat = base_msat + floor(millionths*x_msat / 10^6) - * - * We approximate this fee into a linear function by computing a slope `c_fee` such - * that: - * - * fee_microsat = c_fee * x_sat - * - * Function `linear_fee_cost` computes `c_fee` based on the base and - * proportional fees of a channel. - * The final product if microsat because if only - * the proportional fee was considered we can have c_fee = millionths. - * Moving to costs based in msats means we have to either truncate payments - * below 1ksats or estimate as 0 cost for channels with less than 1000ppm. - * - * TODO(eduardo): shall we build a linear cost function in msats? - * - * # Probability cost - * - * The probability of success P of the payment is the product of the prob. of - * success of forwarding parts of the payment over all routing channels. This - * problem is separable if we log it, and since we would like to increase P, - * then we can seek to minimize -log(P), and that's our prob. cost function [1]. - * - * - log P = sum_{i} - log P_i - * - * The probability of success `P_i` of sending some flow `x` on a channel with - * liquidity l in the range a<=l a - * = 1. ; for x <= a - * - * Notice that unlike the similar formula in [1], the one we propose does not - * contain the quantization shot noise for counting states. The formula remains - * valid independently of the liquidity units (sats or msats). - * - * The cost associated to probability P is then -k log P, where k is some - * constant. For k=1 we get the following table: - * - * prob | cost - * ----------- - * 0.01 | 4.6 - * 0.02 | 3.9 - * 0.05 | 3.0 - * 0.10 | 2.3 - * 0.20 | 1.6 - * 0.50 | 0.69 - * 0.80 | 0.22 - * 0.90 | 0.10 - * 0.95 | 0.05 - * 0.98 | 0.02 - * 0.99 | 0.01 - * - * Clearly -log P(x) is non-linear; we try to linearize it piecewise: - * split the channel into 4 arcs representing 4 liquidity regions: - * - * arc_0 -> [0, a) - * arc_1 -> [a, a+(b-a)*f1) - * arc_2 -> [a+(b-a)*f1, a+(b-a)*f2) - * arc_3 -> [a+(b-a)*f2, a+(b-a)*f3) - * - * where f1 = 0.5, f2 = 0.8, f3 = 0.95; - * We fill arc_0's capacity with complete certainty P=1, then if more flow is - * needed we start filling the capacity in arc_1 until the total probability - * of success reaches P=0.5, then arc_2 until P=1-0.8=0.2, and finally arc_3 until - * P=1-0.95=0.05. We don't go further than 5% prob. of success per channel. - - * TODO(eduardo): this channel linearization is hard coded into - * `CHANNEL_PIVOTS`, maybe we can parametrize this to take values from the config file. - * - * With this choice, the slope of the linear cost function becomes: - * - * m_0 = 0 - * m_1 = 1.38 k /(b-a) - * m_2 = 3.05 k /(b-a) - * m_3 = 9.24 k /(b-a) - * - * Notice that one of the assumptions in [2] for the MCF problem is that flows - * and the slope of the costs functions are integer numbers. The only way we - * have at hand to make it so, is to choose a universal value of `k` that scales - * up the slopes so that floor(m_i) is not zero for every arc. - * - * # Combine fee and prob. costs - * - * We attempt to solve the original problem of finding the solution that - * pays the least fees while keeping the prob. of success above a certain value, - * by constructing a cost function which is a linear combination of fee and - * prob. costs. - * TODO(eduardo): investigate how this procedure is justified, - * possibly with the use of Lagrange optimization theory. - * - * At first, prob. and fee costs live in different dimensions, they cannot be - * summed, it's like comparing apples and oranges. - * 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 - * - * # References - * - * [1] Pickhardt and Richter, https://arxiv.org/abs/2107.05322 - * [2] R.K. Ahuja, T.L. Magnanti, and J.B. Orlin. Network Flows: - * Theory, Algorithms, and Applications. Prentice Hall, 1993. - * - * - * TODO(eduardo) it would be interesting to see: - * how much do we pay for reliability? - * Cost_fee(most reliable solution) - Cost_fee(cheapest solution) - * - * TODO(eduardo): it would be interesting to see: - * how likely is the most reliable path with respect to the cheapest? - * Prob(reliable)/Prob(cheapest) = Exp(Cost_prob(cheapest)-Cost_prob(reliable)) - * - * */ - -#define PARTS_BITS 2 -#define CHANNEL_PARTS (1 << PARTS_BITS) - -// These are the probability intervals we use to decompose a channel into linear -// cost function arcs. -static const double CHANNEL_PIVOTS[]={0,0.5,0.8,0.95}; - -static const s64 INFINITE = INT64_MAX; -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: - * Each channel `c` has two possible directions identified by a bit - * `half` or `!half`, and each one of them has to be - * decomposed into 4 liquidity parts in order to - * linearize the cost function, but also to solve MCF - * problem we need to keep track of flows in the - * residual network hence we need for each directed arc - * in the network there must be another arc in the - * opposite direction refered to as it's dual. In total - * 1+2+1 additional bits of information: - * - * (chan_idx)(half)(part)(dual) - * - * That means, for each channel we need to store the - * information of 16 arcs. If we implement a convex-cost - * solver then we can reduce that number to size(half)size(dual)=4. - * - * In the adjacency of a `node` we are going to store - * the outgoing arcs. If we ever need to loop over the - * incoming arcs then we will define a reverse adjacency - * API. - * Then for each outgoing channel `(c,half)` there will - * be 4 parts for the actual residual capacity, hence - * with the dual bit set to 0: - * - * (c,half,0,0) - * (c,half,1,0) - * (c,half,2,0) - * (c,half,3,0) - * - * and also we need to consider the dual arcs - * corresponding to the channel direction `(c,!half)` - * (the dual has reverse direction): - * - * (c,!half,0,1) - * (c,!half,1,1) - * (c,!half,2,1) - * (c,!half,3,1) - * - * These are the 8 outgoing arcs relative to `node` and - * associated with channel `c`. The incoming arcs will - * be: - * - * (c,!half,0,0) - * (c,!half,1,0) - * (c,!half,2,0) - * (c,!half,3,0) - * - * (c,half,0,1) - * (c,half,1,1) - * (c,half,2,1) - * (c,half,3,1) - * - * but they will be stored as outgoing arcs on the peer - * node `next`. - * - * I hope this will clarify my future self when I forget. - * - * */ - -/* - * We want to use the whole number here for convenience, but - * we can't us a union, since bit order is implementation-defined and - * we want chanidx on the highest bits: - * - * [ 0 1 2 3 4 5 6 ... 31 ] - * dual part chandir chanidx - */ -struct arc { - u32 idx; -}; - -#define ARC_DUAL_BITOFF (0) -#define ARC_PART_BITOFF (1) -#define ARC_CHANDIR_BITOFF (1 + PARTS_BITS) -#define ARC_CHANIDX_BITOFF (1 + PARTS_BITS + 1) -#define ARC_CHANIDX_BITS (32 - ARC_CHANIDX_BITOFF) - -/* How many arcs can we have for a single channel? - * linearization parts, both directions, and dual */ -#define ARCS_PER_CHANNEL ((size_t)1 << (PARTS_BITS + 1 + 1)) - -static inline void arc_to_parts(struct arc arc, - u32 *chanidx, - int *chandir, - u32 *part, - bool *dual) -{ - if (chanidx) - *chanidx = (arc.idx >> ARC_CHANIDX_BITOFF); - if (chandir) - *chandir = (arc.idx >> ARC_CHANDIR_BITOFF) & 1; - if (part) - *part = (arc.idx >> ARC_PART_BITOFF) & ((1 << PARTS_BITS)-1); - if (dual) - *dual = (arc.idx >> ARC_DUAL_BITOFF) & 1; -} - -static inline struct arc arc_from_parts(u32 chanidx, int chandir, u32 part, bool dual) -{ - struct arc arc; - - assert(part < CHANNEL_PARTS); - assert(chandir == 0 || chandir == 1); - assert(chanidx < (1U << ARC_CHANIDX_BITS)); - arc.idx = ((u32)dual << ARC_DUAL_BITOFF) - | (part << ARC_PART_BITOFF) - | ((u32)chandir << ARC_CHANDIR_BITOFF) - | (chanidx << ARC_CHANIDX_BITOFF); - return arc; -} - -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) - -struct pay_parameters { - /* The gossmap we are using */ - struct gossmap *gossmap; - const struct gossmap_node *source; - const struct gossmap_node *target; - - /* Extra information we intuited about the channels */ - struct chan_extra_map *chan_extra_map; - - /* Optional bitarray of disabled channels. */ - const bitmap *disabled; - - // how much we pay - struct amount_msat amount; - - // channel linearization parameters - double cap_fraction[CHANNEL_PARTS], - cost_fraction[CHANNEL_PARTS]; - - struct amount_msat max_fee; - double min_probability; - double base_probability; - double delay_feefactor; - double base_fee_penalty; - u32 prob_cost_factor; -}; - -/* Representation of the linear MCF network. - * This contains the topology of the extended network (after linearization and - * addition of arc duality). - * This contains also the arc probability and linear fee cost, as well as - * capacity; these quantities remain constant during MCF execution. */ -struct linear_network -{ - u32 *arc_tail_node; - // notice that a tail node is not needed, - // because the tail of arc is the head of dual(arc) - - struct arc *node_adjacency_next_arc; - struct arc *node_adjacency_first_arc; - - // probability and fee cost associated to an arc - s64 *arc_prob_cost, *arc_fee_cost; - s64 *capacity; - - size_t max_num_arcs,max_num_nodes; -}; - -/* This is the structure that keeps track of the network properties while we - * seek for a solution. */ -struct residual_network { - /* residual capacity on arcs */ - s64 *cap; - - /* some combination of prob. cost and fee cost on arcs */ - s64 *cost; - - /* potential function on nodes */ - s64 *potential; -}; - -/* Helper function. - * Given an arc idx, return the dual's idx in the residual network. */ -static struct arc arc_dual(struct arc arc) -{ - arc.idx ^= (1U << ARC_DUAL_BITOFF); - return arc; -} -/* Helper function. */ -static bool arc_is_dual(struct arc arc) -{ - bool dual; - arc_to_parts(arc, NULL, NULL, NULL, &dual); - return dual; -} - -/* Helper function. - * Given an arc of the network (not residual) give me the flow. */ -static s64 get_arc_flow( - const struct residual_network *network, - const struct arc arc) -{ - assert(!arc_is_dual(arc)); - assert(arc_dual(arc).idx < tal_count(network->cap)); - return network->cap[ arc_dual(arc).idx ]; -} - -/* Helper function. - * Given an arc idx, return the node from which this arc emanates in the residual network. */ -static u32 arc_tail(const struct linear_network *linear_network, - const struct arc arc) -{ - assert(arc.idx < tal_count(linear_network->arc_tail_node)); - return linear_network->arc_tail_node[ arc.idx ]; -} -/* Helper function. - * Given an arc idx, return the node that this arc is pointing to in the residual network. */ -static u32 arc_head(const struct linear_network *linear_network, - const struct arc arc) -{ - const struct arc dual = arc_dual(arc); - assert(dual.idx < tal_count(linear_network->arc_tail_node)); - return linear_network->arc_tail_node[dual.idx]; -} - -/* Helper function. - * Given node idx `node`, return the idx of the first arc whose tail is `node`. - * */ -static struct arc node_adjacency_begin( - const struct linear_network * linear_network, - const u32 node) -{ - assert(node < tal_count(linear_network->node_adjacency_first_arc)); - return linear_network->node_adjacency_first_arc[node]; -} - -/* Helper function. - * Is this the end of the adjacency list. */ -static bool node_adjacency_end(const struct arc arc) -{ - return arc.idx == INVALID_INDEX; -} - -/* Helper function. - * Given node idx `node` and `arc`, returns the idx of the next arc whose tail is `node`. */ -static struct arc node_adjacency_next( - const struct linear_network *linear_network, - const struct arc arc) -{ - assert(arc.idx < tal_count(linear_network->node_adjacency_next_arc)); - return linear_network->node_adjacency_next_arc[arc.idx]; -} - -static bool channel_is_available(const struct gossmap_chan *c, int dir, - const struct gossmap *gossmap, - const bitmap *disabled) -{ - if (!gossmap_chan_set(c, dir)) - return false; - - const u32 chan_idx = gossmap_chan_idx(gossmap, c); - return !bitmap_test_bit(disabled, chan_idx * 2 + dir); -} - -// TODO(eduardo): unit test this -/* Split a directed channel into parts with linear cost function. */ -static bool linearize_channel(const struct pay_parameters *params, - const struct gossmap_chan *c, const int dir, - s64 *capacity, s64 *cost) -{ - struct chan_extra_half *extra_half = get_chan_extra_half_by_chan( - params->gossmap, - params->chan_extra_map, - c, - dir); - - if (!extra_half) { - return false; - } - - /* FIXME: this assertion has been reported to be triggered in issue - * #7535. A quick and dirty solution is to comment it and work-around - * this case. But in principle if we do things the right way we should - * not have htlc_total>known_max. The problem is likely to be - * asynchronous way in which reserved htlcs are removed and known_max is - * updated. */ - // assert( - // amount_msat_less_eq(extra_half->htlc_total, extra_half->known_max)); - assert( - amount_msat_less_eq(extra_half->known_min, extra_half->known_max)); - - s64 h = (extra_half->htlc_total.millisatoshis+999)/1000; /* Raw: linearize_channel */ - s64 a = extra_half->known_min.millisatoshis/1000, /* Raw: linearize_channel */ - b = 1 + extra_half->known_max.millisatoshis/1000; /* Raw: linearize_channel */ - - /* If HTLCs add up to more than the known_max it means we have a - * completely wrong knowledge. */ - // assert(ha it doesn't mean automatically that our - * known_min should have been updated, because we reserve this HTLC - * after sendpay behind the scenes it might happen that sendpay failed - * because of insufficient funds we haven't noticed yet. */ - // assert(h<=a); - - /* We reduce this channel capacity because HTLC are reserving liquidity. */ - a -= h; - b -= h; - a = MAX(a,0); - b = MAX(a+1,b); - - /* An extra bound on capacity, here we use it to reduce the flow such - * that it does not exceed htlcmax. */ - s64 cap_on_capacity = - gossmap_chan_htlc_max(c, dir).millisatoshis/1000; /* Raw: linearize_channel */ - - capacity[0]=a; - cost[0]=0; - assert(params->base_probability > 5e-7); - assert(params->base_probability <= 1.0); - const double base_prob_factor = -log(params->base_probability); - - for(size_t i=1;icap_fraction[i]*(b-a), cap_on_capacity); - cap_on_capacity -= capacity[i]; - assert(cap_on_capacity>=0); - - cost[i] = (params->cost_fraction[i]*1.0/(b-a) + base_prob_factor) - *params->amount.millisatoshis /* Raw: linearize_channel */ - *params->prob_cost_factor; - } - return true; -} - -static struct residual_network * -alloc_residual_network(const tal_t *ctx, const size_t max_num_nodes, - const size_t max_num_arcs) -{ - struct residual_network *residual_network = - tal(ctx, struct residual_network); - if (!residual_network) - goto function_fail; - - residual_network->cap = tal_arrz(residual_network, s64, max_num_arcs); - residual_network->cost = tal_arrz(residual_network, s64, max_num_arcs); - residual_network->potential = - tal_arrz(residual_network, s64, max_num_nodes); - - if (!residual_network->cap || !residual_network->cost || - !residual_network->potential) { - goto function_fail; - } - return residual_network; - - function_fail: - return tal_free(residual_network); -} - -static void init_residual_network( - const struct linear_network * linear_network, - struct residual_network* residual_network) -{ - const size_t max_num_arcs = linear_network->max_num_arcs; - const size_t max_num_nodes = linear_network->max_num_nodes; - - for(struct arc arc = {0};arc.idx < max_num_arcs; ++arc.idx) - { - if(arc_is_dual(arc)) - continue; - - struct arc dual = arc_dual(arc); - residual_network->cap[arc.idx]=linear_network->capacity[arc.idx]; - residual_network->cap[dual.idx]=0; - - residual_network->cost[arc.idx]=residual_network->cost[dual.idx]=0; - } - for(u32 i=0;ipotential[i]=0; - } -} - -static void combine_cost_function( - const struct linear_network* linear_network, - struct residual_network *residual_network, - s64 mu) -{ - for(struct arc arc = {0};arc.idx < linear_network->max_num_arcs; ++arc.idx) - { - if(arc_tail(linear_network,arc)==INVALID_INDEX) - continue; - - const s64 pcost = linear_network->arc_prob_cost[arc.idx], - fcost = linear_network->arc_fee_cost[arc.idx]; - - const s64 combined = pcost==INFINITE || fcost==INFINITE ? INFINITE : - mu*fcost + (MU_MAX-1-mu)*pcost; - - residual_network->cost[arc.idx] - = mu==0 ? pcost : - (mu==(MU_MAX-1) ? fcost : combined); - } -} - -static void linear_network_add_adjacenct_arc( - struct linear_network *linear_network, - const u32 node_idx, - const struct arc arc) -{ - assert(arc.idx < tal_count(linear_network->arc_tail_node)); - linear_network->arc_tail_node[arc.idx] = node_idx; - - assert(node_idx < tal_count(linear_network->node_adjacency_first_arc)); - const struct arc first_arc = linear_network->node_adjacency_first_arc[node_idx]; - - assert(arc.idx < tal_count(linear_network->node_adjacency_next_arc)); - linear_network->node_adjacency_next_arc[arc.idx]=first_arc; - - assert(node_idx < tal_count(linear_network->node_adjacency_first_arc)); - linear_network->node_adjacency_first_arc[node_idx]=arc; -} - -/* Get the fee cost associated to this directed channel. - * Cost is expressed as PPM of the payment. - * - * Choose and integer `c_fee` to linearize the following fee function - * - * fee_msat = base_msat + floor(millionths*x_msat / 10^6) - * - * into - * - * fee_microsat = c_fee * x_sat - * - * 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) -{ - 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; - - return pfee + bfee* base_fee_penalty+ delay*delay_feefactor; -} - -static struct linear_network * -init_linear_network(const tal_t *ctx, const struct pay_parameters *params, - char **fail) -{ - tal_t *this_ctx = tal(ctx,tal_t); - - struct linear_network * linear_network = tal(ctx, struct linear_network); - if (!linear_network) { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of linear_network"); - goto function_fail; - } - - const size_t max_num_chans = gossmap_max_chan_idx(params->gossmap); - const size_t max_num_arcs = max_num_chans * ARCS_PER_CHANNEL; - const size_t max_num_nodes = gossmap_max_node_idx(params->gossmap); - - linear_network->max_num_arcs = max_num_arcs; - linear_network->max_num_nodes = max_num_nodes; - - linear_network->arc_tail_node = tal_arr(linear_network,u32,max_num_arcs); - if(!linear_network->arc_tail_node) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of arc_tail_node"); - goto function_fail; - } - for(size_t i=0;iarc_tail_node);++i) - linear_network->arc_tail_node[i]=INVALID_INDEX; - - linear_network->node_adjacency_next_arc = tal_arr(linear_network,struct arc,max_num_arcs); - if(!linear_network->node_adjacency_next_arc) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of node_adjacency_next_arc"); - goto function_fail; - } - for(size_t i=0;inode_adjacency_next_arc);++i) - linear_network->node_adjacency_next_arc[i].idx=INVALID_INDEX; - - linear_network->node_adjacency_first_arc = tal_arr(linear_network,struct arc,max_num_nodes); - if(!linear_network->node_adjacency_first_arc) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of node_adjacency_first_arc"); - goto function_fail; - } - for(size_t i=0;inode_adjacency_first_arc);++i) - linear_network->node_adjacency_first_arc[i].idx=INVALID_INDEX; - - linear_network->arc_prob_cost = tal_arr(linear_network,s64,max_num_arcs); - if(!linear_network->arc_prob_cost) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of arc_prob_cost"); - goto function_fail; - } - for(size_t i=0;iarc_prob_cost);++i) - linear_network->arc_prob_cost[i]=INFINITE; - - linear_network->arc_fee_cost = tal_arr(linear_network,s64,max_num_arcs); - if(!linear_network->arc_fee_cost) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of arc_fee_cost"); - goto function_fail; - } - for(size_t i=0;iarc_fee_cost);++i) - linear_network->arc_fee_cost[i]=INFINITE; - - linear_network->capacity = tal_arrz(linear_network,s64,max_num_arcs); - if(!linear_network->capacity) - { - if (fail) - *fail = tal_fmt(ctx, "bad allocation of capacity"); - goto function_fail; - } - - for(struct gossmap_node *node = gossmap_first_node(params->gossmap); - node; - node=gossmap_next_node(params->gossmap,node)) - { - const u32 node_id = gossmap_node_idx(params->gossmap,node); - - for(size_t j=0;jnum_chans;++j) - { - int half; - const struct gossmap_chan *c = gossmap_nth_chan(params->gossmap, - node, j, &half); - - if (!channel_is_available(c, half, params->gossmap, - params->disabled)) - continue; - - const u32 chan_id = gossmap_chan_idx(params->gossmap, c); - - const struct gossmap_node *next = gossmap_nth_node(params->gossmap, - c,!half); - - const u32 next_id = gossmap_node_idx(params->gossmap,next); - - if(node_id==next_id) - continue; - - // `cost` is the word normally used to denote cost per - // unit of flow in the context of MCF. - s64 prob_cost[CHANNEL_PARTS], capacity[CHANNEL_PARTS]; - - // split this channel direction to obtain the arcs - // that are outgoing to `node` - if (!linearize_channel(params, c, half, - capacity, prob_cost)) { - if(fail) - *fail = - tal_fmt(ctx, "linearize_channel failed"); - goto function_fail; - } - - const s64 fee_cost = linear_fee_cost(c,half, - 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 - // when the `i` hits the `next` node. - for(size_t k=0;kcapacity[arc.idx] = capacity[k]; - linear_network->arc_prob_cost[arc.idx] = prob_cost[k]; - - linear_network->arc_fee_cost[arc.idx] = fee_cost; - - // + the respective dual - struct arc dual = arc_dual(arc); - - linear_network_add_adjacenct_arc(linear_network,next_id,dual); - - linear_network->capacity[dual.idx] = 0; - linear_network->arc_prob_cost[dual.idx] = -prob_cost[k]; - - linear_network->arc_fee_cost[dual.idx] = -fee_cost; - } - } - } - - tal_free(this_ctx); - return linear_network; - - function_fail: - tal_free(this_ctx); - return tal_free(linear_network); -} - -/* Simple queue to traverse the network. */ -struct queue_data -{ - u32 idx; - struct lqueue_link ql; -}; - -// TODO(eduardo): unit test this -/* Finds an admissible path from source to target, traversing arcs in the - * residual network with capacity greater than 0. - * The path is encoded into prev, which contains the idx of the arcs that are - * traversed. */ -static bool -find_admissible_path(const tal_t *ctx, - const struct linear_network *linear_network, - const struct residual_network *residual_network, - const u32 source, const u32 target, struct arc *prev) -{ - tal_t *this_ctx = tal(ctx,tal_t); - - bool target_found = false; - - for(size_t i=0;iidx = source; - lqueue_enqueue(&myqueue,qdata); - - while(!lqueue_empty(&myqueue)) - { - qdata = lqueue_dequeue(&myqueue); - u32 cur = qdata->idx; - - tal_free(qdata); - - if(cur==target) - { - target_found = true; - break; - } - - for(struct arc arc = node_adjacency_begin(linear_network,cur); - !node_adjacency_end(arc); - arc = node_adjacency_next(linear_network,arc)) - { - // check if this arc is traversable - if(residual_network->cap[arc.idx] <= 0) - continue; - - u32 next = arc_head(linear_network,arc); - - assert(next < tal_count(prev)); - - // if that node has been seen previously - if(prev[next].idx!=INVALID_INDEX) - continue; - - prev[next] = arc; - - qdata = tal(this_ctx,struct queue_data); - qdata->idx = next; - lqueue_enqueue(&myqueue,qdata); - } - } - tal_free(this_ctx); - return target_found; -} - -/* Get the max amount of flow one can send from source to target along the path - * encoded in `prev`. */ -static s64 get_augmenting_flow( - const struct linear_network* linear_network, - const struct residual_network *residual_network, - const u32 source, - const u32 target, - const struct arc *prev) -{ - s64 flow = INFINITE; - - u32 cur = target; - while(cur!=source) - { - assert(curcap[arc.idx]); - - // we are traversing in the opposite direction to the flow, - // hence the next node is at the tail of the arc. - cur = arc_tail(linear_network,arc); - } - - assert(flow0); - return flow; -} - -/* Augment a `flow` amount along the path defined by `prev`.*/ -static void augment_flow( - const struct linear_network *linear_network, - struct residual_network *residual_network, - const u32 source, - const u32 target, - const struct arc *prev, - s64 flow) -{ - u32 cur = target; - - while(cur!=source) - { - assert(cur < tal_count(prev)); - const struct arc arc = prev[cur]; - const struct arc dual = arc_dual(arc); - - assert(arc.idx < tal_count(residual_network->cap)); - assert(dual.idx < tal_count(residual_network->cap)); - - residual_network->cap[arc.idx] -= flow; - residual_network->cap[dual.idx] += flow; - - assert(residual_network->cap[arc.idx] >=0 ); - - // we are traversing in the opposite direction to the flow, - // hence the next node is at the tail of the arc. - cur = arc_tail(linear_network,arc); - } -} - - -// TODO(eduardo): unit test this -/* Finds any flow that satisfy the capacity and balance constraints of the - * uncertainty network. For the balance function condition we have: - * balance(source) = - balance(target) = amount - * balance(node) = 0 , for every other node - * Returns an error code if no feasible flow is found. - * - * 13/04/2023 This implementation uses a simple augmenting path approach. - * */ -static bool find_feasible_flow(const tal_t *ctx, - const struct linear_network *linear_network, - struct residual_network *residual_network, - const u32 source, const u32 target, s64 amount, - char **fail) -{ - assert(amount>=0); - tal_t *this_ctx = tal(ctx,tal_t); - - /* path information - * prev: is the id of the arc that lead to the node. */ - struct arc *prev = tal_arr(this_ctx,struct arc,linear_network->max_num_nodes); - if(!prev) - { - if(fail) - *fail = tal_fmt(ctx, "bad allocation of prev"); - goto function_fail; - } - - while(amount>0) - { - // find a path from source to target - if (!find_admissible_path(this_ctx, linear_network, - residual_network, source, target, - prev)) - - { - if(fail) - *fail = tal_fmt(ctx, "find_admissible_path failed"); - goto function_fail; - } - - // traverse the path and see how much flow we can send - s64 delta = get_augmenting_flow(linear_network, - residual_network, - source,target,prev); - - // commit that flow to the path - delta = MIN(amount,delta); - assert(delta>0 && delta<=amount); - - augment_flow(linear_network,residual_network,source,target,prev,delta); - amount -= delta; - } - - tal_free(this_ctx); - return true; - - function_fail: - tal_free(this_ctx); - return false; -} - -// TODO(eduardo): unit test this -/* Similar to `find_admissible_path` but use Dijkstra to optimize the distance - * label. Stops when the target is hit. */ -static bool find_optimal_path(const tal_t *ctx, struct dijkstra *dijkstra, - const struct linear_network *linear_network, - const struct residual_network *residual_network, - const u32 source, const u32 target, - struct arc *prev, char **fail) -{ - tal_t *this_ctx = tal(ctx,tal_t); - bool target_found = false; - - bitmap *visited = tal_arrz(this_ctx, bitmap, - BITMAP_NWORDS(linear_network->max_num_nodes)); - - if(!visited) - { - if(fail) - *fail = tal_fmt(ctx, "bad allocation of visited"); - goto finish; - } - - - for(size_t i=0;icap[arc.idx] <= 0) - continue; - - u32 next = arc_head(linear_network,arc); - - s64 cij = residual_network->cost[arc.idx] - - residual_network->potential[cur] - + residual_network->potential[next]; - - // Dijkstra only works with non-negative weights - assert(cij>=0); - - if(distance[next]<=distance[cur]+cij) - continue; - - dijkstra_update(dijkstra,next,distance[cur]+cij); - prev[next]=arc; - } - } - - if (!target_found && fail) - *fail = tal_fmt(ctx, "no route to destination"); - - finish: - tal_free(this_ctx); - return target_found; -} - -/* Set zero flow in the residual network. */ -static void zero_flow( - const struct linear_network *linear_network, - struct residual_network *residual_network) -{ - for(u32 node=0;nodemax_num_nodes;++node) - { - residual_network->potential[node]=0; - for(struct arc arc=node_adjacency_begin(linear_network,node); - !node_adjacency_end(arc); - arc = node_adjacency_next(linear_network,arc)) - { - if(arc_is_dual(arc))continue; - - struct arc dual = arc_dual(arc); - - residual_network->cap[arc.idx] = linear_network->capacity[arc.idx]; - residual_network->cap[dual.idx] = 0; - } - } -} - -// TODO(eduardo): unit test this -/* Starting from a feasible flow (satisfies the balance and capacity - * constraints), find a solution that minimizes the network->cost function. - * - * TODO(eduardo) The MCF must be called several times until we get a good - * compromise between fees and probabilities. Instead of re-computing the MCF at - * each step, we might use the previous flow result, which is not optimal in the - * current iteration but I might be not too far from the truth. - * It comes to mind to use cycle cancelling. */ -static bool optimize_mcf(const tal_t *ctx, struct dijkstra *dijkstra, - const struct linear_network *linear_network, - struct residual_network *residual_network, - const u32 source, const u32 target, const s64 amount, - char **fail) -{ - assert(amount>=0); - tal_t *this_ctx = tal(ctx,tal_t); - char *errmsg; - - zero_flow(linear_network,residual_network); - struct arc *prev = tal_arr(this_ctx,struct arc,linear_network->max_num_nodes); - - const s64 *const distance = dijkstra_distance_data(dijkstra); - - s64 remaining_amount = amount; - - while(remaining_amount>0) - { - if (!find_optimal_path(this_ctx, dijkstra, linear_network, - residual_network, source, target, prev, - &errmsg)) { - if (fail) - *fail = - tal_fmt(ctx, "find_optimal_path failed: %s", - errmsg); - goto function_fail; - } - - // traverse the path and see how much flow we can send - s64 delta = get_augmenting_flow(linear_network,residual_network,source,target,prev); - - // commit that flow to the path - delta = MIN(remaining_amount,delta); - assert(delta>0 && delta<=remaining_amount); - - augment_flow(linear_network,residual_network,source,target,prev,delta); - remaining_amount -= delta; - - // update potentials - for(u32 n=0;nmax_num_nodes;++n) - { - // see page 323 of Ahuja-Magnanti-Orlin - residual_network->potential[n] -= MIN(distance[target],distance[n]); - - /* Notice: - * if node i is permanently labeled we have - * d_i<=d_t - * which implies - * MIN(d_i,d_t) = d_i - * if node i is temporarily labeled we have - * d_i>=d_t - * which implies - * MIN(d_i,d_t) = d_t - * */ - } - } - tal_free(this_ctx); - return true; - - function_fail: - - tal_free(this_ctx); - return false; -} - -// flow on directed channels -struct chan_flow -{ - s64 half[2]; -}; - -/* Search in the network a path of positive flow until we reach a node with - * positive balance. */ -static u32 find_positive_balance( - const struct gossmap *gossmap, - const bitmap *disabled, - const struct chan_flow *chan_flow, - const u32 start_idx, - const s64 *balance, - - const struct gossmap_chan **prev_chan, - int *prev_dir, - u32 *prev_idx) -{ - u32 final_idx = start_idx; - - /* TODO(eduardo) - * This is guaranteed to halt if there are no directed flow cycles. - * There souldn't be any. In fact if cost is strickly - * positive, then flow cycles do not exist at all in the - * MCF solution. But if cost is allowed to be zero for - * some arcs, then we might have flow cyles in the final - * solution. We must somehow ensure that the MCF - * algorithm does not come up with spurious flow cycles. */ - while(balance[final_idx]<=0) - { - // printf("%s: node = %d\n",__func__,final_idx); - u32 updated_idx=INVALID_INDEX; - struct gossmap_node *cur - = gossmap_node_byidx(gossmap,final_idx); - - for(size_t i=0;inum_chans;++i) - { - int dir; - const struct gossmap_chan *c - = gossmap_nth_chan(gossmap, - cur,i,&dir); - - if (!channel_is_available(c, dir, gossmap, disabled)) - continue; - - const u32 c_idx = gossmap_chan_idx(gossmap,c); - - // follow the flow - if(chan_flow[c_idx].half[dir]>0) - { - const struct gossmap_node *next - = gossmap_nth_node(gossmap,c,!dir); - u32 next_idx = gossmap_node_idx(gossmap,next); - - - prev_dir[next_idx] = dir; - prev_chan[next_idx] = c; - prev_idx[next_idx] = final_idx; - - updated_idx = next_idx; - break; - } - } - - assert(updated_idx!=INVALID_INDEX); - assert(updated_idx!=final_idx); - - final_idx = updated_idx; - } - return final_idx; -} - -struct list_data -{ - struct list_node list; - struct flow *flow_path; -}; - -static inline uint64_t pseudorand_interval(uint64_t a, uint64_t b) -{ - if (a == b) - return 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 ** -get_flow_paths(const tal_t *ctx, const struct gossmap *gossmap, - const bitmap *disabled, - - // chan_extra_map cannot be const because we use it to keep - // track of htlcs and in_flight sats. - struct chan_extra_map *chan_extra_map, - const struct linear_network *linear_network, - const struct residual_network *residual_network, - - // how many msats in excess we paid for not having msat accuracy - // in the MCF solver - struct amount_msat excess, - const double base_probability, - - // error message - char **fail) -{ - tal_t *this_ctx = tal(ctx,tal_t); - struct flow **flows = tal_arr(ctx,struct flow*,0); - - assert(amount_msat_less(excess, AMOUNT_MSAT(1000))); - - const size_t max_num_chans = gossmap_max_chan_idx(gossmap); - struct chan_flow *chan_flow = tal_arrz(this_ctx,struct chan_flow,max_num_chans); - - const size_t max_num_nodes = gossmap_max_node_idx(gossmap); - s64 *balance = tal_arrz(this_ctx,s64,max_num_nodes); - - const struct gossmap_chan **prev_chan - = tal_arr(this_ctx,const struct gossmap_chan *,max_num_nodes); - - - int *prev_dir = tal_arr(this_ctx,int,max_num_nodes); - u32 *prev_idx = tal_arr(this_ctx,u32,max_num_nodes); - - if (!chan_flow || !balance || !prev_chan || !prev_idx || !prev_dir) { - if (fail) - *fail = tal_fmt(ctx, "bad allocation"); - goto function_fail; - } - - // Convert the arc based residual network flow into a flow in the - // directed channel network. - // Compute balance on the nodes. - for(u32 n = 0;n 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); - fp->dirs = tal_arr(fp,int,length); - - balance[node_idx] += delta; - balance[final_idx]-= delta; - - // walk backwards, substract flow - for(u32 cur_idx = final_idx; - cur_idx!=node_idx; - cur_idx=prev_idx[cur_idx]) - { - assert(cur_idx!=INVALID_INDEX); - - const int dir = prev_dir[cur_idx]; - const struct gossmap_chan *const c = prev_chan[cur_idx]; - const u32 c_idx = gossmap_chan_idx(gossmap,c); - - length--; - fp->path[length]=c; - fp->dirs[length]=dir; - // notice: fp->path and fp->dirs have the path - // in the correct order. - - chan_flow[c_idx].half[prev_dir[cur_idx]]-=delta; - } - - assert(delta>0); - - // substract the excess of msats for not having msat - // accuracy - struct amount_msat delivered = amount_msat(delta*1000); - if (!amount_msat_sub(&delivered, delivered, excess)) { - if (fail) - *fail = tal_fmt( - ctx, "unable to substract excess"); - goto function_fail; - } - excess = amount_msat(0); - fp->amount = delivered; - - fp->success_prob = - flow_probability(fp, gossmap, chan_extra_map, false) - * pow(base_probability, tal_count(fp->path)); - if (fp->success_prob < 0) { - if (fail) - *fail = - tal_fmt(ctx, "failed to compute " - "flow probability"); - goto function_fail; - } - - // add fp to flows - tal_arr_expand(&flows, fp); - } - } - - /* Establish ownership. */ - for(size_t i=0;i= min_probability; - bool B_prob_pass = B_prob >= min_probability; - - // all bounds are met - if(A_fee_pass && B_fee_pass && A_prob_pass && B_prob_pass) - { - // prefer lower fees - goto fees_or_prob; - } - - // prefer the solution that satisfies both bounds - if(!(A_fee_pass && A_prob_pass) && (B_fee_pass && B_prob_pass)) - { - return false; - } - // prefer the solution that satisfies both bounds - if((A_fee_pass && A_prob_pass) && !(B_fee_pass && B_prob_pass)) - { - return true; - } - - // no solution satisfies both bounds - - // bound on fee is met - if(A_fee_pass && B_fee_pass) - { - // pick the highest prob. - return A_prob > B_prob; - } - - // bound on prob. is met - if(A_prob_pass && B_prob_pass) - { - goto fees_or_prob; - } - - // prefer the solution that satisfies the bound on fees - if(A_fee_pass && !B_fee_pass) - { - return true; - } - if(B_fee_pass && !A_fee_pass) - { - return false; - } - - // none of them satisfy the fee bound - - // prefer the solution that satisfies the bound on prob. - if(A_prob_pass && !B_prob_pass) - { - return true; - } - if(B_prob_pass && !A_prob_pass) - { - return true; - } - - // no bound whatsoever is satisfied - - fees_or_prob: - - // fees are the same, wins the highest prob. - if(amount_msat_eq(A_fee,B_fee)) - { - return A_prob > B_prob; - } - - // go for fees - return amount_msat_less_eq(A_fee,B_fee); -} - -/* Channels that are not in the chan_extra_map should be disabled. */ -static bool check_disabled(const bitmap *disabled, - const struct gossmap *gossmap, - const struct chan_extra_map *chan_extra_map) -{ - assert(disabled); - assert(gossmap); - assert(chan_extra_map); - - if (tal_bytelen(disabled) != - 2 * bitmap_sizeof(gossmap_max_chan_idx(gossmap))) - return false; - - for (struct gossmap_chan *chan = gossmap_first_chan(gossmap); chan; - chan = gossmap_next_chan(gossmap, chan)) { - const u32 chan_idx = gossmap_chan_idx(gossmap, chan); - /* If both directions are disabled anyways, there is no need to - * fetch their information in chan_extra. */ - if (bitmap_test_bit(disabled, chan_idx * 2 + 0) && - bitmap_test_bit(disabled, chan_idx * 2 + 1)) - continue; - - struct short_channel_id scid = gossmap_chan_scid(gossmap, chan); - struct chan_extra *ce = - chan_extra_map_get(chan_extra_map, scid); - if (!ce) - return false; - } - return true; -} - -// TODO(eduardo): choose some default values for the minflow parameters -/* eduardo: I think it should be clear that this module deals with linear - * flows, ie. base fees are not considered. Hence a flow along a path is - * described with a sequence of directed channels and one amount. - * In the `pay_flow` module there are dedicated routes to compute the actual - * amount to be forward on each hop. - * - * TODO(eduardo): notice that we don't pay fees to forward payments with local - * channels and we can tell with absolute certainty the liquidity on them. - * Check that local channels have fee costs = 0 and bounds with certainty (min=max). */ -// TODO(eduardo): we should LOG_DBG the process of finding the MCF while -// adjusting the frugality factor. -struct flow **minflow(const tal_t *ctx, struct gossmap *gossmap, - const struct gossmap_node *source, - const struct gossmap_node *target, - struct chan_extra_map *chan_extra_map, - const bitmap *disabled, struct amount_msat amount, - struct amount_msat max_fee, double min_probability, - double base_probability, - double delay_feefactor, double base_fee_penalty, - u32 prob_cost_factor, char **fail) -{ - tal_t *this_ctx = tal(ctx,tal_t); - char *errmsg; - struct flow **best_flow_paths = NULL; - - struct pay_parameters *params = tal(this_ctx,struct pay_parameters); - struct dijkstra *dijkstra; - - params->gossmap = gossmap; - params->source = source; - params->target = target; - params->chan_extra_map = chan_extra_map; - - params->disabled = disabled; - - if (!check_disabled(disabled, gossmap, chan_extra_map)) { - if (fail) - *fail = tal_fmt(ctx, "Invalid disabled bitmap."); - goto function_fail; - } - - params->amount = amount; - - // template the channel partition into linear arcs - params->cap_fraction[0]=0; - params->cost_fraction[0]=0; - for(size_t i =1;icap_fraction[i]=CHANNEL_PIVOTS[i]-CHANNEL_PIVOTS[i-1]; - params->cost_fraction[i]= - log((1-CHANNEL_PIVOTS[i-1])/(1-CHANNEL_PIVOTS[i])) - /params->cap_fraction[i]; - - // printf("channel part: %ld, fraction: %lf, cost_fraction: %lf\n", - // i,params->cap_fraction[i],params->cost_fraction[i]); - } - - params->max_fee = max_fee; - params->min_probability = min_probability; - params->delay_feefactor = delay_feefactor; - params->base_fee_penalty = base_fee_penalty; - params->prob_cost_factor = prob_cost_factor; - params->base_probability = base_probability; - - // build the uncertainty network with linearization and residual arcs - struct linear_network *linear_network= init_linear_network(this_ctx, params, &errmsg); - if (!linear_network) { - if(fail) - *fail = tal_fmt(ctx, "init_linear_network failed: %s", - errmsg); - goto function_fail; - } - - struct residual_network *residual_network = - alloc_residual_network(this_ctx, linear_network->max_num_nodes, - linear_network->max_num_arcs); - if (!residual_network) { - if (fail) - *fail = tal_fmt( - ctx, "failed to allocate the residual network"); - goto function_fail; - } - - dijkstra = dijkstra_new(this_ctx, gossmap_max_node_idx(params->gossmap)); - - const u32 target_idx = gossmap_node_idx(params->gossmap,target); - const u32 source_idx = gossmap_node_idx(params->gossmap,source); - - init_residual_network(linear_network,residual_network); - - struct amount_msat best_fee; - double best_prob_success; - - /* TODO(eduardo): - * Some MCF algorithms' performance depend on the size of maxflow. If we - * were to work in units of msats we 1. risking overflow when computing - * costs and 2. we risk a performance overhead for no good reason. - * - * Working in units of sats was my first choice, but maybe working in - * units of 10, or 100 sats could be even better. - * - * IDEA: define the size of our precision as some parameter got at - * runtime that depends on the size of the payment and adjust the MCF - * accordingly. - * For example if we are trying to pay 1M sats our precision could be - * set to 1000sat, then channels that had capacity for 3M sats become 3k - * flow units. */ - const u64 pay_amount_msats = params->amount.millisatoshis % 1000; /* Raw: minflow */ - const u64 pay_amount_sats = params->amount.millisatoshis/1000 /* Raw: minflow */ - + (pay_amount_msats ? 1 : 0); - const struct amount_msat excess - = amount_msat(pay_amount_msats ? 1000 - pay_amount_msats : 0); - - if (!find_feasible_flow(this_ctx, linear_network, residual_network, - source_idx, target_idx, pay_amount_sats, - &errmsg)) { - // there is no flow that satisfy the constraints, we stop here - if(fail) - *fail = tal_fmt(ctx, "failed to find a feasible flow: %s", errmsg); - goto function_fail; - } - - // first flow found - best_flow_paths = get_flow_paths( - this_ctx, params->gossmap, params->disabled, params->chan_extra_map, - linear_network, residual_network, excess, params->base_probability, - &errmsg); - if (!best_flow_paths) { - if (fail) - *fail = - tal_fmt(ctx, "get_flow_paths failed: %s", errmsg); - goto function_fail; - } - best_flow_paths = tal_steal(ctx, best_flow_paths); - - best_prob_success = - flowset_probability(this_ctx, best_flow_paths, params->gossmap, - params->chan_extra_map, false, &errmsg) - * pow(params->base_probability, flowset_size(best_flow_paths)); - if (best_prob_success < 0) { - if (fail) - *fail = tal_fmt( - ctx, - "flowset_probability failed on MaxFlow phase: %s", - errmsg); - goto function_fail; - } - if (!flowset_fee(&best_fee, best_flow_paths)) { - if (fail) - *fail = - tal_fmt(ctx, "flowset_fee failed on MaxFlow phase"); - goto function_fail; - } - - // binary search for a value of `mu` that fits our fee and prob. - // constraints. - // mu=0 corresponds to only probabilities - // mu=MU_MAX-1 corresponds to only fee - s64 mu_left = 0, mu_right = MU_MAX; - while(mu_leftgossmap, params->disabled, - params->chan_extra_map, linear_network, - residual_network, excess, params->base_probability, - &errmsg); - if(!flow_paths) - { - // get_flow_paths doesn't fail unless there is a bug. - if (fail) - *fail = - tal_fmt(ctx, "get_flow_paths failed: %s", errmsg); - goto function_fail; - } - - double prob_success = - flowset_probability(this_ctx, flow_paths, params->gossmap, - params->chan_extra_map, false, &errmsg) - * pow(params->base_probability, flowset_size(flow_paths)); - if (prob_success < 0) { - // flowset_probability doesn't fail unless there is a bug. - if (fail) - *fail = - tal_fmt(ctx, "flowset_probability: %s", errmsg); - goto function_fail; - } - - struct amount_msat fee; - if (!flowset_fee(&fee, flow_paths)) { - // flowset_fee doesn't fail unless there is a bug. - if (fail) - *fail = - tal_fmt(ctx, "flowset_fee failed evaluating MinCostFlow candidate"); - goto function_fail; - } - - /* Is this better than the previous one? */ - if(!best_flow_paths || - is_better(params->max_fee,params->min_probability, - fee,prob_success, - best_fee, best_prob_success)) - { - - best_flow_paths = tal_free(best_flow_paths); - best_flow_paths = tal_steal(ctx,flow_paths); - - best_fee = fee; - best_prob_success=prob_success; - flow_paths = NULL; - } - /* I don't like this candidate. */ - else - tal_free(flow_paths); - - if(amount_msat_greater(fee,params->max_fee)) - { - // too expensive - mu_left = mu+1; - - }else if(prob_success < params->min_probability) - { - // too unlikely - mu_right = mu; - }else - { - // with mu constraints are satisfied, now let's optimize - // the fees - mu_left = mu+1; - } - } - - tal_free(this_ctx); - return best_flow_paths; - - function_fail: - tal_free(this_ctx); - return tal_free(best_flow_paths); -} - diff --git a/plugins/renepay/mcf.h b/plugins/renepay/mcf.h deleted file mode 100644 index 88f368ce80b7..000000000000 --- a/plugins/renepay/mcf.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_MCF_H -#define LIGHTNING_PLUGINS_RENEPAY_MCF_H -#include "config.h" -#include -#include -#include - -struct chan_extra_map; - -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 - * @source: the source to start from - * @target: the target to pay - * @chan_extra_map: hashtable of extra per-channel information - * @disabled: NULL, or a bitmap by channel index of channels not to use. - * @amount: the amount we want to reach @target - * - * @max_fee: the maximum allowed in fees - * - * @min_probability: minimum probability accepted - * - * @delay_feefactor converts 1 block delay into msat, as if it were an additional - * fee. So if a CLTV delay on a node is 5 blocks, that's treated as if it - * were a fee of 5 * @delay_feefactor. - * - * @base_fee_penalty: factor to compute additional proportional cost from each - * unit of base fee. So #base_fee_penalty will be added to the effective - * proportional fee for each msat of base fee. - * - * 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, struct gossmap *gossmap, - const struct gossmap_node *source, - const struct gossmap_node *target, - struct chan_extra_map *chan_extra_map, - const bitmap *disabled, struct amount_msat amount, - struct amount_msat max_fee, double min_probability, - double base_probability, - double delay_feefactor, double base_fee_penalty, - u32 prob_cost_factor, char **fail); -#endif /* LIGHTNING_PLUGINS_RENEPAY_MCF_H */ diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index c97414de6238..e8fa91a96286 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -5,17 +5,21 @@ #include #include #include +#include #include -#include #include -#include +#include #include #include -#include #include +#include #include #include +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define MAX_CAPACITY (AMOUNT_MSAT(21000000 * MSAT_PER_BTC)) + #define OP_NULL NULL #define OP_CALL (void *)1 #define OP_IF (void *)2 @@ -29,7 +33,7 @@ struct command_result *payment_continue(struct payment *payment) void *op = payment_virtual_program[payment->exec_state++]; if (op == OP_NULL) { - plugin_err(pay_plugin->plugin, + plugin_err(payment->plugin, "payment_continue reached the end of the virtual " "machine execution."); } else if (op == OP_CALL) { @@ -38,11 +42,11 @@ struct command_result *payment_continue(struct payment *payment) payment_virtual_program[payment->exec_state++]; if (mod == NULL) - plugin_err(pay_plugin->plugin, + plugin_err(payment->plugin, "payment_continue expected payment_modifier " "but NULL found"); - plugin_log(pay_plugin->plugin, LOG_DBG, "Calling modifier %s", + plugin_log(payment->plugin, LOG_DBG, "Calling modifier %s", mod->name); return mod->step_cb(payment); } else if (op == OP_IF) { @@ -51,11 +55,11 @@ struct command_result *payment_continue(struct payment *payment) payment_virtual_program[payment->exec_state++]; if (cond == NULL) - plugin_err(pay_plugin->plugin, + plugin_err(payment->plugin, "payment_continue expected pointer to " "condition but NULL found"); - plugin_log(pay_plugin->plugin, LOG_DBG, + plugin_log(payment->plugin, LOG_DBG, "Calling payment condition %s", cond->name); const u64 position_iftrue = @@ -66,7 +70,7 @@ struct command_result *payment_continue(struct payment *payment) return payment_continue(payment); } - plugin_err(pay_plugin->plugin, "payment_continue op code not defined"); + plugin_err(payment->plugin, "payment_continue op code not defined"); return NULL; } @@ -91,12 +95,18 @@ static struct command_result *payment_rpc_failure(struct command *cmd, json_tok_full_len(toks), json_tok_full(buffer, toks)); } -static void add_hintchan(struct payment *payment, const struct node_id *src, - const struct node_id *dst, u16 cltv_expiry_delta, - const struct short_channel_id scid, u32 fee_base_msat, - u32 fee_proportional_millionths, - const struct amount_msat *chan_htlc_min, - const struct amount_msat *chan_htlc_max); +/* Use this function to log failures in batch requests. */ +static struct command_result *log_payment_err(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *tok, + struct payment *payment) +{ + plugin_log(cmd->plugin, LOG_UNUSUAL, "%s failed: '%.*s'", method, + json_tok_full_len(tok), json_tok_full(buf, tok)); + return command_still_pending(cmd); +} + /***************************************************************************** * previoussuccess @@ -112,7 +122,7 @@ struct success_data { }; /* Extracts success data from listsendpays. */ -static bool success_data_from_listsendpays(const char *buf, +static bool success_data_from_listsendpays(struct command *cmd, const char *buf, const jsmntok_t *arr, struct success_data *success) { @@ -135,13 +145,13 @@ static bool success_data_from_listsendpays(const char *buf, const jsmntok_t *status_tok = json_get_member(buf, t, "status"); if (!status_tok) plugin_err( - pay_plugin->plugin, + cmd->plugin, "%s (line %d) missing status token from json.", __func__, __LINE__); const char *status = json_strdup(tmpctx, buf, status_tok); if (!status) plugin_err( - pay_plugin->plugin, + cmd->plugin, "%s (line %d) failed to allocate status string.", __func__, __LINE__); @@ -164,7 +174,7 @@ static bool success_data_from_listsendpays(const char *buf, JSON_SCAN(json_to_preimage, &success->preimage)); if (err) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "%s (line %d) json_scan of " "listsendpay returns the " "following error: %s", @@ -176,7 +186,7 @@ static bool success_data_from_listsendpays(const char *buf, this_msat) || !amount_msat_add(&success->sent_msat, success->sent_msat, this_sent)) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "%s (line %d) amount_msat overflow.", __func__, __LINE__); @@ -202,7 +212,7 @@ static struct command_result *previoussuccess_done(struct command *cmd, } struct success_data success; - if (!success_data_from_listsendpays(buf, arr, &success)) { + if (!success_data_from_listsendpays(cmd, buf, arr, &success)) { /* There are no success sendpays. */ return payment_continue(payment); } @@ -253,168 +263,6 @@ static struct command_result *initial_sanity_checks_cb(struct payment *payment) REGISTER_PAYMENT_MODIFIER(initial_sanity_checks, initial_sanity_checks_cb); -/***************************************************************************** - * selfpay - */ - -static struct command_result *selfpay_cb(struct payment *payment) -{ - /* A different approach to self-pay: create a fake channel from the - * bolt11 destination to the routing_destination (a fake node_id). */ - if (!payment->payment_info.blinded_paths) { - struct amount_msat htlc_min = AMOUNT_MSAT(0); - struct amount_msat htlc_max = AMOUNT_MSAT((u64)1000*100000000); - struct short_channel_id scid = {.u64 = 0}; - add_hintchan(payment, &payment->payment_info.destination, - payment->routing_destination, - /* cltv delta = */ 0, scid, - /* base fee = */ 0, - /* ppm = */ 0, &htlc_min, &htlc_max); - } - return payment_continue(payment); -} - -REGISTER_PAYMENT_MODIFIER(selfpay, selfpay_cb); - -/***************************************************************************** - * getmychannels - * - * Calls listpeerchannels to get and updated state of the local channels. - */ - -static void -uncertainty_update_from_listpeerchannels(struct uncertainty *uncertainty, - const struct short_channel_id_dir *scidd, - struct amount_msat max, bool enabled, - const char *buf, const jsmntok_t *chantok) -{ - if (!enabled) - return; - - struct amount_msat capacity, min, gap; - const char *errmsg = json_scan(tmpctx, buf, chantok, "{total_msat:%}", - JSON_SCAN(json_to_msat, &capacity)); - if (errmsg) - goto error; - - if (!uncertainty_add_channel(pay_plugin->uncertainty, scidd->scid, - capacity)) { - errmsg = tal_fmt( - tmpctx, - "Unable to find/add scid=%s in the uncertainty network", - fmt_short_channel_id(tmpctx, scidd->scid)); - goto error; - } - - if (!amount_msat_scale(&gap, capacity, 0.1) || - !amount_msat_sub(&min, max, gap)) - min = AMOUNT_MSAT(0); - - // FIXME this does not include pending HTLC of ongoing payments! - /* Allow a gap between min and max so that we don't use up all of our - * channels' spendable sats and avoid our local error: - * WIRE_TEMPORARY_CHANNEL_FAILURE: Capacity exceeded - HTLC fee: Xsat - * - * */ - if (!uncertainty_set_liquidity(pay_plugin->uncertainty, scidd, min, - max)) { - errmsg = tal_fmt( - tmpctx, - "Unable to set liquidity to channel scidd=%s in the " - "uncertainty network.", - fmt_short_channel_id_dir(tmpctx, scidd)); - goto error; - } - return; - -error: - plugin_log( - pay_plugin->plugin, LOG_UNUSUAL, - "Failed to update local channel %s from listpeerchannels rpc: %s", - fmt_short_channel_id(tmpctx, scidd->scid), - errmsg); -} - -static void gossmod_cb(struct gossmap_localmods *mods, - const struct node_id *self, - const struct node_id *peer, - const struct short_channel_id_dir *scidd, - struct amount_msat capacity_msat, - struct amount_msat htlcmin, - struct amount_msat htlcmax, - struct amount_msat spendable, - struct amount_msat fee_base, - u32 fee_proportional, - u16 cltv_delta, - bool enabled, - const char *buf, - const jsmntok_t *chantok, - struct payment *payment) -{ - struct amount_msat min, max; - - if (scidd->dir == node_id_idx(self, peer)) { - /* local channels can send up to what's spendable */ - min = AMOUNT_MSAT(0); - max = spendable; - } else { - /* remote channels can send up no more than spendable */ - min = htlcmin; - max = amount_msat_min(spendable, htlcmax); - } - - /* FIXME: features? */ - gossmap_local_addchan(mods, self, peer, scidd->scid, capacity_msat, - NULL); - gossmap_local_updatechan(mods, scidd, - &enabled, - &min, &max, - &fee_base, &fee_proportional, &cltv_delta); - - /* Is it disabled? */ - if (!enabled) - payment_disable_chan(payment, *scidd, LOG_DBG, - "listpeerchannels says not enabled"); - - /* Also update the uncertainty network by fixing the liquidity of the - * outgoing channel. If we try to set the liquidity of the incoming - * channel as well we would have conflicting information because our - * knowledge model does not take into account channel reserves. */ - if (scidd->dir == node_id_idx(self, peer)) - uncertainty_update_from_listpeerchannels( - pay_plugin->uncertainty, scidd, max, enabled, buf, chantok); -} - -static struct command_result *getmychannels_done(struct command *cmd, - const char *method UNUSED, - const char *buf, - const jsmntok_t *result, - struct payment *payment) -{ - // FIXME: should local gossmods be global (ie. member of pay_plugin) or - // local (ie. member of payment)? - payment->local_gossmods = gossmods_from_listpeerchannels( - payment, &pay_plugin->my_id, buf, result, /* zero_rates = */ true, - gossmod_cb, payment); - - return payment_continue(payment); -} - -static struct command_result *getmychannels_cb(struct payment *payment) -{ - struct command *cmd = payment_command(payment); - if (!cmd) - plugin_err(pay_plugin->plugin, - "getmychannels_pay_mod: cannot get a valid cmd."); - - struct out_req *req = jsonrpc_request_start( - cmd, "listpeerchannels", getmychannels_done, - payment_rpc_failure, payment); - return send_outreq(req); -} - -REGISTER_PAYMENT_MODIFIER(getmychannels, getmychannels_cb); - /***************************************************************************** * refreshgossmap * @@ -423,26 +271,10 @@ REGISTER_PAYMENT_MODIFIER(getmychannels, getmychannels_cb); static struct command_result *refreshgossmap_cb(struct payment *payment) { - assert(pay_plugin->gossmap); // gossmap must be already initialized assert(payment); - assert(payment->local_gossmods); - - bool gossmap_changed = gossmap_refresh(pay_plugin->gossmap); - - if (gossmap_changed) { - gossmap_apply_localmods(pay_plugin->gossmap, - payment->local_gossmods); - int skipped_count = uncertainty_update(pay_plugin->uncertainty, - pay_plugin->gossmap); - gossmap_remove_localmods(pay_plugin->gossmap, - payment->local_gossmods); - if (skipped_count) - plugin_log( - pay_plugin->plugin, LOG_UNUSUAL, - "%s: uncertainty was updated but %d channels have " - "been ignored.", - __func__, skipped_count); - } + struct renepay *renepay = get_renepay(payment->plugin); + assert(renepay->gossmap); // gossmap must be already initialized + gossmap_refresh(renepay->gossmap); return payment_continue(payment); } @@ -455,111 +287,75 @@ REGISTER_PAYMENT_MODIFIER(refreshgossmap, refreshgossmap_cb); * network. */ -static void uncertainty_remove_channel(struct chan_extra *ce, - struct uncertainty *uncertainty) +static struct command_result *hints_done(struct command *cmd, + struct payment *payment) { - chan_extra_map_del(uncertainty->chan_extra_map, ce); + return payment_continue(payment); } -static void add_hintchan(struct payment *payment, const struct node_id *src, - const struct node_id *dst, u16 cltv_expiry_delta, - const struct short_channel_id scid, u32 fee_base_msat, + +static void add_hintchan(struct command *cmd, + struct request_batch *batch, + struct payment *payment, + const struct node_id *src, + const struct node_id *dst, + u16 cltv_expiry_delta, + const struct short_channel_id scid, + u32 fee_base_msat, u32 fee_proportional_millionths, + const struct amount_msat *chan_capacity, const struct amount_msat *chan_htlc_min, const struct amount_msat *chan_htlc_max) { - assert(payment); - assert(payment->local_gossmods); - - const char *errmsg; - struct chan_extra *ce = - uncertainty_find_channel(pay_plugin->uncertainty, scid); - - if (!ce) { - struct short_channel_id_dir scidd; - /* We assume any HTLC is allowed */ - struct amount_msat htlc_min = AMOUNT_MSAT(0), htlc_max = MAX_CAPACITY; - - if (chan_htlc_min) - htlc_min = *chan_htlc_min; - if (chan_htlc_max) - htlc_max = *chan_htlc_max; - - struct amount_msat fee_base = amount_msat(fee_base_msat); - bool enabled = true; - scidd.scid = scid; - scidd.dir = node_id_idx(src, dst); - - /* This channel is not public, we don't know his capacity - One possible solution is set the capacity to - MAX_CAP and the state to [0,MAX_CAP]. Alternatively we could - the capacity to amount and state to [amount,amount], but that - wouldn't work if the recepient provides more than one hints - telling us to partition the payment in multiple routes. */ - ce = uncertainty_add_channel(pay_plugin->uncertainty, scid, - MAX_CAPACITY); - if (!ce) { - errmsg = tal_fmt(tmpctx, - "Unable to find/add scid=%s in the " - "local uncertainty network", - fmt_short_channel_id(tmpctx, scid)); - goto function_error; - } - /* FIXME: features? */ - if (!gossmap_local_addchan(payment->local_gossmods, src, dst, - scid, MAX_CAPACITY, NULL) || - !gossmap_local_updatechan( - payment->local_gossmods, &scidd, - &enabled, &htlc_min, &htlc_max, - &fee_base, &fee_proportional_millionths, - &cltv_expiry_delta)) { - errmsg = tal_fmt( - tmpctx, - "Failed to update scid=%s in the local_gossmods.", - fmt_short_channel_id(tmpctx, scid)); - goto function_error; - } - /* We want these channel hints destroyed when the local_gossmods - * are freed. */ - /* FIXME: these hints are global in the uncertainty network if - * two payments happen concurrently we will have race - * conditions. The best way to avoid this is to use askrene and - * it's layered API. */ - tal_steal(payment->local_gossmods, ce); - tal_add_destructor2(ce, uncertainty_remove_channel, - pay_plugin->uncertainty); - } else { - /* The channel is pubic and we already keep track of it in the - * gossmap and uncertainty network. It would be wrong to assume - * that this channel has sufficient capacity to forward the - * entire payment! Doing so leads to knowledge updates in which - * the known min liquidity is greater than the channel's - * capacity. */ - } - - return; + struct amount_msat htlc_min = AMOUNT_MSAT(0), htlc_max = MAX_CAPACITY, + capacity = MAX_CAPACITY; + + if (chan_capacity) + capacity = *chan_capacity; + if (chan_htlc_min) + htlc_min = *chan_htlc_min; + if (chan_htlc_max) + htlc_max = *chan_htlc_max; + htlc_max = amount_msat_min(htlc_max, capacity); + htlc_min = amount_msat_min(htlc_min, htlc_max); -function_error: - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "Failed to update hint channel %s: %s", - fmt_short_channel_id(tmpctx, scid), - errmsg); + assert(payment); + struct out_req *req; + struct short_channel_id_dir scidd = {.scid = scid, + .dir = node_id_idx(src, dst)}; + + req = add_to_batch(cmd, batch, "askrene-create-channel"); + json_add_string(req->js, "layer", payment->payment_layer); + json_add_node_id(req->js, "source", src); + json_add_node_id(req->js, "destination", dst); + json_add_short_channel_id(req->js, "short_channel_id", scidd.scid); + json_add_amount_msat(req->js, "capacity_msat", capacity); + send_outreq(req); + + req = add_to_batch(cmd, batch, "askrene-update-channel"); + json_add_string(req->js, "layer", payment->payment_layer); + json_add_short_channel_id_dir(req->js, "short_channel_id_dir", scidd); + json_add_bool(req->js, "enabled", true); + json_add_amount_msat(req->js, "htlc_minimum_msat", htlc_min); + json_add_amount_msat(req->js, "htlc_maximum_msat", htlc_max); + json_add_u32(req->js, "fee_base_msat", fee_base_msat); + json_add_u32(req->js, "fee_proportional_millionths", + fee_proportional_millionths); + json_add_u32(req->js, "cltv_expiry_delta", cltv_expiry_delta); + send_outreq(req); } -static struct command_result *routehints_done(struct command *cmd UNUSED, - const char *method UNUSED, - const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct payment *payment) +static struct command_result *routehints_cb(struct payment *payment) { - // FIXME are there route hints for B12? assert(payment); - assert(payment->local_gossmods); - + struct command *cmd = payment_command(payment); const struct node_id *destination = &payment->payment_info.destination; struct route_info **routehints = payment->payment_info.routehints; - assert(routehints); + if (!routehints) + return payment_continue(payment); const size_t nhints = tal_count(routehints); + struct request_batch *batch = + request_batch_new(cmd, NULL, log_payment_err, hints_done, payment); /* Hints are added to the local_gossmods. */ for (size_t i = 0; i < nhints; i++) { /* Each one, presumably, leads to the destination */ @@ -567,40 +363,15 @@ static struct command_result *routehints_done(struct command *cmd UNUSED, const struct node_id *end = destination; for (int j = tal_count(r) - 1; j >= 0; j--) { - add_hintchan(payment, &r[j].pubkey, end, + add_hintchan(cmd, batch, payment, &r[j].pubkey, end, r[j].cltv_expiry_delta, r[j].short_channel_id, r[j].fee_base_msat, r[j].fee_proportional_millionths, - NULL, NULL); + NULL, NULL, NULL); end = &r[j].pubkey; } } - - /* Add hints to the uncertainty network. */ - gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods); - int skipped_count = - uncertainty_update(pay_plugin->uncertainty, pay_plugin->gossmap); - gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods); - if (skipped_count) - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "%s: uncertainty was updated but %d channels have " - "been ignored.", - __func__, skipped_count); - - return payment_continue(payment); -} - -static struct command_result *routehints_cb(struct payment *payment) -{ - if (payment->payment_info.routehints == NULL) - return payment_continue(payment); - struct command *cmd = payment_command(payment); - assert(cmd); - struct out_req *req = jsonrpc_request_start( - cmd, "waitblockheight", routehints_done, - payment_rpc_failure, payment); - json_add_num(req->js, "blockheight", 0); - return send_outreq(req); + return batch_done(cmd, batch); } REGISTER_PAYMENT_MODIFIER(routehints, routehints_cb); @@ -615,64 +386,147 @@ REGISTER_PAYMENT_MODIFIER(routehints, routehints_cb); static struct command_result *blindedhints_cb(struct payment *payment) { - if (payment->payment_info.blinded_paths == NULL) - return payment_continue(payment); - + struct command *cmd = payment_command(payment); struct payment_info *pinfo = &payment->payment_info; - struct short_channel_id scid; - struct node_id src; - - for (size_t i = 0; i < tal_count(pinfo->blinded_paths); i++) { - const struct blinded_payinfo *payinfo = - pinfo->blinded_payinfos[i]; - const struct blinded_path *path = pinfo->blinded_paths[i]; - - scid.u64 = i; // a fake scid - node_id_from_pubkey(&src, &path->first_node_id.pubkey); - - add_hintchan(payment, &src, payment->routing_destination, - payinfo->cltv_expiry_delta, scid, - payinfo->fee_base_msat, - payinfo->fee_proportional_millionths, - &payinfo->htlc_minimum_msat, - &payinfo->htlc_maximum_msat); + struct request_batch *batch = + request_batch_new(cmd, NULL, log_payment_err, hints_done, payment); + + if (payment->payment_info.blinded_paths == NULL){ + /* a BOLT11 invoice, we add only one fake channel */ + struct amount_msat htlc_min = AMOUNT_MSAT(0); + struct amount_msat htlc_max = AMOUNT_MSAT((u64)1000*100000000); + struct short_channel_id scid = {.u64 = 0}; + add_hintchan(cmd, batch, payment, &pinfo->destination, + payment->routing_destination, + /* cltv delta = */ 0, scid, + /* base fee = */ 0, + /* ppm = */ 0, + /* capacity = ? */ NULL, + &htlc_min, &htlc_max); + } else { + struct short_channel_id scid; + struct node_id src; + for (size_t i = 0; i < tal_count(pinfo->blinded_paths); i++) { + const struct blinded_payinfo *payinfo = + pinfo->blinded_payinfos[i]; + const struct blinded_path *path = + pinfo->blinded_paths[i]; + + scid.u64 = i; // a fake scid + node_id_from_pubkey(&src, &path->first_node_id.pubkey); + + add_hintchan(cmd, batch, payment, &src, + payment->routing_destination, + payinfo->cltv_expiry_delta, scid, + payinfo->fee_base_msat, + payinfo->fee_proportional_millionths, + NULL, + &payinfo->htlc_minimum_msat, + &payinfo->htlc_maximum_msat); + } } - return payment_continue(payment); + return batch_done(cmd, batch); } REGISTER_PAYMENT_MODIFIER(blindedhints, blindedhints_cb); /***************************************************************************** - * compute_routes + * getroutes * - * Compute the payment routes. + * Call askrene-getroutes */ -static struct command_result *compute_routes_cb(struct payment *payment) +/* The last hop is an artifact for handling self-payments and blinded paths. */ +static void prune_last_hop(struct route *route) +{ + const size_t pathlen = tal_count(route->hops); + assert(pathlen > 0); + route->path_num = route->hops[pathlen - 1].scid.u64; + tal_arr_remove(&route->hops, pathlen - 1); +} + +static struct command_result *getroutes_done(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *tok, + struct payment *payment) { - assert(payment->status == PAYMENT_PENDING); struct routetracker *routetracker = payment->routetracker; assert(routetracker); - if (routetracker->computed_routes && - tal_count(routetracker->computed_routes)) - plugin_err(pay_plugin->plugin, + if (tal_count(routetracker->computed_routes) > 0) + plugin_err(cmd->plugin, "%s: no previously computed routes expected.", __func__); + routetracker->computed_routes = tal_free(routetracker->computed_routes); + const jsmntok_t *routestok = json_get_member(buf, tok, "routes"); + assert(routestok && routestok->type == JSMN_ARRAY); + routetracker->computed_routes = + tal_arr(routetracker, struct route *, 0); + + size_t i; + const jsmntok_t *r; + json_for_each_arr(i, r, routestok) + { + struct route *route = new_route( + routetracker->computed_routes, payment->groupid, + payment->next_partid++, payment->payment_info.payment_hash, + AMOUNT_MSAT(0), AMOUNT_MSAT(0)); + tal_arr_expand(&routetracker->computed_routes, route); + bool success = json_to_myroute(buf, r, route); + if (!success) { + plugin_err( + cmd->plugin, + "%s: failed to parse route from getroutes, %.*s", + __func__, json_tok_full_len(r), + json_tok_full(buf, r)); + } + prune_last_hop(route); + assert(success); + } + return payment_continue(payment); +} + +static struct command_result *getroutes_fail(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *tok, + struct payment *payment) +{ + // FIXME: read the response + // if can we do something about his failure: + // disable channels or add biases + // return payment_continue(payment); + // else: + // return payment_fail(payment, PAY_STOPPED_RETRYING, "getroutes + // failed to find a feasible solution %s", explain_error(buf, + // tok)); + const jsmntok_t *messtok = json_get_member(buf, tok, "message"); + assert(messtok); + return payment_fail( + payment, PAYMENT_PENDING, + "getroutes failed to find a feasible solution: %.*s", + json_tok_full_len(messtok), json_tok_full(buf, messtok)); +} + +static struct command_result *getroutes_cb(struct payment *payment) +{ + struct renepay *renepay = get_renepay(payment->plugin); + assert(payment->status == PAYMENT_PENDING); struct amount_msat feebudget, fees_spent, remaining; /* Total feebudget */ if (!amount_msat_sub(&feebudget, payment->payment_info.maxspend, payment->payment_info.amount)) - plugin_err(pay_plugin->plugin, "%s: fee budget is negative?", + plugin_err(payment->plugin, "%s: fee budget is negative?", __func__); /* Fees spent so far */ if (!amount_msat_sub(&fees_spent, payment->total_sent, payment->total_delivering)) - plugin_err(pay_plugin->plugin, + plugin_err(payment->plugin, "%s: total_delivering is greater than total_sent?", __func__); @@ -684,64 +538,55 @@ static struct command_result *compute_routes_cb(struct payment *payment) if (!amount_msat_sub(&remaining, payment->payment_info.amount, payment->total_delivering) || amount_msat_is_zero(remaining)) { - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, + plugin_log(payment->plugin, LOG_UNUSUAL, "%s: Payment is pending with full amount already " "committed. We skip the computation of new routes.", __func__); return payment_continue(payment); } - enum jsonrpc_errcode errcode; - const char *err_msg = NULL; - - gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods); - - /* get_routes returns the answer, we assign it to the computed_routes, - * that's why we need to tal_free the older array. Maybe it would be - * better to pass computed_routes as a reference? */ - routetracker->computed_routes = tal_free(routetracker->computed_routes); - - /* Send get_routes a note that it should discard the last hop because we - * are actually solving a multiple destinations problem. */ - bool blinded_destination = true; - - // TODO: add an algorithm selector here - /* We let this return an unlikely path, as it's better to try once than - * simply refuse. Plus, models are not truth! */ - routetracker->computed_routes = get_routes( - routetracker, - &payment->payment_info, - &pay_plugin->my_id, - payment->routing_destination, - pay_plugin->gossmap, - pay_plugin->uncertainty, - payment->disabledmap, - remaining, - feebudget, - &payment->next_partid, - payment->groupid, - blinded_destination, - &errcode, - &err_msg); - - /* Otherwise the error message remains a child of the routetracker. */ - err_msg = tal_steal(tmpctx, err_msg); - - gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods); - - /* Couldn't feasible route, we stop. */ - if (!routetracker->computed_routes || - tal_count(routetracker->computed_routes) == 0) { - if (err_msg == NULL) - err_msg = tal_fmt( - tmpctx, "get_routes returned NULL error message"); - return payment_fail(payment, errcode, "%s", err_msg); - } - - return payment_continue(payment); + /* FIXME: + * call getroutes: + * input: source, destination, amount, maxfee, final_cltv, + * maxdelay, layers: [auto.localchans, auto.sourcefree, + * thispaymenthints, thispaymentexclude, renepayknowledge] + * + * possible outcomes: + * success: then continue + * fail with hint: try to fix and retry or fail payment + * */ + struct command *cmd = payment_command(payment); + struct out_req *req = jsonrpc_request_start( + cmd, "getroutes", getroutes_done, getroutes_fail, payment); + + // FIXME: add an algorithm selection in askrene such that we could + // retrieve a single path route if necessary, see issue 8042 + // FIXME: register layers before using then: + // -> register RENEPAY_LAYER on plugin startup + // -> register payment->payment_layer when payment is created + // -> payment_layer should auto clean + // -> register payment->command_layer when the payment execution + // starts + // -> command_layer should auto clean + + json_add_node_id(req->js, "source", &renepay->my_id); + json_add_node_id(req->js, "destination", payment->routing_destination); + json_add_amount_msat(req->js, "amount_msat", remaining); + json_add_amount_msat(req->js, "maxfee_msat", feebudget); + json_add_u32(req->js, "final_cltv", payment->payment_info.final_cltv); + json_array_start(req->js, "layers"); + json_add_string(req->js, NULL, "auto.localchans"); + json_add_string(req->js, NULL, "auto.sourcefree"); + json_add_string(req->js, NULL, payment->payment_layer); + json_add_string(req->js, NULL, RENEPAY_LAYER); + json_array_end(req->js); + // FIXME: add further constraints here if necessary when they become + // available in getroutes + // eg. json_add_u32(req->js, "maxdelay", payment->payment_info.maxdelay); + return send_outreq(req); } -REGISTER_PAYMENT_MODIFIER(compute_routes, compute_routes_cb); +REGISTER_PAYMENT_MODIFIER(getroutes, getroutes_cb); /***************************************************************************** * send_routes @@ -750,6 +595,223 @@ REGISTER_PAYMENT_MODIFIER(compute_routes, compute_routes_cb); * request calling sendpay. */ +static struct command_result *sendroutes_done(struct command *cmd, + struct payment *payment) +{ + return payment_continue(payment); +} + +static struct command_result *unreserve_done(struct command *aux_cmd, + const char *method UNUSED, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct route *route UNUSED) +{ + return aux_command_done(aux_cmd); +} + +static struct command_result * +unreserve_fail(struct command *aux_cmd, const char *method, const char *buf, + const jsmntok_t *result, struct route *route UNUSED) +{ + plugin_log(aux_cmd->plugin, LOG_UNUSUAL, "%s failed: %.*s", method, + json_tok_full_len(result), json_tok_full(buf, result)); + return aux_command_done(aux_cmd); +} + +static void unreserve_route(struct route *route, struct command *aux_cmd) +{ + struct out_req *req = + jsonrpc_request_start(aux_cmd, "askrene-unreserve", unreserve_done, + unreserve_fail, route); + json_array_start(req->js, "path"); + for (size_t i = 0; i < tal_count(route->hops); i++) { + const struct route_hop *hop = &route->hops[i]; + struct short_channel_id_dir scidd = {.scid = hop->scid, + .dir = hop->direction}; + json_object_start(req->js, NULL); + json_add_short_channel_id_dir(req->js, "short_channel_id_dir", + scidd); + json_add_amount_msat(req->js, "amount_msat", hop->amount); + json_object_end(req->js); + } + json_array_end(req->js); + send_outreq(req); +} + +static struct command_result *reserve_done(struct command *cmd, + const char *method UNUSED, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct route *route) +{ + /* A new command is issued to handle the destruction of this route. + * I hope aux_cmd outlives the current payment session cmd. */ + struct command *aux_cmd = aux_command(cmd); + tal_add_destructor2(route, unreserve_route, aux_cmd); + return command_still_pending(cmd); +} + +static struct command_result *reserve_fail(struct command *cmd, + const char *method UNUSED, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct route *route) +{ + plugin_log(cmd->plugin, LOG_UNUSUAL, "failed to reserve route (%s)", + fmt_routekey(tmpctx, &route->key)); + return command_still_pending(cmd); +} + +/* Callback function for sendpay request success. */ +static struct command_result * +renesendpay_done(struct command *cmd, const char *method UNUSED, + const char *buf, const jsmntok_t *result, struct route *route) +{ + assert(route); + struct renepay *renepay = get_renepay(cmd->plugin); + struct payment *payment = route_get_payment_verify(renepay, route); + route_pending_register(payment, payment->routetracker, route); + + const jsmntok_t *t; + size_t i; + bool ret; + + const jsmntok_t *secretstok = + json_get_member(buf, result, "shared_secrets"); + + if (secretstok) { + assert(secretstok->type == JSMN_ARRAY); + + route->shared_secrets = + tal_arr(route, struct secret, secretstok->size); + json_for_each_arr(i, t, secretstok) + { + ret = json_to_secret(buf, t, &route->shared_secrets[i]); + assert(ret); + } + } else + route->shared_secrets = NULL; + + struct out_req *req = jsonrpc_request_start( + cmd, "askrene-reserve", reserve_done, reserve_fail, route); + json_array_start(req->js, "path"); + for (i = 0; i < tal_count(route->hops); i++) { + const struct route_hop *hop = &route->hops[i]; + struct short_channel_id_dir scidd = {.scid = hop->scid, + .dir = hop->direction}; + json_object_start(req->js, NULL); + json_add_short_channel_id_dir(req->js, "short_channel_id_dir", + scidd); + json_add_amount_msat(req->js, "amount_msat", hop->amount); + json_object_end(req->js); + } + json_array_end(req->js); + return send_outreq(req); +} + +/* FIXME: check when will renesendpay fail */ +static struct command_result * +renesendpay_fail(struct command *cmd, const char *method UNUSED, + const char *buf, const jsmntok_t *tok, struct route *route) +{ + assert(route); + struct renepay *renepay = get_renepay(cmd->plugin); + struct payment *payment = route_get_payment_verify(renepay, route); + struct routetracker *routetracker = payment->routetracker; + assert(routetracker); + + enum jsonrpc_errcode errcode; + const char *msg; + const char *err; + + err = json_scan(tmpctx, buf, tok, "{code:%,message:%}", + JSON_SCAN(json_to_jsonrpc_errcode, &errcode), + JSON_SCAN_TAL(tmpctx, json_strdup, &msg)); + if (err) + plugin_err(cmd->plugin, + "Unable to parse sendpay error: %s, json: %.*s", err, + json_tok_full_len(tok), json_tok_full(buf, tok)); + + payment_note(payment, LOG_INFORM, + "Sendpay failed: partid=%" PRIu64 + " errorcode:%d message=%s", + route->key.partid, errcode, msg); + + if (errcode != PAY_TRY_OTHER_ROUTE) { + plugin_log(cmd->plugin, LOG_UNUSUAL, + "Strange error from sendpay: %.*s", + json_tok_full_len(tok), json_tok_full(buf, tok)); + } + + /* There is no new knowledge from this kind of failure. + * We just disable this scid. */ + // FIXME: askrene disable this channel + struct short_channel_id_dir scidd_disable = { + .scid = route->hops[0].scid, .dir = route->hops[0].direction}; + payment_disable_chan(payment, scidd_disable, LOG_INFORM, + "sendpay didn't like first hop: %s", msg); + + if (!route_map_del(routetracker->sent_routes, route)) + plugin_err(cmd->plugin, "%s: route (%s) is not marked as sent", + __func__, fmt_routekey(tmpctx, &route->key)); + tal_free(route); + return command_still_pending(cmd); +} + +static void add_sendpay_request(struct rpcbatch *batch, struct route *route, + struct payment *payment) +{ + struct payment_info *pinfo = &payment->payment_info; + struct out_req *req = add_to_rpcbatch( + batch, "renesendpay", renesendpay_done, renesendpay_fail, route); + const size_t pathlen = tal_count(route->hops); + json_add_sha256(req->js, "payment_hash", &route->key.payment_hash); + json_add_u64(req->js, "partid", route->key.partid); + json_add_u64(req->js, "groupid", route->key.groupid); + json_add_string(req->js, "invoice", pinfo->invstr); + json_add_node_id(req->js, "destination", &pinfo->destination); + json_add_amount_msat(req->js, "amount_msat", route->amount_deliver); + json_add_amount_msat(req->js, "total_amount_msat", pinfo->amount); + json_add_u32(req->js, "final_cltv", pinfo->final_cltv); + + if (pinfo->label) + json_add_string(req->js, "label", pinfo->label); + if (pinfo->description) + json_add_string(req->js, "description", pinfo->description); + + json_array_start(req->js, "route"); + /* An empty route means a payment to oneself, pathlen=0 */ + for (size_t j = 0; j < pathlen; j++) { + const struct route_hop *hop = &route->hops[j]; + json_object_start(req->js, NULL); + json_add_node_id(req->js, "id", &hop->node_id); + json_add_short_channel_id(req->js, "channel", hop->scid); + json_add_amount_msat(req->js, "amount_msat", hop->amount); + json_add_num(req->js, "direction", hop->direction); + json_add_u32(req->js, "delay", hop->delay); + json_add_string(req->js, "style", "tlv"); + json_object_end(req->js); + } + json_array_end(req->js); + + /* Either we have a payment_secret for BOLT11 or blinded_paths for + * BOLT12 */ + if (pinfo->payment_secret) + json_add_secret(req->js, "payment_secret", + pinfo->payment_secret); + else { + assert(pinfo->blinded_paths); + const struct blinded_path *bpath = + pinfo->blinded_paths[route->path_num]; + json_myadd_blinded_path(req->js, "blinded_path", bpath); + } + send_outreq(req); + route_map_add(payment->routetracker->sent_routes, route); + if (taken(route)) + tal_steal(payment->routetracker->sent_routes, route); +} + static struct command_result *send_routes_cb(struct payment *payment) { assert(payment); @@ -757,18 +819,18 @@ static struct command_result *send_routes_cb(struct payment *payment) assert(routetracker); if (!routetracker->computed_routes || tal_count(routetracker->computed_routes) == 0) { - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, + plugin_log(payment->plugin, LOG_UNUSUAL, "%s: there are no routes to send, skipping.", __func__); return payment_continue(payment); } struct command *cmd = payment_command(payment); assert(cmd); + struct rpcbatch *batch = rpcbatch_new(cmd, sendroutes_done, payment); + for (size_t i = 0; i < tal_count(routetracker->computed_routes); i++) { struct route *route = routetracker->computed_routes[i]; - - route_sendpay_request(cmd, take(route), payment); - + add_sendpay_request(batch, take(route), payment); payment_note(payment, LOG_INFORM, "Sent route request: partid=%" PRIu64 " amount=%s prob=%.3lf fees=%s delay=%u path=%s", @@ -779,7 +841,7 @@ static struct command_result *send_routes_cb(struct payment *payment) route_delay(route), fmt_route_path(tmpctx, route)); } tal_resize(&routetracker->computed_routes, 0); - return payment_continue(payment); + return rpcbatch_done(batch); } REGISTER_PAYMENT_MODIFIER(send_routes, send_routes_cb); @@ -840,7 +902,7 @@ static struct command_result *collect_results_cb(struct payment *payment) if (!amount_msat_greater_eq(payment->total_delivering, payment->payment_info.amount)) { plugin_log( - pay_plugin->plugin, LOG_UNUSUAL, + payment->plugin, LOG_UNUSUAL, "%s: received a success sendpay for this " "payment but the total delivering amount %s " "is less than the payment amount %s.", @@ -885,25 +947,11 @@ REGISTER_PAYMENT_MODIFIER(collect_results, collect_results_cb); * * The default ending of a payment. */ -static struct command_result *end_done(struct command *cmd UNUSED, - const char *method UNUSED, - const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct payment *payment) +static struct command_result *end_cb(struct payment *payment) { return payment_fail(payment, PAY_STOPPED_RETRYING, "Payment execution ended without success."); } -static struct command_result *end_cb(struct payment *payment) -{ - struct command *cmd = payment_command(payment); - assert(cmd); - struct out_req *req = - jsonrpc_request_start(cmd, "waitblockheight", end_done, - payment_rpc_failure, payment); - json_add_num(req->js, "blockheight", 0); - return send_outreq(req); -} REGISTER_PAYMENT_MODIFIER(end, end_cb); @@ -968,7 +1016,7 @@ static struct command_result *pendingsendpays_done(struct command *cmd, } struct success_data success; - if (success_data_from_listsendpays(buf, arr, &success)) { + if (success_data_from_listsendpays(cmd, buf, arr, &success)) { /* Have success data, hence the payment is complete, we stop. */ payment->payment_info.start_time.ts.tv_sec = success.created_at; payment->payment_info.start_time.ts.tv_nsec = 0; @@ -999,7 +1047,7 @@ static struct command_result *pendingsendpays_done(struct command *cmd, JSON_SCAN(json_to_u64, &groupid)); if (err) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "%s json_scan of listsendpay returns the " "following error: %s", __func__, err); @@ -1036,7 +1084,7 @@ static struct command_result *pendingsendpays_done(struct command *cmd, JSON_SCAN(json_to_msat, &this_sent)); if (err) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "%s json_scan of listsendpay returns the " "following error: %s", __func__, err); @@ -1059,7 +1107,7 @@ static struct command_result *pendingsendpays_done(struct command *cmd, this_msat) || !amount_msat_add(&pending_sent, pending_sent, this_sent)) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "%s (line %d) amount_msat overflow.", __func__, __LINE__); } @@ -1083,7 +1131,7 @@ static struct command_result *pendingsendpays_done(struct command *cmd, payment->total_sent = pending_sent; payment->total_delivering = pending_msat; - plugin_log(pay_plugin->plugin, LOG_DBG, + plugin_log(cmd->plugin, LOG_DBG, "There are pending sendpays to this invoice. " "groupid = %" PRIu64 " " "delivering = %s, " @@ -1125,21 +1173,117 @@ REGISTER_PAYMENT_MODIFIER(pendingsendpays, pendingsendpays_cb); * Reduce the knowledge of the network as time goes by. */ +static struct command_result *age_done(struct command *cmd, + const char *method UNUSED, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct payment *payment) +{ + return payment_continue(payment); +} + static struct command_result *knowledgerelax_cb(struct payment *payment) { + struct renepay *renepay = get_renepay(payment->plugin); const u64 now_sec = time_now().ts.tv_sec; - enum renepay_errorcode err = uncertainty_relax( - pay_plugin->uncertainty, now_sec - pay_plugin->last_time); - if (err) - plugin_err(pay_plugin->plugin, - "uncertainty_relax failed with error %s", - renepay_errorcode_name(err)); - pay_plugin->last_time = now_sec; - return payment_continue(payment); + // const u64 time_delta = now_sec - renepay->last_time; + renepay->last_time = now_sec; + /* FIXME: implement a Markovian state relaxation, the time delta is all + * we need to provide. */ + struct command *cmd = payment_command(payment); + assert(cmd); + struct out_req *req = jsonrpc_request_start( + cmd, "askrene-age", age_done, payment_rpc_failure, payment); + json_add_string(req->js, "layer", RENEPAY_LAYER); + json_add_u64(req->js, "cutoff", now_sec - TIMER_FORGET_SEC); + return send_outreq(req); } REGISTER_PAYMENT_MODIFIER(knowledgerelax, knowledgerelax_cb); +/***************************************************************************** + * initpaymentlayer + * + * Initialize a layer in askrene to handle private information regarding this + * payment. + */ + +static struct command_result *createlayer_done(struct command *cmd UNUSED, + const char *method UNUSED, + const char *buf UNUSED, + const jsmntok_t *tok UNUSED, + struct payment *payment) +{ + return payment_continue(payment); +} + +static struct command_result *createlayer_fail(struct command *cmd, + const char *method UNUSED, + const char *buf, + const jsmntok_t *tok, + struct payment *payment) +{ + /* failure means layer already exists. + * FIXME: how do we prevent a layer from expiring before the payment + * finishes? */ + const jsmntok_t *messtok = json_get_member(buf, tok, "message"); + plugin_log(cmd->plugin, LOG_UNUSUAL, + "%s: create-layer failed with error: %.*s", __func__, + json_tok_full_len(messtok), json_tok_full(buf, messtok)); + return payment_continue(payment); +} + +static struct command_result *remove_layer_done(struct command *cmd, + const char *method UNUSED, + const char *buf UNUSED, + const jsmntok_t *tok UNUSED, + struct payment *payment UNUSED) +{ + return timer_complete(cmd); +} +static struct command_result *remove_layer_fail(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *tok, + struct payment *payment) +{ + plugin_log(cmd->plugin, LOG_UNUSUAL, "%s failed: '%.*s'", method, + json_tok_full_len(tok), json_tok_full(buf, tok)); + return remove_layer_done(cmd, method, buf, tok, payment); +} + +static struct command_result *remove_payment_layer(struct command *cmd, + struct payment *payment) +{ + + struct out_req *req = jsonrpc_request_start(cmd, "askrene-remove-layer", + remove_layer_done, + remove_layer_fail, payment); + json_add_string(req->js, "layer", payment->payment_layer); + plugin_log(cmd->plugin, LOG_DBG, "removing payment layer: %s", + payment->payment_layer); + return send_outreq(req); +} + +static struct command_result *initpaymentlayer_cb(struct payment *payment) +{ + struct command *cmd = payment_command(payment); + assert(cmd); + struct out_req *req = jsonrpc_request_start(cmd, "askrene-create-layer", + createlayer_done, createlayer_fail, payment); + json_add_string(req->js, "layer", payment->payment_layer); + json_add_bool(req->js, "persistent", false); + /* Remove this payment layer after one hour. If the plugin crashes + * unexpectedly, we might "leak" by forgetting to remove the layer, but + * the layer is not persistent anyways, therefore restarting CLN will + * remove it. */ + notleak(global_timer(cmd->plugin, time_from_sec(3600), + remove_payment_layer, payment)); + return send_outreq(req); +} + +REGISTER_PAYMENT_MODIFIER(initpaymentlayer, initpaymentlayer_cb); + /***************************************************************************** * channelfilter * @@ -1158,16 +1302,20 @@ REGISTER_PAYMENT_MODIFIER(knowledgerelax, knowledgerelax_cb); * FIXME: shall we set these threshold parameters as plugin options? */ +static struct command_result *channelfilter_done(struct command *cmd, + struct payment *payment) +{ + return payment_continue(payment); +} + static struct command_result *channelfilter_cb(struct payment *payment) { assert(payment); - assert(pay_plugin->gossmap); + struct renepay *renepay = get_renepay(payment->plugin); + assert(renepay->gossmap); const double HTLC_MAX_FRACTION = 0.01; // 1% const u64 HTLC_MAX_STOP_MSAT = 1000000000; // 1M sats - u64 disabled_count = 0; - - u64 htlc_max_threshold = HTLC_MAX_FRACTION * payment->payment_info .amount.millisatoshis; /* Raw: a fraction of this amount. */ /* Don't exclude channels with htlc_max above HTLC_MAX_STOP_MSAT even if @@ -1175,34 +1323,43 @@ static struct command_result *channelfilter_cb(struct payment *payment) * HTLC_MAX_FRACTION. */ htlc_max_threshold = MIN(htlc_max_threshold, HTLC_MAX_STOP_MSAT); - gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods); + struct command *cmd = payment_command(payment); + struct request_batch *batch = request_batch_new( + cmd, NULL, log_payment_err, channelfilter_done, payment); + struct out_req *req; + for (const struct gossmap_node *node = - gossmap_first_node(pay_plugin->gossmap); - node; node = gossmap_next_node(pay_plugin->gossmap, node)) { + gossmap_first_node(renepay->gossmap); + node; node = gossmap_next_node(renepay->gossmap, node)) { for (size_t i = 0; i < node->num_chans; i++) { int dir; const struct gossmap_chan *chan = gossmap_nth_chan( - pay_plugin->gossmap, node, i, &dir); + renepay->gossmap, node, i, &dir); const u64 htlc_max = fp16_to_u64(chan->half[dir].htlc_max); if (htlc_max < htlc_max_threshold) { struct short_channel_id_dir scidd = { .scid = gossmap_chan_scid( - pay_plugin->gossmap, chan), + renepay->gossmap, chan), .dir = dir}; - disabledmap_add_channel(payment->disabledmap, - scidd); + req = add_to_batch(cmd, batch, + "askrene-update-channel"); + json_add_string(req->js, "layer", + payment->payment_layer); + json_add_short_channel_id_dir( + req->js, "short_channel_id_dir", scidd); + json_add_bool(req->js, "enabled", false); + send_outreq(req); disabled_count++; } } } - gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods); // FIXME: prune the network over other parameters, eg. capacity, // fees, ... - plugin_log(pay_plugin->plugin, LOG_DBG, + plugin_log(payment->plugin, LOG_DBG, "channelfilter: disabling %" PRIu64 " channels.", disabled_count); - return payment_continue(payment); + return batch_done(cmd, batch); } REGISTER_PAYMENT_MODIFIER(channelfilter, channelfilter_cb); @@ -1258,26 +1415,25 @@ REGISTER_PAYMENT_CONDITION(retry, retry_cb); // add check pre-approved invoice void *payment_virtual_program[] = { /*0*/ OP_CALL, &previoussuccess_pay_mod, - /*2*/ OP_CALL, &knowledgerelax_pay_mod, - /*4*/ OP_CALL, &getmychannels_pay_mod, - /*6*/ OP_CALL, &selfpay_pay_mod, - /*8*/ OP_CALL, &refreshgossmap_pay_mod, - /*10*/ OP_CALL, &routehints_pay_mod, - /*12*/ OP_CALL, &blindedhints_pay_mod, - /*14*/OP_CALL, &channelfilter_pay_mod, + /*2*/ OP_CALL, &initpaymentlayer_pay_mod, + /*4*/ OP_CALL, &knowledgerelax_pay_mod, + /*6*/ OP_CALL, &refreshgossmap_pay_mod, + /*8*/ OP_CALL, &routehints_pay_mod, + /*10*/ OP_CALL, &blindedhints_pay_mod, + /*12*/OP_CALL, &channelfilter_pay_mod, // TODO shadow_additions /* do */ - /*16*/ OP_CALL, &pendingsendpays_pay_mod, - /*18*/ OP_CALL, &checktimeout_pay_mod, - /*20*/ OP_CALL, &refreshgossmap_pay_mod, - /*22*/ OP_CALL, &compute_routes_pay_mod, - /*24*/ OP_CALL, &send_routes_pay_mod, + /*14*/ OP_CALL, &pendingsendpays_pay_mod, + /*16*/ OP_CALL, &checktimeout_pay_mod, + /*18*/ OP_CALL, &refreshgossmap_pay_mod, + /*20*/ OP_CALL, &getroutes_pay_mod, + /*22*/ OP_CALL, &send_routes_pay_mod, /*do*/ - /*26*/ OP_CALL, &sleep_pay_mod, - /*28*/ OP_CALL, &collect_results_pay_mod, + /*24*/ OP_CALL, &sleep_pay_mod, + /*26*/ OP_CALL, &collect_results_pay_mod, /*while*/ - /*30*/ OP_IF, ¬haveresults_pay_cond, (void *)26, + /*28*/ OP_IF, ¬haveresults_pay_cond, (void *)24, /* while */ - /*33*/ OP_IF, &retry_pay_cond, (void *)16, - /*36*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ - /*38*/ NULL}; + /*31*/ OP_IF, &retry_pay_cond, (void *)14, + /*34*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */ + /*36*/ NULL}; diff --git a/plugins/renepay/payment.c b/plugins/renepay/payment.c index 02566b01cdff..5ac7805a1c20 100644 --- a/plugins/renepay/payment.c +++ b/plugins/renepay/payment.c @@ -7,17 +7,18 @@ #include #include #include -#include +#include #include static struct command_result *payment_finish(struct payment *p); struct payment *payment_new(const tal_t *ctx, const struct sha256 *payment_hash, - const char *invstr TAKES) + const char *invstr TAKES, + struct plugin *plugin) { struct payment *p = tal(ctx, struct payment); memset(p, 0, sizeof(struct payment)); - + p->plugin = plugin; struct payment_info *pinfo = &p->payment_info; assert(payment_hash); @@ -38,12 +39,11 @@ struct payment *payment_new(const tal_t *ctx, const struct sha256 *payment_hash, p->exec_state = INVALID_STATE; p->next_partid = 1; p->cmd_array = tal_arr(p, struct command *, 0); - p->local_gossmods = NULL; - p->disabledmap = disabledmap_new(p); p->have_results = false; p->retry = false; p->waitresult_timer = NULL; p->routetracker = new_routetracker(p, p); + p->payment_layer = fmt_sha256(p, &pinfo->payment_hash); return p; } @@ -52,13 +52,7 @@ static void payment_cleanup(struct payment *p) { p->exec_state = INVALID_STATE; tal_resize(&p->cmd_array, 0); - p->local_gossmods = tal_free(p->local_gossmods); - /* FIXME: for optimization, a cleanup should prune all the data that has - * no use after a payent is completed. The entire disablemap structure - * is no longer needed, hence I guess we should free it not just reset - * it. */ - disabledmap_reset(p->disabledmap); p->waitresult_timer = tal_free(p->waitresult_timer); routetracker_cleanup(p->routetracker); @@ -91,7 +85,6 @@ bool payment_refresh(struct payment *p){ assert(p->cmd_array); assert(tal_count(p->cmd_array) == 0); - p->local_gossmods = tal_free(p->local_gossmods); p->have_results = false; p->retry = false; p->waitresult_timer = tal_free(p->waitresult_timer); @@ -117,6 +110,7 @@ bool payment_set_constraints( bool use_shadow, const struct route_exclusion **exclusions) { + // FIXME: add exclusions to a layer assert(p); struct payment_info *pinfo = &p->payment_info; @@ -136,17 +130,6 @@ bool payment_set_constraints( pinfo->base_prob_success = base_prob_success_millionths / 1e6; pinfo->use_shadow = use_shadow; - assert(p->disabledmap); - disabledmap_reset(p->disabledmap); - - for (size_t i = 0; i < tal_count(exclusions); i++) { - const struct route_exclusion *ex = exclusions[i]; - if (ex->type == EXCLUDE_CHANNEL) - disabledmap_add_channel(p->disabledmap, ex->u.chan_id); - else - disabledmap_add_node(p->disabledmap, ex->u.node_id); - } - return true; } @@ -175,7 +158,7 @@ struct amount_msat payment_fees(const struct payment *p) if (!amount_msat_sub(&fees, sent, delivered)) plugin_err( - pay_plugin->plugin, + p->plugin, "Strange, sent amount (%s) is less than delivered (%s), " "aborting.", fmt_amount_msat(tmpctx, sent), @@ -277,7 +260,7 @@ void payment_note(struct payment *p, enum log_level lvl, const char *fmt, ...) tal_arr_expand(&p->paynotes, str); /* Log at debug, unless it's weird... */ - plugin_log(pay_plugin->plugin, lvl < LOG_UNUSUAL ? LOG_DBG : lvl, "%s", + plugin_log(p->plugin, lvl < LOG_UNUSUAL ? LOG_DBG : lvl, "%s", str); for (size_t i = 0; i < tal_count(p->cmd_array); i++) { @@ -321,7 +304,6 @@ void payment_disable_chan(struct payment *p, struct short_channel_id_dir scidd, enum log_level lvl, const char *fmt, ...) { assert(p); - assert(p->disabledmap); va_list ap; const char *str; @@ -331,14 +313,12 @@ void payment_disable_chan(struct payment *p, struct short_channel_id_dir scidd, payment_note(p, lvl, "disabling %s: %s", fmt_short_channel_id_dir(tmpctx, &scidd), str); - disabledmap_add_channel(p->disabledmap, scidd); } void payment_warn_chan(struct payment *p, struct short_channel_id_dir scidd, enum log_level lvl, const char *fmt, ...) { assert(p); - assert(p->disabledmap); va_list ap; const char *str; @@ -346,23 +326,15 @@ void payment_warn_chan(struct payment *p, struct short_channel_id_dir scidd, str = tal_vfmt(tmpctx, fmt, ap); va_end(ap); - if (disabledmap_channel_is_warned(p->disabledmap, scidd)) { - payment_disable_chan(p, scidd, lvl, "%s, channel warned twice", - str); - return; - } - payment_note( p, lvl, "flagged for warning %s: %s, next time it will be disabled", fmt_short_channel_id_dir(tmpctx, &scidd), str); - disabledmap_warn_channel(p->disabledmap, scidd); } void payment_disable_node(struct payment *p, struct node_id node, enum log_level lvl, const char *fmt, ...) { assert(p); - assert(p->disabledmap); va_list ap; const char *str; @@ -372,5 +344,4 @@ void payment_disable_node(struct payment *p, struct node_id node, payment_note(p, lvl, "disabling node %s: %s", fmt_node_id(tmpctx, &node), str); - disabledmap_add_node(p->disabledmap, node); } diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index e3d30e301c10..611b731e08ea 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -4,7 +4,6 @@ #include #include #include -#include #include enum payment_status { PAYMENT_PENDING, PAYMENT_SUCCESS, PAYMENT_FAIL }; @@ -53,9 +52,6 @@ struct payment { /* Running commands that want this payment */ struct command **cmd_array; - /* Localmods to apply to gossip_map for our own use. */ - struct gossmap_localmods *local_gossmods; - /* Routes will be computed to reach this node, could be a fake node that * we use to handle multiple blinded paths. */ struct node_id *routing_destination; @@ -73,6 +69,12 @@ struct payment { struct plugin_timer *waitresult_timer; struct routetracker *routetracker; + + /* Knowledge layer concerning this payment. */ + const char *payment_layer; + + /* For logs and access to global data. */ + struct plugin *plugin; }; static inline const struct sha256 payment_hash(const struct payment *p) @@ -105,7 +107,8 @@ HTABLE_DEFINE_NODUPS_TYPE(struct payment, payment_hash, payment_hash64, struct payment *payment_new( const tal_t *ctx, const struct sha256 *payment_hash, - const char *invstr TAKES); + const char *invstr TAKES, + struct plugin *plugin); bool payment_set_constraints( struct payment *p, diff --git a/plugins/renepay/main.c b/plugins/renepay/renepay.c similarity index 86% rename from plugins/renepay/main.c rename to plugins/renepay/renepay.c index 01b4d13be34c..6c3e4c5cedb9 100644 --- a/plugins/renepay/main.c +++ b/plugins/renepay/renepay.c @@ -15,9 +15,10 @@ #include #include #include -#include +#include #include #include +#include #include // TODO(eduardo): notice that pending attempts performed with another @@ -25,29 +26,35 @@ // it would be nice if listsendpay would give us the route of pending // sendpays. -struct pay_plugin *pay_plugin; - static void memleak_mark(struct plugin *p, struct htable *memtable) { - memleak_scan_obj(memtable, pay_plugin); - memleak_scan_htable(memtable, - &pay_plugin->uncertainty->chan_extra_map->raw); - memleak_scan_htable(memtable, &pay_plugin->payment_map->raw); - memleak_scan_htable(memtable, &pay_plugin->pending_routes->raw); + struct renepay *renepay = get_renepay(p); + memleak_scan_obj(memtable, renepay); + memleak_scan_htable(memtable, &renepay->payment_map->raw); + memleak_scan_htable(memtable, &renepay->pending_routes->raw); +} + +static struct command_result *createlayer_done(struct command *aux_cmd, + const char *method, + const char *buf, + const jsmntok_t *result, + void *unused) +{ + return aux_command_done(aux_cmd); } static const char *init(struct command *init_cmd, const char *buf UNUSED, const jsmntok_t *config UNUSED) { struct plugin *p = init_cmd->plugin; + struct renepay *renepay = get_renepay(p); size_t num_channel_updates_rejected = 0; - tal_steal(p, pay_plugin); - pay_plugin->plugin = p; - pay_plugin->last_time = 0; + renepay->plugin = p; + renepay->last_time = 0; rpc_scan(init_cmd, "getinfo", take(json_out_obj(NULL, NULL, NULL)), - "{id:%}", JSON_SCAN(json_to_node_id, &pay_plugin->my_id)); + "{id:%}", JSON_SCAN(json_to_node_id, &renepay->my_id)); /* BOLT #4: * ## `max_htlc_cltv` Selection @@ -56,43 +63,39 @@ static const char *init(struct command *init_cmd, * deployed by Lightning implementations. */ /* FIXME: Typo in spec for CLTV in descripton! But it breaks our spelling check, so we omit it above */ - pay_plugin->maxdelay_default = 2016; + renepay->maxdelay_default = 2016; /* max-locktime-blocks deprecated in v24.05, but still grab it! */ rpc_scan(init_cmd, "listconfigs", take(json_out_obj(NULL, NULL, NULL)), "{configs:" "{max-locktime-blocks?:{value_int:%}}}", - JSON_SCAN(json_to_number, &pay_plugin->maxdelay_default) + JSON_SCAN(json_to_number, &renepay->maxdelay_default) ); - pay_plugin->payment_map = tal(pay_plugin, struct payment_map); - payment_map_init(pay_plugin->payment_map); + renepay->payment_map = tal(renepay, struct payment_map); + payment_map_init(renepay->payment_map); - pay_plugin->pending_routes = tal(pay_plugin, struct route_map); - route_map_init(pay_plugin->pending_routes); + renepay->pending_routes = tal(renepay, struct route_map); + route_map_init(renepay->pending_routes); - pay_plugin->gossmap = gossmap_load(pay_plugin, - GOSSIP_STORE_FILENAME, - plugin_gossmap_logcb, - p); + renepay->gossmap = gossmap_load(renepay, GOSSIP_STORE_FILENAME, + plugin_gossmap_logcb, p); - if (!pay_plugin->gossmap) + if (!renepay->gossmap) plugin_err(p, "Could not load gossmap %s: %s", GOSSIP_STORE_FILENAME, strerror(errno)); if (num_channel_updates_rejected) plugin_log(p, LOG_DBG, "gossmap ignored %zu channel updates", num_channel_updates_rejected); - pay_plugin->uncertainty = uncertainty_new(pay_plugin); - int skipped_count = - uncertainty_update(pay_plugin->uncertainty, pay_plugin->gossmap); - if (skipped_count) - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "%s: uncertainty was updated but %d channels have " - "been ignored.", - __func__, skipped_count); plugin_set_memleak_handler(p, memleak_mark); + struct out_req *req = + jsonrpc_request_start(aux_command(init_cmd), "askrene-create-layer", + createlayer_done, plugin_broken_cb, NULL); + json_add_string(req->js, "layer", RENEPAY_LAYER); + json_add_bool(req->js, "persistent", true); + send_outreq(req); return NULL; } @@ -100,6 +103,7 @@ static struct command_result *json_renepaystatus(struct command *cmd, const char *buf, const jsmntok_t *params) { + struct renepay *renepay = get_renepay(cmd->plugin); const char *invstring; struct json_stream *ret; @@ -127,7 +131,7 @@ static struct command_result *json_renepaystatus(struct command *cmd, "Invalid bolt11: %s", fail); struct payment *payment = - payment_map_get(pay_plugin->payment_map, b11->payment_hash); + payment_map_get(renepay->payment_map, b11->payment_hash); if(payment) { @@ -140,8 +144,8 @@ static struct command_result *json_renepaystatus(struct command *cmd, /* show all payments */ struct payment_map_iter it; for (struct payment *p = - payment_map_first(pay_plugin->payment_map, &it); - p; p = payment_map_next(pay_plugin->payment_map, &it)) { + payment_map_first(renepay->payment_map, &it); + p; p = payment_map_next(renepay->payment_map, &it)) { json_object_start(ret, NULL); json_add_payment(ret, p); json_object_end(ret); @@ -156,7 +160,7 @@ static struct command_result * payment_start(struct payment *p) { assert(p); p->status = PAYMENT_PENDING; - plugin_log(pay_plugin->plugin, LOG_DBG, "Starting renepay"); + plugin_log(p->plugin, LOG_DBG, "Starting renepay"); p->exec_state = 0; return payment_continue(p); } @@ -164,6 +168,7 @@ static struct command_result * payment_start(struct payment *p) static struct command_result *json_renepay(struct command *cmd, const char *buf, const jsmntok_t *params) { + struct renepay *renepay = get_renepay(cmd->plugin); /* === Parse command line arguments === */ // TODO check if we leak some of these temporary variables @@ -203,7 +208,7 @@ static struct command_result *json_renepay(struct command *cmd, const char *buf, /* maxdelay has a configuration default value named * "max-locktime-blocks", this is retrieved at * init. */ - pay_plugin->maxdelay_default), + renepay->maxdelay_default), p_opt_def("retry_for", param_number, &retryfor, 60), // 60 seconds @@ -321,11 +326,12 @@ static struct command_result *json_renepay(struct command *cmd, const char *buf, // one payment_hash one payment is not assumed, it is enforced assert(payment_hash); struct payment *payment = - payment_map_get(pay_plugin->payment_map, *payment_hash); + payment_map_get(renepay->payment_map, *payment_hash); if(!payment) { - payment = payment_new(tmpctx, payment_hash, invstr); + payment = + payment_new(tmpctx, payment_hash, invstr, cmd->plugin); if (!payment) return command_fail(cmd, PLUGIN_ERROR, "failed to create a new payment"); @@ -401,8 +407,8 @@ static struct command_result *json_renepay(struct command *cmd, const char *buf, "failed to register command"); // good to go - payment = tal_steal(pay_plugin, payment); - payment_map_add(pay_plugin->payment_map, payment); + payment = tal_steal(renepay, payment); + payment_map_add(renepay->payment_map, payment); return payment_start(payment); } @@ -480,12 +486,12 @@ int main(int argc, char *argv[]) setup_locale(); /* Most gets initialized in init(), but set debug options here. */ - pay_plugin = tal(NULL, struct pay_plugin); - pay_plugin->debug_mcf = pay_plugin->debug_payflow = false; + struct renepay *renepay = tal(NULL, struct renepay); + renepay->debug_mcf = renepay->debug_payflow = false; plugin_main( argv, - init, NULL, + init, take(renepay), PLUGIN_RESTARTABLE, /* init_rpc */ true, /* features */ NULL, @@ -495,10 +501,10 @@ int main(int argc, char *argv[]) /* notification topics */ NULL, 0, plugin_option("renepay-debug-mcf", "flag", "Enable renepay MCF debug info.", - flag_option, NULL, &pay_plugin->debug_mcf), + flag_option, NULL, &renepay->debug_mcf), plugin_option("renepay-debug-payflow", "flag", "Enable renepay payment flows debug info.", - flag_option, NULL, &pay_plugin->debug_payflow), + flag_option, NULL, &renepay->debug_payflow), NULL); return 0; diff --git a/plugins/renepay/payplugin.h b/plugins/renepay/renepay.h similarity index 87% rename from plugins/renepay/payplugin.h rename to plugins/renepay/renepay.h index a0da496f048d..5f1682a015fe 100644 --- a/plugins/renepay/payplugin.h +++ b/plugins/renepay/renepay.h @@ -1,13 +1,11 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_PAYPLUGIN_H -#define LIGHTNING_PLUGINS_RENEPAY_PAYPLUGIN_H +#ifndef LIGHTNING_PLUGINS_RENEPAY_RENEPAY_H +#define LIGHTNING_PLUGINS_RENEPAY_RENEPAY_H #include "config.h" #include #include #include -#include #include #include -#include // TODO(eduardo): renepaystatus should be similar to paystatus @@ -40,7 +38,7 @@ // - with current knowledge there is no flow solution to destination /* Our convenient global data, here in one place. */ -struct pay_plugin { +struct renepay { /* From libplugin */ struct plugin *plugin; @@ -59,9 +57,6 @@ struct pay_plugin { /* All the struct payment */ struct payment_map *payment_map; - /* Per-channel metadata: some persists between payments */ - struct uncertainty *uncertainty; - /* Pending sendpays. Each pending route has an associated HTLC data in * the uncertainty network. Pending routes are matched against sendpay * notifications. */ @@ -87,7 +82,4 @@ struct pay_plugin { u64 last_time; }; -/* Set in init */ -extern struct pay_plugin *pay_plugin; - -#endif /* LIGHTNING_PLUGINS_RENEPAY_PAYPLUGIN_H */ +#endif /* LIGHTNING_PLUGINS_RENEPAY_RENEPAY_H */ diff --git a/plugins/renepay/renepayconfig.h b/plugins/renepay/renepayconfig.h index d1ade031618b..968d82198ea7 100644 --- a/plugins/renepay/renepayconfig.h +++ b/plugins/renepay/renepayconfig.h @@ -2,6 +2,8 @@ #define LIGHTNING_PLUGINS_RENEPAY_RENEPAYCONFIG_H #include "config.h" +#define RENEPAY_LAYER "renepay" + #define MAX_NUM_ATTEMPTS 10 /* Knowledge is proportionally decreased with time up to TIMER_FORGET_SEC when diff --git a/plugins/renepay/route.c b/plugins/renepay/route.c index 521f956ba690..340a4f2f1d7a 100644 --- a/plugins/renepay/route.c +++ b/plugins/renepay/route.c @@ -24,87 +24,6 @@ struct route *new_route(const tal_t *ctx, u64 groupid, return route; } -/* Construct a route from a flow. - * - * @ctx: allocator - * @groupid, @partid, @payment_hash: unique identification keys for this route - * @final_cltv: final delay required by the payment - * @gossmap: global gossmap - * @flow: the flow to convert to route */ -struct route *flow_to_route(const tal_t *ctx, - u64 groupid, u64 partid, struct sha256 payment_hash, - u32 final_cltv, struct gossmap *gossmap, - struct flow *flow, - bool blinded_destination) -{ - struct route *route = - new_route(ctx, groupid, partid, payment_hash, - AMOUNT_MSAT(0), AMOUNT_MSAT(0)); - - size_t pathlen = tal_count(flow->path); - route->hops = tal_arr(route, struct route_hop, pathlen); - - for (size_t i = 0; i < pathlen; i++) { - struct route_hop *hop = &route->hops[i]; - struct gossmap_node *n; - n = gossmap_nth_node(gossmap, flow->path[i], !flow->dirs[i]); - gossmap_node_get_id(gossmap, n, &hop->node_id); - - hop->scid = gossmap_chan_scid(gossmap, flow->path[i]); - hop->direction = flow->dirs[i]; - } - - /* Calculate cumulative delays (backwards) */ - route->hops[pathlen - 1].delay = final_cltv; - route->hops[pathlen - 1].amount = flow->amount; - - for (int i = (int)pathlen - 2; i >= 0; i--) { - const struct half_chan *h = flow_edge(flow, i + 1); - - route->hops[i].delay = route->hops[i + 1].delay + h->delay; - route->hops[i].amount = route->hops[i + 1].amount; - if (!amount_msat_add_fee(&route->hops[i].amount, h->base_fee, - h->proportional_fee)) - goto function_fail; - } - route->success_prob = flow->success_prob; - route->amount_deliver = route->hops[pathlen - 1].amount; - route->amount_sent = route->hops[0].amount; - - if (blinded_destination) { - route->path_num = route->hops[pathlen - 1].scid.u64; - tal_arr_remove(&route->hops, pathlen - 1); - } - - return route; - -function_fail: - return tal_free(route); -} - -struct route **flows_to_routes(const tal_t *ctx, - u64 groupid, u64 partid, - struct sha256 payment_hash, u32 final_cltv, - struct gossmap *gossmap, struct flow **flows) -{ - assert(gossmap); - assert(flows); - const size_t N = tal_count(flows); - struct route **routes = tal_arr(ctx, struct route *, N); - for (size_t i = 0; i < N; i++) { - routes[i] = - flow_to_route(routes, groupid, partid++, - payment_hash, final_cltv, gossmap, flows[i], - false); - if (!routes[i]) - goto function_fail; - } - return routes; - -function_fail: - return tal_free(routes); -} - const char *fmt_route_path(const tal_t *ctx, const struct route *route) { tal_t *this_ctx = tal(ctx, tal_t); diff --git a/plugins/renepay/route.h b/plugins/renepay/route.h index 592513d5b188..a944033cdcc6 100644 --- a/plugins/renepay/route.h +++ b/plugins/renepay/route.h @@ -11,7 +11,6 @@ #include #include #include -#include #include struct payment; @@ -122,17 +121,6 @@ struct route *new_route(const tal_t *ctx, u64 groupid, struct amount_msat amount, struct amount_msat amount_sent); -struct route *flow_to_route(const tal_t *ctx, - u64 groupid, u64 partid, struct sha256 payment_hash, - u32 final_cltv, struct gossmap *gossmap, - struct flow *flow, - bool blinded_destination); - -struct route **flows_to_routes(const tal_t *ctx, - u64 groupid, u64 partid, - struct sha256 payment_hash, u32 final_cltv, - struct gossmap *gossmap, struct flow **flows); - static inline struct short_channel_id_dir hop_to_scidd(const struct route_hop *hop) { diff --git a/plugins/renepay/routebuilder.c b/plugins/renepay/routebuilder.c deleted file mode 100644 index 4e9d15142326..000000000000 --- a/plugins/renepay/routebuilder.c +++ /dev/null @@ -1,433 +0,0 @@ -#include "config.h" -#include -#include -#include - -#include - -static void uncertainty_remove_routes(struct uncertainty *uncertainty, - struct route **routes) -{ - const size_t N = tal_count(routes); - for (size_t i = 0; i < N; i++) - uncertainty_remove_htlcs(uncertainty, routes[i]); -} - -/* Shave-off amounts that do not meet the liquidity constraints. Disable - * channels that produce an htlc_max bottleneck. */ -static enum renepay_errorcode -flow_adjust_htlcmax_constraints(struct flow *flow, struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - bitmap *disabled_bitmap) -{ - assert(flow); - assert(gossmap); - assert(chan_extra_map); - assert(disabled_bitmap); - assert(!amount_msat_is_zero(flow_delivers(flow))); - - enum renepay_errorcode errorcode; - - struct amount_msat max_deliverable; - const struct gossmap_chan *bad_channel; - - errorcode = flow_maximum_deliverable(&max_deliverable, flow, gossmap, - chan_extra_map, &bad_channel); - - if (!errorcode) { - assert(!amount_msat_is_zero(max_deliverable)); - - // no issues - flow->amount = - amount_msat_min(flow_delivers(flow), max_deliverable); - - return errorcode; - } - - if (errorcode == RENEPAY_BAD_CHANNEL) { - // this is a channel that we can disable - // FIXME: log this error? disabling both directions? - bitmap_set_bit(disabled_bitmap, - gossmap_chan_idx(gossmap, bad_channel) * 2 + 0); - bitmap_set_bit(disabled_bitmap, - gossmap_chan_idx(gossmap, bad_channel) * 2 + 1); - } - - // we had an unexpected error - return errorcode; -} - -static enum renepay_errorcode -route_check_constraints(struct route *route, struct gossmap *gossmap, - struct uncertainty *uncertainty, - bitmap *disabled_bitmap) -{ - assert(route); - assert(route->hops); - const size_t pathlen = tal_count(route->hops); - if (pathlen == 0) - return RENEPAY_NOERROR; - if (!amount_msat_eq(route->amount_deliver, - route->hops[pathlen - 1].amount)) - return RENEPAY_PRECONDITION_ERROR; - if (!amount_msat_eq(route->amount_sent, route->hops[0].amount)) - return RENEPAY_PRECONDITION_ERROR; - - for (size_t i = 0; i < pathlen; i++) { - struct route_hop *hop = &route->hops[i]; - int dir = hop->direction; - struct gossmap_chan *chan = - gossmap_find_chan(gossmap, &hop->scid); - assert(chan); - struct chan_extra *ce = - uncertainty_find_channel(uncertainty, hop->scid); - - // check that we stay within the htlc max and min limits - if (amount_msat_greater(hop->amount, - gossmap_chan_htlc_max(chan, dir)) || - amount_msat_less(hop->amount, - gossmap_chan_htlc_min(chan, dir))) { - bitmap_set_bit(disabled_bitmap, - gossmap_chan_idx(gossmap, chan) * 2 + - dir); - return RENEPAY_BAD_CHANNEL; - } - - // check that the sum of all htlcs and this amount does not - // exceed the maximum known by our knowledge - struct amount_msat total_htlcs = ce->half[dir].htlc_total; - if (!amount_msat_add(&total_htlcs, total_htlcs, hop->amount)) - return RENEPAY_AMOUNT_OVERFLOW; - - if (amount_msat_greater(total_htlcs, ce->half[dir].known_max)) - return RENEPAY_UNEXPECTED; - } - return RENEPAY_NOERROR; -} - -static void tal_report_error(const tal_t *ctx, enum jsonrpc_errcode *ecode, - const char **fail, - enum jsonrpc_errcode error_value, const char *fmt, - ...) -{ - tal_t *this_ctx = tal(ctx, tal_t); - - va_list ap; - const char *str; - - va_start(ap, fmt); - str = tal_vfmt(this_ctx, fmt, ap); - va_end(ap); - - if (ecode) - *ecode = error_value; - - if (fail) - *fail = tal_fmt(ctx, "%s", str); - - this_ctx = tal_free(this_ctx); -} - -/* Routes are computed and saved in the payment for later use. */ -struct route **get_routes(const tal_t *ctx, - struct payment_info *payment_info, - - const struct node_id *source, - const struct node_id *destination, - struct gossmap *gossmap, - struct uncertainty *uncertainty, - struct disabledmap *disabledmap, - - struct amount_msat amount_to_deliver, - struct amount_msat feebudget, - - u64 *next_partid, - u64 groupid, - bool blinded_destination, - - enum jsonrpc_errcode *ecode, - const char **fail) -{ - assert(gossmap); - assert(uncertainty); - - const tal_t *this_ctx = tal(ctx, tal_t); - struct route **routes = tal_arr(ctx, struct route *, 0); - - double probability_budget = payment_info->min_prob_success; - const double base_probability = payment_info->base_prob_success; - double delay_feefactor = payment_info->delay_feefactor; - const double base_fee_penalty = payment_info->base_fee_penalty; - const double prob_cost_factor = payment_info->prob_cost_factor; - const unsigned int maxdelay = payment_info->maxdelay; - bool delay_feefactor_updated = true; - - bitmap *disabled_bitmap = - tal_disabledmap_get_bitmap(this_ctx, disabledmap, gossmap); - - if (!disabled_bitmap) { - tal_report_error(ctx, ecode, fail, PLUGIN_ERROR, - "Failed to build disabled_bitmap."); - goto function_fail; - } - if (amount_msat_is_zero(amount_to_deliver)) { - tal_report_error(ctx, ecode, fail, PLUGIN_ERROR, - "amount to deliver is zero"); - goto function_fail; - } - - /* Also disable every channel that we don't have in the chan_extra_map. - * We might have channels in the gossmap that are not usable for - * probability computations for example if we don't know their capacity. - * We can tell the solver to ignore those channels by disabling them - * here. - */ - for (struct gossmap_chan *chan = gossmap_first_chan(gossmap); chan; - chan = gossmap_next_chan(gossmap, chan)) { - const u32 chan_id = gossmap_chan_idx(gossmap, chan); - struct short_channel_id scid = gossmap_chan_scid(gossmap, chan); - struct chan_extra *ce = - chan_extra_map_get(uncertainty->chan_extra_map, scid); - if (!ce) { - bitmap_set_bit(disabled_bitmap, chan_id * 2 + 0); - bitmap_set_bit(disabled_bitmap, chan_id * 2 + 1); - } - } - - const struct gossmap_node *src, *dst; - src = gossmap_find_node(gossmap, source); - if (!src) { - tal_report_error(ctx, ecode, fail, PAY_ROUTE_NOT_FOUND, - "We don't have any channels."); - goto function_fail; - } - dst = gossmap_find_node(gossmap, destination); - if (!dst) { - tal_report_error( - ctx, ecode, fail, PAY_ROUTE_NOT_FOUND, - "Destination is unknown in the network gossip."); - goto function_fail; - } - - char *errmsg; - - while (!amount_msat_is_zero(amount_to_deliver)) { - - /* TODO: choose an algorithm, could be something like - * payment->algorithm, that we set up based on command line - * options and that can be changed according to some conditions - * met during the payment process, eg. add "select_solver" pay - * mod. */ - /* TODO: use uncertainty instead of chan_extra */ - - /* Min. Cost Flow algorithm to find optimal flows. */ - struct flow **flows = - minflow(this_ctx, gossmap, src, dst, - uncertainty_get_chan_extra_map(uncertainty), - disabled_bitmap, amount_to_deliver, feebudget, - probability_budget, - base_probability, - delay_feefactor, - base_fee_penalty, - prob_cost_factor, &errmsg); - delay_feefactor_updated = false; - - if (!flows) { - tal_report_error( - ctx, ecode, fail, PAY_ROUTE_NOT_FOUND, - "minflow couldn't find a feasible flow: %s", - errmsg); - goto function_fail; - } - - enum renepay_errorcode errorcode; - for (size_t i = 0; i < tal_count(flows); i++) { - - // do we overpay? - if (amount_msat_greater(flows[i]->amount, - amount_to_deliver)) { - // should not happen - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "%s: flow is delivering to destination " - "(%s) more than requested (%s)", - __func__, - fmt_amount_msat(this_ctx, flows[i]->amount), - fmt_amount_msat(this_ctx, - amount_to_deliver)); - goto function_fail; - } - - // fees considered, remove the least amount as to fit in - // with the htlcmax constraints - errorcode = flow_adjust_htlcmax_constraints( - flows[i], gossmap, - uncertainty_get_chan_extra_map(uncertainty), - disabled_bitmap); - if (errorcode == RENEPAY_BAD_CHANNEL) - // we handle a bad channel error by disabling - // it, infinite loops are avoided since we have - // everytime less and less channels - continue; - if (errorcode) { - // any other error is bad - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "flow_adjust_htlcmax_constraints returned " - "errorcode: %s", - renepay_errorcode_name(errorcode)); - goto function_fail; - } - - // a bound check, we shouldn't deliver a zero amount, it - // would mean a bug somewhere - if (amount_msat_is_zero(flows[i]->amount)) { - tal_report_error(ctx, ecode, fail, PLUGIN_ERROR, - "flow conveys a zero amount"); - goto function_fail; - } - - const double prob = flow_probability( - flows[i], gossmap, - uncertainty_get_chan_extra_map(uncertainty), true); - if (prob < 0) { - // should not happen - tal_report_error(ctx, ecode, fail, PLUGIN_ERROR, - "flow_probability failed"); - goto function_fail; - } - - // this flow seems good, build me a route - struct route *r = flow_to_route( - this_ctx, groupid, *next_partid, - payment_info->payment_hash, - payment_info->final_cltv, gossmap, flows[i], - blinded_destination); - - if (!r) { - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "%s failed to build route from flow.", - __func__); - goto function_fail; - } - - const struct amount_msat fee = route_fees(r); - const struct amount_msat delivering = route_delivers(r); - - // are we still within the fee budget? - if (amount_msat_greater(fee, feebudget)) { - tal_report_error( - ctx, ecode, fail, PAY_ROUTE_TOO_EXPENSIVE, - "Fee exceeds our fee budget, fee=%s " - "(feebudget=%s)", - fmt_amount_msat(this_ctx, fee), - fmt_amount_msat(this_ctx, feebudget)); - goto function_fail; - } - - // check the CLTV delay does not exceed our settings - const unsigned int delay = route_delay(r); - if (delay > maxdelay) { - if (!delay_feefactor_updated) { - delay_feefactor *= 2; - delay_feefactor_updated = true; - } - - /* FIXME: What is a sane limit? */ - if (delay_feefactor > 1000) { - tal_report_error( - ctx, ecode, fail, - PAY_ROUTE_TOO_EXPENSIVE, - "CLTV delay exceeds our CLTV " - "budget, delay=%u (maxdelay=%u)", - delay, maxdelay); - goto function_fail; - } - continue; - } - - // check that the route satisfy all constraints - errorcode = route_check_constraints( - r, gossmap, uncertainty, disabled_bitmap); - - if (errorcode == RENEPAY_BAD_CHANNEL) - continue; - if (errorcode) { - // any other error is bad - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "route_check_constraints returned " - "errorcode: %s", - renepay_errorcode_name(errorcode)); - goto function_fail; - } - - // update the fee budget - if (!amount_msat_sub(&feebudget, feebudget, fee)) { - // should never happen - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "%s routing fees (%s) exceed fee " - "budget (%s).", - __func__, - fmt_amount_msat(this_ctx, fee), - fmt_amount_msat(this_ctx, feebudget)); - goto function_fail; - } - - // update the amount that we deliver - if (!amount_msat_sub(&amount_to_deliver, - amount_to_deliver, delivering)) { - // should never happen - tal_report_error( - ctx, ecode, fail, PLUGIN_ERROR, - "%s: route delivering to destination (%s) " - "is more than requested (%s)", - __func__, - fmt_amount_msat(this_ctx, delivering), - fmt_amount_msat(this_ctx, - amount_to_deliver)); - goto function_fail; - } - - // update the probability target - if (prob < 1e-10) { - // probability is too small for division - probability_budget = 1.0; - } else { - /* prob here is a conditional probability, the - * next flow will have a conditional - * probability prob2 and we would like that - * prob*prob2 >= probability_budget hence - * probability_budget/prob becomes the next - * iteration's target. */ - probability_budget = - MIN(1.0, probability_budget / prob); - } - - // route added - (*next_partid)++; - uncertainty_commit_htlcs(uncertainty, r); - tal_arr_expand(&routes, r); - } - } - - /* remove the temporary routes from the uncertainty network */ - uncertainty_remove_routes(uncertainty, routes); - - /* ownership */ - for (size_t i = 0; i < tal_count(routes); i++) - routes[i] = tal_steal(routes, routes[i]); - - tal_free(this_ctx); - return routes; - -function_fail: - /* remove the temporary routes from the uncertainty network */ - uncertainty_remove_routes(uncertainty, routes); - - /* Discard any routes we have constructed here. */ - tal_free(this_ctx); - return tal_free(routes); -} diff --git a/plugins/renepay/routebuilder.h b/plugins/renepay/routebuilder.h deleted file mode 100644 index b7297e5410c4..000000000000 --- a/plugins/renepay/routebuilder.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_ROUTEBUILDER_H -#define LIGHTNING_PLUGINS_RENEPAY_ROUTEBUILDER_H - -#include "config.h" -#include -#include -#include -#include -#include -#include - -struct route **get_routes(const tal_t *ctx, - struct payment_info *payment_info, - - const struct node_id *source, - const struct node_id *destination, - struct gossmap *gossmap, - struct uncertainty *uncertainty, - struct disabledmap *disabledmap, - - struct amount_msat amount_to_deliver, - struct amount_msat feebudget, - - u64 *next_partid, - u64 groupid, - bool blinded_destination, - - enum jsonrpc_errcode *ecode, - const char **fail); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_ROUTEBUILDER_H */ diff --git a/plugins/renepay/routefail.c b/plugins/renepay/routefail.c index 4f450c6a4ace..3ba647e23429 100644 --- a/plugins/renepay/routefail.c +++ b/plugins/renepay/routefail.c @@ -1,9 +1,10 @@ #include "config.h" #include #include -#include +#include #include #include +#include #include enum node_type { @@ -14,44 +15,62 @@ enum node_type { }; struct routefail { - struct command *cmd; struct payment *payment; struct route *route; }; -static struct command_result *update_gossip(struct routefail *r); -static struct command_result *handle_failure(struct routefail *r); +static void update_gossip(struct command *cmd, + struct request_batch *batch, + struct routefail *r); -struct command_result *routefail_start(const tal_t *ctx, struct route *route, - struct command *cmd) +static void handle_failure(struct command *cmd, + struct request_batch *batch, + struct routefail *r); + +static struct command_result *routefail_end(struct command *cmd, + struct routefail *r) +{ + /* Notify the tracker that route has failed and routefail have completed + * handling all possible errors cases. */ + routetracker_add_to_final(r->payment, r->payment->routetracker, + r->route); + r = tal_free(r); + return notification_handled(cmd); +} + +static struct command_result *log_routefail_err(struct command *cmd, + const char *method, + const char *buf, + const jsmntok_t *tok, + struct routefail *r) +{ + plugin_log(cmd->plugin, LOG_UNUSUAL, + "routefail batch failed: %s failed: '%.*s'", method, + json_tok_full_len(tok), json_tok_full(buf, tok)); + return command_still_pending(cmd); +} + +struct command_result *routefail_start(const tal_t *ctx, + struct payment *payment, + struct route *route, struct command *cmd) { assert(route); struct routefail *r = tal(ctx, struct routefail); - struct payment *payment = - payment_map_get(pay_plugin->payment_map, route->key.payment_hash); if (payment == NULL) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "%s: payment with hash %s not found.", __func__, fmt_sha256(tmpctx, &route->key.payment_hash)); r->payment = payment; r->route = route; - r->cmd = cmd; - assert(route->result); - return update_gossip(r); -} -static struct command_result *routefail_end(struct routefail *r TAKES) -{ - /* Notify the tracker that route has failed and routefail have completed - * handling all possible errors cases. */ - struct command *cmd = r->cmd; - route_failure_register(r->payment->routetracker, r->route); - if (taken(r)) - r = tal_steal(tmpctx, r); - return notification_handled(cmd); + struct request_batch *batch = + request_batch_new(cmd, NULL, log_routefail_err, routefail_end, r); + update_gossip(cmd, batch, r); + handle_failure(cmd, batch, r); + return batch_done(cmd, batch); } /***************************************************************************** @@ -109,49 +128,9 @@ static u8 *channel_update_from_onion_error(const tal_t *ctx, return patch_channel_update(ctx, take(channel_update)); } -static struct command_result *update_gossip_done(struct command *cmd UNUSED, - const char *method UNUSED, - const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct routefail *r) -{ - return handle_failure(r); -} - -static struct command_result *update_gossip_failure(struct command *cmd UNUSED, - const char *method UNUSED, - const char *buf, - const jsmntok_t *result, - struct routefail *r) -{ - assert(r); - assert(r->payment); - assert(r->route->result->erring_index); - - const int index = *r->route->result->erring_index; - struct short_channel_id_dir scidd; - - if (r->route->result->erring_channel) { - scidd.scid = *r->route->result->erring_channel; - scidd.dir = *r->route->result->erring_direction; - } else if (r->route->hops) { - assert(index < tal_count(r->route->hops)); - const struct route_hop *hop = &r->route->hops[index]; - scidd.scid = hop->scid; - scidd.dir = hop->direction; - - } else /* don't have information to disable the erring channel */ - goto finish; - - payment_disable_chan( - r->payment, scidd, LOG_INFORM, "addgossip failed (%.*s)", - json_tok_full_len(result), json_tok_full(buf, result)); - -finish: - return update_gossip_done(cmd, method, buf, result, r); -} - -static struct command_result *update_gossip(struct routefail *r) +static void update_gossip(struct command *cmd, + struct request_batch *batch, + struct routefail *r) { /* if there is no raw_message we continue */ if (!r->route->result->raw_message) @@ -163,14 +142,12 @@ static struct command_result *update_gossip(struct routefail *r) if (!update) goto skip_update_gossip; - struct out_req *req = - jsonrpc_request_start(r->cmd, "addgossip", - update_gossip_done, update_gossip_failure, r); + struct out_req *req = add_to_batch(cmd, batch, "addgossip"); json_add_hex_talarr(req->js, "message", update); - return send_outreq(req); + send_outreq(req); skip_update_gossip: - return handle_failure(r); + return; } /***************************************************************************** @@ -198,8 +175,42 @@ static void route_final_error(struct route *route, enum jsonrpc_errcode error, route->final_msg = tal_strdup(route, what); } +static void disable_node(struct command *cmd, struct request_batch *batch, + struct routefail *r, struct node_id *node) +{ + struct out_req *req = add_to_batch(cmd, batch, "askrene-disable-node"); + json_add_string(req->js, "layer", r->payment->payment_layer); + json_add_node_id(req->js, "node", node); + send_outreq(req); +} + +static void disable_channel(struct command *cmd, struct request_batch *batch, + struct routefail *r, struct short_channel_id_dir scidd) +{ + struct out_req *req = + add_to_batch(cmd, batch, "askrene-udpate-channel"); + json_add_string(req->js, "layer", r->payment->payment_layer); + json_add_short_channel_id_dir(req->js, "short_channel_id_dir", scidd); + json_add_bool(req->js, "enabled", false); + send_outreq(req); +} + +static void bias_channel(struct command *cmd, struct request_batch *batch, + struct routefail *r, struct short_channel_id_dir scidd, + int bias) +{ + // FIXME: we want to increment the bias, not set it + struct out_req *req = add_to_batch(cmd, batch, "askrene-bias-channel"); + json_add_string(req->js, "layer", r->payment->payment_layer); + json_add_short_channel_id_dir(req->js, "short_channel_id_dir", scidd); + json_add_num(req->js, "bias", bias); + send_outreq(req); +} + /* FIXME: do proper error handling for BOLT12 */ -static struct command_result *handle_failure(struct routefail *r) +static void handle_failure(struct command *cmd, + struct request_batch *batch, + struct routefail *r) { /* BOLT #4: * @@ -322,6 +333,9 @@ static struct command_result *handle_failure(struct routefail *r) payment, route->hops[*result->erring_index].node_id, LOG_DBG, "received %s from previous hop", onion_wire_name(failcode)); + disable_node( + cmd, batch, r, + &route->hops[*result->erring_index].node_id); break; case UNKNOWN_NODE: break; @@ -351,6 +365,9 @@ static struct command_result *handle_failure(struct routefail *r) route->hops[*result->erring_index - 1].node_id, LOG_INFORM, "received error %s", onion_wire_name(failcode)); + disable_node( + cmd, batch, r, + &route->hops[*result->erring_index - 1].node_id); break; case UNKNOWN_NODE: break; @@ -399,6 +416,9 @@ static struct command_result *handle_failure(struct routefail *r) route->hops[*result->erring_index - 1].node_id, LOG_INFORM, "received error %s", onion_wire_name(failcode)); + disable_node( + cmd, batch, r, + &route->hops[*result->erring_index - 1].node_id); break; case UNKNOWN_NODE: break; @@ -442,7 +462,7 @@ static struct command_result *handle_failure(struct routefail *r) .dir = route->hops[*result->erring_index].direction}; payment_disable_chan(payment, scidd, LOG_INFORM, "%s", onion_wire_name(failcode)); - + disable_channel(cmd, batch, r, scidd); break; case UNKNOWN_NODE: break; @@ -469,7 +489,9 @@ static struct command_result *handle_failure(struct routefail *r) route->hops[*result->erring_index - 1].node_id, LOG_INFORM, "received error %s", onion_wire_name(failcode)); - + disable_node( + cmd, batch, r, + &route->hops[*result->erring_index - 1].node_id); break; case ORIGIN_NODE: case FINAL_NODE: @@ -518,7 +540,7 @@ static struct command_result *handle_failure(struct routefail *r) payment_warn_chan(payment, scidd, LOG_INFORM, "received error %s", onion_wire_name(failcode)); - + bias_channel(cmd, batch, r, scidd, -1); break; case UNKNOWN_NODE: break; @@ -554,5 +576,5 @@ static struct command_result *handle_failure(struct routefail *r) } finish: - return routefail_end(take(r)); + return; } diff --git a/plugins/renepay/routefail.h b/plugins/renepay/routefail.h index bad3aabef283..509945b7af9f 100644 --- a/plugins/renepay/routefail.h +++ b/plugins/renepay/routefail.h @@ -6,7 +6,8 @@ #include "config.h" #include -struct command_result *routefail_start(const tal_t *ctx, struct route *route, - struct command *cmd); +struct command_result *routefail_start(const tal_t *ctx, + struct payment *payment, + struct route *route, struct command *cmd); #endif /* LIGHTNING_PLUGINS_RENEPAY_ROUTEFAIL_H */ diff --git a/plugins/renepay/routetracker.c b/plugins/renepay/routetracker.c index 2e8b9ba2b08d..7965a19a80f5 100644 --- a/plugins/renepay/routetracker.c +++ b/plugins/renepay/routetracker.c @@ -2,16 +2,18 @@ #include #include #include -#include +#include #include #include +#include -static struct payment *route_get_payment_verify(struct route *route) +struct payment *route_get_payment_verify(struct renepay *renepay, + struct route *route) { struct payment *payment = - payment_map_get(pay_plugin->payment_map, route->key.payment_hash); + payment_map_get(renepay->payment_map, route->key.payment_hash); if (!payment) - plugin_err(pay_plugin->plugin, + plugin_err(renepay->plugin, "%s: no payment associated with routekey %s", __func__, fmt_routekey(tmpctx, &route->key)); @@ -45,14 +47,13 @@ void routetracker_cleanup(struct routetracker *routetracker) // TODO } -static void routetracker_add_to_final(struct routetracker *routetracker, - struct route *route) +void routetracker_add_to_final(struct payment *payment, + struct routetracker *routetracker, + struct route *route) { tal_arr_expand(&routetracker->finalized_routes, route); tal_steal(routetracker->finalized_routes, route); - struct payment *payment = - payment_map_get(pay_plugin->payment_map, route->key.payment_hash); assert(payment); if (payment->exec_state == INVALID_STATE) { /* payment is offline, collect results now and set the payment @@ -87,61 +88,9 @@ static void routetracker_add_to_final(struct routetracker *routetracker, } } -static void route_success_register(struct routetracker *routetracker, - struct route *route) -{ - if(route->hops){ - uncertainty_route_success(pay_plugin->uncertainty, route); - } - routetracker_add_to_final(routetracker, route); -} -void route_failure_register(struct routetracker *routetracker, - struct route *route) -{ - struct payment_result *result = route->result; - assert(result); - - /* Update the knowledge in the uncertaity network. */ - if (route->hops && result->failcode) { - assert(result->erring_index); - int path_len = tal_count(route->hops); - - /* index of the last channel before the erring node */ - const int last_good_channel = *result->erring_index - 1; - - if (last_good_channel >= path_len) { - plugin_err(pay_plugin->plugin, - "last_good_channel (%d) >= path_len (%d)", - last_good_channel, path_len); - } - - /* All channels before the erring node could forward the - * payment. */ - for (int i = 0; i <= last_good_channel; i++) { - uncertainty_channel_can_send(pay_plugin->uncertainty, - route->hops[i].scid, - route->hops[i].direction); - } - - if (*result->failcode == WIRE_TEMPORARY_CHANNEL_FAILURE && - (last_good_channel + 1) < path_len) { - /* A WIRE_TEMPORARY_CHANNEL_FAILURE could mean not - * enough liquidity to forward the payment or cannot add - * one more HTLC. - */ - uncertainty_channel_cannot_send( - pay_plugin->uncertainty, - route->hops[last_good_channel + 1].scid, - route->hops[last_good_channel + 1].direction); - } - } - routetracker_add_to_final(routetracker, route); -} - static void remove_route(struct route *route, struct route_map *map) { route_map_del(map, route); - uncertainty_remove_htlcs(pay_plugin->uncertainty, route); } /* This route is pending, ie. locked in HTLCs. @@ -149,36 +98,35 @@ static void remove_route(struct route *route, struct route_map *map) * - after a sendpay is accepted, * - or after listsendpays reveals some pending route that we didn't * previously know about. */ -static void route_pending_register(struct routetracker *routetracker, - struct route *route) +void route_pending_register(struct payment *payment, + struct routetracker *routetracker, + struct route *route) { assert(route); assert(routetracker); - struct payment *payment = route_get_payment_verify(route); assert(payment); assert(payment->groupid == route->key.groupid); + struct renepay *renepay = get_renepay(payment->plugin); /* we already keep track of this route */ - if (route_map_get(pay_plugin->pending_routes, &route->key)) - plugin_err(pay_plugin->plugin, + if (route_map_get(renepay->pending_routes, &route->key)) + plugin_err(renepay->plugin, "%s: tracking a route (%s) duplicate?", __func__, fmt_routekey(tmpctx, &route->key)); if (!route_map_del(routetracker->sent_routes, route)) - plugin_err(pay_plugin->plugin, + plugin_err(renepay->plugin, "%s: tracking a route (%s) not computed by this " "payment call", __func__, fmt_routekey(tmpctx, &route->key)); - uncertainty_commit_htlcs(pay_plugin->uncertainty, route); - - if (!tal_steal(pay_plugin->pending_routes, route) || - !route_map_add(pay_plugin->pending_routes, route) || + if (!tal_steal(renepay->pending_routes, route) || + !route_map_add(renepay->pending_routes, route) || !tal_add_destructor2(route, remove_route, - pay_plugin->pending_routes)) - plugin_err(pay_plugin->plugin, "%s: failed to register route.", + renepay->pending_routes)) + plugin_err(renepay->plugin, "%s: failed to register route.", __func__); if (!amount_msat_add(&payment->total_sent, payment->total_sent, @@ -186,99 +134,12 @@ static void route_pending_register(struct routetracker *routetracker, !amount_msat_add(&payment->total_delivering, payment->total_delivering, route_delivers(route))) { - plugin_err(pay_plugin->plugin, + plugin_err(renepay->plugin, "%s: amount_msat arithmetic overflow.", __func__); } } -/* Callback function for sendpay request success. */ -static struct command_result *sendpay_done(struct command *cmd, - const char *method UNUSED, - const char *buf, - const jsmntok_t *result, - struct route *route) -{ - assert(route); - struct payment *payment = route_get_payment_verify(route); - route_pending_register(payment->routetracker, route); - - const jsmntok_t *t; - size_t i; - bool ret; - - const jsmntok_t *secretstok = - json_get_member(buf, result, "shared_secrets"); - - if (secretstok) { - assert(secretstok->type == JSMN_ARRAY); - - route->shared_secrets = - tal_arr(route, struct secret, secretstok->size); - json_for_each_arr(i, t, secretstok) - { - ret = json_to_secret(buf, t, &route->shared_secrets[i]); - assert(ret); - } - } else - route->shared_secrets = NULL; - return command_still_pending(cmd); -} - -/* sendpay really only fails immediately in two ways: - * 1. We screwed up and misused the API. - * 2. The first peer is disconnected. - */ -static struct command_result *sendpay_failed(struct command *cmd, - const char *method UNUSED, - const char *buf, - const jsmntok_t *tok, - struct route *route) -{ - assert(route); - struct payment *payment = route_get_payment_verify(route); - struct routetracker *routetracker = payment->routetracker; - assert(routetracker); - - enum jsonrpc_errcode errcode; - const char *msg; - const char *err; - - err = json_scan(tmpctx, buf, tok, "{code:%,message:%}", - JSON_SCAN(json_to_jsonrpc_errcode, &errcode), - JSON_SCAN_TAL(tmpctx, json_strdup, &msg)); - if (err) - plugin_err(pay_plugin->plugin, - "Unable to parse sendpay error: %s, json: %.*s", err, - json_tok_full_len(tok), json_tok_full(buf, tok)); - - payment_note(payment, LOG_INFORM, - "Sendpay failed: partid=%" PRIu64 - " errorcode:%d message=%s", - route->key.partid, errcode, msg); - - if (errcode != PAY_TRY_OTHER_ROUTE) { - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "Strange error from sendpay: %.*s", - json_tok_full_len(tok), json_tok_full(buf, tok)); - } - - /* There is no new knowledge from this kind of failure. - * We just disable this scid. */ - struct short_channel_id_dir scidd_disable = { - .scid = route->hops[0].scid, .dir = route->hops[0].direction}; - payment_disable_chan(payment, scidd_disable, LOG_INFORM, - "sendpay didn't like first hop: %s", msg); - - if (!route_map_del(routetracker->sent_routes, route)) - plugin_err(pay_plugin->plugin, - "%s: route (%s) is not marked as sent", - __func__, - fmt_routekey(tmpctx, &route->key)); - tal_free(route); - return command_still_pending(cmd); -} - void payment_collect_results(struct payment *payment, struct preimage **payment_preimage, enum jsonrpc_errcode *final_error, @@ -310,7 +171,7 @@ void payment_collect_results(struct payment *payment, * does not have the same groupid as the one we used for our * routes. */ if (payment->groupid != r->key.groupid) { - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, + plugin_log(payment->plugin, LOG_UNUSUAL, "%s: current groupid=%" PRIu64 ", but recieved a sendpay result with " "groupid=%" PRIu64, @@ -335,7 +196,7 @@ void payment_collect_results(struct payment *payment, route_delivers(r)) || !amount_msat_sub(&payment->total_sent, payment->total_sent, route_sends(r))) { - plugin_err(pay_plugin->plugin, + plugin_err(payment->plugin, "%s: routes do not add up to " "payment total amount.", __func__); @@ -346,67 +207,12 @@ void payment_collect_results(struct payment *payment, tal_resize(&routetracker->finalized_routes, 0); } -struct command_result *route_sendpay_request(struct command *cmd, - struct route *route TAKES, - struct payment *payment) -{ - const struct payment_info *pinfo = &payment->payment_info; - struct out_req *req = jsonrpc_request_start( - cmd, "renesendpay", sendpay_done, sendpay_failed, route); - - const size_t pathlen = tal_count(route->hops); - json_add_sha256(req->js, "payment_hash", &pinfo->payment_hash); - json_add_u64(req->js, "partid", route->key.partid); - json_add_u64(req->js, "groupid", route->key.groupid); - json_add_string(req->js, "invoice", pinfo->invstr); - json_add_node_id(req->js, "destination", &pinfo->destination); - json_add_amount_msat(req->js, "amount_msat", route->amount_deliver); - json_add_amount_msat(req->js, "total_amount_msat", pinfo->amount); - json_add_u32(req->js, "final_cltv", pinfo->final_cltv); - - if (pinfo->label) - json_add_string(req->js, "label", pinfo->label); - if (pinfo->description) - json_add_string(req->js, "description", pinfo->description); - - json_array_start(req->js, "route"); - /* An empty route means a payment to oneself, pathlen=0 */ - for (size_t j = 0; j < pathlen; j++) { - const struct route_hop *hop = &route->hops[j]; - json_object_start(req->js, NULL); - json_add_node_id(req->js, "id", &hop->node_id); - json_add_short_channel_id(req->js, "channel", hop->scid); - json_add_amount_msat(req->js, "amount_msat", hop->amount); - json_add_num(req->js, "direction", hop->direction); - json_add_u32(req->js, "delay", hop->delay); - json_add_string(req->js, "style", "tlv"); - json_object_end(req->js); - } - json_array_end(req->js); - - /* Either we have a payment_secret for BOLT11 or blinded_paths for - * BOLT12 */ - if (pinfo->payment_secret) - json_add_secret(req->js, "payment_secret", pinfo->payment_secret); - else { - assert(pinfo->blinded_paths); - const struct blinded_path *bpath = - pinfo->blinded_paths[route->path_num]; - json_myadd_blinded_path(req->js, "blinded_path", bpath); - - } - - route_map_add(payment->routetracker->sent_routes, route); - if (taken(route)) - tal_steal(payment->routetracker->sent_routes, route); - return send_outreq(req); -} - struct command_result *notification_sendpay_failure(struct command *cmd, const char *buf, const jsmntok_t *params) { - plugin_log(pay_plugin->plugin, LOG_DBG, + struct renepay *renepay = get_renepay(cmd->plugin); + plugin_log(cmd->plugin, LOG_DBG, "sendpay_failure notification: %.*s", json_tok_full_len(params), json_tok_full(buf, params)); @@ -416,12 +222,12 @@ struct command_result *notification_sendpay_failure(struct command *cmd, struct routekey *key = tal_routekey_from_json( tmpctx, buf, json_get_member(buf, sub, "data")); if (!key) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "Unable to get routekey from sendpay_failure: %.*s", json_tok_full_len(sub), json_tok_full(buf, sub)); struct payment *payment = - payment_map_get(pay_plugin->payment_map, key->payment_hash); + payment_map_get(renepay->payment_map, key->payment_hash); if (!payment) { /* This sendpay is not linked to any route in our database, we @@ -432,12 +238,12 @@ struct command_result *notification_sendpay_failure(struct command *cmd, struct routetracker *routetracker = payment->routetracker; assert(routetracker); struct route *route = - route_map_get(pay_plugin->pending_routes, key); + route_map_get(renepay->pending_routes, key); if (!route) { route = tal_route_from_json(tmpctx, buf, json_get_member(buf, sub, "data")); if (!route) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "Failed to get route information from " "sendpay_failure: %.*s", json_tok_full_len(sub), @@ -448,7 +254,7 @@ struct command_result *notification_sendpay_failure(struct command *cmd, route->result = tal_sendpay_result_from_json(route, buf, sub, route->shared_secrets); if (route->result == NULL) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "Unable to parse sendpay_failure: %.*s", json_tok_full_len(sub), json_tok_full(buf, sub)); @@ -459,7 +265,7 @@ struct command_result *notification_sendpay_failure(struct command *cmd, json_get_member(buf, datatok, "status"); const char *status_str = json_strdup(tmpctx, buf, statustok); - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, + plugin_log(cmd->plugin, LOG_UNUSUAL, "sendpay_failure notification returned status=%s", status_str); route->result->status = SENDPAY_FAILED; @@ -467,14 +273,15 @@ struct command_result *notification_sendpay_failure(struct command *cmd, /* we do some error processing steps before calling * route_failure_register. */ - return routefail_start(payment, route, cmd); + return routefail_start(payment, payment, route, cmd); } struct command_result *notification_sendpay_success(struct command *cmd, const char *buf, const jsmntok_t *params) { - plugin_log(pay_plugin->plugin, LOG_DBG, + struct renepay *renepay = get_renepay(cmd->plugin); + plugin_log(cmd->plugin, LOG_DBG, "sendpay_success notification: %.*s", json_tok_full_len(params), json_tok_full(buf, params)); @@ -482,12 +289,12 @@ struct command_result *notification_sendpay_success(struct command *cmd, struct routekey *key = tal_routekey_from_json(tmpctx, buf, sub); if (!key) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "Unable to get routekey from sendpay_success: %.*s", json_tok_full_len(sub), json_tok_full(buf, sub)); struct payment *payment = - payment_map_get(pay_plugin->payment_map, key->payment_hash); + payment_map_get(renepay->payment_map, key->payment_hash); if (!payment) { /* This sendpay is not linked to any route in our database, we @@ -498,13 +305,13 @@ struct command_result *notification_sendpay_success(struct command *cmd, struct routetracker *routetracker = payment->routetracker; assert(routetracker); struct route *route = - route_map_get(pay_plugin->pending_routes, key); + route_map_get(renepay->pending_routes, key); if (!route) { /* This route was not created by us, make a basic route * information dummy without hop details to pass onward. */ route = tal_route_from_json(tmpctx, buf, sub); if(!route) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "Failed to get route information from sendpay_success: %.*s", json_tok_full_len(sub), json_tok_full(buf, sub)); } @@ -513,11 +320,11 @@ struct command_result *notification_sendpay_success(struct command *cmd, route->result = tal_sendpay_result_from_json(route, buf, sub, route->shared_secrets); if (route->result == NULL) - plugin_err(pay_plugin->plugin, + plugin_err(cmd->plugin, "Unable to parse sendpay_success: %.*s", json_tok_full_len(sub), json_tok_full(buf, sub)); assert(route->result->status == SENDPAY_COMPLETE); - route_success_register(payment->routetracker, route); + routetracker_add_to_final(payment, payment->routetracker, route); return notification_handled(cmd); } diff --git a/plugins/renepay/routetracker.h b/plugins/renepay/routetracker.h index 2e4c440a2339..2f84e1ddf5c1 100644 --- a/plugins/renepay/routetracker.h +++ b/plugins/renepay/routetracker.h @@ -47,10 +47,17 @@ struct command_result *notification_sendpay_success(struct command *cmd, const char *buf, const jsmntok_t *params); -/* Notify the tracker that this route has failed. */ -void route_failure_register(struct routetracker *routetracker, +void routetracker_add_to_final(struct payment *payment, + struct routetracker *routetracker, + struct route *route); + +void route_pending_register(struct payment *payment, + struct routetracker *routetracker, struct route *route); +struct payment *route_get_payment_verify(struct renepay *renepay, + struct route *route); + // FIXME: double-check that we actually get one notification for each sendpay, // ie. that after some time we don't have yet pending sendpays for old failed or // successful payments that we havent processed because we haven't received the diff --git a/plugins/renepay/sendpay.c b/plugins/renepay/sendpay.c index 3c3049902147..afb5880a1805 100644 --- a/plugins/renepay/sendpay.c +++ b/plugins/renepay/sendpay.c @@ -3,8 +3,9 @@ #include #include #include -#include +#include #include +#include static struct command_result *param_route_hops(struct command *cmd, const char *name, @@ -395,6 +396,7 @@ static struct command_result *waitblockheight_done(struct command *cmd, const jsmntok_t *toks, struct renesendpay *renesendpay) { + struct renepay *renepay = get_renepay(cmd->plugin); const char *err; err = json_scan(tmpctx, buffer, toks, "{blockheight:%}", JSON_SCAN(json_to_u32, &renesendpay->blockheight)); @@ -456,7 +458,7 @@ static struct command_result *waitblockheight_done(struct command *cmd, // we need to make sure first that we don't lose older features, // like for example to be able to show in listsendpays the // recepient of the payment. - onion = create_onion(tmpctx, renesendpay, pay_plugin->my_id, 0); + onion = create_onion(tmpctx, renesendpay, renepay->my_id, 0); req = jsonrpc_request_start(cmd, "injectpaymentonion", renesendpay_done, rpc_fail, renesendpay); diff --git a/plugins/renepay/test/Makefile b/plugins/renepay/test/Makefile index ba3c49b1ff94..a74763facc24 100644 --- a/plugins/renepay/test/Makefile +++ b/plugins/renepay/test/Makefile @@ -9,12 +9,9 @@ ALL_TEST_PROGRAMS += $(PLUGIN_RENEPAY_TEST_PROGRAMS) $(PLUGIN_RENEPAY_TEST_OBJS): $(PLUGIN_RENEPAY_SRC) plugins/renepay/test/common.h PLUGIN_RENEPAY_TEST_COMMON_OBJS := \ - plugins/renepay/dijkstra.o \ - plugins/renepay/chan_extra.o \ bitcoin/chainparams.o \ common/gossmap.o \ common/fp16.o \ - common/dijkstra.o \ gossipd/gossip_store_wiregen.o $(PLUGIN_RENEPAY_TEST_PROGRAMS): $(PLUGIN_RENEPAY_TEST_COMMON_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) diff --git a/plugins/renepay/test/run-arc.c b/plugins/renepay/test/run-arc.c deleted file mode 100644 index 793a0e70a0e2..000000000000 --- a/plugins/renepay/test/run-arc.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../flow.c" -#include "../mcf.c" - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -int main(int argc, char *argv[]) -{ - bool dual; - u32 part; - int chandir; - u32 chanidx; - - common_setup(argv[0]); - - for (int i = 0; i < 32; i++) { - for (int j = 0; j < 32; j++) { - for (int k = 0; k < 32; k++) { - struct arc a, a2; - - a.idx = (1U << i) | (1U << j) | (1U << k); - arc_to_parts(a, &chanidx, &chandir, &part, &dual); - a2 = arc_from_parts(chanidx, chandir, part, dual); - assert(a.idx == a2.idx); - } - } - } - - /* Test all chanidx */ - for (int i = 0; i < (1U << ARC_CHANIDX_BITS); i++) { - struct arc a = arc_from_parts(i, chandir, part, dual); - - arc_to_parts(a, &chanidx, NULL, NULL, NULL); - assert(chanidx == i); - } - - /* Test both chandir */ - for (int i = 0; i < 2; i++) { - struct arc a = arc_from_parts(chanidx, i, part, dual); - - arc_to_parts(a, NULL, &chandir, NULL, NULL); - assert(chandir == i); - } - - /* Test all parts */ - for (int i = 0; i < CHANNEL_PARTS; i++) { - struct arc a = arc_from_parts(chanidx, chandir, i, dual); - - arc_to_parts(a, NULL, NULL, &part, NULL); - assert(part == i); - } - - /* Test both dual */ - for (int i = 0; i < 2; i++) { - struct arc a = arc_from_parts(chanidx, chandir, part, i); - - arc_to_parts(a, NULL, NULL, NULL, &dual); - assert(dual == i); - - assert(arc_is_dual(a) == dual); - - a = arc_dual(a); - arc_to_parts(a, NULL, NULL, NULL, &dual); - assert(dual == !i); - assert(arc_is_dual(a) == dual); - } - - common_shutdown(); -} - diff --git a/plugins/renepay/test/run-bottleneck.c b/plugins/renepay/test/run-bottleneck.c deleted file mode 100644 index f22d45fbbf6e..000000000000 --- a/plugins/renepay/test/run-bottleneck.c +++ /dev/null @@ -1,278 +0,0 @@ -/* Checks that get_route can handle bottleneck situations assigning values to - * routes that do not exceed the liquidity constraints. */ -#include "config.h" - -#include "../errorcodes.c" -#include "../flow.c" -#include "../mcf.c" -#include "../uncertainty.c" -#include "../disabledmap.c" -#include "../route.c" -#include "../routebuilder.c" -#include "common.h" - -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static u8 empty_map[] = {10}; - -static const char *print_flows(const tal_t *ctx, const char *desc, - const struct gossmap *gossmap, - struct chan_extra_map *chan_extra_map, - struct flow **flows) -{ - tal_t *this_ctx = tal(ctx, tal_t); - double tot_prob = - flowset_probability(tmpctx, flows, gossmap, chan_extra_map, false, NULL); - assert(tot_prob >= 0); - char *buff = tal_fmt(ctx, "%s: %zu subflows, prob %2lf\n", desc, - tal_count(flows), tot_prob); - 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; -} - -#define NUM_NODES 8 - -static void remove_file(char *fname) { assert(!remove(fname)); } - -int main(int argc, char *argv[]) -{ - int fd; - char *gossfile; - struct gossmap *gossmap; - struct node_id nodes[NUM_NODES]; - - common_setup(argv[0]); - chainparams = chainparams_for_network("regtest"); - - fd = tmpdir_mkstemp(tmpctx, "run-bottleneck.XXXXXX", &gossfile); - tal_add_destructor(gossfile, remove_file); - assert(write(fd, empty_map, sizeof(empty_map)) == sizeof(empty_map)); - - gossmap = gossmap_load(tmpctx, gossfile, NULL, NULL); - assert(gossmap); - - for (size_t i = 0; i < NUM_NODES; i++) { - struct privkey tmp; - memset(&tmp, i+1, sizeof(tmp)); - node_id_from_privkey(&tmp, &nodes[i]); - } - - /* We will try a payment from 1 to 8, forcing a payment split between - * two routes 1->2->4->5->6->8 and 1->3->4->5->7->8. - * To force the split the total payment amount will be greater than the - * channel 1-2 and 1-3 capacities. Then channel 4--5 will be a common - * edge in the payment routes. - * - * MCF does not handle fees hence if the capacity of 4--5 is enough to - * let the entire payment pass, we expect that minflow computes two - * routes that are scaled down by get_route algorithm - * to fit for the fee constraints. - * - * +--2--+ +--6--+ - * | | | | - * 1 4---5 8 - * | | | | - * +--3--+ +--7--+ - * - * */ - struct short_channel_id scid; - - assert(mk_short_channel_id(&scid, 1, 2, 0)); - add_connection(fd, &nodes[0], &nodes[1], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(60 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(60 * 1000)); - - assert(mk_short_channel_id(&scid, 1, 3, 0)); - add_connection(fd, &nodes[0], &nodes[2], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(60 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(60 * 1000)); - - assert(mk_short_channel_id(&scid, 2, 4, 0)); - add_connection(fd, &nodes[1], &nodes[3], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000)); - - assert(mk_short_channel_id(&scid, 3, 4, 0)); - add_connection(fd, &nodes[2], &nodes[3], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000)); - - assert(mk_short_channel_id(&scid, 4, 5, 0)); - add_connection(fd, &nodes[3], &nodes[4], scid, - AMOUNT_MSAT(0), - /* MCF cuts off at 95% of the conditional capacity, for - * cap = 106k that means only 100.7k sats can be sent - * through this channel. */ - AMOUNT_MSAT(106 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(110 * 1000)); - - assert(mk_short_channel_id(&scid, 5, 6, 0)); - add_connection(fd, &nodes[4], &nodes[5], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 100 * 1000 /* 10% */, 5, - AMOUNT_SAT(1000 * 1000)); - - assert(mk_short_channel_id(&scid, 5, 7, 0)); - add_connection(fd, &nodes[4], &nodes[6], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 100 * 1000 /* 10% */, 5, - AMOUNT_SAT(1000 * 1000)); - - assert(mk_short_channel_id(&scid, 6, 8, 0)); - add_connection(fd, &nodes[5], &nodes[7], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000)); - - assert(mk_short_channel_id(&scid, 7, 8, 0)); - add_connection(fd, &nodes[6], &nodes[7], scid, - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000 * 1000 * 1000), - 0, 0, 5, - AMOUNT_SAT(1000 * 1000)); - - assert(gossmap_refresh(gossmap)); - struct uncertainty *uncertainty = uncertainty_new(tmpctx); - int skipped_count = - uncertainty_update(uncertainty, gossmap); - assert(skipped_count==0); - - bitmap *disabled = tal_arrz( - tmpctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); - assert(disabled); - - char *errmsg; - struct flow **flows; - flows = - minflow(tmpctx, gossmap, gossmap_find_node(gossmap, &nodes[0]), - gossmap_find_node(gossmap, &nodes[7]), - uncertainty->chan_extra_map, disabled, - /* Half the capacity */ - AMOUNT_MSAT(100 * 1000 * 1000), - /* max_fee = */ AMOUNT_MSAT(20 * 1000 * 1000), - /* min probability = */ 0.9, - /* base probability = */ 1.0, - /* delay fee factor = */ 1e-6, - /* base fee penalty */ 10, - /* prob cost factor = */ 10, &errmsg); - - if (!flows) { - printf("Minflow has failed with: %s\n", errmsg); - assert(flows); - } - - if(flows) - printf("%s\n", print_flows(tmpctx, "Simple minflow", gossmap, - uncertainty->chan_extra_map, flows)); - - struct preimage preimage; - - struct amount_msat maxfee = AMOUNT_MSAT(20*1000*1000); - struct payment_info pinfo; - pinfo.invstr = NULL; - pinfo.label = NULL; - pinfo.description = NULL; - pinfo.payment_secret = NULL; - pinfo.payment_metadata = NULL; - pinfo.routehints = NULL; - pinfo.destination = nodes[7]; - pinfo.amount = AMOUNT_MSAT(100 * 1000 * 1000); - - assert(amount_msat_add(&pinfo.maxspend, maxfee, pinfo.amount)); - pinfo.maxdelay = 100; - pinfo.final_cltv = 5; - - pinfo.start_time = time_now(); - pinfo.stop_time = timeabs_add(pinfo.start_time, time_from_sec(10000)); - - pinfo.base_fee_penalty = 1e-5; - pinfo.prob_cost_factor = 1e-5; - pinfo.delay_feefactor = 1e-6; - pinfo.min_prob_success = 0.9; - pinfo.base_prob_success = 1.0; - pinfo.use_shadow = false; - - randombytes_buf(&preimage, sizeof(preimage)); - sha256(&pinfo.payment_hash, &preimage, sizeof(preimage)); - - // char hex_preimage[600], hex_sha256[600]; - // assert(hex_encode(preimage.r, sizeof(preimage.r), hex_preimage, sizeof(hex_preimage))); - // assert(hex_encode(pinfo.payment_hash.u.u8, sizeof(pinfo.payment_hash), hex_sha256, sizeof(hex_sha256))); - // printf("preimage: %s\npayment_hash: %s\n", hex_preimage, hex_sha256); - - struct disabledmap *disabledmap = disabledmap_new(tmpctx); - - enum jsonrpc_errcode errcode; - const char *err_msg; - - u64 groupid = 1; - u64 next_partid=1; - - struct route **routes = get_routes( - /* ctx */tmpctx, - /* payment */&pinfo, - /* source */&nodes[0], - /* destination */&nodes[7], - /* gossmap */gossmap, - /* uncertainty */uncertainty, - disabledmap, - /* amount */ pinfo.amount, - /* feebudget */maxfee, - &next_partid, - groupid, - false, - &errcode, - &err_msg); - - if (!routes) { - printf("get_route failed with error %d: %s\n", errcode, err_msg); - assert(routes); - } - if(routes) - printf("get_routes: %s\n", print_routes(tmpctx, routes)); - - common_shutdown(); -} diff --git a/plugins/renepay/test/run-dijkstra.c b/plugins/renepay/test/run-dijkstra.c deleted file mode 100644 index 49e7a364f53b..000000000000 --- a/plugins/renepay/test/run-dijkstra.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static void insertion_in_increasing_distance(const tal_t *ctx) -{ - struct dijkstra *dijkstra = dijkstra_new(ctx,10); - - for(int i=0;i -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for disabledmap_add_channel */ -void disabledmap_add_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_add_channel called!\n"); abort(); } -/* Generated stub for disabledmap_add_node */ -void disabledmap_add_node(struct disabledmap *p UNNEEDED, struct node_id node UNNEEDED) -{ fprintf(stderr, "disabledmap_add_node called!\n"); abort(); } -/* Generated stub for disabledmap_channel_is_warned */ -bool disabledmap_channel_is_warned(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_channel_is_warned called!\n"); abort(); } -/* Generated stub for disabledmap_new */ -struct disabledmap *disabledmap_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "disabledmap_new called!\n"); abort(); } -/* Generated stub for disabledmap_reset */ -void disabledmap_reset(struct disabledmap *p UNNEEDED) -{ fprintf(stderr, "disabledmap_reset called!\n"); abort(); } -/* Generated stub for disabledmap_warn_channel */ -void disabledmap_warn_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_warn_channel called!\n"); abort(); } -/* Generated stub for json_add_payment */ -void json_add_payment(struct json_stream *s UNNEEDED, const struct payment *payment UNNEEDED) -{ fprintf(stderr, "json_add_payment called!\n"); abort(); } -/* Generated stub for new_routetracker */ -struct routetracker *new_routetracker(const tal_t *ctx UNNEEDED, struct payment *payment UNNEEDED) -{ fprintf(stderr, "new_routetracker called!\n"); abort(); } -/* Generated stub for pay_plugin */ -struct pay_plugin *pay_plugin; -/* Generated stub for routetracker_cleanup */ -void routetracker_cleanup(struct routetracker *routetracker UNNEEDED) -{ fprintf(stderr, "routetracker_cleanup called!\n"); abort(); } -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static u8 empty_map[] = { - 0 -}; - -static const char* print_flows( - const tal_t *ctx, - const char *desc, - const struct gossmap *gossmap, - struct chan_extra_map* chan_extra_map, - struct flow **flows) -{ - tal_t *this_ctx = tal(ctx,tal_t); - double tot_prob = - flowset_probability(tmpctx, flows, gossmap, chan_extra_map, false, NULL); - assert(tot_prob>=0); - char *buff = tal_fmt(ctx,"%s: %zu subflows, prob %2lf\n", desc, tal_count(flows),tot_prob); - 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; -} - -static void remove_file(char *fname) { assert(!remove(fname)); } - -int main(int argc, char *argv[]) -{ - int fd; - char *gossfile; - struct gossmap *gossmap; - struct node_id l1, l2, l3, l4; - struct short_channel_id scid12, scid13, scid24, scid34; - struct gossmap_localmods *mods; - struct chan_extra_map *chan_extra_map; - - char *errmsg; - - common_setup(argv[0]); - - fd = tmpdir_mkstemp(tmpctx, "run-not_mcf-diamond.XXXXXX", &gossfile); - tal_add_destructor(gossfile, remove_file); - assert(write_all(fd, empty_map, sizeof(empty_map))); - - gossmap = gossmap_load(tmpctx, gossfile, NULL, NULL); - assert(gossmap); - - /* These are in ascending order, for easy direction setting */ - assert(node_id_from_hexstr("022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", 66, &l1)); - assert(node_id_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", 66, &l2)); - assert(node_id_from_hexstr("035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", 66, &l3)); - assert(node_id_from_hexstr("0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199", 66, &l4)); - assert(short_channel_id_from_str("1x2x0", 5, &scid12)); - assert(short_channel_id_from_str("1x3x0", 5, &scid13)); - assert(short_channel_id_from_str("2x4x0", 5, &scid24)); - assert(short_channel_id_from_str("3x4x0", 5, &scid34)); - - mods = gossmap_localmods_new(tmpctx); - - /* 1->2->4 has capacity 10k sat, 1->3->4 has capacity 5k sat (lower fee!) */ - assert(gossmap_local_addchan(mods, &l1, &l2, scid12, AMOUNT_MSAT(10000000), NULL)); - assert(gossmap_local_setchan(mods, scid12, - /*htlc_min=*/ AMOUNT_MSAT(0), - /*htlc_max=*/ AMOUNT_MSAT(10000000), - /*base_fee=*/ AMOUNT_MSAT(0), - /*ppm_fee =*/ 1001, - /* delay =*/ 5, - /* enabled=*/ true, - /* dir =*/ 0)); - assert(gossmap_local_addchan(mods, &l2, &l4, scid24, AMOUNT_MSAT(10000000), NULL)); - assert(gossmap_local_setchan(mods, scid24, - AMOUNT_MSAT(0), - AMOUNT_MSAT(10000000), - AMOUNT_MSAT(0), 1002, 5, - true, - 0)); - assert(gossmap_local_addchan(mods, &l1, &l3, scid13, AMOUNT_MSAT(5000000), NULL)); - assert(gossmap_local_setchan(mods, scid13, - AMOUNT_MSAT(0), - AMOUNT_MSAT(5000000), - AMOUNT_MSAT(0), 503, 5, - true, - 0)); - assert(gossmap_local_addchan(mods, &l3, &l4, scid34, AMOUNT_MSAT(5000000), NULL)); - assert(gossmap_local_setchan(mods, scid34, - AMOUNT_MSAT(0), - AMOUNT_MSAT(5000000), - AMOUNT_MSAT(0), 504, 5, - true, - 0)); - - gossmap_apply_localmods(gossmap, mods); - chan_extra_map = tal(tmpctx, struct chan_extra_map); - chan_extra_map_init(chan_extra_map); - /* The local chans have no "capacity", so set them manually. */ - new_chan_extra(chan_extra_map, - scid12, - AMOUNT_MSAT(10000000)); - new_chan_extra(chan_extra_map, - scid24, - AMOUNT_MSAT(10000000)); - new_chan_extra(chan_extra_map, - scid13, - AMOUNT_MSAT(5000000)); - new_chan_extra(chan_extra_map, - scid34, - AMOUNT_MSAT(5000000)); - - bitmap *disabled = tal_arrz( - tmpctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); - - struct flow **flows; - flows = minflow(tmpctx, gossmap, - gossmap_find_node(gossmap, &l1), - gossmap_find_node(gossmap, &l4), - chan_extra_map, - disabled, - /* Half the capacity */ - AMOUNT_MSAT(1000000), // 1000 sats - /* max_fee = */ AMOUNT_MSAT(10000), // 10 sats - /* min probability = */ 0.8, // 80% - /* base probability = */ 1.0, - /* delay fee factor = */ 0, - /* base fee penalty */ 0, - /* prob cost factor = */ 1, - &errmsg); - if (!flows) { - printf("Minflow has failed with: %s", errmsg); - assert(0 && "minflow failed"); - } - - printf("%s\n", - print_flows(tmpctx,"Simple minflow", gossmap,chan_extra_map, flows)); - - common_shutdown(); -} diff --git a/plugins/renepay/test/run-mcf.c b/plugins/renepay/test/run-mcf.c deleted file mode 100644 index 1a95dff44220..000000000000 --- a/plugins/renepay/test/run-mcf.c +++ /dev/null @@ -1,609 +0,0 @@ -#include "config.h" - -#define RENEPAY_UNITTEST // logs are written in /tmp/debug.txt -#include "../payment.c" -#include "../flow.c" -#include "../route.c" -#include "../uncertainty.c" -#include "../mcf.c" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for disabledmap_add_channel */ -void disabledmap_add_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_add_channel called!\n"); abort(); } -/* Generated stub for disabledmap_add_node */ -void disabledmap_add_node(struct disabledmap *p UNNEEDED, struct node_id node UNNEEDED) -{ fprintf(stderr, "disabledmap_add_node called!\n"); abort(); } -/* Generated stub for disabledmap_channel_is_warned */ -bool disabledmap_channel_is_warned(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_channel_is_warned called!\n"); abort(); } -/* Generated stub for disabledmap_new */ -struct disabledmap *disabledmap_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "disabledmap_new called!\n"); abort(); } -/* Generated stub for disabledmap_reset */ -void disabledmap_reset(struct disabledmap *p UNNEEDED) -{ fprintf(stderr, "disabledmap_reset called!\n"); abort(); } -/* Generated stub for disabledmap_warn_channel */ -void disabledmap_warn_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_warn_channel called!\n"); abort(); } -/* Generated stub for json_add_payment */ -void json_add_payment(struct json_stream *s UNNEEDED, const struct payment *payment UNNEEDED) -{ fprintf(stderr, "json_add_payment called!\n"); abort(); } -/* Generated stub for new_routetracker */ -struct routetracker *new_routetracker(const tal_t *ctx UNNEEDED, struct payment *payment UNNEEDED) -{ fprintf(stderr, "new_routetracker called!\n"); abort(); } -/* Generated stub for pay_plugin */ -struct pay_plugin *pay_plugin; -/* Generated stub for routetracker_cleanup */ -void routetracker_cleanup(struct routetracker *routetracker UNNEEDED) -{ fprintf(stderr, "routetracker_cleanup called!\n"); abort(); } -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static void swap(int *a, int *b) -{ - int temp = *a; - *a = *b; - *b = temp; -} - -/* Canned gossmap, taken from tests/test_gossip.py, with channels mined. - * $> od -tx1 -Anone -v < /tmp/ltests-rtchpzh1/test_gossip_store_compact_noappend_1/lightning-2/regtest/gossip_store | sed 's/ / 0x/g'| cut -c2- | sed -e 's/ /,/g' -e 's/$/,/' - */ -static u8 canned_map[] = { - 0x0c,0x80,0x00,0x01,0xbc,0x09,0x8b,0x67,0xe6,0x00,0x00,0x00,0x00,0x10,0x08,0x00, - 0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x01,0xb0,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x22,0x6e, - 0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f, - 0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67, - 0x00,0x00,0x01,0x00,0x00,0x02,0x2d,0x22,0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7, - 0xac,0x44,0x7c,0x85,0xc4,0x6c,0x92,0x3d,0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1, - 0x1c,0x1e,0x3c,0xa3,0x1d,0x59,0x03,0x5d,0x2b,0x11,0x92,0xdf,0xba,0x13,0x4e,0x10, - 0xe5,0x40,0x87,0x5d,0x36,0x6e,0xbc,0x8b,0xc3,0x53,0xd5,0xaa,0x76,0x6b,0x80,0xc0, - 0x90,0xb3,0x9c,0x3a,0x5d,0x88,0x5d,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64,0x40, - 0x99,0x5d,0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19,0xff, - 0x9c,0x17,0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64, - 0x40,0x99,0x5d,0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19, - 0xff,0x9c,0x17,0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x00,0x00,0x01,0xb0,0x6e,0x30,0x94, - 0x60,0x65,0x55,0xb3,0x60,0x01,0x00,0x00,0x33,0x57,0x33,0xf5,0x94,0x2d,0xf5,0xd9, - 0x50,0xeb,0x87,0x66,0xde,0xe3,0xa9,0xd6,0x62,0x69,0x22,0x84,0x4e,0xd6,0xae,0x7a, - 0x11,0x0d,0xd7,0xe7,0xed,0xc3,0x2e,0x3f,0x6f,0x3d,0x9a,0xc5,0xcd,0xea,0x23,0xce, - 0x25,0xbb,0x8d,0xbf,0x76,0x1f,0xd3,0xd5,0xfc,0x56,0xc0,0x5b,0x68,0x56,0x31,0x6d, - 0x12,0xe9,0xd3,0x2c,0xa0,0xf0,0x8c,0x69,0xca,0x03,0x06,0xfe,0x71,0x6e,0x7b,0x51, - 0x51,0x31,0x7d,0x64,0x40,0xb7,0x37,0x3d,0x9f,0xbc,0x64,0x6e,0xad,0x48,0xf2,0x16, - 0x3f,0x2b,0x6d,0x51,0x1a,0xfe,0x6a,0x79,0xc7,0x55,0x51,0xc2,0x62,0x0f,0xc8,0x09, - 0x74,0xf2,0xf8,0x64,0x32,0x9d,0x97,0x78,0xa0,0x8c,0xdf,0xbc,0x9f,0x2c,0x9c,0x13, - 0x44,0xc4,0x32,0x70,0x2c,0x66,0x80,0x7c,0xfb,0x4d,0xb6,0x9b,0x80,0xfa,0xe8,0xc3, - 0x3c,0x70,0x14,0x3d,0x94,0x8b,0x36,0x61,0x4d,0x62,0x08,0x91,0xbe,0xe2,0xdf,0x99, - 0xc8,0x6b,0xc6,0x22,0x07,0xc1,0x7e,0x3b,0x91,0x86,0x21,0x4c,0x0c,0xcf,0xf2,0xde, - 0xd5,0x59,0x8a,0xcc,0xc9,0x0e,0xb1,0xd5,0xb2,0xf7,0xa8,0x3c,0xd7,0xf6,0x8d,0x71, - 0x2e,0xa0,0x47,0xd8,0x01,0x9f,0x34,0x30,0x63,0xb0,0xa2,0x36,0x35,0x6a,0x38,0x71, - 0x46,0xf5,0x8f,0xa8,0x32,0xdd,0xc1,0x3c,0x47,0x14,0x52,0x2c,0xbb,0x50,0x3f,0x5f, - 0x3c,0xa8,0xfc,0xec,0x66,0x02,0xbe,0x24,0x38,0xad,0x3f,0x98,0xfa,0x0c,0xee,0xd5, - 0x8f,0xe3,0xa0,0x66,0xd3,0x85,0xfc,0xac,0xd9,0x8c,0x70,0x4b,0x2a,0x8e,0x98,0xa3, - 0xe2,0x0b,0xf7,0x6b,0x35,0xb7,0x36,0x00,0x00,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b, - 0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a, - 0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00, - 0x00,0x02,0x2d,0x22,0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7,0xac,0x44,0x7c,0x85, - 0xc4,0x6c,0x92,0x3d,0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1,0x1c,0x1e,0x3c,0xa3, - 0x1d,0x59,0x03,0x5d,0x2b,0x11,0x92,0xdf,0xba,0x13,0x4e,0x10,0xe5,0x40,0x87,0x5d, - 0x36,0x6e,0xbc,0x8b,0xc3,0x53,0xd5,0xaa,0x76,0x6b,0x80,0xc0,0x90,0xb3,0x9c,0x3a, - 0x5d,0x88,0x5d,0x02,0x90,0x53,0x52,0x1d,0x6e,0xa7,0xa5,0x2c,0xdd,0x55,0xf7,0x33, - 0xd0,0xfb,0x2d,0x07,0x7c,0x03,0x73,0xb0,0x05,0x3b,0x5b,0x81,0x0d,0x92,0x72,0x44, - 0x06,0x1b,0x75,0x73,0x02,0xd6,0x06,0x3d,0x02,0x26,0x91,0xb2,0x49,0x0a,0xb4,0x54, - 0xde,0xe7,0x3a,0x57,0xc6,0xff,0x5d,0x30,0x83,0x52,0xb4,0x61,0xec,0xe6,0x9f,0x3c, - 0x28,0x4f,0x2c,0x24,0x12,0x00,0x00,0x00,0x0a,0x91,0x11,0x83,0xf6,0x00,0x00,0x00, - 0x00,0x10,0x05,0x00,0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x80,0x00,0x00,0x8a,0xc5, - 0x33,0xff,0x38,0x65,0x55,0xb3,0x60,0x01,0x02,0x2f,0xd9,0x23,0x60,0x1e,0x1c,0xa0, - 0xac,0xe5,0x06,0x8c,0xe4,0x8e,0x14,0xf3,0xcd,0x31,0x44,0x16,0xc4,0x0d,0x2e,0x14, - 0x8c,0xa1,0xc8,0x4f,0xa6,0xa8,0xe4,0x64,0x9b,0x45,0x79,0xd1,0xb5,0x2f,0x04,0x19, - 0x86,0xe5,0x5c,0x99,0x43,0xf1,0xd0,0xf3,0x6f,0x52,0xd6,0x88,0xf0,0x9b,0x9c,0x58, - 0x98,0x69,0x0d,0x4e,0x76,0x3e,0xbd,0x6e,0x95,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b, - 0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a, - 0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00, - 0x00,0x65,0x55,0xb3,0x60,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x3b,0x02,0x33, - 0x80,0x00,0x00,0x00,0x95,0x97,0xd7,0xa4,0x39,0x65,0x55,0xb3,0x65,0x01,0x01,0x36, - 0x59,0x20,0x77,0x0e,0xf4,0x73,0x10,0xd7,0xb6,0x59,0x5a,0x7c,0xbe,0xd0,0x56,0x51, - 0x3d,0x97,0xbe,0x84,0xb7,0x02,0xb5,0x89,0x72,0xbd,0xb3,0x19,0x2a,0x54,0x5f,0x57, - 0x52,0x09,0x1d,0xff,0x0f,0xe6,0x70,0x1e,0x71,0x23,0xaa,0x3f,0x98,0x89,0x86,0x35, - 0x87,0xc2,0x66,0xd9,0x99,0xbd,0xa2,0x7e,0x16,0x12,0xd9,0x6b,0xc4,0xd3,0x0c,0x00, - 0x07,0x88,0xa0,0x00,0x0a,0x0a,0x69,0xa2,0x65,0x55,0xb3,0x65,0x02,0x2d,0x22,0x36, - 0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7,0xac,0x44,0x7c,0x85,0xc4,0x6c,0x92,0x3d,0xa5, - 0x33,0x89,0x22,0x1a,0x00,0x54,0xc1,0x1c,0x1e,0x3c,0xa3,0x1d,0x59,0x02,0x2d,0x22, - 0x53,0x49,0x4c,0x45,0x4e,0x54,0x41,0x52,0x54,0x49,0x53,0x54,0x2d,0x31,0x2d,0x31, - 0x2d,0x67,0x63,0x63,0x65,0x34,0x66,0x36,0x38,0x2d,0x6d,0x6f,0x64,0x64,0x65,0x64, - 0x00,0x00,0x80,0x00,0x00,0x8a,0xd0,0xde,0x20,0xc6,0x65,0x55,0xb3,0x60,0x01,0x02, - 0x11,0xd6,0x4b,0xc5,0x88,0x33,0x4b,0x6c,0x1e,0x0c,0x4c,0x5c,0x65,0x69,0x35,0x66, - 0x45,0x80,0xbc,0x4b,0x56,0x3d,0x98,0x2c,0xb3,0x45,0xef,0x22,0x44,0x6e,0xca,0x6c, - 0x1f,0xe9,0x2a,0xf2,0x4a,0xe1,0x26,0xdb,0x15,0x6f,0x7e,0x3b,0xdd,0xd3,0x40,0x54, - 0xb2,0xc7,0x07,0xfe,0x67,0xb0,0xf3,0x35,0x56,0x25,0xc6,0x53,0xfd,0x54,0x56,0x5e, - 0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf, - 0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f, - 0x00,0x00,0x67,0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3,0x60,0x01,0x01,0x00,0x06, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a, - 0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x00,0x00,0x00,0x95,0x87,0xf8,0x92,0xf3, - 0x65,0x55,0xb3,0x65,0x01,0x01,0x32,0x6c,0xde,0x4a,0xc5,0xe8,0xd4,0xa0,0xed,0xbc, - 0x2c,0x13,0x5e,0xb9,0x1e,0xc3,0xc3,0x86,0xcb,0x75,0xeb,0x6f,0xce,0xc5,0xc5,0x57, - 0x01,0x1c,0x9c,0xb7,0x32,0x17,0x01,0x10,0x8c,0xdd,0x04,0x31,0x78,0xae,0xb4,0x88, - 0x8d,0xf8,0xe8,0x35,0x90,0x69,0x91,0x84,0xd0,0x16,0xd8,0x44,0xbc,0xde,0x37,0xe8, - 0x4e,0x1a,0x95,0xb6,0xa6,0x73,0x00,0x07,0x88,0xa0,0x00,0x0a,0x0a,0x69,0xa2,0x65, - 0x55,0xb3,0x65,0x03,0x5d,0x2b,0x11,0x92,0xdf,0xba,0x13,0x4e,0x10,0xe5,0x40,0x87, - 0x5d,0x36,0x6e,0xbc,0x8b,0xc3,0x53,0xd5,0xaa,0x76,0x6b,0x80,0xc0,0x90,0xb3,0x9c, - 0x3a,0x5d,0x88,0x5d,0x03,0x5d,0x2b,0x48,0x4f,0x50,0x50,0x49,0x4e,0x47,0x46,0x49, - 0x52,0x45,0x2d,0x63,0x31,0x2d,0x31,0x2d,0x67,0x63,0x63,0x65,0x34,0x66,0x36,0x38, - 0x2d,0x6d,0x6f,0x64,0x64,0x65,0x64,0x00,0x00,0x00,0x00,0x00,0x8a,0xcb,0xd4,0xc7, - 0xa1,0x65,0x55,0xb3,0x65,0x01,0x02,0x1d,0x3f,0x42,0x7f,0x3d,0xdb,0x58,0x2b,0xcb, - 0x78,0x5d,0x24,0xf1,0x67,0xc6,0xc7,0xd8,0x6e,0x6c,0x5b,0xf8,0xfb,0x27,0x17,0x58, - 0xaa,0x7e,0x46,0x86,0x49,0x66,0x21,0x47,0x76,0xbd,0xf2,0x2d,0xae,0x29,0xf0,0x6f, - 0x17,0x6e,0xf2,0x7f,0x01,0xda,0x16,0xa9,0x1d,0x6a,0x61,0x4c,0x41,0x71,0x19,0x4d, - 0x37,0xac,0x8a,0x28,0xd8,0x62,0xfb,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca, - 0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7, - 0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00,0x00,0x65, - 0x55,0xb3,0x65,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x80, - 0x00,0x01,0xbc,0x39,0xdb,0xc7,0xdf,0x00,0x00,0x00,0x00,0x10,0x08,0x00,0x00,0x00, - 0x00,0x00,0x0f,0x42,0x40,0x01,0xb0,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x22,0x6e,0x46,0x11, - 0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e, - 0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00, - 0x01,0x00,0x00,0x02,0x2d,0x22,0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7,0xac,0x44, - 0x7c,0x85,0xc4,0x6c,0x92,0x3d,0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1,0x1c,0x1e, - 0x3c,0xa3,0x1d,0x59,0x02,0x66,0xe4,0x59,0x8d,0x1d,0x3c,0x41,0x5f,0x57,0x2a,0x84, - 0x88,0x83,0x0b,0x60,0xf7,0xe7,0x44,0xed,0x92,0x35,0xeb,0x0b,0x1b,0xa9,0x32,0x83, - 0xb3,0x15,0xc0,0x35,0x18,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64,0x40,0x99,0x5d, - 0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19,0xff,0x9c,0x17, - 0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64,0x40,0x99, - 0x5d,0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19,0xff,0x9c, - 0x17,0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x80,0x00,0x00,0x8e,0x85,0x67,0x9b,0xcf,0x00, - 0x00,0x00,0x00,0x10,0x06,0x00,0x8a,0x01,0x02,0x27,0x9e,0x4e,0x43,0x39,0xa6,0x92, - 0x19,0x35,0x50,0x19,0xbb,0x51,0x5b,0xf9,0xac,0xdb,0xda,0x9c,0xde,0x81,0x8b,0x56, - 0x2e,0x0a,0x3d,0xd5,0xb0,0x5f,0x10,0x2e,0x6c,0x22,0x55,0x7e,0x07,0xc2,0x5f,0x4b, - 0x9c,0xc1,0x21,0x6e,0x07,0x66,0x41,0x60,0xde,0x3e,0xe2,0x24,0xa5,0x9e,0xec,0xaf, - 0xd7,0xcc,0x3f,0x87,0x7c,0x32,0x29,0xca,0xe7,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b, - 0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a, - 0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00, - 0x00,0x65,0x55,0xb3,0x6c,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x3b,0x02,0x33, - 0x80,0x80,0x00,0x00,0x8e,0xca,0xa4,0x8e,0x70,0x00,0x00,0x00,0x00,0x10,0x06,0x00, - 0x8a,0x01,0x02,0x4e,0x3f,0x3b,0x6d,0xda,0xcc,0xd3,0xef,0x5f,0xaf,0x26,0x76,0x16, - 0x64,0xa2,0x82,0x97,0xe8,0xb4,0xe4,0xb1,0x2d,0xec,0xa1,0x9e,0x91,0x69,0xd3,0xde, - 0xe9,0x58,0xc7,0x19,0x06,0x90,0x42,0x86,0x97,0xf3,0x88,0xca,0x35,0xd5,0xec,0x79, - 0x5e,0x59,0x33,0x31,0xf4,0x0c,0xdb,0x55,0x5d,0x78,0xd7,0x22,0x59,0xa2,0xe5,0x8d, - 0xeb,0x65,0x41,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43, - 0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1, - 0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3,0x6c,0x01, - 0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00, - 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x00,0x00,0x00,0x8a,0xbf, - 0x0d,0x7a,0xbc,0x65,0x55,0xb3,0x6f,0x01,0x02,0x59,0xc9,0x36,0x84,0xaa,0x13,0x70, - 0x73,0x7d,0xa7,0xe6,0x8a,0x72,0x05,0x15,0xa9,0x06,0x69,0xe0,0xc0,0x0d,0x15,0x6d, - 0x22,0x5c,0xd6,0x1a,0x3e,0x56,0xaa,0x9d,0x98,0x68,0x5a,0x3c,0xdc,0x38,0x71,0xee, - 0x0d,0x02,0x3e,0x2f,0xd0,0x97,0xd7,0xab,0xe2,0x0d,0xdf,0xf5,0xa6,0xd1,0x9c,0xb6, - 0xb9,0x30,0xd6,0xdd,0x10,0xa7,0xa3,0xce,0x5e,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b, - 0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a, - 0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00, - 0x00,0x65,0x55,0xb3,0x6f,0x01,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b,0x02,0x33, - 0x80,0x80,0x00,0x00,0x8e,0x80,0x00,0x75,0xd3,0x00,0x00,0x00,0x00,0x10,0x06,0x00, - 0x8a,0x01,0x02,0x26,0x62,0x25,0x68,0x2d,0x27,0x06,0x9a,0x3b,0xaa,0x1a,0x43,0x93, - 0xac,0xb5,0x05,0x14,0x1c,0x09,0x22,0x8d,0xfb,0x54,0x43,0x73,0x3a,0x88,0xaa,0xa3, - 0x18,0xc8,0xd8,0x0d,0x36,0xd4,0x26,0x10,0xe9,0x82,0xa6,0x3c,0xb4,0x38,0x24,0xaf, - 0xdc,0x06,0xd5,0x3d,0xe3,0x7a,0xe8,0x39,0xdd,0xd4,0x5f,0xd8,0x92,0xf5,0x4b,0xe7, - 0x8d,0xf7,0xab,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43, - 0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1, - 0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3,0x71,0x01, - 0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00, - 0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x80,0x00,0x00,0x8e,0xdc, - 0x5e,0x38,0x7a,0x00,0x00,0x00,0x00,0x10,0x06,0x00,0x8a,0x01,0x02,0x5d,0xf9,0x14, - 0xa0,0xd1,0x46,0x69,0x9a,0x2a,0x97,0xf7,0xb3,0x83,0xf0,0x30,0x73,0x48,0x5e,0x7b, - 0x3e,0x6c,0x45,0xd3,0xf1,0x51,0xf8,0xbc,0x4b,0xd7,0xae,0x02,0xd9,0x57,0x51,0x73, - 0x8f,0x0f,0xd0,0xdd,0x38,0x36,0x48,0xcd,0xda,0xea,0x01,0x24,0x16,0x3d,0x14,0x7e, - 0x5a,0x9e,0xac,0xcd,0x81,0xd1,0x96,0x30,0xd9,0xbc,0xa6,0xe4,0xe8,0x06,0x22,0x6e, - 0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f, - 0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e, - 0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3,0x71,0x01,0x00,0x00,0x06,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00, - 0x00,0x3b,0x02,0x33,0x80,0x00,0x00,0x01,0xb0,0xb8,0x17,0x71,0xe9,0x65,0x55,0xb3, - 0x71,0x01,0x00,0x0d,0xef,0xc0,0x12,0x4f,0xd4,0xe4,0x1c,0xce,0xc3,0xf4,0x83,0x17, - 0x21,0x7e,0x33,0x11,0x5a,0x8f,0x8c,0x39,0xae,0xbb,0x06,0x2b,0x4e,0x73,0xa6,0x52, - 0xee,0x0e,0x61,0x03,0xf9,0xf1,0x16,0x7b,0x22,0xbd,0xcb,0x92,0xcf,0x82,0x2d,0x0f, - 0xcc,0x26,0xcb,0x11,0x9f,0x0a,0xf5,0x23,0xf4,0x26,0x97,0x86,0x7e,0x21,0x51,0x85, - 0x16,0x3a,0x0e,0x4b,0x1f,0x64,0xf3,0x62,0xf4,0x2f,0x13,0x54,0x5c,0x9a,0xed,0xd6, - 0x9d,0x5d,0xf5,0x17,0x85,0xcc,0x5d,0x53,0xa4,0x84,0x36,0x9b,0xe9,0xa7,0x4a,0x04, - 0x1f,0x8c,0x1c,0x28,0xc7,0x1e,0xab,0xb3,0xf2,0x53,0x3d,0xf8,0xb6,0xcd,0xd8,0x45, - 0x6e,0xc3,0x77,0xdb,0xb1,0x2b,0xa5,0xdd,0xc9,0xbf,0x69,0xa7,0xdf,0x98,0x2c,0x54, - 0xa7,0xa8,0xf5,0x4f,0xc3,0xd1,0x57,0x81,0x98,0x60,0xa0,0xb6,0x32,0x83,0x08,0x65, - 0x1e,0x79,0x24,0xf4,0xbe,0xfe,0x14,0xa6,0xfd,0x31,0x9b,0xfa,0x15,0x6f,0x1b,0xab, - 0x64,0x8b,0x69,0x48,0xb4,0x2e,0x99,0xca,0xd7,0x46,0x97,0x57,0x17,0x1d,0x5d,0x53, - 0xe5,0xde,0xae,0xbb,0x2d,0x74,0x86,0xa5,0xe5,0x8e,0x02,0x5c,0x2c,0x8d,0x0d,0x78, - 0xdc,0xf1,0xa0,0x05,0xb8,0xbd,0x64,0x7d,0x51,0x47,0x5e,0x39,0xaa,0x08,0x2d,0xf2, - 0xd0,0x1a,0x96,0x59,0xab,0x88,0x94,0xa2,0xf7,0x2e,0x09,0x3b,0xd0,0xed,0xfc,0xb4, - 0x1b,0xaa,0xba,0x34,0xf8,0x7b,0x11,0x51,0x36,0x7b,0x13,0x84,0x6f,0xc9,0x4a,0x58, - 0x92,0x3c,0x0c,0xa2,0x4c,0x8f,0xc5,0x78,0xb4,0x4a,0xad,0x98,0x08,0x4e,0x4a,0xf3, - 0x7a,0x28,0x80,0x00,0x00,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12, - 0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7, - 0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00,0x00,0x02,0x2d,0x22, - 0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7,0xac,0x44,0x7c,0x85,0xc4,0x6c,0x92,0x3d, - 0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1,0x1c,0x1e,0x3c,0xa3,0x1d,0x59,0x02,0x66, - 0xe4,0x59,0x8d,0x1d,0x3c,0x41,0x5f,0x57,0x2a,0x84,0x88,0x83,0x0b,0x60,0xf7,0xe7, - 0x44,0xed,0x92,0x35,0xeb,0x0b,0x1b,0xa9,0x32,0x83,0xb3,0x15,0xc0,0x35,0x18,0x03, - 0xbb,0xee,0x60,0xc3,0x95,0x05,0x6b,0x8a,0x12,0x01,0xe0,0x6e,0xd7,0x9e,0x29,0x14, - 0xc1,0x1a,0x61,0xd7,0xb1,0xaa,0x78,0x18,0x46,0x46,0x8d,0x02,0x48,0x9d,0xba,0x69, - 0x02,0x32,0x42,0x66,0xde,0x84,0x03,0xb3,0xab,0x15,0x7a,0x09,0xf1,0xf7,0x84,0xd5, - 0x87,0xaf,0x61,0x83,0x1c,0x99,0x8c,0x15,0x1b,0xcc,0x21,0xbb,0x74,0xc2,0xb2,0x31, - 0x4b,0x00,0x00,0x00,0x0a,0x91,0x11,0x83,0xf6,0x00,0x00,0x00,0x00,0x10,0x05,0x00, - 0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x00,0x00,0x00,0x8a,0xe1,0x31,0x6b,0x76,0x65, - 0x55,0xb3,0x71,0x01,0x02,0x5d,0xf9,0x14,0xa0,0xd1,0x46,0x69,0x9a,0x2a,0x97,0xf7, - 0xb3,0x83,0xf0,0x30,0x73,0x48,0x5e,0x7b,0x3e,0x6c,0x45,0xd3,0xf1,0x51,0xf8,0xbc, - 0x4b,0xd7,0xae,0x02,0xd9,0x57,0x51,0x73,0x8f,0x0f,0xd0,0xdd,0x38,0x36,0x48,0xcd, - 0xda,0xea,0x01,0x24,0x16,0x3d,0x14,0x7e,0x5a,0x9e,0xac,0xcd,0x81,0xd1,0x96,0x30, - 0xd9,0xbc,0xa6,0xe4,0xe8,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12, - 0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7, - 0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00,0x01,0x00,0x00,0x65,0x55,0xb3, - 0x71,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80,0x00,0x00,0x00, - 0x8a,0xbd,0x6f,0x26,0xdf,0x65,0x55,0xb3,0x71,0x01,0x02,0x26,0x62,0x25,0x68,0x2d, - 0x27,0x06,0x9a,0x3b,0xaa,0x1a,0x43,0x93,0xac,0xb5,0x05,0x14,0x1c,0x09,0x22,0x8d, - 0xfb,0x54,0x43,0x73,0x3a,0x88,0xaa,0xa3,0x18,0xc8,0xd8,0x0d,0x36,0xd4,0x26,0x10, - 0xe9,0x82,0xa6,0x3c,0xb4,0x38,0x24,0xaf,0xdc,0x06,0xd5,0x3d,0xe3,0x7a,0xe8,0x39, - 0xdd,0xd4,0x5f,0xd8,0x92,0xf5,0x4b,0xe7,0x8d,0xf7,0xab,0x06,0x22,0x6e,0x46,0x11, - 0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e, - 0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x6e,0x00,0x00, - 0x01,0x00,0x00,0x65,0x55,0xb3,0x71,0x01,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x03,0xe8,0x00,0x00,0x00,0x00,0x3b, - 0x02,0x33,0x80,0x00,0x00,0x00,0x95,0xf6,0xd7,0x24,0x69,0x65,0x55,0xb3,0x79,0x01, - 0x01,0x42,0x62,0x2b,0xf0,0x0a,0x44,0xc3,0xe0,0x78,0x3c,0xa7,0x08,0x5a,0xa0,0xaa, - 0x9d,0xb2,0xdc,0x2e,0xa7,0x29,0x0a,0x39,0x3d,0x01,0x57,0xbf,0x34,0x23,0x0f,0xb9, - 0x19,0x33,0x6f,0xc2,0xfb,0x9f,0x25,0xac,0xc4,0xc9,0x47,0xb7,0x41,0x92,0xdb,0x2f, - 0xd5,0xf0,0x1f,0x24,0x32,0xdf,0x38,0x30,0x89,0x93,0xeb,0xc9,0xec,0xf1,0x09,0x51, - 0xbb,0x00,0x07,0x88,0xa0,0x00,0x0a,0x0a,0x69,0xa2,0x65,0x55,0xb3,0x79,0x02,0x66, - 0xe4,0x59,0x8d,0x1d,0x3c,0x41,0x5f,0x57,0x2a,0x84,0x88,0x83,0x0b,0x60,0xf7,0xe7, - 0x44,0xed,0x92,0x35,0xeb,0x0b,0x1b,0xa9,0x32,0x83,0xb3,0x15,0xc0,0x35,0x18,0x02, - 0x66,0xe4,0x4a,0x55,0x4e,0x49,0x4f,0x52,0x42,0x45,0x41,0x4d,0x2d,0x72,0x63,0x31, - 0x2d,0x31,0x2d,0x67,0x63,0x63,0x65,0x34,0x66,0x36,0x38,0x2d,0x6d,0x6f,0x64,0x64, - 0x65,0x64,0x00,0x00, -}; - -/* not_mcf sets NDEBUG, so assert() is useless */ -#define ASSERT(x) do { if (!(x)) abort(); } while(0) - -static const char *print_flows( - const tal_t *ctx, - const char *desc, - const struct gossmap *gossmap, - struct flow **flows) -{ - tal_t *this_ctx = tal(ctx,tal_t); - char *buff = tal_fmt(ctx,"%s: %zu subflows\n", desc, 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; -} - -static void remove_file(char *fname) { assert(!remove(fname)); } - -int main(int argc, char *argv[]) -{ - int fd; - char *gossfile; - struct gossmap *gossmap; - struct node_id l1, l2, l3; - struct flow **flows; - struct route **routes; - struct short_channel_id scid12, scid23; - struct sha256 payment_hash; - struct amount_msat *amounts; - char *errmsg; - - if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102", - strlen("0001020304050607080900010203040506070809000102030405060708090102"), - &payment_hash, sizeof(payment_hash))) - abort(); - - common_setup(argv[0]); - - fd = tmpdir_mkstemp(tmpctx, "run-not_mcf.XXXXXX", &gossfile); - tal_add_destructor(gossfile, remove_file); - assert(write_all(fd, canned_map, sizeof(canned_map))); - - gossmap = gossmap_load(tmpctx, gossfile, NULL, NULL); - assert(gossmap); - - /* There is a public channel 2<->3 (103x1x0), and 1<->2 (110x1x1). */ - assert(node_id_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", 66, &l1)); - assert(node_id_from_hexstr("022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", 66, &l2)); - assert(node_id_from_hexstr("035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", 66, &l3)); - assert(short_channel_id_from_str("110x1x0", 7, &scid12)); - assert(short_channel_id_from_str("103x1x0", 7, &scid23)); - - struct uncertainty *uncertainty = uncertainty_new(tmpctx); - int skipped_count = uncertainty_update(uncertainty, gossmap); - assert(skipped_count == 0); - - bitmap *disabled = tal_arrz( - tmpctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); - - printf("All set, now let's call minflow ...\n"); - - flows = minflow(tmpctx, gossmap, - gossmap_find_node(gossmap, &l1), - gossmap_find_node(gossmap, &l3), - uncertainty_get_chan_extra_map(uncertainty), - disabled, - /* Half the capacity */ - AMOUNT_MSAT(500000000), - /* max_fee = */ AMOUNT_MSAT(1000000), // 1k sats - /* min probability = */ 0.1, - /* base probability = */ 1.0, - /* delay fee factor = */ 1, - /* base fee penalty */ 1, - /* prob cost factor = */ 10, - &errmsg); - printf("minflow completed.\n"); - - if (!flows) { - printf("Minflow has failed with: %s", errmsg); - assert(0 && "minflow failed"); - } - - routes = flows_to_routes(tmpctx, 1, 1, payment_hash, 10, gossmap, flows); - assert(routes); - for (size_t i = 0; i < tal_count(routes); i++) { - uncertainty_commit_htlcs(uncertainty, routes[i]); - } - - printf("%s\n", - print_flows(tmpctx,"Flow via single path l1->l2->l3", gossmap, flows)); - - - printf("Checking results.\n"); - /* Should go 1->2->3 */ - amounts = tal_flow_amounts(tmpctx, flows[0], true); - assert(amounts); - assert(tal_count(flows) == 1); - assert(tal_count(flows[0]->path) == 2); - assert(tal_count(flows[0]->dirs) == 2); - assert(tal_count(amounts) == 2); - - assert(flows[0]->path[0] == gossmap_find_chan(gossmap, &scid12)); - assert(flows[0]->path[1] == gossmap_find_chan(gossmap, &scid23)); - assert(flows[0]->dirs[0] == 1); - assert(flows[0]->dirs[1] == 0); - assert(amount_msat_eq(amounts[1], AMOUNT_MSAT(500000000))); - /* fee_base_msat == 20, fee_proportional_millionths == 1000 */ - assert(amount_msat_eq(amounts[0], AMOUNT_MSAT(500000000 + 500000 + 20))); - - /* Each one has probability ~ 0.5 */ - assert(flows[0]->success_prob > 0.249); - assert(flows[0]->success_prob <= 0.251); - - - /* Should have filled in some extra data! */ - struct chan_extra *ce = uncertainty_find_channel(uncertainty, scid12); - assert(short_channel_id_eq(ce->scid, scid12)); - /* l1->l2 dir is 1 */ - assert(ce->half[1].num_htlcs == 1); - assert(amount_msat_eq(ce->half[1].htlc_total, AMOUNT_MSAT(500000000 + 500000 + 20))); - assert(amount_msat_eq(ce->half[1].known_min, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[1].known_max, AMOUNT_MSAT(1000000000))); - assert(ce->half[0].num_htlcs == 0); - assert(amount_msat_eq(ce->half[0].htlc_total, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[0].known_min, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[0].known_max, AMOUNT_MSAT(1000000000))); - - ce = uncertainty_find_channel(uncertainty, scid23); - assert(short_channel_id_eq(ce->scid, scid23)); - /* l2->l3 dir is 0 */ - assert(ce->half[0].num_htlcs == 1); - assert(amount_msat_eq(ce->half[0].htlc_total, AMOUNT_MSAT(500000000))); - assert(amount_msat_eq(ce->half[0].known_min, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[0].known_max, AMOUNT_MSAT(1000000000))); - assert(ce->half[1].num_htlcs == 0); - assert(amount_msat_eq(ce->half[1].htlc_total, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[1].known_min, AMOUNT_MSAT(0))); - assert(amount_msat_eq(ce->half[1].known_max, AMOUNT_MSAT(1000000000))); - - /* Clear that */ - for (size_t i = 0; i < tal_count(routes); i++) { - uncertainty_remove_htlcs(uncertainty, routes[i]); - } - - // /* Now try adding a local channel scid */ - - struct short_channel_id scid13; - struct gossmap_localmods *mods = gossmap_localmods_new(tmpctx); - assert(short_channel_id_from_str("111x1x1", 7, &scid13)); - - /* 400,000sat channel from 1->3, basefee 0, ppm 1000, delay 5 */ - assert(gossmap_local_addchan(mods, &l1, &l3, scid13, - AMOUNT_MSAT(400000000), NULL)); - assert(gossmap_local_setchan(mods, scid13, - AMOUNT_MSAT(0), - AMOUNT_MSAT(400000000), - AMOUNT_MSAT(0), 1000, 5, - true, - 0)); - - /* Apply changes, check they work. */ - gossmap_apply_localmods(gossmap, mods); - struct gossmap_chan *local_chan = gossmap_find_chan(gossmap, &scid13); - assert(local_chan); - - /* The local chans have no "capacity", so set it manually. */ - /* FIXME: They do now! */ - uncertainty_add_channel(uncertainty, scid13, AMOUNT_MSAT(400000000)); - - // flows = minflow(tmpctx, gossmap, - // gossmap_find_node(gossmap, &l1), - // gossmap_find_node(gossmap, &l3), - // chan_extra_map, NULL, - // /* This will go first via 1-2-3, then 1->3. */ - // AMOUNT_MSAT(500000000), - // /* max_fee = */ AMOUNT_MSAT(1000000), // 1k sats - // /* min probability = */ 0.4, - // /* delay fee factor = */ 1, - // /* base fee penalty */ 1, - // /* prob cost factor = */ 10); - - // print_flows("Flow via two paths, low mu", gossmap, flows); - - // assert(tal_count(flows) == 2); - // - // if(tal_count(flows[0]->path)path)) - // { - // struct flow* tmp = flows[0]; - // flows[0] = flows[1]; - // flows[1]=tmp; - // } - // - // assert(tal_count(flows[0]->path) == 2); - // assert(tal_count(flows[0]->dirs) == 2); - // assert(tal_count(flows[0]->amounts) == 2); - - // assert(flows[0]->path[0] == gossmap_find_chan(gossmap, &scid12)); - // assert(flows[0]->path[1] == gossmap_find_chan(gossmap, &scid23)); - // assert(flows[0]->dirs[0] == 1); - // assert(flows[0]->dirs[1] == 0); - - // /* First one has probability ~ 50% */ - // assert(flows[0]->success_prob < 0.55); - // assert(flows[0]->success_prob > 0.45); - - // assert(tal_count(flows[1]->path) == 1); - // assert(tal_count(flows[1]->dirs) == 1); - // assert(tal_count(flows[1]->amounts) == 1); - - // /* We will try cheaper path first, but not to fill it! */ - // assert(flows[1]->path[0] == gossmap_find_chan(gossmap, &scid13)); - // assert(flows[1]->dirs[0] == 0); - // assert(amount_msat_less(flows[1]->amounts[0], AMOUNT_MSAT(400000000))); - - // /* Second one has probability ~ 50% */ - // assert(flows[1]->success_prob < 0.55); - // assert(flows[1]->success_prob > 0.45); - - // /* Delivered amount must be the total! */ - // assert(flows[0]->amounts[1].millisatoshis - // + flows[1]->amounts[0].millisatoshis == 500000000); - - // /* Clear them. */ - // remove_completed_flow(gossmap, chan_extra_map, flows[0]); - // remove_completed_flow(gossmap, chan_extra_map, flows[1]); - - printf("All set, let's call minflow ... \n"); - - /* Higher mu values mean we pay more for certainty! */ - struct flow **flows2 = minflow(tmpctx, gossmap, - gossmap_find_node(gossmap, &l1), - gossmap_find_node(gossmap, &l3), - uncertainty_get_chan_extra_map(uncertainty), - disabled, - /* This will go 400000000 via 1->3, rest via 1-2-3. */ - /* amount = */ AMOUNT_MSAT(500000000), //500k sats - /* max_fee = */ AMOUNT_MSAT(1000000), // 1k sats - /* min probability = */ 0.1, // 10% - /* base probability = */ 1.0, - /* delay fee factor = */ 1, - /* base fee penalty */ 1, - /* prob cost factor = */ 10, - &errmsg); - - printf("minflow completed execution.\n"); - - if (!flows) { - printf("Minflow has failed with: %s", errmsg); - assert(0 && "minflow failed"); - } - printf("%s\n", - print_flows(tmpctx,"Flow via two paths, high mu", gossmap, flows2)); - - printf("Verifying results ... \n"); - - assert(tal_count(flows2) == 2); - - /* The solution is composed by two paths, one with lenght 1 and the - * other with lenght 2. There is no guaranteed order of the solutions - * returning from minflow, hence we need to test them. */ - int ID1 = 0, ID2 = 1; - if(tal_count(flows2[ID1]->path)==2) - { - swap(&ID1,&ID2); - } - assert(tal_count(flows2[ID1]->path) == 1); - assert(tal_count(flows2[ID2]->path) == 2); - - // /* Sends more via 1->3, since it's more expensive (but lower prob) */ - assert(amount_msat_greater(flows2[ID1]->amount, flows2[ID2]->amount)); - assert(flows2[ID1]->success_prob < flows2[ID2]->success_prob); - - /* Delivered amount must be the total! */ - assert(flows2[ID1]->amount.millisatoshis - + flows2[ID2]->amount.millisatoshis == 500000000); - - common_shutdown(); -} diff --git a/plugins/renepay/test/run-route_map.c b/plugins/renepay/test/run-route_map.c index 5dad081810a1..980f3d887fe7 100644 --- a/plugins/renepay/test/run-route_map.c +++ b/plugins/renepay/test/run-route_map.c @@ -17,7 +17,6 @@ #include #define RENEPAY_UNITTEST -#include "../flow.c" #include "../route.c" /* AUTOGENERATED MOCKS START */ diff --git a/plugins/renepay/test/run-testflow.c b/plugins/renepay/test/run-testflow.c deleted file mode 100644 index 83ffe80e3766..000000000000 --- a/plugins/renepay/test/run-testflow.c +++ /dev/null @@ -1,842 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MYLOG "/tmp/debug.txt" -#define RENEPAY_UNITTEST // logs are written in MYLOG -#include "../payment.c" -#include "../flow.c" -#include "../route.c" -#include "../uncertainty.c" -#include "../mcf.c" - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for disabledmap_add_channel */ -void disabledmap_add_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_add_channel called!\n"); abort(); } -/* Generated stub for disabledmap_add_node */ -void disabledmap_add_node(struct disabledmap *p UNNEEDED, struct node_id node UNNEEDED) -{ fprintf(stderr, "disabledmap_add_node called!\n"); abort(); } -/* Generated stub for disabledmap_channel_is_warned */ -bool disabledmap_channel_is_warned(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_channel_is_warned called!\n"); abort(); } -/* Generated stub for disabledmap_new */ -struct disabledmap *disabledmap_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "disabledmap_new called!\n"); abort(); } -/* Generated stub for disabledmap_reset */ -void disabledmap_reset(struct disabledmap *p UNNEEDED) -{ fprintf(stderr, "disabledmap_reset called!\n"); abort(); } -/* Generated stub for disabledmap_warn_channel */ -void disabledmap_warn_channel(struct disabledmap *p UNNEEDED, - struct short_channel_id_dir scidd UNNEEDED) -{ fprintf(stderr, "disabledmap_warn_channel called!\n"); abort(); } -/* Generated stub for json_add_payment */ -void json_add_payment(struct json_stream *s UNNEEDED, const struct payment *payment UNNEEDED) -{ fprintf(stderr, "json_add_payment called!\n"); abort(); } -/* Generated stub for new_routetracker */ -struct routetracker *new_routetracker(const tal_t *ctx UNNEEDED, struct payment *payment UNNEEDED) -{ fprintf(stderr, "new_routetracker called!\n"); abort(); } -/* Generated stub for pay_plugin */ -struct pay_plugin *pay_plugin; -/* Generated stub for routetracker_cleanup */ -void routetracker_cleanup(struct routetracker *routetracker UNNEEDED) -{ fprintf(stderr, "routetracker_cleanup called!\n"); abort(); } -/* Generated stub for sciddir_or_pubkey_from_node_id */ -bool sciddir_or_pubkey_from_node_id(struct sciddir_or_pubkey *sciddpk UNNEEDED, - const struct node_id *node_id UNNEEDED) -{ fprintf(stderr, "sciddir_or_pubkey_from_node_id called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -static const u8 canned_map[] = { -0x0c, 0x80, 0x00, 0x01, 0xbc, 0x86, 0xe4, 0xbf, 0x95, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, -0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x22, 0x6e, -0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, -0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, -0x00, 0x00, 0x01, 0x00, 0x01, 0x02, 0x4f, 0x9d, 0xa0, 0xd7, 0x26, 0xad, 0xf0, 0xd9, 0xa4, 0xa3, -0xac, 0x32, 0xe3, 0x28, 0xb9, 0x3a, 0xd5, 0x27, 0xcc, 0xb9, 0xdb, 0x70, 0x77, 0xc5, 0x7a, 0x12, -0xc6, 0xf9, 0xfa, 0x9a, 0xdd, 0x74, 0x03, 0x7f, 0x97, 0xaf, 0x8e, 0x4f, 0xa6, 0x7c, 0x9b, 0x8d, -0x69, 0x70, 0x84, 0x95, 0x36, 0xf8, 0x88, 0xc3, 0x04, 0x31, 0x6d, 0x01, 0x5a, 0xf5, 0x12, 0x9e, -0x26, 0x8e, 0x01, 0x64, 0x96, 0x1b, 0x5e, 0x03, 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, 0x40, -0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, 0xff, -0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f, 0x03, 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, -0x40, 0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, -0xff, 0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f, 0x40, 0x00, 0x01, 0xb0, 0x24, 0x3a, 0xa3, -0x76, 0x64, 0x62, 0x19, 0xec, 0x01, 0x00, 0x66, 0x7f, 0x0f, 0xad, 0x6d, 0x9d, 0x58, 0x1b, 0x28, -0x8a, 0x67, 0x9d, 0xf8, 0xd1, 0x9d, 0x79, 0x4e, 0x67, 0xc8, 0x76, 0xbb, 0xdd, 0x4d, 0x8e, 0x45, -0x0d, 0xc9, 0x0e, 0x24, 0x76, 0xda, 0x44, 0x68, 0x7b, 0xe2, 0x14, 0xe8, 0x48, 0xfa, 0xd7, 0xc2, -0x35, 0xc5, 0x98, 0xd9, 0x7a, 0x6c, 0xcb, 0xb1, 0x4b, 0x19, 0xf9, 0xfa, 0xb2, 0x19, 0x3f, 0x87, -0xc1, 0xe9, 0x47, 0x51, 0x16, 0x64, 0x36, 0x2a, 0xeb, 0xc5, 0xaa, 0x20, 0x59, 0x4e, 0xdf, 0xae, -0x4e, 0x10, 0x38, 0x34, 0x8e, 0x06, 0x6e, 0x5d, 0x1b, 0x44, 0x30, 0xfb, 0x20, 0xed, 0xea, 0xde, -0x83, 0xcd, 0xa4, 0x8a, 0x5c, 0xad, 0x70, 0x2d, 0x8b, 0x04, 0xfb, 0xa2, 0xbd, 0x95, 0x7c, 0xdd, -0x66, 0xb5, 0x4e, 0xd6, 0xc6, 0x27, 0xdb, 0xa8, 0xe1, 0x26, 0x22, 0x81, 0x57, 0xe2, 0xaa, 0xe4, -0x82, 0xbe, 0x9e, 0x90, 0xc5, 0xc2, 0x59, 0x56, 0x9b, 0x79, 0xf3, 0xc3, 0xfe, 0x0c, 0xb3, 0x35, -0xeb, 0xba, 0xad, 0xf7, 0xd3, 0x24, 0x4e, 0x16, 0x15, 0x2d, 0x86, 0xd9, 0xe9, 0xd2, 0x38, 0x9b, -0xf9, 0xb3, 0x5f, 0x2c, 0x9b, 0xeb, 0xe0, 0x1c, 0xb3, 0xf0, 0x0f, 0xc1, 0x9d, 0x0b, 0x20, 0xa2, -0x19, 0xeb, 0x1a, 0x05, 0x8b, 0x8d, 0xb1, 0x22, 0x74, 0x7c, 0xa4, 0x39, 0x94, 0x6f, 0xfc, 0x34, -0x1b, 0xe5, 0x9f, 0x45, 0x8e, 0x12, 0x6e, 0x65, 0x73, 0x28, 0x21, 0x80, 0xfd, 0x9c, 0x0c, 0x89, -0x2b, 0xcb, 0x43, 0x2e, 0x7f, 0x47, 0xa1, 0xd7, 0x7e, 0xa9, 0xd7, 0x3e, 0xdd, 0xa0, 0xf8, 0x60, -0x9d, 0xde, 0x51, 0x3d, 0xc4, 0x21, 0x06, 0x61, 0xb3, 0x4d, 0xd8, 0x94, 0x4a, 0x3a, 0xc9, 0xb9, -0xc3, 0xcb, 0x09, 0xa3, 0x2f, 0x7b, 0x96, 0x53, 0x13, 0x1d, 0x6d, 0x7a, 0x28, 0xdd, 0xc8, 0x8d, -0xe4, 0x10, 0xad, 0x4c, 0xc6, 0xa0, 0x1b, 0x00, 0x00, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, -0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, -0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x00, -0x01, 0x02, 0x4f, 0x9d, 0xa0, 0xd7, 0x26, 0xad, 0xf0, 0xd9, 0xa4, 0xa3, 0xac, 0x32, 0xe3, 0x28, -0xb9, 0x3a, 0xd5, 0x27, 0xcc, 0xb9, 0xdb, 0x70, 0x77, 0xc5, 0x7a, 0x12, 0xc6, 0xf9, 0xfa, 0x9a, -0xdd, 0x74, 0x03, 0x7f, 0x97, 0xaf, 0x8e, 0x4f, 0xa6, 0x7c, 0x9b, 0x8d, 0x69, 0x70, 0x84, 0x95, -0x36, 0xf8, 0x88, 0xc3, 0x04, 0x31, 0x6d, 0x01, 0x5a, 0xf5, 0x12, 0x9e, 0x26, 0x8e, 0x01, 0x64, -0x96, 0x1b, 0x5e, 0x02, 0xca, 0x1a, 0xac, 0x5f, 0x7b, 0x86, 0x3a, 0x01, 0xc8, 0x69, 0x90, 0x82, -0xdf, 0x9a, 0x4d, 0xf8, 0x14, 0x0d, 0xd6, 0xe7, 0x10, 0x59, 0xd4, 0xec, 0x7f, 0x48, 0x13, 0xb0, -0x96, 0xb4, 0xa3, 0xad, 0x02, 0x21, 0x55, 0x92, 0x46, 0x1c, 0x84, 0x3d, 0x40, 0xe6, 0x01, 0x8d, -0x3d, 0x0c, 0xb6, 0xf4, 0xe1, 0x61, 0xe2, 0x4b, 0x59, 0x41, 0xdb, 0x3b, 0x20, 0x44, 0xbc, 0x0c, -0xb2, 0x0e, 0x4d, 0x3f, 0x9b, 0x00, 0x00, 0x00, 0x0a, 0x91, 0x11, 0x83, 0xf6, 0x00, 0x00, 0x00, -0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 0xc0, 0x00, 0x00, 0x8a, 0x01, -0x3d, 0x6f, 0x9a, 0x64, 0x62, 0x19, 0xec, 0x01, 0x02, 0x4c, 0x45, 0x7e, 0x21, 0xb8, 0xd5, 0x36, -0x98, 0xcd, 0x45, 0x03, 0x78, 0xa6, 0x51, 0xf1, 0xda, 0x1a, 0xb4, 0x46, 0xed, 0xfb, 0xed, 0x86, -0xf9, 0x31, 0x85, 0x2e, 0x3d, 0x80, 0x77, 0xf2, 0x13, 0x76, 0x91, 0x08, 0xe7, 0x52, 0x3d, 0xf4, -0xe5, 0x2e, 0x3b, 0x80, 0x2a, 0xbf, 0x54, 0xf8, 0x80, 0xbb, 0x77, 0x6f, 0xc6, 0xca, 0x9e, 0x3f, -0xe8, 0x96, 0xfa, 0x54, 0x7e, 0x94, 0x78, 0x0a, 0xec, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, -0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, -0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x00, -0x01, 0x64, 0x62, 0x19, 0xec, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, -0x80, 0x40, 0x00, 0x00, 0xa4, 0x07, 0xd2, 0xf1, 0x5d, 0x64, 0x62, 0x19, 0xf1, 0x01, 0x01, 0x4d, -0xbe, 0x8a, 0xf5, 0xd8, 0x19, 0x2b, 0x99, 0xb0, 0xa0, 0xde, 0x24, 0x36, 0x32, 0x06, 0xac, 0x40, -0x4c, 0x41, 0x94, 0xc1, 0xd3, 0x85, 0xb5, 0xb8, 0x76, 0xbf, 0x98, 0xa9, 0x8e, 0xdb, 0xca, 0x43, -0x73, 0x98, 0xa0, 0xe0, 0x11, 0xa9, 0x95, 0xf3, 0xce, 0xde, 0xe5, 0x85, 0x80, 0x63, 0x8c, 0x12, -0x11, 0xee, 0xee, 0xa1, 0x3e, 0xcf, 0x4e, 0xd5, 0xae, 0x8d, 0x93, 0x22, 0xce, 0xbb, 0x02, 0x00, -0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, 0x19, 0xf1, 0x02, 0x4f, 0x9d, 0xa0, -0xd7, 0x26, 0xad, 0xf0, 0xd9, 0xa4, 0xa3, 0xac, 0x32, 0xe3, 0x28, 0xb9, 0x3a, 0xd5, 0x27, 0xcc, -0xb9, 0xdb, 0x70, 0x77, 0xc5, 0x7a, 0x12, 0xc6, 0xf9, 0xfa, 0x9a, 0xdd, 0x74, 0x02, 0x4f, 0x9d, -0x4c, 0x4f, 0x55, 0x44, 0x54, 0x52, 0x41, 0x57, 0x4c, 0x2d, 0x2e, 0x30, 0x32, 0x2d, 0x33, 0x37, -0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, 0x6d, 0x6f, 0x64, 0x64, 0x65, 0x64, -0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, 0x4c, 0x4b, -0x40, 0xc0, 0x00, 0x00, 0x8a, 0x06, 0x22, 0xaa, 0xb5, 0x64, 0x62, 0x19, 0xec, 0x01, 0x02, 0x2b, -0x9e, 0x17, 0x25, 0x0f, 0x3d, 0x8c, 0x1c, 0x07, 0x6b, 0xb8, 0x7f, 0xdc, 0xc4, 0x30, 0xf4, 0xa7, -0xf8, 0x8b, 0x91, 0x53, 0xd6, 0xc1, 0x9d, 0x06, 0xb9, 0x18, 0xfb, 0xf0, 0x0b, 0x9a, 0x79, 0x2a, -0x56, 0x12, 0x35, 0x75, 0x4e, 0xf4, 0xb8, 0xb4, 0x2e, 0x72, 0x10, 0x3c, 0x8d, 0x76, 0x69, 0x1c, -0x67, 0xb0, 0x7f, 0x94, 0x07, 0xee, 0xb4, 0x38, 0x11, 0x0b, 0x7f, 0x62, 0x4e, 0x2a, 0x2d, 0x06, -0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, -0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, -0x00, 0x71, 0x00, 0x00, 0x01, 0x00, 0x01, 0x64, 0x62, 0x19, 0xec, 0x01, 0x01, 0x00, 0x06, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, -0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, 0x00, 0x00, 0xa4, 0xde, 0x6a, 0x84, 0x4d, 0x64, -0x62, 0x19, 0xf1, 0x01, 0x01, 0x47, 0x72, 0x62, 0xe8, 0xc7, 0x43, 0xa8, 0x2e, 0x1c, 0x97, 0x2a, -0x06, 0xce, 0x2f, 0xa2, 0xfa, 0x27, 0x4f, 0x28, 0x7f, 0x55, 0x32, 0x19, 0x62, 0x58, 0xc6, 0x18, -0x07, 0x23, 0x5f, 0x8a, 0x59, 0x00, 0x52, 0x4d, 0xc9, 0x18, 0x22, 0x9e, 0xf7, 0x87, 0xa3, 0x36, -0x9d, 0x01, 0x73, 0x7c, 0x5b, 0xb8, 0xb4, 0x08, 0x50, 0x0f, 0x89, 0x52, 0x3f, 0x2e, 0x44, 0xa0, -0xe0, 0x32, 0x3a, 0xf7, 0x20, 0x00, 0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, -0x19, 0xf1, 0x03, 0x7f, 0x97, 0xaf, 0x8e, 0x4f, 0xa6, 0x7c, 0x9b, 0x8d, 0x69, 0x70, 0x84, 0x95, -0x36, 0xf8, 0x88, 0xc3, 0x04, 0x31, 0x6d, 0x01, 0x5a, 0xf5, 0x12, 0x9e, 0x26, 0x8e, 0x01, 0x64, -0x96, 0x1b, 0x5e, 0x03, 0x7f, 0x97, 0x53, 0x4c, 0x49, 0x43, 0x4b, 0x45, 0x52, 0x43, 0x48, 0x49, -0x50, 0x4d, 0x55, 0x4e, 0x4b, 0x2d, 0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, -0x6d, 0x6f, 0x64, 0x64, 0x65, 0x64, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, -0x00, 0x00, 0x00, 0x02, 0x4c, 0x4b, 0x40, 0x00, 0x00, 0x01, 0xb0, 0x31, 0xd6, 0x97, 0xf8, 0x64, -0x62, 0x19, 0xec, 0x01, 0x00, 0x3f, 0x22, 0x04, 0x81, 0x00, 0xfb, 0xfe, 0x52, 0x4e, 0xdf, 0x7e, -0xef, 0x65, 0xff, 0x41, 0xcf, 0xfc, 0x33, 0xfc, 0x27, 0xba, 0x5b, 0x5f, 0xc5, 0x40, 0xd7, 0xff, -0x65, 0x20, 0x37, 0x3f, 0x00, 0x0d, 0x7c, 0x9b, 0xa9, 0xf1, 0x8c, 0xc6, 0xf1, 0xf7, 0x30, 0xd8, -0x1a, 0x44, 0xea, 0x6a, 0xf8, 0x95, 0xde, 0xe9, 0x35, 0x5f, 0x2b, 0x09, 0xc8, 0x5e, 0xf4, 0xa4, -0x58, 0x5a, 0xef, 0x24, 0x14, 0x1e, 0x17, 0x5b, 0xb1, 0xa7, 0xbf, 0x69, 0xb6, 0x44, 0xbe, 0xcc, -0x37, 0xb3, 0x48, 0x0a, 0x83, 0x37, 0xfa, 0xdb, 0x1d, 0x2a, 0x57, 0x83, 0x50, 0x88, 0x39, 0xd7, -0x2d, 0xa6, 0x70, 0x19, 0x94, 0x63, 0xa3, 0x09, 0x57, 0x47, 0x80, 0x47, 0xa7, 0x9b, 0xb5, 0x20, -0x4a, 0x33, 0x67, 0xf7, 0x5c, 0x5d, 0x4c, 0xa3, 0xc3, 0x05, 0x81, 0x48, 0xa7, 0x5e, 0x10, 0x13, -0x5d, 0x64, 0x4c, 0x2e, 0x53, 0x28, 0xd1, 0x82, 0xc3, 0x7d, 0xbf, 0xb2, 0xcd, 0x36, 0xcc, 0x1e, -0xc6, 0xc7, 0x42, 0x65, 0x12, 0x61, 0x82, 0x5d, 0xc7, 0x3b, 0x6a, 0xaf, 0x71, 0xd4, 0xf0, 0xe9, -0xff, 0xdd, 0x75, 0x33, 0x96, 0x3e, 0xb7, 0x92, 0xc2, 0xcd, 0x0e, 0xda, 0xec, 0x55, 0x43, 0x20, -0x07, 0xe8, 0x9e, 0xff, 0x3f, 0xea, 0x2f, 0x44, 0x64, 0x43, 0xe9, 0xfd, 0x82, 0x0a, 0xd4, 0x1d, -0xf6, 0x14, 0x02, 0x30, 0x78, 0x34, 0x02, 0x62, 0x73, 0x90, 0x41, 0x38, 0xbe, 0xc0, 0xd2, 0xac, -0x59, 0xc1, 0x82, 0xd2, 0x6f, 0x4e, 0x28, 0xd9, 0x2e, 0x3c, 0x6d, 0x4b, 0xa2, 0x25, 0xc9, 0x46, -0x42, 0x95, 0x64, 0xb9, 0x89, 0x73, 0x30, 0xce, 0xb7, 0xca, 0x1a, 0x78, 0xac, 0xa8, 0x72, 0x71, -0xe8, 0x1e, 0x48, 0xe9, 0x7c, 0xe5, 0x49, 0x78, 0x16, 0x50, 0x3e, 0x26, 0x15, 0x4f, 0xaf, 0x7f, -0x53, 0x17, 0x14, 0xeb, 0xa6, 0x00, 0x00, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, -0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, -0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, -0x45, 0x1e, 0x9b, 0xaf, 0xf8, 0x1f, 0xaf, 0x5f, 0x41, 0x0b, 0x0b, 0xbe, 0x9e, 0x36, 0xee, 0x83, -0xbf, 0x8f, 0x79, 0x69, 0x8e, 0x66, 0x05, 0xa5, 0x1a, 0x97, 0x48, 0xbc, 0x73, 0xc7, 0xdc, 0x28, -0x03, 0x7f, 0x97, 0xaf, 0x8e, 0x4f, 0xa6, 0x7c, 0x9b, 0x8d, 0x69, 0x70, 0x84, 0x95, 0x36, 0xf8, -0x88, 0xc3, 0x04, 0x31, 0x6d, 0x01, 0x5a, 0xf5, 0x12, 0x9e, 0x26, 0x8e, 0x01, 0x64, 0x96, 0x1b, -0x5e, 0x02, 0xd0, 0xbf, 0x3d, 0xe0, 0x25, 0x7a, 0xe4, 0x02, 0x4a, 0x88, 0x2b, 0x20, 0x63, 0xb4, -0x68, 0x6b, 0x72, 0x27, 0x91, 0xc2, 0xe4, 0x7a, 0xd1, 0x75, 0x93, 0x5d, 0xf3, 0x3a, 0xbe, 0x99, -0x7b, 0x76, 0x02, 0x49, 0x4d, 0xb8, 0x75, 0x3e, 0x66, 0xc7, 0x73, 0x63, 0xec, 0xf4, 0x40, 0xa4, -0xcb, 0xe5, 0xe0, 0x3e, 0xc6, 0x28, 0x2b, 0xea, 0x8a, 0xd3, 0x3f, 0x66, 0x4b, 0xa3, 0x9b, 0x86, -0x37, 0xf7, 0x7b, 0x00, 0x00, 0x00, 0x0a, 0xea, 0x64, 0x27, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10, -0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x84, 0x80, 0x80, 0x00, 0x00, 0x8a, 0x67, 0x5f, 0xf2, -0xad, 0x64, 0x62, 0x19, 0xec, 0x01, 0x02, 0x28, 0x06, 0xbe, 0x81, 0x8d, 0x13, 0xe3, 0xe9, 0x45, -0x09, 0xdd, 0x6a, 0xbe, 0x96, 0xb5, 0x08, 0xe4, 0x87, 0xca, 0xfd, 0x72, 0xc1, 0xfd, 0xa9, 0xe8, -0x32, 0x68, 0x95, 0x97, 0x06, 0x47, 0x57, 0x3a, 0x38, 0x28, 0x22, 0xa1, 0x78, 0x45, 0x22, 0xd5, -0xac, 0x0d, 0x1d, 0x2f, 0x25, 0xf0, 0x3a, 0x11, 0x85, 0x34, 0xcc, 0xae, 0xf8, 0xdd, 0x44, 0x05, -0xdd, 0xe6, 0x6d, 0xfc, 0xc2, 0xa0, 0x7e, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, -0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, -0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x03, 0x00, 0x01, 0x64, -0x62, 0x19, 0xec, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x76, 0x04, 0x67, 0x00, 0x80, -0x00, 0x00, 0x8a, 0xdc, 0x8e, 0xb4, 0xa3, 0x64, 0x62, 0x19, 0xec, 0x01, 0x02, 0x27, 0x9a, 0x87, -0xb6, 0x8b, 0xcb, 0xc9, 0x41, 0xea, 0xc3, 0x1b, 0x18, 0xf5, 0x51, 0x2f, 0x9b, 0x71, 0xe3, 0x8d, -0x24, 0x8d, 0x1e, 0x53, 0xdc, 0x83, 0x6f, 0x30, 0xfe, 0x00, 0xeb, 0xbb, 0x6b, 0x35, 0xc3, 0x20, -0xea, 0xae, 0x27, 0xb4, 0x8a, 0xdc, 0x30, 0x9f, 0xb5, 0xee, 0xbf, 0x3c, 0x16, 0x58, 0xe1, 0xa6, -0xec, 0x87, 0xfd, 0xb0, 0x43, 0x8c, 0xed, 0x4d, 0x00, 0x2d, 0x85, 0x33, 0xbe, 0x06, 0x22, 0x6e, -0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, -0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, -0x00, 0x00, 0x03, 0x00, 0x01, 0x64, 0x62, 0x19, 0xec, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, -0x00, 0x76, 0x04, 0x67, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x1e, 0xb8, 0x7e, 0x0a, 0x64, 0x62, 0x19, -0xf1, 0x01, 0x01, 0x70, 0xc5, 0x12, 0xaa, 0x59, 0xee, 0xe5, 0xb5, 0x1f, 0x4c, 0x56, 0x77, 0xa1, -0xc5, 0x3c, 0x6b, 0x03, 0x37, 0xf9, 0x8f, 0xa9, 0x50, 0xa7, 0xe3, 0x22, 0x7b, 0x6e, 0x37, 0xd5, -0x46, 0x03, 0xff, 0x12, 0x91, 0x0a, 0xb8, 0x4f, 0x35, 0x63, 0xdf, 0xda, 0x03, 0xda, 0xee, 0x86, -0xe4, 0x43, 0xef, 0xa0, 0x8a, 0x90, 0xeb, 0xa8, 0xf3, 0x7f, 0x05, 0x84, 0x8a, 0xd8, 0xb0, 0xf8, -0x1b, 0x4b, 0xcf, 0x00, 0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, 0x19, 0xf1, -0x02, 0x45, 0x1e, 0x9b, 0xaf, 0xf8, 0x1f, 0xaf, 0x5f, 0x41, 0x0b, 0x0b, 0xbe, 0x9e, 0x36, 0xee, -0x83, 0xbf, 0x8f, 0x79, 0x69, 0x8e, 0x66, 0x05, 0xa5, 0x1a, 0x97, 0x48, 0xbc, 0x73, 0xc7, 0xdc, -0x28, 0x02, 0x45, 0x1e, 0x4c, 0x4f, 0x55, 0x44, 0x54, 0x4f, 0x54, 0x45, 0x2d, 0x33, 0x2e, 0x30, -0x32, 0x2d, 0x33, 0x37, 0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, 0x6d, 0x6f, -0x64, 0x64, 0x65, 0x64, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, 0x00, 0x00, -0x00, 0x02, 0x4c, 0x4b, 0x40, 0x00, 0x00, 0x01, 0xb0, 0x17, 0xe0, 0xd7, 0x83, 0x64, 0x62, 0x19, -0xed, 0x01, 0x00, 0x48, 0xa3, 0x33, 0x5f, 0x33, 0x6c, 0x33, 0x85, 0x0f, 0xc7, 0xeb, 0x46, 0x04, -0x5a, 0xe7, 0x1a, 0x2d, 0xe1, 0x37, 0xb0, 0xc3, 0x8a, 0xa7, 0x6a, 0xe0, 0xa2, 0xfd, 0x1f, 0x30, -0x9f, 0xdc, 0x8d, 0x38, 0x05, 0xf7, 0xaf, 0x0b, 0xe6, 0xb3, 0x4d, 0x62, 0xb9, 0xa4, 0x9c, 0x53, -0x7d, 0x6e, 0x59, 0x5b, 0xb2, 0x2b, 0x5c, 0xda, 0x35, 0xf9, 0x90, 0x63, 0x21, 0xa8, 0xb1, 0x53, -0xc3, 0x35, 0x7c, 0x36, 0x76, 0x21, 0x76, 0xae, 0xa3, 0xad, 0x05, 0x53, 0xa7, 0xbd, 0x9d, 0x38, -0x54, 0x03, 0xc0, 0x98, 0x1d, 0x66, 0xc1, 0x04, 0x39, 0xc1, 0x88, 0xd1, 0x1f, 0x90, 0x08, 0x96, -0xbc, 0x59, 0x54, 0x4f, 0x5f, 0xa2, 0x70, 0xcd, 0xf0, 0xda, 0x96, 0x3c, 0x51, 0x04, 0x67, 0x5c, -0x1f, 0x07, 0xed, 0xf9, 0x9e, 0x98, 0xd0, 0x3b, 0x5e, 0x51, 0xa9, 0xa6, 0x82, 0xc1, 0xed, 0x35, -0x45, 0xa1, 0xd6, 0x36, 0x3b, 0xa1, 0xe6, 0x5d, 0x1f, 0xec, 0xe2, 0xb7, 0xf8, 0xa2, 0xe4, 0x45, -0xf9, 0xb6, 0xa7, 0x07, 0x18, 0xc7, 0xb5, 0x0c, 0x08, 0xd7, 0x50, 0x36, 0x98, 0x82, 0xd3, 0xc8, -0x40, 0xc8, 0xdc, 0x64, 0x27, 0xe2, 0x14, 0x42, 0x44, 0x0a, 0xe4, 0x1d, 0x41, 0x61, 0x57, 0x88, -0xfe, 0xd2, 0x51, 0x99, 0x24, 0x55, 0x1e, 0x3b, 0xaa, 0x8d, 0xa7, 0xb4, 0xc0, 0x6e, 0xf5, 0x70, -0x8c, 0x2a, 0xe3, 0x75, 0xcc, 0x36, 0xbf, 0xbe, 0xfc, 0x3f, 0x09, 0x83, 0x5e, 0xe4, 0x20, 0x9a, -0xcc, 0x11, 0x48, 0x8e, 0x2b, 0xc8, 0x8a, 0xef, 0xc0, 0x78, 0x45, 0xee, 0x1e, 0xc7, 0xce, 0x00, -0xfc, 0x3c, 0x0e, 0x32, 0xd2, 0x8f, 0x15, 0x8c, 0x02, 0xb3, 0x7b, 0x4c, 0xa9, 0x7a, 0x9c, 0xec, -0x5e, 0x6e, 0xf2, 0xd3, 0xd9, 0x15, 0x32, 0xa3, 0x74, 0x14, 0xbf, 0x1f, 0xdd, 0x2f, 0x63, 0x3c, -0x47, 0x04, 0x6c, 0x00, 0x00, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, -0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, -0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x45, 0x1e, -0x9b, 0xaf, 0xf8, 0x1f, 0xaf, 0x5f, 0x41, 0x0b, 0x0b, 0xbe, 0x9e, 0x36, 0xee, 0x83, 0xbf, 0x8f, -0x79, 0x69, 0x8e, 0x66, 0x05, 0xa5, 0x1a, 0x97, 0x48, 0xbc, 0x73, 0xc7, 0xdc, 0x28, 0x02, 0xd1, -0xab, 0x24, 0xfe, 0x53, 0xcf, 0xca, 0xc4, 0xa4, 0x77, 0x74, 0x52, 0x35, 0xc7, 0xac, 0x3e, 0x75, -0x16, 0x1a, 0x5b, 0xf8, 0x42, 0x6e, 0xa8, 0xe0, 0x18, 0x2a, 0xd5, 0x8c, 0xab, 0x36, 0x7f, 0x02, -0x7e, 0x2a, 0xc0, 0xec, 0x93, 0xfd, 0xb3, 0xfb, 0xe3, 0x8d, 0x7a, 0x3f, 0x5e, 0xa0, 0xa6, 0x3d, -0xdb, 0xa9, 0x8a, 0x51, 0xb7, 0x7a, 0xf5, 0x51, 0x6f, 0xe5, 0xca, 0x10, 0x10, 0xd7, 0x95, 0x34, -0x02, 0x17, 0xd5, 0xb1, 0x80, 0x7d, 0x8b, 0x95, 0x7c, 0xe1, 0x0b, 0xb0, 0xaf, 0xf3, 0xc1, 0x84, -0x81, 0xee, 0x2f, 0xed, 0x6a, 0x7b, 0x65, 0x9c, 0xbf, 0xfd, 0x48, 0x20, 0xd0, 0x9d, 0x1a, 0xfd, -0xa4, 0x00, 0x00, 0x00, 0x0a, 0x91, 0x11, 0x83, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, -0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 0x80, 0x00, 0x00, 0x8a, 0xbd, 0x52, 0xa0, 0x78, 0x64, -0x62, 0x19, 0xed, 0x01, 0x02, 0x40, 0xf0, 0x06, 0x07, 0x97, 0xb8, 0x87, 0xef, 0x73, 0xdc, 0x1b, -0xf0, 0x20, 0x31, 0x55, 0xc9, 0xb9, 0x6f, 0xec, 0x6f, 0xad, 0x46, 0x86, 0x0a, 0xcc, 0xd9, 0x95, -0x61, 0x62, 0x15, 0x84, 0x70, 0x2a, 0x47, 0xd7, 0x68, 0xa9, 0xbc, 0x98, 0xb3, 0x1f, 0xc4, 0xbc, -0x78, 0xab, 0x5d, 0xf2, 0xf7, 0xc4, 0x97, 0x75, 0x21, 0x13, 0xcf, 0xfc, 0xd4, 0x36, 0xcd, 0xf6, -0xb4, 0x85, 0x7c, 0xad, 0x01, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, -0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, -0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x02, 0x00, 0x00, 0x64, 0x62, 0x19, -0xed, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x80, 0x00, 0x00, -0x8a, 0xf5, 0x5d, 0xd1, 0x12, 0x64, 0x62, 0x19, 0xed, 0x01, 0x02, 0x08, 0x97, 0x08, 0x72, 0xbe, -0xc8, 0x1e, 0xd0, 0xb9, 0xb8, 0x4b, 0x0f, 0x63, 0x5c, 0xeb, 0x28, 0xa5, 0xf8, 0x7a, 0x3d, 0xa1, -0x6a, 0xb3, 0xb4, 0x30, 0x91, 0x31, 0x57, 0xd4, 0x5b, 0x69, 0x26, 0x4d, 0xd1, 0xbb, 0xd5, 0x49, -0x95, 0xe9, 0x75, 0x53, 0xa4, 0xae, 0x87, 0xe9, 0x88, 0xf6, 0x86, 0x1f, 0x31, 0x8f, 0x35, 0xf9, -0x15, 0xcc, 0x04, 0x0a, 0x01, 0xed, 0x6e, 0x47, 0xe0, 0xea, 0x68, 0x06, 0x22, 0x6e, 0x46, 0x11, -0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, -0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, -0x02, 0x00, 0x00, 0x64, 0x62, 0x19, 0xed, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x3b, -0x02, 0x33, 0x80, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x3d, 0xb7, 0xfe, 0x64, 0x62, 0x19, 0xf2, 0x01, -0x01, 0x29, 0x2a, 0x41, 0x8f, 0xb7, 0x24, 0xc2, 0x82, 0xc5, 0x75, 0x0e, 0x28, 0xd9, 0x8b, 0xd4, -0xad, 0xa1, 0xb1, 0x9a, 0x65, 0xa8, 0x7a, 0x78, 0xc7, 0x6c, 0xc8, 0x94, 0xcb, 0xf7, 0xb1, 0xb8, -0x3b, 0x29, 0xce, 0xbf, 0xcc, 0x47, 0x1b, 0x5a, 0xb4, 0xec, 0xab, 0xa3, 0xbe, 0xaf, 0xd1, 0xde, -0xd7, 0x0e, 0x8b, 0xcc, 0xaa, 0xdb, 0x6b, 0x88, 0x51, 0xb9, 0x7a, 0x0c, 0xcd, 0x1c, 0x9c, 0x4d, -0x5c, 0x00, 0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, 0x19, 0xf2, 0x02, 0xd1, -0xab, 0x24, 0xfe, 0x53, 0xcf, 0xca, 0xc4, 0xa4, 0x77, 0x74, 0x52, 0x35, 0xc7, 0xac, 0x3e, 0x75, -0x16, 0x1a, 0x5b, 0xf8, 0x42, 0x6e, 0xa8, 0xe0, 0x18, 0x2a, 0xd5, 0x8c, 0xab, 0x36, 0x7f, 0x02, -0xd1, 0xab, 0x43, 0x48, 0x49, 0x4c, 0x4c, 0x59, 0x46, 0x49, 0x52, 0x45, 0x2d, 0x30, 0x32, 0x2d, -0x33, 0x37, 0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, 0x6d, 0x6f, 0x64, 0x64, -0x65, 0x64, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, -0x4c, 0x4b, 0x40, 0x00, 0x00, 0x01, 0xb0, 0x5f, 0xad, 0x58, 0xa1, 0x64, 0x62, 0x19, 0xed, 0x01, -0x00, 0x63, 0x42, 0xba, 0xf1, 0x21, 0xe0, 0x09, 0x57, 0x0d, 0x40, 0xa4, 0xc6, 0x05, 0x78, 0x02, -0x8e, 0x35, 0x71, 0x66, 0x7c, 0x24, 0x51, 0x3f, 0x58, 0x3a, 0xaa, 0x14, 0x65, 0x5a, 0x2b, 0xbd, -0x09, 0x5b, 0xd3, 0xa8, 0x4e, 0x73, 0x3e, 0x38, 0xd3, 0x4d, 0x19, 0x9c, 0x18, 0x04, 0x60, 0x57, -0x32, 0xd3, 0x75, 0xf5, 0x36, 0x15, 0xc4, 0x6a, 0xf1, 0x1b, 0x3d, 0xc9, 0x07, 0x89, 0x08, 0x7a, -0x37, 0x2f, 0xf1, 0x89, 0x12, 0xdb, 0xf2, 0xff, 0x04, 0xbd, 0x93, 0x23, 0x00, 0x3c, 0x10, 0x05, -0x8a, 0x58, 0x9b, 0x96, 0xf3, 0x76, 0x94, 0x16, 0x29, 0x51, 0xc8, 0x76, 0x89, 0x61, 0xc3, 0x21, -0xc0, 0x0e, 0x47, 0xac, 0xa3, 0xbe, 0xc7, 0xfd, 0xa2, 0x6b, 0xe9, 0x1d, 0xe2, 0x11, 0x1c, 0x3e, -0xfc, 0x6d, 0x4d, 0x0b, 0x85, 0xff, 0xe9, 0x8a, 0x39, 0x3a, 0xb3, 0x0e, 0x2f, 0x28, 0x96, 0x6b, -0x96, 0x59, 0x4d, 0x53, 0x71, 0xd5, 0x38, 0x23, 0xe1, 0xe0, 0xad, 0x0a, 0xbf, 0x00, 0x58, 0x15, -0xbf, 0x53, 0x07, 0xe1, 0x13, 0x06, 0x88, 0xb3, 0xf8, 0x31, 0x06, 0x72, 0x92, 0x6f, 0xd1, 0xf0, -0x9b, 0x3b, 0xf2, 0x8f, 0x9c, 0xc6, 0x73, 0xf8, 0x91, 0x3e, 0x84, 0xc0, 0xed, 0xdf, 0x92, 0x43, -0x92, 0x5f, 0x4a, 0x6b, 0x96, 0x02, 0xaf, 0xd9, 0xd9, 0xd9, 0xf9, 0x65, 0xae, 0x08, 0xd8, 0x62, -0x93, 0x2b, 0xb7, 0xd3, 0x48, 0xe3, 0x02, 0x19, 0x53, 0xf9, 0x49, 0x24, 0xfa, 0x22, 0x24, 0x87, -0xc2, 0xd2, 0x0b, 0xc0, 0x56, 0xae, 0x09, 0x5a, 0x94, 0xc3, 0x54, 0x59, 0xb5, 0xe7, 0xbe, 0xa6, -0x4a, 0x47, 0xc1, 0x79, 0x80, 0xe8, 0xc2, 0xd1, 0xc5, 0xda, 0x6b, 0x25, 0x85, 0xc6, 0x02, 0x32, -0x8b, 0x52, 0x0e, 0x7f, 0x18, 0x1c, 0x5b, 0xf6, 0xb9, 0xaf, 0x69, 0xdc, 0xc6, 0x3d, 0x93, 0xc1, -0x27, 0x00, 0x00, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, -0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, -0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0xd1, 0xab, 0x24, 0xfe, -0x53, 0xcf, 0xca, 0xc4, 0xa4, 0x77, 0x74, 0x52, 0x35, 0xc7, 0xac, 0x3e, 0x75, 0x16, 0x1a, 0x5b, -0xf8, 0x42, 0x6e, 0xa8, 0xe0, 0x18, 0x2a, 0xd5, 0x8c, 0xab, 0x36, 0x7f, 0x03, 0xca, 0xec, 0x54, -0x08, 0x55, 0x08, 0xda, 0x06, 0xf1, 0x4f, 0xfb, 0x83, 0x61, 0x66, 0xca, 0x22, 0x04, 0x2d, 0x0f, -0xda, 0x69, 0x69, 0x03, 0x56, 0x23, 0x2a, 0x24, 0xb8, 0x36, 0x6c, 0x8f, 0x85, 0x03, 0xf6, 0x19, -0x62, 0x15, 0xf2, 0x5c, 0xfc, 0x5c, 0xae, 0x8c, 0xb6, 0x90, 0xa7, 0x81, 0xe0, 0x14, 0xb5, 0xc1, -0xc5, 0xda, 0xf9, 0x6d, 0x44, 0x6d, 0x1a, 0x6e, 0x24, 0x4f, 0xb6, 0x42, 0x3f, 0xdb, 0x03, 0xf9, -0x84, 0xe3, 0xec, 0xa9, 0x24, 0x5d, 0x1b, 0xba, 0xd2, 0xc7, 0xf3, 0x5a, 0x32, 0xaa, 0x6e, 0xdb, -0x21, 0xb6, 0xe8, 0xb1, 0x86, 0x5b, 0x18, 0x30, 0xe8, 0x4d, 0x23, 0xa4, 0x45, 0x23, 0x88, 0x00, -0x00, 0x00, 0x0a, 0x08, 0x85, 0x8a, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, -0x00, 0x00, 0x2d, 0xc6, 0xc0, 0x80, 0x00, 0x00, 0x8a, 0xe9, 0x51, 0x74, 0x9b, 0x64, 0x62, 0x19, -0xed, 0x01, 0x02, 0x4b, 0x82, 0x87, 0x3b, 0xc9, 0x03, 0x1c, 0x6e, 0xc9, 0xbe, 0x96, 0x22, 0x97, -0xf7, 0xa8, 0xb0, 0xb2, 0x7c, 0x22, 0x69, 0x23, 0x2d, 0x97, 0xfb, 0x9b, 0xc2, 0xf1, 0x1e, 0x66, -0xfb, 0xfd, 0x80, 0x5d, 0xd7, 0xf0, 0x23, 0x31, 0x47, 0xaa, 0x54, 0x8d, 0x95, 0xbb, 0xdd, 0x33, -0x13, 0x32, 0x6d, 0x91, 0xc6, 0x45, 0xd5, 0x84, 0xf4, 0x76, 0x6c, 0x74, 0xf3, 0x51, 0x45, 0x24, -0xee, 0x5b, 0xc3, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, -0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, -0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, 0x01, 0x64, 0x62, 0x19, 0xed, 0x01, -0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, -0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x06, 0x9a, 0x80, 0x80, 0x00, 0x00, 0x8a, 0xd2, -0xc8, 0xd2, 0x7c, 0x64, 0x62, 0x19, 0xed, 0x01, 0x02, 0x0f, 0x90, 0xcb, 0xb6, 0xa1, 0x44, 0x65, -0x10, 0x00, 0xae, 0x2f, 0x60, 0x26, 0x2f, 0x41, 0x58, 0x5b, 0xab, 0xde, 0xff, 0x7e, 0x11, 0x44, -0xf4, 0x2e, 0x96, 0x96, 0xfa, 0x98, 0x09, 0xee, 0xb1, 0x5d, 0x43, 0xff, 0x44, 0x7b, 0xa6, 0x03, -0xf6, 0x4a, 0x07, 0x38, 0x97, 0x59, 0xee, 0x5e, 0xee, 0xcb, 0xdb, 0x77, 0x69, 0xab, 0x61, 0xd8, -0xc3, 0x42, 0xb3, 0x1f, 0x57, 0xea, 0xf3, 0xfd, 0xe2, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, -0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, -0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, -0x01, 0x64, 0x62, 0x19, 0xed, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x06, 0x9a, -0x80, 0x40, 0x00, 0x00, 0x8a, 0x64, 0xa7, 0x4f, 0x57, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x02, 0x28, -0x15, 0x9f, 0xa7, 0x51, 0x3a, 0xbb, 0x33, 0xd9, 0x25, 0xaa, 0x7d, 0xe8, 0xfb, 0x3a, 0x92, 0x45, -0x41, 0xb3, 0x22, 0x9b, 0x12, 0x3b, 0xb0, 0x16, 0x47, 0xd6, 0xf7, 0x61, 0x44, 0x1d, 0xa7, 0x35, -0xfe, 0xa9, 0x7b, 0xa6, 0x42, 0x91, 0x3f, 0x5e, 0xe4, 0xca, 0x98, 0x1c, 0x0f, 0x2d, 0xed, 0x36, -0x0e, 0x2b, 0x2e, 0x08, 0x81, 0x2e, 0xcc, 0xc1, 0x76, 0x61, 0xf9, 0x1b, 0xd3, 0x44, 0x3e, 0x06, -0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, -0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, -0x00, 0x71, 0x00, 0x00, 0x01, 0x00, 0x01, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x00, 0x00, 0x06, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x64, 0x00, -0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, 0x00, 0x00, 0xa4, 0x30, 0x77, 0x80, 0xee, 0x64, -0x62, 0x19, 0xf2, 0x01, 0x01, 0x67, 0x07, 0x1d, 0x3b, 0x62, 0x2d, 0xb7, 0x1a, 0xba, 0xb8, 0x93, -0x56, 0xaa, 0xfa, 0xb1, 0x47, 0x4f, 0x0e, 0x02, 0x8b, 0x73, 0xd5, 0x5b, 0xce, 0xd6, 0x40, 0x55, -0xaf, 0xa7, 0x29, 0xd0, 0x51, 0x24, 0x5a, 0x19, 0x22, 0xc6, 0x7b, 0x6e, 0x4a, 0xae, 0x57, 0x9c, -0x16, 0x99, 0x46, 0x6c, 0xc3, 0x64, 0xd8, 0x20, 0x10, 0x44, 0x1e, 0xd0, 0x6b, 0x8d, 0x36, 0xdc, -0xae, 0x75, 0x06, 0x6e, 0xdc, 0x00, 0x07, 0x88, 0xa0, 0x80, 0x2a, 0x02, 0x69, 0xa2, 0x64, 0x62, -0x19, 0xf2, 0x03, 0xca, 0xec, 0x54, 0x08, 0x55, 0x08, 0xda, 0x06, 0xf1, 0x4f, 0xfb, 0x83, 0x61, -0x66, 0xca, 0x22, 0x04, 0x2d, 0x0f, 0xda, 0x69, 0x69, 0x03, 0x56, 0x23, 0x2a, 0x24, 0xb8, 0x36, -0x6c, 0x8f, 0x85, 0x03, 0xca, 0xec, 0x56, 0x49, 0x4f, 0x4c, 0x45, 0x54, 0x53, 0x45, 0x54, 0x2d, -0x2e, 0x30, 0x32, 0x2d, 0x33, 0x37, 0x2d, 0x67, 0x63, 0x39, 0x66, 0x38, 0x30, 0x33, 0x35, 0x2d, -0x6d, 0x6f, 0x64, 0x64, 0x65, 0x64, 0x00, 0x00, 0x01, 0x0d, 0x02, 0x9a, 0x00, 0x32, 0x00, 0x64, -0x00, 0x00, 0x00, 0x02, 0x4c, 0x4b, 0x40, 0x40, 0x00, 0x00, 0x8a, 0x07, 0xf6, 0xe8, 0x9b, 0x64, -0x62, 0x19, 0xf7, 0x01, 0x02, 0x62, 0xf7, 0xdc, 0xf1, 0xa6, 0x3c, 0xf0, 0xae, 0x64, 0x9c, 0x03, -0x62, 0x98, 0x6a, 0x18, 0x78, 0x97, 0xed, 0x8c, 0x2e, 0x3f, 0xc4, 0x1d, 0x9f, 0xa7, 0xfb, 0x58, -0x26, 0x48, 0x2e, 0x96, 0x9d, 0x33, 0x75, 0x60, 0x6e, 0x33, 0x95, 0xf7, 0x6e, 0x9f, 0x4f, 0xa2, -0xed, 0xd6, 0xa9, 0x83, 0x1b, 0x94, 0x79, 0xee, 0x4f, 0xdc, 0x20, 0xc5, 0x39, 0x74, 0x0d, 0x31, -0x52, 0xc7, 0x25, 0x36, 0x47, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, -0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, -0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x01, 0x00, 0x01, 0x64, 0x62, 0x19, -0xf7, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x14, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, 0x00, 0x00, -0x8a, 0x16, 0x2b, 0xff, 0x08, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x02, 0x39, 0x36, 0x2a, 0x56, 0x61, -0xad, 0x48, 0x3f, 0x4e, 0x13, 0x15, 0x66, 0x43, 0x58, 0xc5, 0xc2, 0x14, 0x6e, 0xb2, 0x72, 0xfa, -0x73, 0xd7, 0xb5, 0x2d, 0x86, 0x14, 0xc2, 0xe8, 0xf7, 0x53, 0x8f, 0x38, 0xea, 0x35, 0x5c, 0xec, -0xe3, 0xc7, 0xc0, 0x46, 0x1c, 0x9f, 0x1d, 0x93, 0x94, 0x31, 0x1f, 0xf8, 0x49, 0xb1, 0x50, 0x4c, -0x2c, 0x2f, 0xc7, 0xe4, 0x0c, 0xaa, 0xd0, 0xa9, 0x53, 0x14, 0xca, 0x06, 0x22, 0x6e, 0x46, 0x11, -0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, -0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, -0x03, 0x00, 0x01, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x76, -0x04, 0x67, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xd5, 0x4a, 0x70, 0x0c, 0x64, 0x62, 0x19, 0xf7, 0x01, -0x02, 0x54, 0x16, 0x95, 0x41, 0x4f, 0x0e, 0x0f, 0xdf, 0x49, 0xb5, 0x87, 0xdc, 0x26, 0xb4, 0xef, -0x73, 0x3c, 0xb8, 0x19, 0x96, 0x62, 0x87, 0xfa, 0x4f, 0x02, 0x53, 0xbe, 0x12, 0x53, 0x93, 0x4b, -0x57, 0x3b, 0xe9, 0xb9, 0x26, 0x46, 0xda, 0x77, 0xaa, 0xdd, 0x8d, 0xf6, 0x86, 0x22, 0xf0, 0x3f, -0xd5, 0x56, 0xdd, 0xaa, 0xa2, 0x4e, 0x4a, 0x9a, 0x70, 0x81, 0xf8, 0xf9, 0x72, 0x7b, 0xd7, 0x90, -0x48, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, -0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, -0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x03, 0x00, 0x01, 0x64, 0x62, 0x19, 0xf7, 0x01, 0x00, 0x00, -0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0xc8, 0x00, 0x00, 0x00, 0x00, 0x76, 0x04, 0x67, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xc5, 0x6d, 0x8a, -0x5a, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x02, 0x1d, 0x80, 0x09, 0x30, 0x1a, 0x4b, 0x26, 0x60, 0x6b, -0x9a, 0x54, 0x8d, 0x7f, 0x9b, 0x35, 0x78, 0x76, 0x7a, 0xc1, 0xe5, 0x22, 0xdc, 0x08, 0x77, 0xac, -0x54, 0xc7, 0xc0, 0x9b, 0x13, 0x85, 0x20, 0x2c, 0xa4, 0xa3, 0x7e, 0xc5, 0xde, 0xfd, 0x60, 0x43, -0xdb, 0x2e, 0xb0, 0x5b, 0xcc, 0x95, 0xc1, 0xf3, 0x02, 0x09, 0x8a, 0xe1, 0x55, 0x2a, 0x8a, 0x9a, -0x18, 0xe5, 0xa9, 0xee, 0xcd, 0x11, 0x27, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, -0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, -0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x02, 0x00, 0x00, 0x64, -0x62, 0x19, 0xf6, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, -0x00, 0x00, 0x8a, 0x67, 0xa5, 0x58, 0xd4, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x02, 0x5a, 0xa5, 0x3e, -0xb8, 0x73, 0xf5, 0xdf, 0xfc, 0x72, 0x16, 0x52, 0xa1, 0x07, 0x8a, 0x2b, 0xf1, 0xc3, 0x92, 0xc5, -0x87, 0xa4, 0x45, 0x07, 0x1e, 0xb3, 0x7d, 0x4c, 0x1c, 0x47, 0x41, 0x2c, 0x93, 0x14, 0x46, 0x16, -0xba, 0xe4, 0xf9, 0xc9, 0x52, 0x4c, 0x5e, 0x6c, 0x4f, 0xc9, 0xec, 0xde, 0x83, 0x15, 0xe0, 0x8e, -0x39, 0xbe, 0xa9, 0x8f, 0x9d, 0xfe, 0xcf, 0xc4, 0x12, 0x32, 0xa4, 0x17, 0x2b, 0x06, 0x22, 0x6e, -0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, -0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, -0x00, 0x00, 0x02, 0x00, 0x00, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, -0x00, 0x3b, 0x02, 0x33, 0x80, 0x00, 0x00, 0x00, 0x8a, 0x2f, 0x71, 0xed, 0xec, 0x64, 0x62, 0x19, -0xf6, 0x01, 0x02, 0x75, 0x4f, 0x11, 0x1c, 0x56, 0x9f, 0x4a, 0x9d, 0x6f, 0x98, 0x96, 0x1c, 0x5a, -0x9f, 0x0f, 0xb9, 0x24, 0x23, 0x82, 0x7d, 0x86, 0xcf, 0xbc, 0x41, 0x14, 0x38, 0x76, 0x2e, 0x86, -0x47, 0x96, 0xef, 0x14, 0x91, 0x2e, 0x30, 0xe2, 0x4b, 0x1c, 0x47, 0x2d, 0x4a, 0xdc, 0xf6, 0x79, -0xb6, 0x11, 0x80, 0xcc, 0x51, 0xbb, 0xc4, 0x29, 0x33, 0x60, 0xc1, 0x78, 0x1e, 0x82, 0xe3, 0x40, -0xc0, 0xf7, 0x25, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, -0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, -0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, 0x01, 0x64, 0x62, 0x19, 0xf6, 0x01, -0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, -0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x06, 0x9a, 0x80, 0x00, 0x00, 0x00, 0x8a, 0xc8, -0x56, 0xb7, 0xb1, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x02, 0x3c, 0x76, 0x7a, 0x28, 0x5e, 0x65, 0x30, -0xac, 0x0d, 0x0f, 0x43, 0x31, 0x02, 0x56, 0xcd, 0x14, 0x51, 0x46, 0x69, 0x33, 0xa0, 0x12, 0x61, -0x9c, 0x34, 0xc5, 0xd8, 0x9a, 0x0c, 0x81, 0x94, 0xad, 0x5e, 0x98, 0xc4, 0xd0, 0x45, 0x3d, 0x32, -0x84, 0xdd, 0xd7, 0x18, 0x2b, 0xdb, 0x13, 0xa8, 0xfc, 0xb2, 0x0d, 0xd6, 0xf6, 0x8a, 0x97, 0xc7, -0xe9, 0x7e, 0x27, 0xb7, 0x86, 0x7a, 0x3e, 0xee, 0xfa, 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, -0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, -0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f, 0x00, 0x00, 0x71, 0x00, 0x00, 0x04, 0x00, -0x01, 0x64, 0x62, 0x19, 0xf6, 0x01, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x06, 0x9a, -0x80 - }; - -static void test_edge_probability(void) -{ - const double eps = 1e-8; - - struct amount_msat min = AMOUNT_MSAT(10); // known min - struct amount_msat max = AMOUNT_MSAT(19); // known max - - struct amount_msat X = AMOUNT_MSAT(0); // in flight - struct amount_msat f; - - for(int i=0;i<=min.millisatoshis;++i) - { - f.millisatoshis = i; - // prob = 1 - assert(fabs(edge_probability(min,max,X,f)-1.0)< eps); - } - for(int i=max.millisatoshis+1;i<=100;++i) - { - f.millisatoshis = i; - // prob = 0 - assert(fabs(edge_probability(min,max,X,f))< eps); - } - f.millisatoshis=11; - assert(fabs(edge_probability(min,max,X,f)-0.9)< eps); - - f.millisatoshis=12; - assert(fabs(edge_probability(min,max,X,f)-0.8)< eps); - - f.millisatoshis=13; - assert(fabs(edge_probability(min,max,X,f)-0.7)< eps); - - f.millisatoshis=14; - assert(fabs(edge_probability(min,max,X,f)-0.6)< eps); - - f.millisatoshis=15; - assert(fabs(edge_probability(min,max,X,f)-0.5)< eps); - - f.millisatoshis=16; - assert(fabs(edge_probability(min,max,X,f)-0.4)< eps); - - f.millisatoshis=17; - assert(fabs(edge_probability(min,max,X,f)-0.3)< eps); - - f.millisatoshis=18; - assert(fabs(edge_probability(min,max,X,f)-0.2)< eps); - - f.millisatoshis=19; - assert(fabs(edge_probability(min,max,X,f)-0.1)< eps); - - X = AMOUNT_MSAT(5); - - // X=B-X - for(int i=15;i<100;++i) - { - f.millisatoshis = i; - // prob = 0 - assert(fabs(edge_probability(min,max,X,f))< eps); - } - // X=B-X - for(int i=5;i<100;++i) - { - f.millisatoshis = i; - assert(fabs(edge_probability(min,max,X,f))< eps); - } - - // X>=A, 0<=f<=B-X - f.millisatoshis=0; - assert(fabs(edge_probability(min,max,X,f)-1.0)< eps); - f.millisatoshis=1; - assert(fabs(edge_probability(min,max,X,f)-0.8)< eps); - f.millisatoshis=2; - assert(fabs(edge_probability(min,max,X,f)-0.6)< eps); - f.millisatoshis=3; - assert(fabs(edge_probability(min,max,X,f)-0.4)< eps); - f.millisatoshis=4; - assert(fabs(edge_probability(min,max,X,f)-0.2)< eps); - f.millisatoshis=5; - assert(fabs(edge_probability(min,max,X,f)-0.0)< eps); -} - -static void remove_file(char *fname) { assert(!remove(fname)); } - -static void test_flow_to_route(void) -{ - const double eps = 1e-8; - - const tal_t *this_ctx = tal(tmpctx, tal_t); - - char *gossfile; - int fd = tmpdir_mkstemp(this_ctx,"run-testflow.XXXXXX",&gossfile); - tal_add_destructor(gossfile, remove_file); - - assert(write_all(fd,canned_map,sizeof(canned_map))); - struct gossmap *gossmap = gossmap_load(this_ctx,gossfile,NULL,NULL); - assert(gossmap); - - // for(struct gossmap_node *node = gossmap_first_node(gossmap); - // node; - // node=gossmap_next_node(gossmap,node)) - // { - // struct node_id node_id; - // gossmap_node_get_id(gossmap,node,&node_id); - // const char *node_hex = node_id_to_hexstr(this_ctx,&node_id); - // printf("node: %s\n",node_hex); - // for(size_t i=0;inum_chans;++i) - // { - // int half; - // const struct gossmap_chan *c = gossmap_nth_chan(gossmap,node,i,&half); - // if(!gossmap_chan_set(c,half)) - // continue; - // - // const struct short_channel_id scid = gossmap_chan_scid(gossmap,c); - // - // const char *scid_str = short_channel_id_to_str(this_ctx,&scid); - // - // printf(" scid: %s, half: %d\n",scid_str,half); - // } - // } - - struct node_id l1, l2, l3, l4, l5; - assert(node_id_from_hexstr("024f9da0d726adf0d9a4a3ac32e328b93ad527ccb9db7077c57a12c6f9fa9add74", 66, &l1)); - assert(node_id_from_hexstr("037f97af8e4fa67c9b8d6970849536f888c304316d015af5129e268e0164961b5e", 66, &l2)); - assert(node_id_from_hexstr("02451e9baff81faf5f410b0bbe9e36ee83bf8f79698e6605a51a9748bc73c7dc28", 66, &l3)); - assert(node_id_from_hexstr("02d1ab24fe53cfcac4a477745235c7ac3e75161a5bf8426ea8e0182ad58cab367f", 66, &l4)); - assert(node_id_from_hexstr("03caec54085508da06f14ffb836166ca22042d0fda69690356232a24b8366c8f85", 66, &l5)); - - struct short_channel_id scid12, scid23, scid34,scid45; - assert(short_channel_id_from_str("113x1x1", 7, &scid12)); - assert(short_channel_id_from_str("113x3x1", 7, &scid23)); - assert(short_channel_id_from_str("113x2x0", 7, &scid34)); - assert(short_channel_id_from_str("113x4x1", 7, &scid45)); - - struct chan_extra_map *chan_extra_map = tal(this_ctx, struct chan_extra_map); - chan_extra_map_init(chan_extra_map); - - struct chan_extra_half *h0,*h1; - struct gossmap_chan *c; - struct amount_msat cap; - struct amount_msat sum_min1_max0,sum_min0_max1; - - // check the bounds channel 1--2 - c = gossmap_find_chan(gossmap,&scid12); - cap = gossmap_chan_get_capacity(gossmap,c); - - h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); - assert(h0); - h0->known_min = AMOUNT_MSAT(0); - h0->known_max = AMOUNT_MSAT(500000000); - - h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); - assert(h1); - h1->known_min = AMOUNT_MSAT(500000000); - h1->known_max = AMOUNT_MSAT(1000000000); - - assert(amount_msat_less_eq(h0->known_min,h0->known_max)); - assert(amount_msat_less_eq(h0->known_max,cap)); - assert(amount_msat_less_eq(h1->known_min,h1->known_max)); - assert(amount_msat_less_eq(h1->known_max,cap)); - assert(amount_msat_add(&sum_min1_max0,h1->known_min,h0->known_max)); - assert(amount_msat_add(&sum_min0_max1,h0->known_min,h1->known_max)); - assert(amount_msat_eq(sum_min1_max0,cap)); - assert(amount_msat_eq(sum_min0_max1,cap)); - - // check the bounds channel 2--3 - c = gossmap_find_chan(gossmap,&scid23); - cap = gossmap_chan_get_capacity(gossmap,c); - - h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); - assert(h1); - h1->known_min = AMOUNT_MSAT(0); - h1->known_max = AMOUNT_MSAT(2000000000); - - h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); - assert(h0); - h0->known_min = AMOUNT_MSAT(0); - h0->known_max = AMOUNT_MSAT(2000000000); - - assert(amount_msat_less_eq(h0->known_min,h0->known_max)); - assert(amount_msat_less_eq(h0->known_max,cap)); - assert(amount_msat_less_eq(h1->known_min,h1->known_max)); - assert(amount_msat_less_eq(h1->known_max,cap)); - assert(amount_msat_add(&sum_min1_max0,h1->known_min,h0->known_max)); - assert(amount_msat_add(&sum_min0_max1,h0->known_min,h1->known_max)); - assert(amount_msat_eq(sum_min1_max0,cap)); - assert(amount_msat_eq(sum_min0_max1,cap)); - - // check the bounds channel 3--4 - c = gossmap_find_chan(gossmap,&scid34); - cap = gossmap_chan_get_capacity(gossmap,c); - - h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); - assert(h0); - h0->known_min = AMOUNT_MSAT(500000000); - h0->known_max = AMOUNT_MSAT(1000000000); - - h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); - assert(h1); - h1->known_min = AMOUNT_MSAT(0); - h1->known_max = AMOUNT_MSAT(500000000); - - assert(amount_msat_less_eq(h0->known_min,h0->known_max)); - assert(amount_msat_less_eq(h0->known_max,cap)); - assert(amount_msat_less_eq(h1->known_min,h1->known_max)); - assert(amount_msat_less_eq(h1->known_max,cap)); - assert(amount_msat_add(&sum_min1_max0,h1->known_min,h0->known_max)); - assert(amount_msat_add(&sum_min0_max1,h0->known_min,h1->known_max)); - assert(amount_msat_eq(sum_min1_max0,cap)); - assert(amount_msat_eq(sum_min0_max1,cap)); - - // check the bounds channel 4--5 - c = gossmap_find_chan(gossmap,&scid45); - cap = gossmap_chan_get_capacity(gossmap,c); - - h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); - assert(h0); - h0->known_min = AMOUNT_MSAT(1000000000); - h0->known_max = AMOUNT_MSAT(2000000000); - - h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); - assert(h1); - h1->known_min = AMOUNT_MSAT(1000000000); - h1->known_max = AMOUNT_MSAT(2000000000); - - assert(amount_msat_less_eq(h0->known_min,h0->known_max)); - assert(amount_msat_less_eq(h0->known_max,cap)); - assert(amount_msat_less_eq(h1->known_min,h1->known_max)); - assert(amount_msat_less_eq(h1->known_max,cap)); - assert(amount_msat_add(&sum_min1_max0,h1->known_min,h0->known_max)); - assert(amount_msat_add(&sum_min0_max1,h0->known_min,h1->known_max)); - assert(amount_msat_eq(sum_min1_max0,cap)); - assert(amount_msat_eq(sum_min0_max1,cap)); - - struct flow *F; - struct route *route; - struct sha256 payment_hash; - struct amount_msat deliver; - - if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102", - strlen("0001020304050607080900010203040506070809000102030405060708090102"), - &payment_hash, sizeof(payment_hash))) - abort(); - - // flow 1->2 - F = tal(this_ctx, struct flow); - F->path = tal_arr(F,const struct gossmap_chan *,1); - F->dirs = tal_arr(F,int,1); - F->path[0]=gossmap_find_chan(gossmap,&scid12); - F->dirs[0]=0; - deliver = AMOUNT_MSAT(250000000); - F->amount = deliver; - route = flow_to_route(this_ctx, 1, 1, payment_hash, 0, gossmap, F, false); - assert(route); - - assert(amount_msat_eq(route->hops[0].amount, deliver)); - assert(fabs(flow_probability(F, gossmap, chan_extra_map, true) - 0.5)4->5 - F = tal(this_ctx, struct flow); - F->path = tal_arr(F,const struct gossmap_chan *,2); - F->dirs = tal_arr(F,int,2); - F->path[0]=gossmap_find_chan(gossmap,&scid34); - F->path[1]=gossmap_find_chan(gossmap,&scid45); - F->dirs[0]=0; - F->dirs[1]=0; - deliver = AMOUNT_MSAT(250000000); - F->amount=deliver; - route = flow_to_route(this_ctx, 1, 1, payment_hash, 0, gossmap, F, false); - assert(route); - - assert(amount_msat_eq(route->hops[0].amount, amount_msat(250050016))); - assert(fabs(flow_probability(F, gossmap, chan_extra_map, true) - 1.)3->4->5 - F = tal(this_ctx, struct flow); - F->path = tal_arr(F,const struct gossmap_chan *,3); - F->dirs = tal_arr(F,int,3); - F->path[0]=gossmap_find_chan(gossmap,&scid23); - F->path[1]=gossmap_find_chan(gossmap,&scid34); - F->path[2]=gossmap_find_chan(gossmap,&scid45); - F->dirs[0]=1; - F->dirs[1]=0; - F->dirs[2]=0; - deliver = AMOUNT_MSAT(250000000); - F->amount=deliver; - route = flow_to_route(this_ctx, 1, 1, payment_hash, 0, gossmap, F, false); - assert(route); - - assert(amount_msat_eq(route->hops[0].amount, amount_msat(250087534))); - assert(fabs(flow_probability(F, gossmap, chan_extra_map, true) - 1. + 250.087534/2000)2->3->4->5 - F = tal(this_ctx, struct flow); - F->path = tal_arr(F,const struct gossmap_chan *,4); - F->dirs = tal_arr(F,int,4); - F->path[0]=gossmap_find_chan(gossmap,&scid12); - F->path[1]=gossmap_find_chan(gossmap,&scid23); - F->path[2]=gossmap_find_chan(gossmap,&scid34); - F->path[3]=gossmap_find_chan(gossmap,&scid45); - F->dirs[0]=0; - F->dirs[1]=1; - F->dirs[2]=0; - F->dirs[3]=0; - deliver = AMOUNT_MSAT(250000000); - F->amount=deliver; - route = flow_to_route(this_ctx, 1, 1, payment_hash, 0, gossmap, F, false); - assert(route); - - assert(amount_msat_eq(route->hops[0].amount, amount_msat(250112544))); - assert(fabs(flow_probability(F, gossmap, chan_extra_map, true) - 0.43728117)half[0].base_fee = basefee; - c->half[0].proportional_fee = ppm; - struct amount_msat out; - - assert(channel_maximum_forward( - &out, c, 0, in) == RENEPAY_NOERROR); - - // do we satisfy the fee constraint? - assert(check_fee_inequality(in, out, basefee, - ppm)); - - // is this the best we can do? - assert( - amount_msat_add(&out, out, amount_msat(1))); - assert(!check_fee_inequality(in, out, basefee, - ppm)); - } - } - } - tal_free(this_ctx); -} - -int main(int argc, char *argv[]) -{ - common_setup(argv[0]); - - test_edge_probability(); - test_flow_to_route(); - test_channel_maximum_forward(); - - common_shutdown(); -} - diff --git a/plugins/renepay/uncertainty.c b/plugins/renepay/uncertainty.c deleted file mode 100644 index 15ca19288aef..000000000000 --- a/plugins/renepay/uncertainty.c +++ /dev/null @@ -1,198 +0,0 @@ -#include "config.h" -#include -#include - -void uncertainty_route_success(struct uncertainty *uncertainty, - const struct route *route) -{ - if (!route->hops) - return; - - for (size_t i = 0; i < tal_count(route->hops); i++) { - const struct route_hop *hop = &route->hops[i]; - struct short_channel_id_dir scidd = {hop->scid, hop->direction}; - - // FIXME: check errors here, report back - chan_extra_sent_success(uncertainty->chan_extra_map, &scidd, - route->hops[i].amount); - } -} -void uncertainty_remove_htlcs(struct uncertainty *uncertainty, - const struct route *route) -{ - // FIXME: how could we get the route details of a sendpay that we did - // not send? - if (!route->hops) - return; - - const size_t pathlen = tal_count(route->hops); - for (size_t i = 0; i < pathlen; i++) { - const struct route_hop *hop = &route->hops[i]; - struct short_channel_id_dir scidd = {hop->scid, hop->direction}; - - // FIXME: check error - chan_extra_remove_htlc(uncertainty->chan_extra_map, &scidd, - hop->amount); - } -} - -void uncertainty_commit_htlcs(struct uncertainty *uncertainty, - const struct route *route) -{ - // FIXME: how could we get the route details of a sendpay that we did - // not send? - if (!route->hops) - return; - - const size_t pathlen = tal_count(route->hops); - for (size_t i = 0; i < pathlen; i++) { - const struct route_hop *hop = &route->hops[i]; - struct short_channel_id_dir scidd = {hop->scid, hop->direction}; - - // FIXME: check error - chan_extra_commit_htlc(uncertainty->chan_extra_map, &scidd, - hop->amount); - } -} - -void uncertainty_channel_can_send(struct uncertainty *uncertainty, - struct short_channel_id scid, int direction) -{ - struct short_channel_id_dir scidd = {scid, direction}; - // FIXME: check error - chan_extra_can_send(uncertainty->chan_extra_map, &scidd); -} -void uncertainty_channel_cannot_send(struct uncertainty *uncertainty, - struct short_channel_id scid, - int direction) -{ - struct short_channel_id_dir scidd = {scid, direction}; - // FIXME: check error - chan_extra_cannot_send(uncertainty->chan_extra_map, &scidd); -} - -int uncertainty_update(struct uncertainty *uncertainty, struct gossmap *gossmap) -{ - /* Each channel in chan_extra_map should be either in gossmap or in - * local_gossmods. */ - assert(uncertainty); - struct chan_extra_map *chan_extra_map = uncertainty->chan_extra_map; - assert(chan_extra_map); - struct chan_extra **del_list = tal_arr(NULL, struct chan_extra*, 0); - struct chan_extra_map_iter it; - for (struct chan_extra *ch = chan_extra_map_first(chan_extra_map, &it); - ch; ch = chan_extra_map_next(chan_extra_map, &it)) { - - /* If we cannot find that channel in the gossmap, add it to the - * delete list. */ - if (!gossmap_find_chan(gossmap, &ch->scid)) - tal_arr_expand(&del_list, ch); - } - for(size_t i=0;ichan_extra_map = tal(uncertainty, struct chan_extra_map); - if (uncertainty->chan_extra_map == NULL) - goto function_fail; - - chan_extra_map_init(uncertainty->chan_extra_map); - - return uncertainty; - -function_fail: - return tal_free(uncertainty); -} - -struct chan_extra_map * -uncertainty_get_chan_extra_map(struct uncertainty *uncertainty) -{ - // TODO: do we really need this function? - return uncertainty->chan_extra_map; -} - -/* Add channel to the Uncertainty Network if it doesn't already exist. */ -struct chan_extra * -uncertainty_add_channel(struct uncertainty *uncertainty, - const struct short_channel_id scid, - struct amount_msat capacity) -{ - struct chan_extra *ce = - chan_extra_map_get(uncertainty->chan_extra_map, scid); - if (ce) - return ce; - - return new_chan_extra(uncertainty->chan_extra_map, scid, capacity); -} - -bool uncertainty_set_liquidity(struct uncertainty *uncertainty, - const struct short_channel_id_dir *scidd, - struct amount_msat min, - struct amount_msat max) -{ - // FIXME check error - enum renepay_errorcode err = chan_extra_set_liquidity( - uncertainty->chan_extra_map, scidd, min, max); - - return err == RENEPAY_NOERROR; -} - -struct chan_extra *uncertainty_find_channel(struct uncertainty *uncertainty, - const struct short_channel_id scid) -{ - return chan_extra_map_get(uncertainty->chan_extra_map, scid); -} - -enum renepay_errorcode uncertainty_relax(struct uncertainty *uncertainty, - double seconds) -{ - assert(seconds >= 0); - const double fraction = MIN(seconds / TIMER_FORGET_SEC, 1.0); - struct chan_extra_map *chan_extra_map = - uncertainty_get_chan_extra_map(uncertainty); - struct chan_extra_map_iter it; - for (struct chan_extra *ce = chan_extra_map_first(chan_extra_map, &it); - ce; ce = chan_extra_map_next(chan_extra_map, &it)) { - enum renepay_errorcode err = - chan_extra_relax_fraction(ce, fraction); - - if (err) - return err; - } - return RENEPAY_NOERROR; -} diff --git a/plugins/renepay/uncertainty.h b/plugins/renepay/uncertainty.h deleted file mode 100644 index 8ae65eab6c99..000000000000 --- a/plugins/renepay/uncertainty.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_H -#define LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_H -#include "config.h" -#include -#include -#include -#include - -/* FIXME a hard coded constant to indicate a limit on any channel - capacity. Channels for which the capacity is unknown (because they are not - announced) use this value. It makes sense, because if we don't even know the - channel capacity the liquidity could be anything but it will never be greater - than the global number of msats. - It remains to be checked if this value does not lead to overflow somewhere in - the code. */ -#define MAX_CAPACITY (AMOUNT_MSAT(21000000 * MSAT_PER_BTC)) - -struct uncertainty { - struct chan_extra_map *chan_extra_map; -}; - -/* FIXME: add bool return value and WARN_UNUSED_RESULT */ -void uncertainty_route_success(struct uncertainty *uncertainty, - const struct route *route); -void uncertainty_remove_htlcs(struct uncertainty *uncertainty, - const struct route *route); - -void uncertainty_commit_htlcs(struct uncertainty *uncertainty, - const struct route *route); - -void uncertainty_channel_can_send(struct uncertainty *uncertainty, - struct short_channel_id scid, int direction); - -void uncertainty_channel_cannot_send(struct uncertainty *uncertainty, - struct short_channel_id scid, - int direction); - -WARN_UNUSED_RESULT int uncertainty_update(struct uncertainty *uncertainty, - struct gossmap *gossmap); - -struct uncertainty *uncertainty_new(const tal_t *ctx); - -struct chan_extra_map * -uncertainty_get_chan_extra_map(struct uncertainty *uncertainty); - -struct chan_extra * -uncertainty_add_channel(struct uncertainty *uncertainty, - const struct short_channel_id scid, - struct amount_msat capacity); - -bool uncertainty_set_liquidity(struct uncertainty *uncertainty, - const struct short_channel_id_dir *scidd, - struct amount_msat min, - struct amount_msat max); - -struct chan_extra *uncertainty_find_channel(struct uncertainty *uncertainty, - const struct short_channel_id scid); - -/* Adds randomness to the current state simulating the natural evolution of the - * liquidity in the network. It should be a markovian process with the minimum - * requirement that the transition operator T satisfies: - * T(t1) T(t2) = T(t1+t2) - * i.e. the transition operator is a one parameter semigroup. - * For the moment we omit the continuous and linear aspects of the problem for a - * lack for formulation. */ -enum renepay_errorcode uncertainty_relax(struct uncertainty *uncertainty, - double seconds); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_H */ diff --git a/plugins/renepay/utils.c b/plugins/renepay/utils.c new file mode 100644 index 000000000000..f5d9905ad5ef --- /dev/null +++ b/plugins/renepay/utils.c @@ -0,0 +1,106 @@ +#include "config.h" +#include +#include + +struct renepay *get_renepay(struct plugin *plugin) +{ + return plugin_get_data(plugin, struct renepay); +} + +struct rpcbatch { + size_t num_remaining; + struct command *cmd; + struct command_result *(*finalcb)(struct command *, void *); + void *arg; +}; + +struct rpcbatch_aux { + struct rpcbatch *batch; + void *arg; + struct command_result *(*cb)(struct command *cmd, const char *, + const char *, const jsmntok_t *, void *); + struct command_result *(*errcb)(struct command *cmd, const char *, + const char *, const jsmntok_t *, + void *); +}; + +struct rpcbatch * +rpcbatch_new_(struct command *cmd, + struct command_result *(*finalcb)(struct command *, void *), + void *arg) +{ + struct rpcbatch *batch = tal(cmd, struct rpcbatch); + batch->num_remaining = 0; + batch->cmd = cmd; + batch->finalcb = finalcb; + batch->arg = arg; + return batch; +} + +static struct command_result *batch_one_complete(struct rpcbatch *batch) +{ + assert(batch->num_remaining); + if (--batch->num_remaining != 0) + return command_still_pending(batch->cmd); + struct command *cmd = batch->cmd; + void *arg = batch->arg; + struct command_result *(*finalcb)(struct command *, void *) = + batch->finalcb; + tal_free(batch); + return finalcb(cmd, arg); +} + +static struct command_result * +batch_one_success(struct command *cmd, const char *method, const char *buf, + const jsmntok_t *result, struct rpcbatch_aux *aux) +{ + /* Little hack to get the value of "complete" from libplugin. */ + struct command_result *complete = command_param_failed(); + /* If this frees stuff (e.g. fails), just return */ + if (aux->cb && aux->cb(cmd, method, buf, result, aux->arg) == complete) + return complete; + struct rpcbatch *batch = aux->batch; + tal_free(aux); + return batch_one_complete(batch); +} + +static struct command_result * +batch_one_failed(struct command *cmd, const char *method, const char *buf, + const jsmntok_t *result, struct rpcbatch_aux *aux) +{ + /* Little hack to get the value of "complete" from libplugin. */ + struct command_result *complete = command_param_failed(); + /* If this frees stuff (e.g. fails), just return */ + if (aux->errcb && + aux->errcb(cmd, method, buf, result, aux->arg) == complete) + return complete; + struct rpcbatch *batch = aux->batch; + tal_free(aux); + return batch_one_complete(batch); +} + +struct out_req *add_to_rpcbatch_( + struct rpcbatch *batch, const char *cmdname, + struct command_result *(*cb)(struct command *, const char *, const char *, + const jsmntok_t *, void *arg), + struct command_result *(*errcb)(struct command *, const char *, + const char *, const jsmntok_t *, void *arg), + void *arg) +{ + batch->num_remaining++; + struct rpcbatch_aux *aux = tal(batch, struct rpcbatch_aux); + aux->arg = arg; + aux->batch = batch; + aux->cb = cb; + aux->errcb = errcb; + return jsonrpc_request_start(batch->cmd, cmdname, batch_one_success, + batch_one_failed, aux); +} + +/* Runs finalcb immediately if batch is empty. */ +struct command_result *rpcbatch_done(struct rpcbatch *batch) +{ + /* Same path as completion */ + batch->num_remaining++; + return batch_one_complete(batch); +} diff --git a/plugins/renepay/utils.h b/plugins/renepay/utils.h new file mode 100644 index 000000000000..c509058ca869 --- /dev/null +++ b/plugins/renepay/utils.h @@ -0,0 +1,59 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_UTILS_H +#define LIGHTNING_PLUGINS_RENEPAY_UTILS_H + +#include "config.h" + +struct renepay *get_renepay(struct plugin *plugin); + +struct rpcbatch; + +struct rpcbatch * +rpcbatch_new_(struct command *cmd, + struct command_result *(*finalcb)(struct command *, void *), + void *arg); + +/* Returns a new rpcbatch object. This is meant to process multiple RPC calls + * and execute a callback function after all of them have returned. + * + * @cmd: command involved in all RPC requests + * @finalcb: after all RPCs have returned this function is called + * @arg: argument passed to finalcb + * */ +#define rpcbatch_new(cmd, finalcb, arg) \ + rpcbatch_new_((cmd), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (finalcb), (arg), \ + struct command *command), \ + (arg)) + +struct out_req *add_to_rpcbatch_( + struct rpcbatch *batch, const char *cmdname, + struct command_result *(*cb)(struct command *, const char *, const char *, + const jsmntok_t *, void *arg), + struct command_result *(*errcb)(struct command *, const char *, + const char *, const jsmntok_t *, void *arg), + void *arg); + +/* Append a new RPC request to this batch. + * + * @batch: RPC request batch + * @cmdname: RPC name + * @cb: callback function on success + * @errcb: callback function on failure + * @arg: callback functions argument + * */ +#define add_to_rpcbatch(batch, cmdname, cb, errcb, arg) \ + add_to_rpcbatch_( \ + (batch), (cmdname), \ + typesafe_cb_preargs(struct command_result *, void *, (cb), (arg), \ + struct command *command, const char *method, \ + const char *buf, const jsmntok_t *result), \ + typesafe_cb_preargs(struct command_result *, void *, (errcb), \ + (arg), struct command *command, \ + const char *method, const char *buf, \ + const jsmntok_t *result), \ + (arg)) + +struct command_result *rpcbatch_done(struct rpcbatch *batch); + +#endif /* LIGHTNING_PLUGINS_RENEPAY_UTILS_H */